Предлагаю делиться здесь опытом, кто как активирует работу с внешней флешкой (уж вряд ли кто-то пользуется прямой загрузкой с неё и дефолтным режимом работы при этом?). А в перспективе, может, кто-то уже сделал подобное тому, что обсуждалось тут?
Кратко напомню суть:
Чтобы код из внешней флешки выполнялся быстро и хорошо, лучше загрузиться вначале со встроенной EEPROM, запихнув туда хотя бы некий код, который настроит работу SPIFI контроллера побыстрее, а затем сделает переход в начала основной прошивки.
В качестве основы, пообрежем как следует пример, который пришёл к нам вместе с "Mikron IDE", получив таким образом минимально необходимый загрузчик:
Рассмотрим, что у нас тут происходит:
После включения питания у нас автоматически уже активна работа встроенного RC-тактового генератора, а также тактирование от него блока SPIFI. Поскольку мы пока не используем, скажем, UART на высокой скорости, этого нам достаточно, и никаких настроек в этой части не требуется.
Следующим шагом мы делаем сброс контроллера SPIFI. Не уверен насколько это необходимо на самом деле, но в примере это было.
Дальше самое главное - нам нужно заколотить шаблон команды в регистры SPIFI, который он будет использовать каждый раз, когда ядро через него полезет читать виртуальное адресное пространство основной прошивки.
Для тех, кто совсем не в курсе, поясню:
Поскольку нельзя так просто взять и приделать сложную память с последовательной структурой к более-менее классическому процессорному ядру, то этот "костыль" как раз таки реализует SPIFI модуль. Он сам вычитывает содержимое флешки, после чего подсовывает полученные данные в общее адресное пространство начиная с адреса 0x80000000. Так что, когда ядро RISC-V лезет туда, оно думает, что там обычное классическое ПЗУ. Но для этого нам требуется немного "обучить" SPIFI читать данные, а именно, объяснить ему, какой код команды, и какой её формат ему нужно отправлять флешке. Да, это раньше всё было просто: вот шина адреса, вот шина данных. Выставил адрес - получил результат. А теперь прям каждая флешка - это целый блок со своими командами и протоколом связи! Набор команд стандартизован и в целом общий между различными флешками и производителями. Но тем не менее возможности конкретных чипов могут различаться. Если внимательно почитать микроновский даташит, можно увидеть, что при подаче питания SPIFI изначально обучен выполнять команду "Read Data Bytes" aka "код 03h". Это самая базовая и самая медленная команда чтения, в которой обмен данными идёт в один поток. Её поддерживают все без исключения флешки. Но это, наверно, не совсем то, чего нам бы хотелось?
В приведённом выше примере мы обучаем SPIFI использовать уже команду "Quad I/O Fast Read" с кодом EBh. При её использовании сам код команды пропихивается по прежнему через один канал, но адрес и данные - уже через четыре канала! Эту команду в принципе поддерживает большая часть современных чипов, но здесь уже возможны нюансы конкретной реализации! Например, в чипе W25Q64 эта команда будет работать только тогда, когда в Регистре статуса 2 выставлен бит QE! Этот самый регистр там тоже выполнен в виде флеш-памяти, так что достаточно один раз тем или иным образом скорректировать его содержимое, и больше его не трогать. Более того, даташит нам рассказывает, что в чипах с исполненем W25Q64JVSSIQ этот бит выставлен уже на заводе-изготовителе - сразу бери и пользуй команду EBh. Но вот, скажем, в исполнении W25Q64FVSSIG этот бит изначально НЕ выставлен! Или вот, для чипа EN25Q80B в даташите написано, мол, что "команда EBh в нём работает всегда и независимо от состояний битов в регистре статуса". В общем, как-то они с этим не загонялись...
Помимо этого, микроновский даташит нам вещает, что SPIFI у нас имеет некие возможности по кэшированию данных, которые он читает из флешки. Эта тема недостаточно раскрыта, но в любом случае, не вижу причин не пользоваться данной фичей. Так что включаем.
И в конце всего - немного ассемблера. Закидываем в регистр ra начало адресного пространства, где сидит наш "костыль"-эмулятор SPIFI блока, и делаем jump по адресу в регистре. Дальше в игру вступает код основной прошивки.
Кратко напомню суть:
Чтобы код из внешней флешки выполнялся быстро и хорошо, лучше загрузиться вначале со встроенной EEPROM, запихнув туда хотя бы некий код, который настроит работу SPIFI контроллера побыстрее, а затем сделает переход в начала основной прошивки.
В качестве основы, пообрежем как следует пример, который пришёл к нам вместе с "Mikron IDE", получив таким образом минимально необходимый загрузчик:
C:
//Исходник main.c для минимального bootloader-а
#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_MemoryCommandTypeDef spifi_quad_addr_memory_read;
SPIFI_MemoryModeConfig_HandleTypeDef spifi_mem_config;
spifi.Instance = SPIFI_CONFIG;
//сброс контроллера и его прерываний
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_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"
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); //забиваем настройки кэша и команду, которая будет делать чтение из флэхи
}
Рассмотрим, что у нас тут происходит:
После включения питания у нас автоматически уже активна работа встроенного RC-тактового генератора, а также тактирование от него блока SPIFI. Поскольку мы пока не используем, скажем, UART на высокой скорости, этого нам достаточно, и никаких настроек в этой части не требуется.
Следующим шагом мы делаем сброс контроллера SPIFI. Не уверен насколько это необходимо на самом деле, но в примере это было.
Дальше самое главное - нам нужно заколотить шаблон команды в регистры SPIFI, который он будет использовать каждый раз, когда ядро через него полезет читать виртуальное адресное пространство основной прошивки.
Для тех, кто совсем не в курсе, поясню:
Поскольку нельзя так просто взять и приделать сложную память с последовательной структурой к более-менее классическому процессорному ядру, то этот "костыль" как раз таки реализует SPIFI модуль. Он сам вычитывает содержимое флешки, после чего подсовывает полученные данные в общее адресное пространство начиная с адреса 0x80000000. Так что, когда ядро RISC-V лезет туда, оно думает, что там обычное классическое ПЗУ. Но для этого нам требуется немного "обучить" SPIFI читать данные, а именно, объяснить ему, какой код команды, и какой её формат ему нужно отправлять флешке. Да, это раньше всё было просто: вот шина адреса, вот шина данных. Выставил адрес - получил результат. А теперь прям каждая флешка - это целый блок со своими командами и протоколом связи! Набор команд стандартизован и в целом общий между различными флешками и производителями. Но тем не менее возможности конкретных чипов могут различаться. Если внимательно почитать микроновский даташит, можно увидеть, что при подаче питания SPIFI изначально обучен выполнять команду "Read Data Bytes" aka "код 03h". Это самая базовая и самая медленная команда чтения, в которой обмен данными идёт в один поток. Её поддерживают все без исключения флешки. Но это, наверно, не совсем то, чего нам бы хотелось?
В приведённом выше примере мы обучаем SPIFI использовать уже команду "Quad I/O Fast Read" с кодом EBh. При её использовании сам код команды пропихивается по прежнему через один канал, но адрес и данные - уже через четыре канала! Эту команду в принципе поддерживает большая часть современных чипов, но здесь уже возможны нюансы конкретной реализации! Например, в чипе W25Q64 эта команда будет работать только тогда, когда в Регистре статуса 2 выставлен бит QE! Этот самый регистр там тоже выполнен в виде флеш-памяти, так что достаточно один раз тем или иным образом скорректировать его содержимое, и больше его не трогать. Более того, даташит нам рассказывает, что в чипах с исполненем W25Q64JVSSIQ этот бит выставлен уже на заводе-изготовителе - сразу бери и пользуй команду EBh. Но вот, скажем, в исполнении W25Q64FVSSIG этот бит изначально НЕ выставлен! Или вот, для чипа EN25Q80B в даташите написано, мол, что "команда EBh в нём работает всегда и независимо от состояний битов в регистре статуса". В общем, как-то они с этим не загонялись...
Помимо этого, микроновский даташит нам вещает, что SPIFI у нас имеет некие возможности по кэшированию данных, которые он читает из флешки. Эта тема недостаточно раскрыта, но в любом случае, не вижу причин не пользоваться данной фичей. Так что включаем.
И в конце всего - немного ассемблера. Закидываем в регистр ra начало адресного пространства, где сидит наш "костыль"-эмулятор SPIFI блока, и делаем jump по адресу в регистре. Дальше в игру вступает код основной прошивки.
Последнее редактирование: