diff --git a/include/README b/include/README new file mode 100644 index 0000000..45496b1 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..8c9c29c --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..0d9b8d4 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + + +[env:mik32v2] +platform = MIK32 +board = mik32v2 +framework = framework-mik32v2-sdk +board_debug.ldscript = eeprom diff --git a/src/bootloader.c b/src/bootloader.c new file mode 100644 index 0000000..8a501c2 --- /dev/null +++ b/src/bootloader.c @@ -0,0 +1,409 @@ +#include "mik32_hal_pcc.h" +#include "mik32_hal_spifi_w25.h" + +#include "power_manager.h" +#include "uart.h" +#include "pad_config.h" +#include "mcu32_memory_map.h" + +#include "riscv_csr_encoding.h" +#include "csr.h" + +#include "string.h" + +#define JALR_TO_SPIFI() \ + asm volatile( "la ra, 0x80000000\n\t" \ + "jalr ra" \ + ); + + +#define ACK 0x0F /* Подтверждение */ +#define NACK 0xF0 /* Нет подтверждения */ +#define MAX_PACKAGE_SIZE 256 /* максимальный размер пакета */ +#define TIMEOUT_VALUE 1000000 /* Время ожидания загрузчика до прыжка по умолчанию в RAM 1000000 */ + +/* Виды команд */ +typedef enum +{ + PACKAGE_SIZE = 0x30, /* Команда размера пакета */ + SEND_PACKAGE = 0x60, /* Команда отправить пакет */ + FULL_ERASE = 0xFE /* Команда стирания spifi*/ +} BotloaderComand; + +typedef enum +{ + SPIFI_ADDRESS = 0x80000000 +} AddressMemory; + +/* Виды ошибок */ +typedef enum +{ + ERROR_NONE = 0, + ERROR_TIMEOUT = 1, // Время ожидания истекло +} Bootloader_error; + +typedef struct +{ + uint8_t* address; // Адрес для записи присылаемых байт + uint16_t size_package; // Размер пакета + uint8_t error; + uint8_t command; // Текущая принятая загрузчиком команда +} Bootloader_attributes; + +Bootloader_attributes hBootloader = {(uint8_t*) SPIFI_ADDRESS, MAX_PACKAGE_SIZE, ERROR_NONE, 0}; +uint32_t timeout = 0; + + +/* Инициализация UART */ +void Bootloader_UART_Init() +{ + PM->CLK_APB_P_SET = PM_CLOCK_APB_P_UART_0_M; // Включение тактирования UART0 + + PAD_CONFIG->PORT_0_CFG |= (0b01 << (5 << 1)) | (0b01 << (6 << 1)); // Настройка выводов PORT0.5 и PORT0.6 + + /* + * Настройки USART: + * Асинхронный режим. Включен RX и TX; + * Кадр: 8 бит данных, бит четности выключен, 1 стоп бит; + * Байт LSB - первый бит нулевой. + */ + UART_0->CONTROL1 = 0; + UART_0->CONTROL2 = 0; + UART_0->CONTROL3 = 0; + UART_0->DIVIDER = 138; /* Baudrate = 230400 */ + UART_0->FLAGS = 0xFFFFFFFF; + UART_0->CONTROL1 = UART_CONTROL1_RE_M | UART_CONTROL1_TE_M | UART_CONTROL1_UE_M; + + /* Ожидание флагов готовности RX и TX */ + while (!(UART_0->FLAGS & (UART_FLAGS_REACK_M | UART_FLAGS_TEACK_M))) + ; +} + +void Bootloader_UART_Deinit() +{ + UART_0->CONTROL1 = 0; + UART_0->CONTROL2 = 0; + UART_0->CONTROL3 = 0; + UART_0->DIVIDER = 0x0000; // сброс бодрейта + UART_0->FLAGS = 0xFFFFFFFF; // сброс всех флагов + UART_0->TXDATA = 0x00; + + PAD_CONFIG->PORT_0_CFG &= (0b00 << (5 << 1)) | (0b00 << (6 << 1)); // Настройка выводов PORT0.5 и PORT0.6 + + PM->CLK_APB_P_SET &= !PM_CLOCK_APB_P_UART_0_M; // Выключение тактирования UART0 +} + +/* Отправить байт */ +void Bootloader_UART_WriteByte(uint16_t Write_Byte) +{ + UART_0->TXDATA = Write_Byte; + /* Ожидаем успешную передачу */ + while (!(UART_0->FLAGS & UART_FLAGS_TC_M)) + ; +} + +/* Ожидание и считывание байта */ +uint16_t Bootloader_UART_ReadByte() +{ + timeout = 0; + while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) + { + timeout++; + } + + if (timeout == TIMEOUT_VALUE) + { + hBootloader.error = ERROR_TIMEOUT; + } + + return (uint16_t)UART_0->RXDATA; +} + +/* Обработчик ошибок */ +void Bootloader_ErrorHandler() +{ + + switch (hBootloader.error) + { + case ERROR_TIMEOUT: + Bootloader_UART_WriteByte(NACK); + if (UART_0->FLAGS & UART_FLAGS_ORE_M) + { + UART_0->FLAGS |= UART_FLAGS_ORE_M; + } + break; + } + + hBootloader.error = ERROR_NONE; +} + +SPIFI_HandleTypeDef spifi = {.Instance = SPIFI_CONFIG}; + +uint8_t erase_chip(SPIFI_HandleTypeDef *spifi) +{ + const uint32_t cmd_chip_erase = + SPIFI_DIRECTION_INPUT | + SPIFI_CONFIG_CMD_INTLEN(0) | + SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_SERIAL) | + SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE) | + SPIFI_CONFIG_CMD_OPCODE(0xC7); //CHIP_ERASE = 0x60 или 0xC7 + HAL_SPIFI_W25_WriteEnable(spifi); + uint8_t stat = HAL_SPIFI_SendCommand_LL(spifi, cmd_chip_erase, 0, 0, 0, 0, 0, HAL_SPIFI_TIMEOUT); + stat = HAL_SPIFI_W25_WaitBusy(spifi, 10000000); // SPIFI_W25_PROGRAM_BUSY = 100000 + return stat; +} + +/* Загрузить данные пакета в RAM */ +#define SIZE_4K 4096 +void Bootloader_LoadArrayInRam(uint8_t uart_data[]) +{ + static uint32_t relative_write_address; + + if ((relative_write_address % SIZE_4K) == 0) + HAL_SPIFI_W25_SectorErase4K(&spifi, relative_write_address); + + HAL_SPIFI_W25_PageProgram(&spifi, (uint32_t)hBootloader.address, hBootloader.size_package, uart_data); + hBootloader.address += hBootloader.size_package; + relative_write_address += hBootloader.size_package; +} + +// разметка строки в хекс-файле +#define BYTE_COUNT_POS 0 // индекс счетчика байт данных +#define ADDRES_POS 1 // индекс адреса +#define ADDRES_QTY 2 // количество байт адреса +#define RECORD_TYPE_POS 3 // индекс типа записи +#define DATA_POS 4 // индекс начала данных в команде + +// типы записей в хекс-фйле +#define REC_TYPE_DATA 0x00 +#define REC_TYPE_EOF 0x01 +#define REC_TYPE_EXT_LIN_ADDR 0x04 + +uint32_t abs_addr = 0; // адрес из хекса +uint32_t rel_addr = 0; // адрес от начала области spifi, по нему определяем, надо ли стирать сектор и какой именно +uint8_t page_data[256] = {0}; // сюда собираем распарсенные данные из хекса +uint16_t page_fill_size = 0; // счетчик, сколько заполнно в page_data. когда page_data заполнена до конца - будем записывать в spifi + +void go_to_spifi(); + +void mem_write() +{ + // если адрес дошел до начала нового сектора, стираем новый сектор + rel_addr = (uint32_t)(hBootloader.address) - 0x80000000; + if ((rel_addr % SIZE_4K) == 0) + HAL_SPIFI_W25_SectorErase4K(&spifi, rel_addr); + + // записываем страницу в 256 байт в spifi + HAL_SPIFI_W25_PageProgram(&spifi, (uint32_t)hBootloader.address, 256, page_data); + // увеличиваем адреса, по которым писать и стирать + hBootloader.address += 256; + // обнуляем буфер и счетчик заполнения буфера + page_fill_size = 0; + memset(page_data, 0, 256); +} +void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[]) +{ + // из принятых данных вытаскиваем тип записи + uint8_t rec_type = rx_data[RECORD_TYPE_POS]; + switch (rec_type) + { + case REC_TYPE_EXT_LIN_ADDR: + // если так получилось, что нам слали данные, буфер на 256 не заполнился, а тут прилетела команда смены адреса, то пишем сколько есть + if (page_fill_size != 0) + mem_write(); + // собираем адрес, с которого начинаем писать из данных команды смены адреса. нам присылают только 2 старших байта адреса + abs_addr = (rx_data[DATA_POS] << 24) + (rx_data[DATA_POS+1] << 16); + hBootloader.address = (uint8_t*)abs_addr; + break; + + case REC_TYPE_DATA: + // перекладываем из приемного буфера данные команды в буфер для записи + memcpy(&page_data[page_fill_size], &rx_data[DATA_POS], rx_data[BYTE_COUNT_POS]); + // указываем, на сколько заполнился буфер + page_fill_size += rx_data[BYTE_COUNT_POS]; + // если пора записывать целую страницу - пишемм + if (page_fill_size == 256) + mem_write(); + break; + + case REC_TYPE_EOF: // конец прошивки + Bootloader_UART_WriteByte(ACK); + // если есть недозаполненная страница, записываем ее как есть + if (page_fill_size != 0) + mem_write(); + // и идем в записанную программу + go_to_spifi(); + break; + + default: + break; + } +} +uint8_t uart_data[MAX_PACKAGE_SIZE] = {0}; // Массив данных из полученного пакета +void Bootloader_UART_ReadPackage() +{ + for (uint32_t counter = 0; counter < hBootloader.size_package; counter++) + { + timeout = 0; + while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Ожидание байта пакета + { + timeout++; + } + + if (timeout == TIMEOUT_VALUE) + { + hBootloader.error = ERROR_TIMEOUT; + break; + } + + uart_data[counter] = UART_0->RXDATA; + } + + if (!hBootloader.error) + { + Bootloader_parseHexAndLoadInMemory(uart_data); + } +} + +void Bootloader_Commands() +{ + while (1) + { + hBootloader.command = Bootloader_UART_ReadByte(); // Ожидание и считывание команды + if (hBootloader.error) + { + Bootloader_ErrorHandler(); // Обработчик ошибок + } + else + { + switch (hBootloader.command) + { + case PACKAGE_SIZE: + Bootloader_UART_WriteByte(ACK); // Подтвердить команду + hBootloader.size_package = Bootloader_UART_ReadByte() + 1; // Прочитать размер пакета + Bootloader_UART_WriteByte(ACK); // Подтвердить + + break; + case SEND_PACKAGE: + Bootloader_UART_WriteByte(ACK); // Подтвердить команду + Bootloader_UART_ReadPackage(); // Получить пакет и скопировать его в RAM + + if (hBootloader.error) + { + Bootloader_ErrorHandler(); // Обработчик ошибок + } + else + { + Bootloader_UART_WriteByte(ACK); // Подтвердить считывание и копирования пакета в RAM + } + + break; + + case FULL_ERASE: + if (erase_chip(&spifi) != HAL_OK) + Bootloader_UART_WriteByte(NACK); + else + Bootloader_UART_WriteByte(ACK); + break; + default: + break; + } + } + } +} + + +void SystemClock_Config(); + + + +int main() +{ + SystemClock_Config(); + HAL_SPIFI_MspInit(&spifi); + HAL_SPIFI_Reset(&spifi); + + + Bootloader_UART_Init(); // Инициализация UART. НАстройка выводов и тактирования + + timeout = 0; + while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Загрузчик ожидает команду + { + timeout++; + } + + if (timeout == TIMEOUT_VALUE) + { + go_to_spifi(); + } + else + { + Bootloader_Commands(); // Обработка и ожидания команд + } + + while (1) + { + /* code */ + } +} + +void SystemClock_Config(void) +{ + PCC_InitTypeDef PCC_OscInit = {0}; + + PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; + PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; + PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; + PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; + PCC_OscInit.AHBDivider = 0; + PCC_OscInit.APBMDivider = 0; + PCC_OscInit.APBPDivider = 0; + PCC_OscInit.HSI32MCalibrationValue = 128; + PCC_OscInit.LSI32KCalibrationValue = 128; + PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; + PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; + HAL_PCC_Config(&PCC_OscInit); +} +void SPIFI_disableDataCache(SPIFI_MemoryModeConfig_HandleTypeDef *spifi) +{ + spifi->Instance->CTRL |= SPIFI_CONFIG_CTRL_D_CACHE_DIS_M; +} +void SPIFI_Init() +{ + HAL_SPIFI_MspInit(&spifi); + HAL_SPIFI_Reset(&spifi); + + uint8_t sreg1 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG1); + uint8_t sreg2 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG2); + + + /*В Winbond для выставления QE используется команда 0x01 в 1-м бите 2го статус регистра. + Количество промежуточных данных в команде 4READ = 0xEB равно 3 байта (в cmd_mem)*/ + HAL_SPIFI_W25_WriteSREG(&spifi, sreg1, sreg2 | (1 << 1)); // ? HAL_SPIFI_W25_QuadEnable(&spifi); + + SPIFI_MemoryCommandTypeDef cmd_mem = { + .OpCode = 0xEB, + .FieldForm = SPIFI_CONFIG_CMD_FIELDFORM_OPCODE_SERIAL, + .FrameForm = SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR, + .InterimData = 0, + .InterimLength = 3, + }; + + SPIFI_MemoryModeConfig_HandleTypeDef spifi_mem = { + .Instance = spifi.Instance, + .CacheEnable = SPIFI_CACHE_ENABLE, + .CacheLimit = 0x00010000, + .Command = cmd_mem, + }; + + HAL_SPIFI_MemoryMode_Init(&spifi_mem); + // SPIFI_disableDataCache(&spifi_mem); +} + +void go_to_spifi() +{ + Bootloader_UART_Deinit(); + SPIFI_Init(); + write_csr(mtvec, 0x80000000); + JALR_TO_SPIFI(); +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..b0416ad --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html