Как сделать свой bootloader. Изучаем работу SPIFI

ejsanyo

Active member
У всех этих используемых микросхем есть режим XIP, который позволяет при последующей операции не отправлять повторно ту же самую команду.
Так-то, конечно, всякие "потоковые" режимы в этих чипах есть. Но, так понимаю, это разве что может ускорить прошивку/верификацию. Если это имеет смысл, поскольку, например, выше скорости UART-а всё равно не прыгнешь. Или всё же...можно как-то обучить SPIFI даже в рабочем режиме читать данные пачками (а рандомное чтение кода ядром при этом компенсирует кэш)?
 

cryptozoy

Member
Начальный загрузчик EEPROM -> SPIFI (SPI, QSPI, QPI и XIP)

Обновлённый пример начального загрузчика "HAL_EEPROM_to_SPIFI_XIP" позволяет настраивать SPIFI с внешней микросхемой флеш-памяти на максимально возможное быстродействие, причём не только вручную, но и автоматически. Теперь он умеет переводить в режим XIP и те микросхемы флеш-памяти из серии W25Qxx, которые не поддерживают режим QPI.

Скачать пример можно в репозитории сообщества на GitFlic: https://gitflic.ru/project/mik32-amur/platform_mik32_amur/

Блок SPIFI микроконтроллера К1948ВК018 позволяет уменьшить задержки чтения из микросхемы внешней памяти до предела на уровне протокола связи, и разработчикам MIK32 Амур за это особая благодарность.

Популярные микросхемы флеш-памяти с интерфейсом QSPI:

Отечественные: GSN2516Y и GSN2517Y (функциональные аналоги W25Q16JL).

Импортные: W25Q16JL, W25Q16JV, W25Q32JV, W25Q64JV, W25Q128JV, W25Q256JV, W25Q512JV, W25Q32FV, W25Q64FV, W25Q128FV, W25Q256FV, W25Q16JV-DTR, W25Q32JV-DTR, W25Q64JV-DTR, W25Q128JV-DTR, W25Q256JV-DTR, W25Q512JV-DTR, P25Q80H, P25Q16H, P25Q32H, P25Q64H, P25Q128H, EN25Q80B, GD25Q128C, PY25Q64HA, PY25Q128HA.

У всех этих микросхем имеется так называемый режим "Continuous Read Mode" или "Performance Enhance Mode" или "XIP" (eXecute-In-Place), который позволяет при каждой последующей операции не отправлять повторно ту же самую команду (код операции). Это экономит 8 тактов команды в QSPI режиме и 2 такта команды в QPI режиме. Таким образом при случайном побайтовом доступе к внешней флеш-памяти количество тактов в режиме QPI сокращается на 16,66% (с 12 до 10), а в режиме QSPI Quad Input/Output на 36,36% (с 22 до 14). Проверено на микросхемах W25Q64FV, W25Q128FV, W25Q256FV и W25Q128JV-DTR. Судя по документации, и другие модели будут работать без проблем. Для этого нужно выполнить операцию чтения любого адреса, где в промежуточных данных после него нужно отправить 0x20. Затем переключить SPIFI в режим без команды, где только 3 байта адреса в запросе плюс промежуточный байт данных 0x20. Для микросхемы EN25Q80B этот байт должен иметь исключающие друг друга полубайты, например: 0xA5, 0x5A, 0x0F, 0xF0 и т.п. Об этом написано на странице 28 в документации по EN25Q80B. У самых дешёвых микросхем время ожидания вывода данных даже на относительно низких тактовых частотах может составлять от 4 до 6 тактов (от 2 до 3 промежуточных байтов) и больше.

Документация на все эти микросхемы имеется в файлах сообщества К1948ВК018 (MIK32 Amur) ВКонтакте.

Микросхема W25Q128FV на сегодня имеет лучшее соотношение цены, объёма и быстродействия. К тому же она адресуется полностью в режиме старта со SPIFI (128 Мбит или 16 Мбайт — это использование всего диапазона 24-битного адреса SPIFI по умолчанию). Продаётся на различных торговых площадках, в том числе и в сети радиомагазинов "ЧИП и ДИП": https://www.chipdip.ru/search?searchtext=W25Q128FV

Часто встречаются более современные чипы микросхем типа W25Q128JV-DTR с названиями от старых версий. Это легко определить с помощью данного начального загрузчика. Если при старте MIK32 Амур выведет в последовательный порт, что он запустился в режиме XIP, то значит микросхема перемаркирована под старину. Все старые протоколы связи данные чипы поддерживают, поэтому китайским заводам не выгодно производить и корпусировать разные устаревшие кристаллы, а достаточно лишь сменить название на корпусе. Не верьте тому, что написано на заборе, ведь при ближайшем рассмотрении там обнаруживаются всё те же доски.

Более новая и/или подробная информация может находиться в обсуждении сообщества К1948ВК018 (MIK32 Amur) ВКонтакте
 
Последнее редактирование:

cryptozoy

Member

W25Q128JVSIM

Именно такая микросхема флеш-памяти установлена на плате ELBEAR ACE-UNO 16 МБ у Sparrow196. Я сначала подумал, что это W25Q128JV (без поддержки QPI), и лучше бы заменить её на полноценную W25Q128FV, но мой обновлённый начальный загрузчик успешно вогнал её в режим QPI да ещё и с XIP (без команд). Выходит, что это W25Q128JV-DTR. Судя по документации, маркировка с буквой М на конце только у DTR вариантов. А у этой маркировка на корпусе 25Q128JVSM. Значит можно покупать подобные версии для работы с MIK32.
 
Последнее редактирование:

cryptozoy

Member
Второе обновление начального загрузчика для отладочной платы ELBEAR ACE-UNO компании Элрон. Теперь можно выбрать режим QPI, если микросхема флеш-памяти поддерживает его. Выбирается константой CHIP_MODE: 0 = QSPI, 1 = QPI. Таким образом достигнута максимальная производительность при работе с внешней флеш-памятью типа W25Q64JV, W25Q128JV, без поддержки режима QPI, так и типа W25Q64FV, W25Q128FV или W25Q64JV-DTR, W25Q128JV-DTR, которые режим QPI поддерживают. Естественно без повторяющейся команды чтения, то есть в той же XIP-модификации. В режиме QPI используется всего один байт промежуточных данных против трёх байт в режиме QSPI. Быстрее некуда!

Забираем в отечественном репозитории Элрона: https://gitflic.ru/project/elron-tech/elbear_fw_bootloader

Не забудьте обновить программу загрузки прошивки «mik32-uploader», она умеет сбрасывать режим QPI и XIP. Предыдущие версии не смогут загружать прошивку по SPIFI, потому что данный начальный загрузчик переводит микросхему флеш-памяти в режимы QSPI-XIP или QPI-XIP. Иначе придётся аппаратно переключать начальную загрузку с EEPROM на RAM или на SPIFI.

С загрузкой прошивки через последовательный порт должно быть всё нормально. Скрипт актуальной версии скачивайте в отечественном репозитории Элрона: https://gitflic.ru/project/elron-tech/elbear_uploader

Если в QPI не запустится, смените значение константы CHIP_MODE c «1» на «0» и перепрошивайте обратно в QSPI. Если очень хочется ускориться, но не получается, то замените микросхему памяти на более функциональную. Цена вопроса не выше 200 рублей: Поискать "W25Q128FV" в сети магазинов «ЧИП и ДИП»
 
Последнее редактирование:

ejsanyo

Active member
По случаю, попробовал прикрутить флеху MX25L3233F от Macronix ёмкостью аж в 4 мегабайта! Зачем? Ну, в целом просто для разнообразия (чтоб не Winbond-ом единым :p). И это оказалась одна из тех флэх, у которых с завода всякие "четверные" режимы доступа отключены, и чтобы они всё же заработали, придётся кое-что станцевать дополнительно. Что-ж, немного модифицируем бутлодырь из шапки темы чтобы заставить её работать по максимуму:
C:
/*
 * Простой bootloader, изначально под флэху MX25L3233F
 * E.J.SanYo 2024
 *
*/
#include "mik32_hal.h"
#include "mik32_hal_spifi.h"

void SPIFI_Config();

void main()
{
    // Конфигурация кэш памяти
    // SPIFI переключаем в 4 битный режим
    SPIFI_Config();

    /* Загрузка из внешней flash по SPIFI */
    asm volatile( "la ra, 0x80000000\n\t"
                    "jalr ra"
                );

    while (1)
    {
    }
}

void SPIFI_Config()
{
    SPIFI_HandleTypeDef spifi;
    SPIFI_CommandTypeDef spifi_rdsr, spifi_rdcr, spifi_write_enable, spifi_wrsr;
    SPIFI_MemoryCommandTypeDef spifi_quad_addr_memory_read;
    SPIFI_MemoryModeConfig_HandleTypeDef spifi_mem_config;

    uint8_t Sregs[2]; //хранить результат чтения регистров

    spifi.Instance = SPIFI_CONFIG;
    HAL_SPIFI_MspInit(); //делает ноги флешечным интерфейсом

    //сброс контроллера и его прерываний
    SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M | SPIFI_CONFIG_STAT_RESET_M;

    //сбрасывает регистры на дефолтное значение
    SPIFI_CONFIG->ADDR = 0x00;
    SPIFI_CONFIG->IDATA = 0x00;
    SPIFI_CONFIG->CLIMIT = 0x00000000;

    //заполняем шаблоны команд
    spifi_rdsr.Direction = SPIFI_DIRECTION_INPUT;
    spifi_rdsr.InterimLength = 0;
    spifi_rdsr.FieldForm = SPIFI_FIELDFORM_ALL_SERIAL;
    spifi_rdsr.FrameForm = SPIFI_FRAMEFORM_OPCODE;
    spifi_rdsr.InterimData = 0;
    spifi_rdsr.OpCode = 0x05; //команда "Read Status Register"

    spifi_rdcr.Direction = SPIFI_DIRECTION_INPUT;
    spifi_rdcr.InterimLength = 0;
    spifi_rdcr.FieldForm = SPIFI_FIELDFORM_ALL_SERIAL;
    spifi_rdcr.FrameForm = SPIFI_FRAMEFORM_OPCODE;
    spifi_rdcr.InterimData = 0;
    spifi_rdcr.OpCode = 0x15; //команда "Read Configuration Register"

    spifi_write_enable.Direction = SPIFI_DIRECTION_INPUT;
    spifi_write_enable.InterimLength = 0;
    spifi_write_enable.FieldForm = SPIFI_FIELDFORM_ALL_SERIAL;
    spifi_write_enable.FrameForm = SPIFI_FRAMEFORM_OPCODE;
    spifi_write_enable.OpCode = 0x06; //команда "Write Enable"

    spifi_wrsr.Direction = SPIFI_DIRECTION_OUTPUT;
    spifi_wrsr.InterimLength = 0;
    spifi_wrsr.FieldForm = SPIFI_FIELDFORM_ALL_SERIAL;
    spifi_wrsr.FrameForm = SPIFI_FRAMEFORM_OPCODE;
    spifi_wrsr.InterimData = 0;
    spifi_wrsr.OpCode = 0x01; //команда "Write Status Register"

    spifi_quad_addr_memory_read.InterimLength = 3;
    spifi_quad_addr_memory_read.FieldForm = SPIFI_FIELDFORM_OPCODE_SERIAL;
    spifi_quad_addr_memory_read.FrameForm = SPIFI_FRAMEFORM_OPCODE_3ADDR;
    spifi_quad_addr_memory_read.InterimData = 0;
    spifi_quad_addr_memory_read.OpCode = 0xEB; //команда "Quad I/O Fast Read"

    HAL_SPIFI_SendCommand(&spifi, &spifi_rdsr, 0, 1, &Sregs[0], 0, HAL_SPIFI_TIMEOUT); //читаем регистр флэхи
    if ((Sregs[0] & 0x40) == 0) //проверяем бит QE
    {
    HAL_SPIFI_SendCommand(&spifi, &spifi_rdcr, 0, 1, &Sregs[1], 0, HAL_SPIFI_TIMEOUT); //дополнительно читаем другой регистр
    Sregs[0] |= 0x40; //выставляем бит QE
    HAL_SPIFI_SendCommand(&spifi, &spifi_write_enable, 0, 0, 0, 0, HAL_SPIFI_TIMEOUT); //открываем запись
    HAL_SPIFI_SendCommand(&spifi, &spifi_wrsr, 0, 2, 0, Sregs, HAL_SPIFI_TIMEOUT); //перезаписываем регистры
    do
    {
        HAL_SPIFI_SendCommand(&spifi, &spifi_rdsr, 0, 1, &Sregs[0], 0, HAL_SPIFI_TIMEOUT);
    }
    while (Sregs[0] & 0x01); //топчемся на месте пока не сбросится бит WIP
    }

    spifi_mem_config.CacheEnable = SPIFI_CACHE_ENABLE;
    spifi_mem_config.CacheLimit = 0x90000000; //не кэшировать всё, чей адрес выше 2,5 гигов?
    spifi_mem_config.Instance = SPIFI_CONFIG;
    spifi_mem_config.Command = spifi_quad_addr_memory_read;
    HAL_SPIFI_MemoryMode_Init(&spifi_mem_config); //забиваем настройки кэша и команду, которая будет делать чтение из флэхи
}
Как видно, мы проверяем и, если надо, выставляем бит QE в "регистре статуса" флеш-чипа. Поскольку команда записи, согласно даташиту, записывает два регистра сразу, приходится в довесок вычитывать ещё один лишний.
 
Сверху