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

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 в "регистре статуса" флеш-чипа. Поскольку команда записи, согласно даташиту, записывает два регистра сразу, приходится в довесок вычитывать ещё один лишний.
 
Вот у Вас грамотно написано - если QE установлен, то и писать ничего не надо. А в загрузчике от elron-tech вот такое вот:
Код:
    /* В Winbond для выставления QE используется команда 0x01 в 1-м бите 2го статус регистра. */
    uint8_t sreg1 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG1);
    if (sreg1 > 0x03) sreg1 = 0; // снятие защиты от записи (protection bits)
    uint8_t sreg2 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG2);
    if (!(sreg2 & 0x02)) sreg2 |= 0x02; // установка бита QE (quad enable)
    HAL_SPIFI_W25_WriteSREG(&spifi, sreg1, sreg2);
В любом случае производится запись всех "энергонезависимых" битов в двух регистрах. Так что пока стёр этот загрузчик из EEPROM :^) Может конечно "флэшак" умный и не станет перезаписывать те же самые значения. Но может и нет :)
 
Попытался прикрутить "двойной буфер" для загрузчика, пока читаем очередной буфер из UART (по DMA), пишем в SPIFI предыдущий буфер (тоже по DMA) и заодно отправляем предыдущий буфер в CRC32 (ну тут уж DMA не предусмотрено). Пока что не получилось скрестить SPIFI и DMA, даже по "методичке" из документации. Кто-нибудь использует SPIFI вместе с DMA?
 
Переделал загрузчик на передачу блоками в 4К (размер сектора флэша, ну и как раз два буфера в ОЗУ помещаются спокойно). Прошивка (.bin) размером 48384 залетает за 1.144 секунды (на моём экземпляре отладочной платы) при скорости последовательного порта 460800.
Если кому вдруг интересно:
загрузчик (bootloader, в контроллере) https://gitflic.ru/project/rabidrabbit/mik32_amur_simple/file/?file=ex_loader_2&branch=master
загрузчик (uploader, на компе) https://gitflic.ru/project/rabidrabbit/mik32_amur_simple/blob?file=tools/ex_uploader.c&branch=master
 
Кстати, добавил отключение кэширования данных при запуске прошивки из SPIFI - при не очень большом количестве константных данных на одном из примеров у меня выигрыш по скорости почти в два раза.
А в загрузчике от ELBEAR почему-то закомментировано отключение кэширования. Видимо, обстоятельства бывают разные, где-то больше пользы от включенного кэша данных, когда константных данных совсем мало. Хотя они всё равно будут вытесняться кодом. В общем, дело тёмное :)
 
Сверху