Bootloader fixes and updates

- Add canary value for bootloader entry
- Fix initial GPIO config for this application
This commit is contained in:
true
2023-11-12 16:59:53 -08:00
parent 60bd8d88b7
commit 00e0dcecf3
16 changed files with 391 additions and 161 deletions

View File

@@ -33,8 +33,13 @@ typedef enum {
extern uint32_t bootcheck;
BL_flash_status flash_erase_page(uint32_t addr);
BL_flash_status flash_write(uint32_t addr, uint8_t *data, uint16_t length);
void jump_to_user_app();

View File

@@ -64,7 +64,7 @@
//#include "hk32f030m_i2c.h"
//#include "hk32f030m_iwdg.h"
#include "hk32f030m_pwr.h"
//#include "hk32f030m_spi.h"
#include "hk32f030m_spi.h"
//#include "hk32f030m_tim.h"
#include "hk32f030m_usart.h"
//#include "hk32f030m_iwdg.h"

View File

@@ -0,0 +1,47 @@
/*
* sk_led.h
*
* Created on: Jul 17, 2023
* Author: true
*/
#ifndef CODE_INC_LED_SK_H_
#define CODE_INC_LED_SK_H_
#include <stdint.h>
#include "hk32f030m.h"
#define SK6X_PORT GPIOD
#define SK6X_PIN (1 << 4)
#define SK6X_LED_COUNT 5
#ifdef USE_SK_LEDS
void led_init();
void led_set(uint8_t idx, uint8_t r, uint8_t g, uint8_t b);
void led_set_all(uint8_t r, uint8_t g, uint8_t b);
void led_tx();
#else
#define led_init()
#define led_set(i, r, g, b)
#define led_set_all(r, g, b)
#define led_tx()
#endif
#endif /* CODE_INC_LED_SK_H_ */

View File

@@ -28,7 +28,6 @@
void user_io_init();
void user_led_init();

View File

@@ -64,12 +64,13 @@
/* Status report for the functions. */
typedef enum {
X_OK = 0x00u, /**< The action was successful. */
X_ERROR_CRC = 0x01u, /**< CRC calculation error. */
X_ERROR_NUMBER = 0x02u, /**< Packet number mismatch error. */
X_ERROR_UART = 0x04u, /**< UART communication error. */
X_ERROR_FLASH = 0x08u, /**< Flash related error. */
X_ERROR = 0xFFu /**< Generic error. */
X_OK = 0x00u, /**< The action was successful. */
X_ERROR_CRC = 0x01u, /**< CRC calculation error. */
X_ERROR_NUMBER = 0x02u, /**< Packet number mismatch error. */
X_ERROR_UART = 0x04u, /**< UART communication error. */
X_ERROR_FLASH = 0x08u, /**< Flash related error. */
X_TIMEOUT = 0xfe,
X_ERROR = 0xFFu /**< Generic error. */
} BL_xmodem_status;

View File

@@ -8,6 +8,8 @@
#include "hk32f030m.h"
#include "flash.h"
#include "led_sk.h"
/**
@@ -42,6 +44,10 @@ __attribute__ ((long_call, section(".ramfunc"))) BL_flash_status flash_write(uin
FLASH_Unlock();
// note: using the halfword programming method instead of the byte method
// uses approximately 20 more bytes of program space, and might only
// be slightly faster.
/* Loop through the data. */
for (uint32_t i = 0u; (i < length) && (status == FLASH_OK); i++) {
if (addr >= USER_APP_END_ADDR) {
@@ -72,39 +78,28 @@ void jump_to_user_app()
{
void (*user_app)(void);
// stop systick interrupt; reset to defaults
SysTick->CTRL = 0;
SysTick->LOAD = 0xffffff;
SysTick->VAL = 0xffffff;
// check reset key to know what to do
if (bootcheck == 0x1337b007) {
// configure jump location
user_app = (void (*)(void))(*(volatile uint32_t *)(USER_APP_START_ADDR + 4));
// stop USART; reset to defaults
USART1->CR1 = 0;
USART1->CR1 = 0;
USART1->BRR = 0;
// configure stack pointer
__set_MSP(*(volatile uint32_t*)USER_APP_START_ADDR);
// configure GPIO back to default
GPIOA->PUPDR = (uint32_t)0xffff; // although marked as reserved, it is the default per datasheet
GPIOA->MODER = (uint32_t)0xffff;
GPIOB->MODER = (uint32_t)0xfbff;
GPIOD->MODER = (uint32_t)0xfbff;
// configure interrupt vector table
// SCB->VTOR = USER_APP_START_ADDR;
// disable configured interrupts
// NVIC->ICER[0] = 0;
// cortex m0 doesn't have VTOR to remap vector table.
// to address this, HK MCU has a flash offset thing which
// remaps 256 bytes specified by an offset, to to 0x08000000
FLASH->INT_VEC_OFFSET = USER_APP_START_ADDR & 0x3ffc;
// configure jump location
user_app = (void (*)(void))(*(volatile uint32_t *)(USER_APP_START_ADDR + 4));
// configure stack pointer
__set_MSP(*(volatile uint32_t*)USER_APP_START_ADDR);
// configure interrupt vector table
// SCB->VTOR = USER_APP_START_ADDR;
// cortex m0 doesn't have VTOR to remap vector table.
// to address this, HK MCU has a flash offset thing which
// remaps 256 bytes specified by an offset, to to 0x08000000
FLASH->INT_VEC_OFFSET = USER_APP_START_ADDR & 0x3ffc;
// let's do this
user_app();
// let's do this
user_app();
} else {
// we want the MCU to be at default settings
// so set our flag, then reboot
bootcheck = 0x1337b007;
NVIC_SystemReset();
}
}

View File

@@ -120,13 +120,8 @@ __attribute__ ((long_call, section(".ramfunc"))) void SysTick_Handler(void)
led ^= 1;
#if LED_ACT_DIR == 0
if (led) LED_PORT->BRR = (1 << LED_PIN);
else LED_PORT->BSRR = (1 << LED_PIN);
#else
if (led) LED_PORT->BSRR = (1 << LED_PIN);
else LED_PORT->BRR = (1 << LED_PIN);
#endif
}
/******************************************************************************/

View File

@@ -0,0 +1,100 @@
/*
* sk_led.c
*
* Created on: July 17, 2023
* Author: true
*
* communicates with SK68xx LEDs using bitbang.
*
* not yet implemented.
*/
#include <led_sk.h>
#include <stdint.h>
#include "hk32f030m.h"
#define SPI_TX_PORT GPIOD
#define SPI_MOSI_PINSRC GPIO_PinSource2
#define SK_SET() SK6X_PORT->BSRR = SK6X_PIN;
#define SK_CLR() SK6X_PORT->BRR = SK6X_PIN;
#define SK_SEND_BIT1() __NOP(); __NOP(); SK_CLR(); __NOP();
#define SK_SEND_BIT0() SK_CLR(); __NOP(); __NOP(); __NOP();
uint8_t sk6x_led[SK6X_LED_COUNT][3] = {0}; // G-R-B order
const uint8_t adxl_cmds[] = {0x80, 0x80, 0x80};
#ifdef USE_SK_LEDS
void led_init()
{
uint8_t idx;
// enable SPI with appropriate settings to talk to ADXL
GPIO_PinAFConfig(SPI_TX_PORT, SPI_MOSI_PINSRC, GPIO_AF_2);
// disable ADXL interrupts, configure INT1 pin active low (so idle high)
volatile uint8_t *spi_dr = (volatile uint8_t *)((uint32_t)SPI1 + 0x0c);
// transfer all of our data
idx = 0;
while (idx < sizeof(adxl_cmds)) {
// transmit FIFO has space? if so, send byte
if ((SPI1->SR & SPI_SR_FTLVL) != SPI_SR_FTLVL) {
*spi_dr = adxl_cmds[idx];
idx++;
}
}
}
void led_tx()
{
uint8_t i;
uint16_t j;
uint8_t b;
uint8_t *p;
p = sk6x_led[0];
for (i = 0; i < SK6X_LED_COUNT; i++) {
b = *p;
p++;
j = 0x100;
do {
j >>= 1;
SK_SET();
if (!(b & j)) { SK_SEND_BIT0(); } else { SK_SEND_BIT1(); }
} while (j);
}
}
void led_set(uint8_t idx, uint8_t r, uint8_t g, uint8_t b)
{
sk6x_led[idx][0] = g;
sk6x_led[idx][1] = r;
sk6x_led[idx][2] = b;
}
void led_set_all(uint8_t r, uint8_t g, uint8_t b)
{
uint8_t i;
for (i = 0; i < SK6X_LED_COUNT; i++) {
led_set(i, r, g, b);
}
}
#endif

View File

@@ -20,15 +20,90 @@
__attribute__((section(".noinit"))) uint32_t bootcheck;
void welcome_banner()
{
uint8_t i;
comm_tx_str("\r\n\r\n");
for (i = 0; i < 31; i++) {
comm_tx_byte('=');
}
comm_tx_str("\r\ntrueControl HK32F Loader v0.0.3\r\n");
for (i = 0; i < 31; i++) {
comm_tx_byte('=');
}
}
int main(void)
{
uint32_t i;
uint32_t x;
uint32_t btn;
uint8_t flash_empty = 0;
// ensure the bootloader is write protected
// TODO
x = *(volatile uint32_t *)USER_APP_START_ADDR; // first byte of user flash
if ((x == 0x00000000) || (x == 0xffffffff)) {
flash_empty = 1;
}
// configure button and LED
// if we software reset, but have contents in flash
if (!flash_empty && (RCC->CSR & RCC_CSR_SFTRSTF)) {
// and have the bootload done key loaded,
if (bootcheck == 0x1337b007) {
// then forego all of this shit and jump to the user app
jump_to_user_app();
}
}
/*
// set with a debugger to enable this codepath (testing)
volatile uint8_t do_this = 0;
// make sure bootloader is write protected before we write anything to main flash
const uint8_t pages = (uint8_t)(OB_WRP_Pages0to3 | OB_WRP_Pages4to7);
uint8_t pagecheck = (uint8_t)(~FLASH_OB_GetWRP());
if (do_this && (pagecheck & pages) != pages) {
uint32_t flash_cr;
uint16_t wrp0;
uint32_t wrp_addr = FLASH_OB_WRP_ADDRESS;
uint16_t i;
wrp0 = (uint16_t)((pages << 8) | (~pages & 0xff));
FLASH_Unlock();
FLASH_OB_Unlock();
flash_cr = FLASH->CR;
for(i = 0; i < 2; i++) {
FLASH->CR = flash_cr | FLASH_CR_OPTER;
FLASH->AR = wrp_addr;
FLASH->CR = flash_cr | FLASH_CR_OPTER | FLASH_CR_STRT;
FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
wrp_addr++;
}
FLASH->CR = flash_cr | FLASH_CR_OPTPG;
OB->WRP0 = wrp0;
FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
FLASH->CR = flash_cr;
NVIC_SystemReset();
}
*/
// start clocks used by peripherals
RCC->AHBENR = RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | \
RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD;
RCC->APB2ENR = RCC_APB2Periph_USART1;
// set up user IO
user_io_init();
// get button state
@@ -38,41 +113,39 @@ int main(void)
#endif
// bootloader activation methods
i = *(volatile uint32_t *)USER_APP_START_ADDR; // first byte of user flash
if (btn || (i == 0x00000000) || (i = 0xffffffff)) { // is button pushed, or flash empty?
// bootloader is activated; configure systick to flash LED
user_led_init();
// configure serial comms and print welcome banner
// todo: implement autobaud?
if (btn || flash_empty) { // is button pushed, or flash empty or null?
// bootloader is activated
// this is where you can can blink an LED... at least
// when you don't fuck up your circuit so the LED doesn't work
// in our case we start up systick to do the blinker for us
SysTick_Config(8000000 / 20);
// configure serial comms
comm_init();
comm_tx_byte('\r');
comm_tx_byte('\n');
for (i = 0; i < 31; i++) {
comm_tx_byte('=');
}
comm_tx_str("\r\ntrueControl HK32F Loader v0.0.2\r\n");
for (i = 0; i < 31; i++) {
comm_tx_byte('=');
}
welcome_banner();
// wait for firmware load or reset
while(1) {
while (1) {
// announce bootloader start
comm_tx_str("\r\n\r\nsend firmware file using XMODEM\r\n\r\n");
comm_tx_str("\r\n\r\nsend firm.bin file using XMODEM\r\n\r\n");
// start listening for data and letting user know to send XMODEM data
if (xmodem_receive() == X_OK) {
comm_tx_str("\r\nflash updated; booting.\r\n\r\n");
x = xmodem_receive();
if (x == X_OK) {
comm_tx_str("\r\nfw updated; starting...\r\n\r\n");
break;
}
// if xmodem exits, there was a failure. repeat the process.
comm_tx_str("\r\nfailed to update; try again.\r\n");
// if we've spammed enough, then reboot so the banner is shown again
if (x == X_TIMEOUT) {
welcome_banner();
} else {
// if xmodem_receive exits, there was a failure. repeat the process.
comm_tx_str("\r\nfailed to update. try again.\r\n");
}
}
}

View File

@@ -44,9 +44,11 @@ void comm_init()
// enable GPIO clock
// RCC_AHBPeriphClockCmd(USART_TX_GPIO_CLK | USART_RX_GPIO_CLK, ENABLE);
// configure USART GPIO
GPIO_PinAFConfig(USART_TX_PORT, USART_TX_PINSRC, GPIO_AF_1);
GPIO_PinAFConfig(USART_RX_PORT, USART_RX_PINSRC, GPIO_AF_1);
// attach USART peripheral to pins
// GPIO_PinAFConfig(USART_TX_PORT, USART_TX_PINSRC, GPIO_AF_1);
// GPIO_PinAFConfig(USART_RX_PORT, USART_RX_PINSRC, GPIO_AF_1);
GPIOB->AFR[0] = 0x00010000;
GPIOD->AFR[0] = 0x00000010;
/* these have been configured in user_io.c to save space
USART_RX_PORT->MODER &= ~(GPIO_MODER_MODER0 << 2 * USART_RX_PIN);
@@ -59,18 +61,18 @@ void comm_init()
*/
// enable USART clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// configure USART peripheral manually
// we do this as the library bloats flash space (division, verbose, ...)
USART1->CR1 = 0; // disable USART
USART1->CR1 = USART_Mode_Rx | USART_Mode_Tx; // 8 bits, no parity, tx/rx enabled
USART1->CR2 = 0; // 1 stop bit
USART1->CR3 = 0;
USART1->CR1 = USART_Mode_Rx | USART_Mode_Tx; // 8 bits, no parity, tx/rx enabled
USART1->BRR = 0x45; // 115200 baud rate @8MHz (per datasheet)
// enable USART
USART1->CR1 |= USART_CR1_UE;
USART1->CR1 = USART_Mode_Rx | USART_Mode_Tx | USART_CR1_UE;
}
/**
@@ -86,15 +88,15 @@ BL_comm_status comm_rx(uint8_t *data, uint16_t length)
for (i = 0; i < length; i++) {
timeout = COMM_TIMEOUT;
while (!(USART1->ISR & USART_ISR_RXNE_Msk) && timeout) {
while (!(USART1->ISR & USART_ISR_RXNE) && timeout) {
timeout--;
}
if (timeout) {
// there is new data waiting for us
// we didn't time out and now there is new data waiting for us
data[i] = USART1->RDR;
} else {
// there is no data returned in time
// no data returned in time
return COMM_ERROR;
}
}
@@ -111,7 +113,7 @@ BL_comm_status comm_tx_byte(uint8_t data)
{
USART_SendData(USART1, data);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
while (!(USART1->ISR & USART_ISR_TXE));
return COMM_OK;
}
@@ -132,7 +134,7 @@ BL_comm_status comm_tx_str(char *data)
comm_tx_byte(data[i]);
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
while (!(USART1->ISR & USART_ISR_TC));
return COMM_OK;
}

View File

@@ -13,10 +13,10 @@
void user_io_init()
{
// enable gpio clocks
RCC_AHBPeriphClockCmd(BTN_CLK | LED_CLK | RCC_AHBPeriph_GPIOB, ENABLE);
/* --original method, not space efficient
// enable gpio clocks
// RCC_AHBPeriphClockCmd(BTN_CLK | LED_CLK | RCC_AHBPeriph_GPIOB, ENABLE);
// configure led
#if LED_ACT_DIR == 0
LED_PORT->BSRR = (1 << LED_PIN); // idle high
@@ -59,16 +59,15 @@ void user_io_init()
// configure LED as PP output, USART TX as AF
LED_PORT->OTYPER = 0;
LED_PORT->OSPEEDR = 0;
LED_PORT->MODER = 0x800 | (GPIO_Mode_OUT << (2 * LED_PIN)) | (GPIO_Mode_AF << (2 * 1));
LED_PORT->ODR = 0;
LED_PORT->MODER = 0x800
| (GPIO_Mode_OUT << (2 * 7))
| (GPIO_Mode_AF << (2 * 1));
// configure I2C SCK as low PP output
GPIOC->ODR = 0;
GPIOC->MODER = (GPIO_Mode_OUT << (2 * 6));
// configure USART RX as AF
GPIOB->MODER = 0x800 | (GPIO_Mode_AF << (2 * 4));
GPIOB->MODER = 0x800 | (GPIO_Mode_AF << (2 * 4));
}
void user_led_init()
{
// led blinking timer; set LED flash rate here
SysTick_Config(8000000 / 20);
}

View File

@@ -17,9 +17,10 @@
/* Global variables. */
static uint8_t xmodem_packet_number = 1u; /**< Packet number counter. */
static uint32_t xmodem_flash_w_addr = 0u; /**< Address where we have to write. */
static uint8_t x_first_packet_rcvd = 0; /**< First packet or not. */
static uint8_t xmodem_packet_number; /**< Packet number counter. */
static uint32_t xmodem_flash_w_addr; /**< Address where we have to write. */
static uint8_t x_first_packet_rcvd; /**< First packet or not. */
/* Local functions. */
static uint16_t xmodem_calc_crc(uint8_t *data, uint16_t length);
@@ -43,6 +44,8 @@ BL_xmodem_status xmodem_receive(void)
xmodem_packet_number = 1u;
xmodem_flash_w_addr = USER_APP_START_ADDR;
uint8_t x_c_sent = 0;
/* Loop until there isn't any error (or until we jump to the user application). */
while (status == X_OK) {
uint8_t header = 0x00u;
@@ -52,6 +55,13 @@ BL_xmodem_status xmodem_receive(void)
/* Spam the host (until we receive something) with ACSII "C", to notify it, we want to use CRC-16. */
if ((status_c != COMM_OK) && (x_first_packet_rcvd == 0)) {
// return with timeout if we've tried too many times
// this is so main can repeat our banner
x_c_sent++;
if (x_c_sent >= 240) {
return X_TIMEOUT;
}
comm_tx_byte(X_C);
}
@@ -101,8 +111,7 @@ BL_xmodem_status xmodem_receive(void)
/* Abort from host. */
case X_CAN: {
status = X_ERROR;
break;
return X_ERROR;
}
/* Wrong header. */