В этом файле readme раскрываются разные моменты, связанные со сменой окна компрессии zlib (слайдер "Compression Window Size" на закладке Compression раздела Load настройки свойств проекта Project Options).
Что здесь рассматривается:
1. Окно компрессии. Описываются некоторые технические детали библиотеки zlib, и почему должны соответствовать друг другу размеры кона компрессии и окна декомпрессии.
2. Как работает загрузчик. Здесь рассматривается, как загрузчик вычисляет наложение друг на друга приложения пользователя и ядра инициализации (INIT kernel), и как INIT перезаписывается по окончании процесса декомпрессии.
3. Компромисс при выборе размера окна загрузки.
4. Как перекомпилировать ADSP-BF5xx_zlib_init. Описывается процесс пересборки ADSP-BF5xx_zlib_init, чтобы можно было использовать другой, не по умолчанию размер окна компрессии-декомпрессии, и как подключить новую сборку как INIT kernel.
[1. Окно компрессии]
Поддержка компрессии образа загрузки процессоров Blackfin реализована благодаря алгоритмам сжатия библиотеки zLib [2] (в VisualDSP++ используется версия zLib 1.2.3). Дополнительную информацию по библиотеке zlib можно получить на сайте проекта zlib.net.
Чтобы понять, требуется ли пересборка кода инициализации с поддержкой библиотеки zLib, следует рассмотреть работу её алгоритма сжатия (deflate). Алгоритм deflate работает как комбинация варианта кодирования Хаффмана и компрессии LZ77. Полное описание этих алгоритмов можно найти на страничке [3], здесь будет сделан только краткий обзор.
В документе [3] написано следующее: "Компрессия LZ77 работает путем поиска повторяющихся последовательностей данных. При этом используется понятие 'скользящее окно', или 'окно компрессии'; в реальности это означает для любой точки в данных запись о том, какие символы проходили ранее. Скользящее окно размером 32K означает, что компрессор (и декомпрессора) имеет запись о том, какими были последние 32768 (32 * 1024) символов. Когда следующая последовательность сжимаемых символов идентична одной, которую можно найти в скользящем окне, то последовательность символов заменяется двумя числами: дистанцией (distance) для обозначения, как далеко начинается последовательность в скользящем окне, и длиной (length), представляющей количество символов, для которых последовательность одинакова."
Таким образом, как упомянуто выше, это 'скользящее окно' должно быть одинакового размера для компрессора и декомпрессора, потому что декомпрессор потенциально должен иметь возможность "оглянуться назад" до размера окна. Иначе если размеры окна для компрессии и декомпрессии будут отличаться, то декомпрессор будет работать с другой историей, чем у компрессора, и в результате получатся ошибки в декомпрессии.
В текущей реализации поддержки компрессии кода для Blackfin размеры окна компрессии/декомпрессии выбираются в разных местах сборки проекта, что может привести к проблеме и потребует пересборки проекта ADSP-BF5xx_zlib_init.
1. Окно компрессии выбирается слайдером "Compression Window Size" на закладке Compression в разделе Load свойств проекта (окно диалога Project Options). Этот выбор делается на этапе сборки "сжатого" проекта.
2. Однако размер окна декомпрессии определяется константой ZLIB_WSIZE в файле blkfin_zlib_init.h как часть ядра инициализации с применением библиотеки декомпрессии (zlib INIT kernel). Это было определено при сборке инструментария VisualDSP++, что может отличаться от выбора на шаге 1.
Таким образом, когда пользователь выбирает слайдером "Compression Window Size" размер окна компрессии, которое отличается от размера по умолчанию 9, проект декомпрессии zlib должен быть обновлен (изменением ZLIB_WSIZE) и заново собран, чтобы окно декомпрессии совпадало с окном компрессии.
[2. Как работает загрузчик]
Проект ADSP-BF5xx_zlib_init (код инициализации + ядро декомпрессии zlib) подключается как INIT-секция в поток загрузки LDR "сжатого" проекта приложения пользователя (для дополнительной информации по секциям INIT обратитесь к руководству VisualDSP++ Loader Reference Manual, также см. [4, 5]).
Ядро декомпрессии работает из памяти L1, окно декомпрессии может быть расположено либо в L1, либо в L3 SDRAM (см. файл .ldf проекта ADSP-BF5xx_zlib_init для получения дополнительной информации о том, где размещен код и данные этого проекта). Однако наверняка есть шанс, что сжатое приложение после распаковки должно размещаться области в памяти L1, откуда выполняется ядро распаковки. Таким образом, должна быть обеспечена возможность, что эти области памяти должны быть перезаписаны распакованным приложением пользователя.
Это то место, где загрузчик VisualDSP++ немного помогает решить проблему. Когда пользователь выбирает компрессию, то подходящий DXE-файл ADSP-BF5xx_zlib_init подключается как INIT kernel (либо по умолчанию утилитой загрузчика, либо с помощью опции командной строки -init filename). Затем утилита загрузчика проверяет карту памяти этого DXE (она определяется ldf-файлом проекта ADSP-BF5xx_zlib_init), чтобы вычислить части приложения пользователя, которые накладываются на INIT kernel. Секции с наложением остаются не сжатыми. По окончании процесса декомпрессии, ядро декомпрессии передает управление загрузкой обратно в код Boot ROM (встроенный в кристалл процессора код обработки потока загрузки), чтобы загрузка продолжилась из не запакованных секций. Эти не сжатые секции затем перезапишут области памяти L1, которые были ранее были заняты выполнением кода INIT kernel.
Таким образом важно обеспечить, чтобы LDF-файл проекта ADSP-BF5xx_zlib_init был максимально консервативен при выделении памяти, насколько это возможно. Если LDF проекта ADSP-BF5xx_zlib_init выделит больше памяти, чем это необходимо для другого кода/данных, то загрузчик вычислит, что слишком много перекрытий приложения пользователя на код INIT kernel, и поскольку перекрывающиеся секции не сжимаются, то это снижает общую эффективность сжатия для приложения пользователя.
По этим причинам, когда осуществляется сборка с новым размером окна декомпрессии, пользователь должен вычислить требования к размеру данных INIT kernel, и увеличить область code/memory в файле LDF в при необходимости.
Например, если для окна декомпрессии выбрано значение 9, что соответствует размеру окна компрессии 0x200 байт (используется по умолчанию), то приблизительные потребности в памяти следующие (все размеры указаны в байтах):
Назначение |
Размер |
Библиотека zlib (обычные данные) |
0xB24 |
Библиотека zlib (данные, инициированные нулем) |
0x0 |
Библиотека malloc (_heap_table) |
0x18 |
Библиотека malloc (данные, инициированные нулем) |
0x64 |
Данные для обработки окна декомпрессии (данные, инициированные нулем), из них 0x200 используется непосредственно под окно декомпрессии |
0x28c |
Другие структуры данных |
0x162 |
Всего |
0xF8E |
Выделения памяти по умолчанию следующие:
MEM_L1_CODE { START(0xFFA12200) END(0xFFA13FFF) TYPE(RAM) WIDTH(8) }
MEM_L1_DATA { START(0xFF807000) END(0xFF807FFF) TYPE(RAM) WIDTH(8) }
Это транслируется в приблизительно 0xFFF байт памяти для данных. Это только на 0x71 байт больше, чем требуется для хранения этих данных.
Таким образом, для нового размера окна пользователь должен выполнить подобные вычисления, чтобы достичь наиболее консервативного LDF для этого размера окна. Рекомендуется начать перераспределять память путем просмотра карты памяти линкера, или карты отображения на память (генерация файла карты памяти включается галочкой 'Generate Symbol Map' раздела настроек Link диалога свойств проекта Project Options ADSP-BF5xx_zlib_init), и после этого вычислить требования к памяти. Затем в соответствии с этими вычислениями следует уменьшить выделение памяти в файле LDF.
Хотя верно, что большее окно компрессии означает лучшее сжатие, пользователю следует иметь в виду, что чем больше будет перекрытие кодом приложения кода INIT kernel, тем меньше секций кода будет сжато, что в результате снизит общую эффективность сжатия приложения пользователя. Для дополнительной информации см. ниже "3. Компромисс при выборе размера окна загрузки.".
Размещение ядер INIT kernel в памяти кэша L1. Обратите внимание, что INIT kernel может выполнится из памяти L1, которая будет использоваться как кэш в приложении пользователя (это в случае выделения памяти в LDF по умолчанию). Запуск INIT kernel в L1, которая будет использоваться впоследствии как кэш, будет означать, что ни одна часть кода распакованного приложения не будет перекрывать INIT, так что не нужно перезаписывать INIT kernel после завершения декомпрессии, потому что память L1, используемая кодом декомпрессии, будет потом использоваться как кэш. Ни одна из распакованных секций не перезапишут код INIT kernel, что улучшает общий коэффициент сжатия приложения пользователя. Как упоминалось выше, конфигурация по умолчанию zlib INIT kernel выполняется из памяти, которая могла быть сконфигурирована как кэш (как для кода, так и для данных).
[3. Компромисс при выборе размера окна загрузки]
Как упоминалось выше, хотя чем больше окно сжатия, тем лучше коэффициент компрессии, из-за необходимости перекрытия кода INIT kernel декомпрессии кодом распакованного приложения некоторые части кода будут несжатыми. Увеличение размер окна соответственно повышает вероятность такого перекрытия, что снизит общую эффективность компрессии приложения. В общем, из-за наличия ограничений по ресурсам памяти процессора Blackfin, слишком большое окно компрессии может не увеличить, а снизить эффективность сжатия.
К примеру предположим, что показанные ниже коэффициенты сжатия относятся к обычному приложению Blackfin. Коэффициент сжатия, полученный увеличением размера окна компрессии, не линеен по отношению к изменению размера окна компрессии от 9 до 15 (размер окна от 512 байт до 32 килобайт) дает прирост компрессии примерно на ~10%.
Оригинальное приложение: 117180 байт. Размер окна 512 байт дает сжатый поток 55755 байт (47.58% от оригинального размера). Размер окна 32 килобайт дает сжатый поток 43416 байт (37.05% от оригинального размера).
Однако с другой стороны, любая часть приложения, которая перекроет в памяти области, используемые кодом zlib INIT, не могут быть сжаты (потому что они перезапишут код декомпрессии, см. выше "2. Как работает загрузчик"). Таким образом, в случае окна компрессии 32K это может добавить 32K к образу загрузки, что увеличит общий размер образа ldr в памяти flash. Обратите внимание, что если INIT kernel запускается из памяти L1, сконфигурированной под кэш в приложении пользователя, то в образе ldr не будет несжатых секций.
В соответствии с вышесказанным, когда приложение реально большое, увеличение размера окна до максимума может иметь значение, как например 10% от выигрыша сжатого кода 1 мегабайт составит 100 килобайт. Если вычесть 32 килобайта увеличенного размера окна из выигрыша, то получим результат всего лишь в 68 килобайт - отсюда можно видеть, что самое большое окно может быть полезно только для очень больших приложений.
[4. Как перекомпилировать ADSP-BF5xx_zlib_init]
Здесь объясняется, как перекомпилировать проект ADSP-BF5xx_zlib_init для нового значения ZLIB_WSIZE.
Проект ADSP-BF5xx_zlib_init (ядро инициализации с поддержкой декомпрессии zlib) подключается как секция INIT в поток загрузки LDR сжатого проекта приложения пользователя (подробнее про секции INIT см. руководство VisualDSP++ Loader Reference Manual, также см. [4, 5]).
Когда пользователь выбирает слайдером "Compression Window Size" размер окна сжатия, отличающееся от выбора по умолчанию 9, проект ADSP-BF5xx_zlib_init должен быть обновлен и пересобран с окном декомпрессии того же самого размера, какое было выбрано для компрессии. Это делается определением нового значения для ZLIB_WSIZE.
1. Обновите значение макроса ZLIB_WSIZE в заголовочном файле blkfin_zlib_init.h новым выбранным размером окна.
2. Пересоберите проект ADSP-BF5xx_zlib_init либо в конфигурации 'SDRAM', либо в конфигурации 'L1' (конфигурация 'SDRAM' использует SDRAM для распаковки приложения, т. е. окно декомпрессии размещается в SDRAM. Конфигурация 'L1' использует память L1 для декомпрессии приложения пользователя).
Возможны случаи, когда линкер VisualDSP++ будет выдавать ошибки из-за нехватки памяти 'Out of memory in output section ...'. Это означает, что были сделаны неправильные вычисления при выборе размера окна декомпрессии.
[Error li1040] "..\src\blkfin_zlib_init.ldf":173 Out of memory in output
section 'l1_data' in processor 'P0'
Total of 0xb20 word(s) were not mapped.
Причина такой ошибки в том, что LDF-файл по умолчанию выделяет "минимально достаточный" объем памяти, чтобы снизить вероятность перекрытия кодом приложения кода декомпрессии INIT kernel. Таким образом ожидается, что если выбран размер окна больше 9 (512 байт), то линкер выйдет за пределы предоставленной памяти при размещении данных. Чтобы исправить эту проблему, измените blkfin_zlib_init.ldf, чтобы добавить место для увеличенного окна декомпрессии (см. "2. Как работает загрузчик").
После необходимых изменений в файле LDF пересоберите проект ADSP-BF5xx_zlib_init, чтобы создать новый INIT dxe. Этот новый dxe должен быть подключен как ядро инициализации для сжатого приложения пользователя - либо через страницу настройки свойств загрузки Load диалога Project Options, либо через опцию командной строки -init filename.dxe утилиты загрузчика [4].