SPIFI
Контроллер SPIFI c КЭШ предназначен для организации взаимодействия микропроцессорного ядра с микросхемой внешней FLASH-памяти. Это позволяет обеспечить исполнение кода программы, записанного в микросхеме флеш-памяти, а также при необходимости чтение и запись произвольных данных во внешнюю флэш-память в процессе выполнения программы.
Контроллер SPIFI c КЭШ обеспечивает работу с микросхемами FLASH – памяти через SPI интерфейс в одном из трех режимов: одноканальный; двухканальный; четырёхканальный. По умолчанию используется одноканальный режим, двухканальный и четырёхканальный режим включается через программное обеспечение.
Микросхема внешней FLASH – памяти, подключаемая к блоку SPI Flash c КЭШ, должна иметь встроенный интерфейс SPI (Single, Dual, Quatro) и поддерживать набор команд управления, определенных в стандарте JEDEC.
Пример использования SPIFI
В данном примере используется микросхема памяти GSN2516Y (GS Nanotech) или W25Q64FV (Winbond). В микросхему памяти с помощью MIK32 загружается программа Blink. Для этого требуется получить массив байтов из hex файла firmware.hex с помощью специального скрипта mcu32flash.py, который можно скачать по ссылке.
Blink main.c
#include <mcu32_memory_map.h>
#include <pad_config.h>
#include <gpio.h>
#include <power_manager.h>
#include "common.h"
#define PIN_LED2 7 // LED2 управляется выводом PORT_2_7
#define PIN_button 6 // LED2 управляется выводом PORT_2_6
void initClock() {
PM->CLK_APB_P_SET |= PM_CLOCK_GPIO_2_M; // включение тактирования GPIO2
PM->CLK_APB_M_SET |= PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M; // включение тактирования блока для смены режима выводов
for (volatile int i = 0; i < 10; i++) ;
}
void ledBlink() {
GPIO_2->OUTPUT |= 1 << PIN_LED2; //Установка значения вывода 7 порта 2 в высокий уровень
xprintf("ON \n");
for (volatile int i = 0; i < 100000; i++);
GPIO_2->OUTPUT &= ~(1 << PIN_LED2); //Установка значения вывода 7 порта в низкий уровень
xprintf("OFF \n");
for (volatile int i = 0; i < 100000; i++);
}
void ledButton() {
if(GPIO_2->SET == (1 << PIN_button))
{
GPIO_2->OUTPUT |= 1 << PIN_LED2; //Установка значения вывода 7 порта 2 в высокий уровень
}
else
{
GPIO_2->OUTPUT &= ~(1 << PIN_LED2); //Установка значения вывода 7 порта в низкий уровень
}
}
void main() {
initClock(); //включние тактирования GPIO2
PAD_CONFIG->PORT_2_CFG |= (1 << (2 * PIN_LED2)); // Установка порта 2 в режим GPIO
PAD_CONFIG->PORT_2_CFG |= (1 << (2 * PIN_button)); // Установка порта 2 в режим GPIO
GPIO_2->DIRECTION_OUT = 1 << PIN_LED2; // Установка направления вывода в выход
GPIO_2->DIRECTION_IN = 1 << PIN_button; // Установка направления порта в вход
while (1) {
ledBlink();
//ledButton();
}
}
Запуск скрипта
Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. Сделать это можно в Visual Studio Code - рисунок 1.
После этого нажать правой кнопкой мыши по файлу и выбрать "Открыть во встроенном терминале" как на рисунке 2.
Для запуска скрипта нужно в терминале написать "python.exe .\mcu32flash.py" и нажать "Enter" - рисунок 3.
Скрипт создаст файл "array.h" в папке, в которой расположен hex файл. В "array.h" находится массив "bin_data" с байтами, которые будут загружаться в чип памяти.
Загрузка программы в микросхему памяти
Полученный файл "array.h" следует переместить в папку "src" проекта SPIFI.
Инициализация SPIFI
Инициализация контроллера SPIFI проводится с помощью функции spifi_init, которая включает тактирование контроллера SPIFI на шине AHB.
Отправка команд
Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 4.
Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее.
Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 5. В регистре состояния есть бит BUSY, который устанавливается в состояние 1, когда устройство выполняет инструкцию Page Program, Quad Page Program, Sector Erase, Block Erase, Chip Erase, Write Status Register или Erase/Program Security Register. В течение этого времени устройство будет игнорировать дальнейшие инструкции, за исключением инструкций Read Status Register и Erase/Program Suspend. Когда инструкция по программированию, стиранию или записи состояния/защиты регистра будет завершена, бит BUSY будет установлен в состояние 0, указывающее, что устройство готово к дальнейшим инструкциям. После отправки "Стирание микросхемы" программа ожидает когда бит BUSY станет раным нулю.
функция erase
void erase()
{
write_enable();
chip_erase();
wait_busy();
}
После стирания начинается запись страниц по 256 байт. Если количество байт массива bin_data не кратно 256, то в конце записывается оставшиеся байты. Запись производится с помощью функции write, которая отправляет инструкции "Включение записи", "Программирование страниц" и "Чтение регистра состояния - 1".
void write(int address, char data[], int data_len);
Функция принимает следующие аргументы: - int address - адрес, с которого начинается запись;
- char data[] - массив данных;
- int data_len - количество байт. Не может быть больше 256.
Разберем отправку команд на примере инструкции "Программирование страниц" ("Page Program"). На рисунке 4 видно, что инструкция состоит из первого байта 0x02, трех байтов адреса и не менее одного байта данных, но не более 256.
Инструкция "Программирование страниц" использует адрес, который нужно записать в регистр ADDR. С этого адреса начнется запись.
Для отправки команды следует настроить биты OPCODE, FRAMEFORM и FIELDFORM в регистре CMD.
OPCODE - код инструкции. В данном случае 0x02.
FRAMEFORM - Бит управления полями кода операции и адреса команды:
- 1 – выдается только код операции, адреса нет - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR;
- 2 – код операции и младший байт адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_1ADDR;
- 3 – код операции и два младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_2ADDR;
- 4 – код операции и три младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR;
- 5 – код операции и 4 байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_4ADDR;
- 6 – нет кода операции, три младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_NOOPCODE_3ADDR;
- 7 – нет кода операции, 4 байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_NOOPCODE_3ADDR.
FIELDFORM - Формат вывода полей команды:
- 0 – все поля выводятся в последовательном режиме - SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL;
- 1 – данные выводятся в четырех или двух битовом режиме, а остальные поля в последовательном режиме - SPIFI_CONFIG_CMD_FIELDFORM_DATA_PARALLEL;
- 2 – код операции выводится в последовательном режиме, а остальные в четырех или двух битовом - SPIFI_CONFIG_CMD_FIELDFORM_OPCODE_SERIAL;
- 3 – все поля в четырех или двух битовом режиме - SPIFI_CONFIG_CMD_FIELDFORM_ALL_PARALLEL.
DOUT - Бит направления передачи данных:
- 0 – чтение из флэш;
- 1 – запись во флэш
POLL - Бит должен быть установлен при выполнении команды, которая содержит входное поле данных и циклически запрашивает состояние бита входного потока битов из регистра статуса флэш-памяти.
DATALEN - В тех случаях, когда бит POLL равен 0, это поле определяет количество байт данных при выполнении команды. Если это поле равно 0, то выполняемая команда не содержит данных
Так как в примере используются стандартные инструкции, то FIELDFORM = 0 - SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL.
Инструкция "Программирование страниц" требует 3 байта адреса, поэтому FRAMEFORM = 4 - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR.
Направление передачи - запись. DOUT = 1.
Режим поллинга не используется. POLL = 0.
Количество байт зависит от аргумента функции byte_count. DATALEN = byte_count.
После заполнения регистра CMD каждый байт массива bin_data записывается в регистр данных DATA. . Если на микросхему памяти отправлено более 256 байт данных, адресация вернется к началу страницы и перезапишет ранее отправленные данные.
функция page_program
void page_program(unsigned int ByteAddress, char data[], int byte_count) {
if(byte_count > 256)
{
xprintf("Количество байт больше 256\n");
}
xprintf("Start page program\n");
//STAT:INTRQ
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M;
//ADDRESS
SPIFI_CONFIG->ADDR = ByteAddress;
//IDATA
SPIFI_CONFIG->IDATA = 0x00;
//CLIMIT
SPIFI_CONFIG->CLIMIT = 0x00000000;
//CMD
SPIFI_CONFIG->CMD = (PAGE_PROGRAM_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) |
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) |
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S) |
(0 << SPIFI_CONFIG_CMD_INTLEN_S) |
(1 << SPIFI_CONFIG_CMD_DOUT_S) |
(0 << SPIFI_CONFIG_CMD_POLL_S) |
(byte_count << SPIFI_CONFIG_CMD_DATALEN_S);
for(int i = ByteAddress; i < (ByteAddress + byte_count); i++)
{
SPIFI_CONFIG->DATA8 = data[i];
}
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M;
}
После записи страницы проводится чтение только что записанных байтов и сравнение с исходным массивом для исключения ошибок при записи.