Add shared emulator services, waveform generation and STM32 USB boot ports
This commit is contained in:
47
c/set-protocol/ports/stm32g474-usb-boot/README.md
Normal file
47
c/set-protocol/ports/stm32g474-usb-boot/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# STM32G474xB USB boot port
|
||||
|
||||
SET v2 USB CDC bootloader for STM32G474 with 128 KiB Flash. Uses HSI16 for
|
||||
the CPU and HSI48 with CRS USB SOF synchronization for USB; no HSE required.
|
||||
Dedicated USB pins: PA11/PA12. The board must supply VDDUSB correctly.
|
||||
|
||||
Compile the shared `src/set_protocol.c`, `src/set_firmware.c`, `src/set_boot.c`,
|
||||
this port's `boot_main.c`, `boot_flash.c`, `boot_request.c`, `boot_usb_stream.c`
|
||||
and `boot_usb_port.c`. Supply G4 CMSIS/HAL/startup, ST USB Device CDC and the
|
||||
board's CDC byte transport (`usb_cdc.h`, `usb_setp_stream.h`).
|
||||
Copy `boot_config.template.h` as `boot_config.h`. The wrapper uses the common
|
||||
`../stm32-usb-boot/boot_usb_protocol.inc`; retain that directory when importing.
|
||||
|
||||
For the shared emulator CDC transport, set `EMU_USB_SET_V2=1`,
|
||||
`USB_CDC_IRQn=USB_LP_IRQn` and optionally `USB_CDC_PRODUCT_NAME`.
|
||||
Provide `usbd_conf.h` using `stm32g4xx_hal.h`. The low-level port allocates
|
||||
non-overlapping PMA buffers for EP0, CDC IN/OUT and notifications.
|
||||
|
||||
Link boot code below `0x0800F000`, reserve `0x0800F000..0x0800FFFF` for metadata,
|
||||
and link the application at `0x08010000` with VTOR at that address.
|
||||
Application capacity is 64 KiB, ending at `0x08020000` (exclusive).
|
||||
Flash size must be 128 KiB and DBANK must be zero (single bank, 4 KiB pages).
|
||||
BFB2 and FB_MODE bank swapping must be disabled. Unsupported geometry is
|
||||
rejected before erase. On xB in dual-bank mode the upper 64 KiB starts at
|
||||
`0x08040000`, leaving a hole at the application's link address. Therefore this
|
||||
profile deliberately rejects dual-bank mode. Set DBANK=0 via SWD before the
|
||||
initial installation, then program both bootloader and application afresh.
|
||||
The port does not modify Option Bytes.
|
||||
|
||||
HAL doubleword programming plus an eight-byte staging buffer handles arbitrary
|
||||
SET block sizes without programming ECC words twice. Only the final word is
|
||||
padded with FF; CRC covers the declared image size. END checks written Flash
|
||||
and vectors, commits metadata last; ACTIVATE defers reset by 500 ms for the reply.
|
||||
|
||||
The reset policy matches F407: valid vectors alone allow SWD-installed images.
|
||||
Metadata is not required at reset, so an incomplete image with valid vectors can
|
||||
also start after an interrupted update. Erase the first application page through
|
||||
SWD to recover. No rollback, resume or signature authentication is implemented.
|
||||
|
||||
Application integration: compile `boot_request.c`, reserve `TAMP->BKP0R`, call
|
||||
`g474_request_boot(now)` for an update request and `g474_reset_poll(now)` from
|
||||
the main loop. Reset consumes and clears the backup-register request.
|
||||
|
||||
Host test `test_boot_port.c` covers the 64 KiB slot boundaries, geometry rejection, vector validation,
|
||||
every block size 1..256, all final-word tails, sequential offsets and failures.
|
||||
Compile it with this directory and `../../include` on the include path.
|
||||
Hardware USB/Flash/power-loss tests remain required on the target board.
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef G474_BOOT_CONFIG_H
|
||||
#define G474_BOOT_CONFIG_H
|
||||
#define G474_BOOT_NODE 16U
|
||||
#define G474_BOOT_MODEL "STM32G474RB-USB-BOOT"
|
||||
#endif
|
||||
109
c/set-protocol/ports/stm32g474-usb-boot/boot_flash.c
Normal file
109
c/set-protocol/ports/stm32g474-usb-boot/boot_flash.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "boot_port.h"
|
||||
#include "boot_image.h"
|
||||
#include "boot_writer.h"
|
||||
#include "stm32g4xx_hal.h"
|
||||
#include <string.h>
|
||||
#define COMMIT 0x53455442UL
|
||||
static g474_writer writer;
|
||||
static bool geometry_valid(void)
|
||||
{
|
||||
__HAL_RCC_SYSCFG_CLK_ENABLE();
|
||||
return g474_geometry_supported(*(const uint16_t *)FLASHSIZE_BASE,
|
||||
(FLASH->OPTR & FLASH_OPTR_DBANK) != 0,
|
||||
(FLASH->OPTR & FLASH_OPTR_BFB2) != 0 ||
|
||||
(SYSCFG->MEMRMP & SYSCFG_MEMRMP_FB_MODE) != 0);
|
||||
}
|
||||
static bool erase_page(uint32_t address)
|
||||
{
|
||||
FLASH_EraseInitTypeDef config = {0};
|
||||
uint32_t bank, page, error;
|
||||
if (!g474_flash_page(address, (FLASH->OPTR & FLASH_OPTR_DBANK) != 0,
|
||||
&bank, &page)) return false;
|
||||
config.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||
config.Banks = bank == 2U ? FLASH_BANK_2 : FLASH_BANK_1;
|
||||
config.Page = page; config.NbPages = 1;
|
||||
return HAL_FLASHEx_Erase(&config, &error) == HAL_OK;
|
||||
}
|
||||
static bool invalidate(void *user)
|
||||
{
|
||||
bool ok; (void)user;
|
||||
if (!geometry_valid() || HAL_FLASH_Unlock() != HAL_OK) return false;
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||
ok = erase_page(G474_META_BASE);
|
||||
(void)HAL_FLASH_Lock(); return ok;
|
||||
}
|
||||
static bool erase(void *user, uint32_t size)
|
||||
{
|
||||
uint32_t address, step;
|
||||
bool ok = true; (void)user;
|
||||
if (size < 8 || size > G474_APP_SIZE || !geometry_valid()) return false;
|
||||
g474_writer_init(&writer, size);
|
||||
if (HAL_FLASH_Unlock() != HAL_OK) return false;
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||
step = (FLASH->OPTR & FLASH_OPTR_DBANK) ? 2048U : 4096U;
|
||||
for (address = G474_APP_BASE; address < G474_APP_BASE + size; address += step) {
|
||||
if (!erase_page(address)) { ok = false; break; }
|
||||
}
|
||||
(void)HAL_FLASH_Lock(); return ok;
|
||||
}
|
||||
static bool program_word(void *user, uint32_t offset, uint64_t word)
|
||||
{
|
||||
uint64_t actual; (void)user;
|
||||
if ((offset & 7U) || offset > G474_APP_SIZE - 8U) return false;
|
||||
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, G474_APP_BASE + offset,
|
||||
word) != HAL_OK) return false;
|
||||
memcpy(&actual, (const void *)(G474_APP_BASE + offset), 8);
|
||||
return actual == word;
|
||||
}
|
||||
static bool write(void *user, uint32_t offset, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
bool ok; (void)user;
|
||||
if (HAL_FLASH_Unlock() != HAL_OK) return false;
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||
ok = g474_writer_write(&writer, offset, data, length, program_word, 0);
|
||||
(void)HAL_FLASH_Lock(); return ok;
|
||||
}
|
||||
static bool verify(void *user, uint32_t size, uint32_t crc)
|
||||
{
|
||||
(void)user;
|
||||
return !writer.failed && !writer.used && writer.received == size &&
|
||||
writer.size == size &&
|
||||
g474_vectors_valid((const uint8_t *)G474_APP_BASE, size) &&
|
||||
setp_crc32((const uint8_t *)G474_APP_BASE, size) == crc;
|
||||
}
|
||||
static bool commit(void *user, uint32_t size, uint32_t crc)
|
||||
{
|
||||
uint32_t record[4] = {size, crc, ~size, COMMIT};
|
||||
uint64_t word; unsigned i; bool ok = true; (void)user;
|
||||
if (HAL_FLASH_Unlock() != HAL_OK) return false;
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||
for (i = 0; i < sizeof(record); i += 8) {
|
||||
memcpy(&word, (const uint8_t *)record + i, 8);
|
||||
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, G474_META_BASE + i,
|
||||
word) != HAL_OK) { ok = false; break; }
|
||||
}
|
||||
(void)HAL_FLASH_Lock();
|
||||
return ok && !memcmp((const void *)G474_META_BASE, record, sizeof(record));
|
||||
}
|
||||
bool g474_app_valid(void)
|
||||
{
|
||||
/* Same reset policy as F407: SWD images need no USB commit record. */
|
||||
return geometry_valid() &&
|
||||
g474_vectors_valid((const uint8_t *)G474_APP_BASE, G474_APP_SIZE);
|
||||
}
|
||||
static void activate(void *user) { (void)user; }
|
||||
const setp_boot_ops g474_boot_ops = {invalidate, erase, write, verify, commit, activate};
|
||||
__attribute__((naked,noreturn)) static void enter(uint32_t sp, uint32_t pc)
|
||||
{
|
||||
__asm volatile("msr msp, r0\nmovs r2, #0\nmsr control, r2\ndsb\nisb\ncpsie i\nbx r1\n");
|
||||
}
|
||||
void g474_jump_app(void)
|
||||
{
|
||||
unsigned i;
|
||||
uint32_t sp = *(const uint32_t *)G474_APP_BASE;
|
||||
uint32_t pc = *(const uint32_t *)(G474_APP_BASE + 4);
|
||||
__disable_irq(); SysTick->CTRL = 0;
|
||||
for (i = 0; i < 8; ++i) { NVIC->ICER[i] = 0xffffffff; NVIC->ICPR[i] = 0xffffffff; }
|
||||
SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk | SCB_ICSR_PENDSVCLR_Msk;
|
||||
SCB->VTOR = G474_APP_BASE; enter(sp, pc);
|
||||
}
|
||||
34
c/set-protocol/ports/stm32g474-usb-boot/boot_image.h
Normal file
34
c/set-protocol/ports/stm32g474-usb-boot/boot_image.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef G474_BOOT_IMAGE_H
|
||||
#define G474_BOOT_IMAGE_H
|
||||
#include "boot_port.h"
|
||||
#include <string.h>
|
||||
/* SRAM1+SRAM2: 96 KB; CCM: 32 KB, also aliased after SRAM2. */
|
||||
static inline bool g474_vectors_valid(const uint8_t *image, uint32_t size)
|
||||
{
|
||||
uint32_t sp, pc;
|
||||
if (size < 8 || size > G474_APP_SIZE) return false;
|
||||
memcpy(&sp, image, 4); memcpy(&pc, image + 4, 4);
|
||||
return !(sp & 7U) &&
|
||||
((sp > 0x20000000UL && sp <= 0x20020000UL) ||
|
||||
(sp > 0x10000000UL && sp <= 0x10008000UL)) &&
|
||||
(pc & 1U) && (pc & ~1UL) >= G474_APP_BASE &&
|
||||
(pc & ~1UL) < G474_APP_BASE + size;
|
||||
}
|
||||
/* A contiguous xB slot at 0x08010000 exists only with DBANK=0.
|
||||
* In dual mode the upper 64 KB is at 0x08040000, not at this slot address. */
|
||||
static inline bool g474_geometry_supported(uint16_t flash_kb, bool dual, bool swapped)
|
||||
{
|
||||
return flash_kb == 128U && !dual && !swapped;
|
||||
}
|
||||
static inline bool g474_flash_page(uint32_t address, bool dual,
|
||||
uint32_t *bank, uint32_t *page)
|
||||
{
|
||||
uint32_t offset;
|
||||
if (dual || address < G474_META_BASE || address >= G474_APP_BASE + G474_APP_SIZE)
|
||||
return false;
|
||||
offset = address - 0x08000000UL;
|
||||
*bank = 1U;
|
||||
*page = offset / 4096U;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
39
c/set-protocol/ports/stm32g474-usb-boot/boot_main.c
Normal file
39
c/set-protocol/ports/stm32g474-usb-boot/boot_main.c
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "stm32g4xx_hal.h"
|
||||
#include "boot_port.h"
|
||||
#include "usb_cdc.h"
|
||||
static volatile uint32_t clock_fault;
|
||||
void SysTick_Handler(void) { HAL_IncTick(); }
|
||||
static void fatal(uint32_t code) { clock_fault = code; for (;;) {} }
|
||||
static void usb_clock_init(void)
|
||||
{
|
||||
RCC_OscInitTypeDef osc = {0};
|
||||
RCC_PeriphCLKInitTypeDef peripheral = {0};
|
||||
RCC_CRSInitTypeDef crs = {0};
|
||||
/* CPU stays on reset HSI16. USB uses HSI48 disciplined by USB SOF. */
|
||||
osc.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
|
||||
osc.HSI48State = RCC_HSI48_ON;
|
||||
if (HAL_RCC_OscConfig(&osc) != HAL_OK) fatal(2);
|
||||
peripheral.PeriphClockSelection = RCC_PERIPHCLK_USB;
|
||||
peripheral.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&peripheral) != HAL_OK) fatal(3);
|
||||
__HAL_RCC_CRS_CLK_ENABLE();
|
||||
crs.Prescaler = RCC_CRS_SYNC_DIV1;
|
||||
crs.Source = RCC_CRS_SYNC_SOURCE_USB;
|
||||
crs.Polarity = RCC_CRS_SYNC_POLARITY_RISING;
|
||||
crs.ReloadValue = __HAL_RCC_CRS_RELOADVALUE_CALCULATE(48000000U, 1000U);
|
||||
crs.ErrorLimitValue = RCC_CRS_ERRORLIMIT_DEFAULT;
|
||||
crs.HSI48CalibrationValue = RCC_CRS_HSI48CALIBRATION_DEFAULT;
|
||||
HAL_RCCEx_CRSConfig(&crs);
|
||||
}
|
||||
void boot_protocol_init(void);
|
||||
void boot_protocol_poll(uint32_t now);
|
||||
int main(void)
|
||||
{
|
||||
bool requested;
|
||||
SCB->VTOR = FLASH_BASE;
|
||||
requested = g474_boot_requested();
|
||||
if (!requested && g474_app_valid()) g474_jump_app();
|
||||
if (HAL_Init() != HAL_OK) fatal(1);
|
||||
usb_clock_init(); boot_protocol_init(); usb_cdc_init();
|
||||
for (;;) { uint32_t now = HAL_GetTick(); usb_cdc_poll(now); boot_protocol_poll(now); }
|
||||
}
|
||||
15
c/set-protocol/ports/stm32g474-usb-boot/boot_port.h
Normal file
15
c/set-protocol/ports/stm32g474-usb-boot/boot_port.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef G474_BOOT_PORT_H
|
||||
#define G474_BOOT_PORT_H
|
||||
#include "set_boot.h"
|
||||
/* STM32G474xB (128 KB), single-bank mode, normal bank mapping. */
|
||||
#define G474_APP_BASE 0x08010000UL
|
||||
#define G474_APP_SIZE 0x00010000UL
|
||||
#define G474_META_BASE 0x0800F000UL
|
||||
#define G474_BOOT_REQUEST 0x1003U
|
||||
extern const setp_boot_ops g474_boot_ops;
|
||||
bool g474_app_valid(void);
|
||||
bool g474_boot_requested(void);
|
||||
void g474_request_boot(uint32_t now);
|
||||
void g474_reset_poll(uint32_t now);
|
||||
void g474_jump_app(void);
|
||||
#endif
|
||||
26
c/set-protocol/ports/stm32g474-usb-boot/boot_request.c
Normal file
26
c/set-protocol/ports/stm32g474-usb-boot/boot_request.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "boot_port.h"
|
||||
#include "stm32g4xx_hal.h"
|
||||
#define REQUEST_MAGIC 0x42555342UL
|
||||
static uint32_t reset_at;
|
||||
static bool reset_pending;
|
||||
static void backup_access(void)
|
||||
{
|
||||
__HAL_RCC_PWR_CLK_ENABLE();
|
||||
HAL_PWR_EnableBkUpAccess();
|
||||
__HAL_RCC_RTCAPB_CLK_ENABLE();
|
||||
}
|
||||
bool g474_boot_requested(void)
|
||||
{
|
||||
bool requested;
|
||||
backup_access(); requested = TAMP->BKP0R == REQUEST_MAGIC;
|
||||
TAMP->BKP0R = 0; return requested;
|
||||
}
|
||||
void g474_request_boot(uint32_t now)
|
||||
{
|
||||
backup_access(); TAMP->BKP0R = REQUEST_MAGIC;
|
||||
reset_at = now + 500; reset_pending = true;
|
||||
}
|
||||
void g474_reset_poll(uint32_t now)
|
||||
{
|
||||
if (reset_pending && (int32_t)(now - reset_at) >= 0) NVIC_SystemReset();
|
||||
}
|
||||
94
c/set-protocol/ports/stm32g474-usb-boot/boot_usb_port.c
Normal file
94
c/set-protocol/ports/stm32g474-usb-boot/boot_usb_port.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/* STM32G474 USB FS device, dedicated PA11/PA12 pins, single-buffer PMA. */
|
||||
#include "usbd_core.h"
|
||||
#include "usbd_cdc.h"
|
||||
|
||||
static PCD_HandleTypeDef pcd;
|
||||
/* Единственный статический объект CDC вместо heap. Контракт предполагает один экземпляр класса. */
|
||||
static USBD_CDC_HandleTypeDef class_memory;
|
||||
/* Проверяет размер запроса и возвращает статический объект либо NULL. Не является универсальным malloc. */
|
||||
void *BoardUsb_Alloc(uint32_t size) { return size<=sizeof(class_memory)?&class_memory:NULL; }
|
||||
/* Освобождение не требуется: объект существует до перезапуска MCU. */
|
||||
void BoardUsb_Free(void *ptr) { (void)ptr; }
|
||||
/* Согласует enum HAL и USB: OK -> OK, BUSY -> BUSY, остальные результаты -> FAIL. */
|
||||
static USBD_StatusTypeDef status(HAL_StatusTypeDef s) {
|
||||
return s==HAL_OK?USBD_OK:s==HAL_BUSY?USBD_BUSY:USBD_FAIL;
|
||||
}
|
||||
/* Единственная точка входа USB ISR; события декодирует HAL_PCD_IRQHandler. */
|
||||
void USB_LP_IRQHandler(void) { HAL_PCD_IRQHandler(&pcd); }
|
||||
/* Передаёт 8-байтовый SETUP control-запрос стандартному обработчику USB Device. */
|
||||
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_SetupStage(h->pData,(uint8_t *)h->Setup); }
|
||||
/* OUT: хост отправил данные устройству; передаём номер endpoint и его принятый буфер. */
|
||||
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_DataOutStage(h->pData,ep,h->OUT_ep[ep].xfer_buff); }
|
||||
/* IN: завершена передача данных от устройства к хосту; уведомляем USB Device. */
|
||||
void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_DataInStage(h->pData,ep,h->IN_ep[ep].xfer_buff); }
|
||||
/* Передаёт событие начала USB-кадра; в текущей конфигурации SOF-прерывания отключены. */
|
||||
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_SOF(h->pData); }
|
||||
/* При reset шины фиксируем Full Speed и сбрасываем состояние USB Device. */
|
||||
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *h) {
|
||||
(void)USBD_LL_SetSpeed(h->pData,USBD_SPEED_FULL); (void)USBD_LL_Reset(h->pData);
|
||||
}
|
||||
/* Сообщает middleware о приостановке шины; аппаратный low-power режим здесь не включается. */
|
||||
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_Suspend(h->pData); }
|
||||
/* Сообщает middleware о возобновлении обмена после suspend. */
|
||||
void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_Resume(h->pData); }
|
||||
/* Уведомление верхнего уровня о подключении контроллера. */
|
||||
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_DevConnected(h->pData); }
|
||||
/* Уведомление верхнего уровня об отключении контроллера. */
|
||||
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_DevDisconnected(h->pData); }
|
||||
/* Переадресует незавершённую изохронную IN-передачу; обычный CDC использует bulk endpoints. */
|
||||
void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_IsoINIncomplete(h->pData,ep); }
|
||||
/* Переадресует незавершённую изохронную OUT-передачу для полноты порта PCD. */
|
||||
void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_IsoOUTIncomplete(h->pData,ep); }
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *d) {
|
||||
/* Dedicated USB pins: no GPIO alternate-function selection on G474. */
|
||||
__HAL_RCC_USB_CLK_ENABLE();
|
||||
pcd.Instance = USB; pcd.Init.dev_endpoints = 8;
|
||||
pcd.Init.speed = PCD_SPEED_FULL; pcd.Init.phy_itface = PCD_PHY_EMBEDDED;
|
||||
pcd.Init.Sof_enable = DISABLE; pcd.Init.low_power_enable = DISABLE;
|
||||
pcd.Init.lpm_enable = DISABLE; pcd.Init.battery_charging_enable = DISABLE;
|
||||
pcd.pData = d; d->pData = &pcd;
|
||||
if (HAL_PCD_Init(&pcd) != HAL_OK) return USBD_FAIL;
|
||||
/* BTABLE: 0x00..0x3f; EP0 RX/TX 64 bytes each; CDC data RX/TX 64;
|
||||
* CDC notification 16. Byte offsets are disjoint within the 1 KB PMA. */
|
||||
if (HAL_PCDEx_PMAConfig(&pcd, 0x00, PCD_SNG_BUF, 0x40) != HAL_OK ||
|
||||
HAL_PCDEx_PMAConfig(&pcd, 0x80, PCD_SNG_BUF, 0x80) != HAL_OK ||
|
||||
HAL_PCDEx_PMAConfig(&pcd, CDC_OUT_EP, PCD_SNG_BUF, 0xc0) != HAL_OK ||
|
||||
HAL_PCDEx_PMAConfig(&pcd, CDC_IN_EP, PCD_SNG_BUF, 0x100) != HAL_OK ||
|
||||
HAL_PCDEx_PMAConfig(&pcd, CDC_CMD_EP, PCD_SNG_BUF, 0x140) != HAL_OK)
|
||||
return USBD_FAIL;
|
||||
HAL_NVIC_SetPriority(USB_LP_IRQn, 7, 0); HAL_NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
return USBD_OK;
|
||||
}
|
||||
/* Запрещает USB IRQ до деинициализации контроллера, чтобы callbacks не работали с разобранным состоянием. */
|
||||
USBD_StatusTypeDef USBD_LL_DeInit(USBD_HandleTypeDef *d) {
|
||||
HAL_NVIC_DisableIRQ(USB_LP_IRQn); return status(HAL_PCD_DeInit(d->pData));
|
||||
}
|
||||
/* Включает USB device через HAL после завершения настройки класса и endpoints. */
|
||||
USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *d) { return status(HAL_PCD_Start(d->pData)); }
|
||||
/* Останавливает контроллер USB device; память состояния остаётся статической. */
|
||||
USBD_StatusTypeDef USBD_LL_Stop(USBD_HandleTypeDef *d) { return status(HAL_PCD_Stop(d->pData)); }
|
||||
/* Открывает endpoint: ep содержит номер и направление (бит 7), type — тип, mps — максимальный пакет в байтах. */
|
||||
USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *d,uint8_t ep,uint8_t type,uint16_t mps) { return status(HAL_PCD_EP_Open(d->pData,ep,mps,type)); }
|
||||
/* Закрывает endpoint по USB-адресу ep, включая бит направления. */
|
||||
USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_Close(d->pData,ep)); }
|
||||
/* Очищает FIFO указанного endpoint через HAL. */
|
||||
USBD_StatusTypeDef USBD_LL_FlushEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_Flush(d->pData,ep)); }
|
||||
/* Устанавливает STALL: endpoint отклоняет неподдерживаемую операцию хоста. */
|
||||
USBD_StatusTypeDef USBD_LL_StallEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_SetStall(d->pData,ep)); }
|
||||
/* Снимает STALL по запросу верхнего уровня USB Device. */
|
||||
USBD_StatusTypeDef USBD_LL_ClearStallEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_ClrStall(d->pData,ep)); }
|
||||
/* Применяет адрес устройства, назначенный хостом при перечислении USB. */
|
||||
USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *d,uint8_t address) { return status(HAL_PCD_SetAddress(d->pData,address)); }
|
||||
/* Запускает IN-передачу size байтов из buf. Буфер должен жить до callback завершения; копии здесь нет. */
|
||||
USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *d,uint8_t ep,uint8_t *buf,uint32_t size) { return status(HAL_PCD_EP_Transmit(d->pData,ep,buf,size)); }
|
||||
/* Взводит OUT endpoint для приёма в buf ёмкостью size байтов; завершение придёт через callback PCD. */
|
||||
USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *d,uint8_t ep,uint8_t *buf,uint32_t size) { return status(HAL_PCD_EP_Receive(d->pData,ep,buf,size)); }
|
||||
/* Читает состояние endpoint из HAL handle: bit7 выбирает IN, младшие 7 бит — номер. */
|
||||
uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *d,uint8_t ep) {
|
||||
PCD_HandleTypeDef *h=d->pData; return ep&0x80?h->IN_ep[ep&0x7F].is_stall:h->OUT_ep[ep].is_stall;
|
||||
}
|
||||
/* Возвращает фактическое число принятых байтов последней OUT-передачи. */
|
||||
uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *d,uint8_t ep) { return HAL_PCD_EP_GetRxCount(d->pData,ep); }
|
||||
/* Блокирующая задержка ST middleware в миллисекундах HAL; требует работающего SysTick. */
|
||||
void USBD_LL_Delay(uint32_t ms) { HAL_Delay(ms); }
|
||||
10
c/set-protocol/ports/stm32g474-usb-boot/boot_usb_stream.c
Normal file
10
c/set-protocol/ports/stm32g474-usb-boot/boot_usb_stream.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "stm32g4xx_hal.h"
|
||||
#include "boot_port.h"
|
||||
#include "boot_config.h"
|
||||
#define BOOT_OPS g474_boot_ops
|
||||
#define BOOT_APP_BASE G474_APP_BASE
|
||||
#define BOOT_APP_SIZE G474_APP_SIZE
|
||||
#define BOOT_NODE G474_BOOT_NODE
|
||||
#define BOOT_MODEL G474_BOOT_MODEL
|
||||
#define BOOT_RESET() NVIC_SystemReset()
|
||||
#include "../stm32-usb-boot/boot_usb_protocol.inc"
|
||||
37
c/set-protocol/ports/stm32g474-usb-boot/boot_writer.h
Normal file
37
c/set-protocol/ports/stm32g474-usb-boot/boot_writer.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef G474_BOOT_WRITER_H
|
||||
#define G474_BOOT_WRITER_H
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
/* ECC Flash must be programmed once per 64-bit word. Keep partial words in RAM
|
||||
* across SET packets; pad only the final word, without changing the image CRC. */
|
||||
typedef bool (*g474_program_word)(void *user, uint32_t offset, uint64_t word);
|
||||
typedef struct {
|
||||
uint32_t size, received;
|
||||
uint8_t pending[8];
|
||||
uint8_t used;
|
||||
bool failed;
|
||||
} g474_writer;
|
||||
static inline void g474_writer_init(g474_writer *w, uint32_t size)
|
||||
{
|
||||
memset(w, 0, sizeof(*w)); w->size = size;
|
||||
memset(w->pending, 0xff, sizeof(w->pending));
|
||||
}
|
||||
static inline bool g474_writer_write(g474_writer *w, uint32_t offset,
|
||||
const uint8_t *data, uint16_t length, g474_program_word program, void *user)
|
||||
{
|
||||
if (w->failed || !length || offset != w->received ||
|
||||
offset > w->size || length > w->size - offset) return false;
|
||||
while (length--) {
|
||||
w->pending[w->used++] = *data++; ++w->received;
|
||||
if (w->used == 8 || w->received == w->size) {
|
||||
uint64_t word;
|
||||
uint32_t start = w->received - w->used;
|
||||
memcpy(&word, w->pending, sizeof(word));
|
||||
if (!program(user, start, word)) { w->failed = true; return false; }
|
||||
w->used = 0; memset(w->pending, 0xff, sizeof(w->pending));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
97
c/set-protocol/ports/stm32g474-usb-boot/test_boot_port.c
Normal file
97
c/set-protocol/ports/stm32g474-usb-boot/test_boot_port.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "boot_image.h"
|
||||
#include "boot_writer.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
static uint8_t flash[1024];
|
||||
static bool touched[128];
|
||||
static bool fail_write;
|
||||
static bool program(void *user, uint32_t offset, uint64_t word)
|
||||
{
|
||||
(void)user;
|
||||
assert(!(offset & 7U)); assert(offset <= sizeof(flash) - 8);
|
||||
assert(!touched[offset / 8]);
|
||||
if (fail_write) return false;
|
||||
touched[offset / 8] = true;
|
||||
memcpy(flash + offset, &word, 8); return true;
|
||||
}
|
||||
static void test_writer(void)
|
||||
{
|
||||
uint8_t image[521];
|
||||
uint32_t block, size, i;
|
||||
for (i = 0; i < sizeof(image); ++i) image[i] = (uint8_t)(i * 19U);
|
||||
/* Every block size, all possible final-word tails, including all-FF words. */
|
||||
memset(image + 128, 0xff, 32);
|
||||
for (block = 1; block <= 256; ++block) {
|
||||
for (size = 512; size <= sizeof(image); ++size) {
|
||||
g474_writer w;
|
||||
memset(flash, 0xff, sizeof(flash)); memset(touched, 0, sizeof(touched));
|
||||
g474_writer_init(&w, size);
|
||||
for (i = 0; i < size;) {
|
||||
uint16_t n = (uint16_t)(size - i < block ? size - i : block);
|
||||
assert(g474_writer_write(&w, i, image + i, n, program, 0)); i += n;
|
||||
}
|
||||
assert(!w.failed && !w.used && w.received == size);
|
||||
assert(!memcmp(flash, image, size));
|
||||
for (i = size; i < sizeof(flash); ++i) assert(flash[i] == 0xff);
|
||||
assert(!g474_writer_write(&w, size, image, 1, program, 0));
|
||||
}
|
||||
}
|
||||
{
|
||||
g474_writer w;
|
||||
g474_writer_init(&w, 9);
|
||||
assert(!g474_writer_write(&w, 1, image, 1, program, 0));
|
||||
assert(!g474_writer_write(&w, 0, image, 10, program, 0));
|
||||
assert(!g474_writer_write(&w, 0, image, 0, program, 0));
|
||||
assert(w.received == 0);
|
||||
memset(touched, 0, sizeof(touched)); fail_write = true;
|
||||
assert(!g474_writer_write(&w, 0, image, 8, program, 0));
|
||||
assert(w.failed);
|
||||
fail_write = false;
|
||||
assert(!g474_writer_write(&w, w.received, image, 1, program, 0));
|
||||
g474_writer_init(&w, 9);
|
||||
assert(g474_writer_write(&w, 0, image, 9, program, 0));
|
||||
}
|
||||
}
|
||||
static void check_page(uint32_t address, bool dual, uint32_t bank, uint32_t page)
|
||||
{
|
||||
uint32_t b, p;
|
||||
assert(g474_flash_page(address, dual, &b, &p));
|
||||
assert(b == bank && p == page);
|
||||
}
|
||||
int main(void)
|
||||
{
|
||||
uint32_t vectors[2] = {0x20018000UL, G474_APP_BASE + 9};
|
||||
uint32_t b, p;
|
||||
const uint8_t *image = (const uint8_t *)vectors;
|
||||
assert(g474_geometry_supported(128, false, false));
|
||||
assert(!g474_geometry_supported(128, true, false));
|
||||
assert(!g474_geometry_supported(128, false, true));
|
||||
assert(!g474_geometry_supported(512, false, false));
|
||||
assert(!g474_geometry_supported(256, false, false));
|
||||
assert(!g474_geometry_supported(0xffff, false, false));
|
||||
check_page(G474_META_BASE, false, 1, 15);
|
||||
check_page(G474_APP_BASE, false, 1, 16);
|
||||
check_page(0x0801ffffUL, false, 1, 31);
|
||||
assert(!g474_flash_page(G474_META_BASE - 1, false, &b, &p));
|
||||
assert(!g474_flash_page(0x08020000UL, false, &b, &p));
|
||||
assert(!g474_flash_page(0x08040000UL, false, &b, &p));
|
||||
assert(!g474_flash_page(G474_META_BASE, true, &b, &p));
|
||||
assert(!g474_flash_page(G474_APP_BASE, true, &b, &p));
|
||||
assert(g474_vectors_valid(image, G474_APP_SIZE));
|
||||
vectors[0] = 0x10008000UL; assert(g474_vectors_valid(image, 16));
|
||||
vectors[0] = 0x20020000UL; assert(g474_vectors_valid(image, 16));
|
||||
vectors[0] = 0x20020008UL; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[0] = 0x10008008UL; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[0] = 0x20000000UL; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[0] = 0x20017fffUL; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[0] = 0x20018000UL;
|
||||
vectors[1] = G474_APP_BASE + 8; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[1] = G474_APP_BASE - 1; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[1] = G474_APP_BASE + 17; assert(!g474_vectors_valid(image, 16));
|
||||
vectors[1] = G474_APP_BASE + 9;
|
||||
assert(!g474_vectors_valid(image, 7));
|
||||
assert(!g474_vectors_valid(image, G474_APP_SIZE + 1));
|
||||
test_writer();
|
||||
puts("G474: bank/page boundaries, vectors, all block sizes and ECC word tails passed");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user