Commit 00da9d13 authored by Richard Miller's avatar Richard Miller

Add iceboot STM32 program to program the ICE40, first looking for

a valid bitstream at end of flash (offset 0x080Df000), and then
reading further bitstreams from uart1.
parent 46301628
/*
* Configure the ICE40 with new bitstreams:
* - first from flash at offset FLASH_ICE40_START (if one exists there)
* - then repeatedly from uart1
*/
#include "main.h"
#include "stm32l4xx_hal.h"
#include "errno.h"
enum { FLASH_ICE40_START = 0x080DF000, FLASH_ICE40_END = 0x08100000 };
enum { OK, TIMEOUT, ICE_ERROR };
enum { UART_FIFOSIZE = 128 }; /* must be power of 2 */
typedef int (*Reader)(uint8_t*, uint16_t*);
#define gpio_low(pin) HAL_GPIO_WritePin(pin##_GPIO_Port, pin##_Pin, GPIO_PIN_RESET)
#define gpio_high(pin) HAL_GPIO_WritePin(pin##_GPIO_Port, pin##_Pin, GPIO_PIN_SET)
#define gpio_ishigh(pin) (HAL_GPIO_ReadPin(pin##_GPIO_Port, pin##_Pin) == GPIO_PIN_SET)
#define gpio_toggle(pin) HAL_GPIO_TogglePin(pin##_GPIO_Port, pin##_Pin)
extern UART_HandleTypeDef huart1;
extern SPI_HandleTypeDef hspi3;
static uint16_t crc;
static uint8_t *memp, *endmem;
static uint8_t uartbyte;
/*
* Dummy memory allocator for newlib, so we can call snprintf
*/
caddr_t
_sbrk(int incr)
{
errno = ENOMEM;
return (caddr_t) -1;
}
/*
* Readahead fifo for uart
*/
static struct fifo {
int head, tail, max;
uint8_t buf[UART_FIFOSIZE];
} uart_fifo;
static int
fifo_put(struct fifo *f, int c)
{
int tl;
int count;
tl = f->tail;
count = tl - f->head;
if (count < 0)
count += UART_FIFOSIZE;
if (count > f->max)
f->max = count;
if (count == UART_FIFOSIZE - 1)
return -1;
f->buf[tl++] = c;
f->tail = tl & (UART_FIFOSIZE-1);
return OK;
}
static int
fifo_get(struct fifo *f, uint8_t *b)
{
int hd;
int count;
hd = f->head;
count = f->tail - hd;
if (count == 0)
return -1;
*b = f->buf[hd++];
f->head = hd & (UART_FIFOSIZE-1);
return OK;
}
/*
* Write a string to uart1, adding an extra CR if it ends with LF
*/
static void
uart_puts(char *s)
{
char *p;
static unsigned char cr = '\r';
for (p = s; *p; p++)
;
HAL_UART_Transmit(&huart1, (unsigned char*)s, p - s, 500);
if (p > s && p[-1] == '\n')
HAL_UART_Transmit(&huart1, &cr, 1, 1);
}
/*
* Read one byte from uart1 or fifo, waiting until one is available
*/
static int
uart_getc(uint8_t *b)
{
while (uart_fifo.head == uart_fifo.tail)
;
return fifo_get(&uart_fifo, b);
}
/*
* Read one byte from uart1, returning TIMEOUT immediately if one is not available
*/
static int
uart_trygetc(uint8_t *b)
{
if (HAL_UART_Receive(&huart1, b, 1, 0) != HAL_OK)
return TIMEOUT;
return OK;
}
/*
* Enable reading from uart1 in interrupt mode
*/
static void
uart_startread(void)
{
int err;
err = HAL_UART_Receive_IT(&huart1, &uartbyte, 1);
if (err != OK)
uart_puts("HAL_UART_Receive_IT failed!\n");
}
/*
* Interrupt callback when a byte has been read from uart1
*/
void
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
fifo_put(&uart_fifo, uartbyte);
uart_startread();
}
/*
* Interrupt callback on uart error (probably overrun)
*/
void
HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
uart_puts("UART Error!\n");
}
/*
* Delay (spin loop) for n msec
*/
static void
msec_delay(int n)
{
HAL_Delay(n);
}
/*
* Write to spi3 in multiple chunks (HAL_SPI_Transmit length is limited to 16 bits)
*/
static int
spi_write(uint8_t *p, uint32_t len)
{
int ret;
uint16_t n;
ret = HAL_OK;
n = 0x8000;
while (len > 0) {
if (len < n)
n = len;
ret = HAL_SPI_Transmit(&hspi3, p, n, HAL_MAX_DELAY);
if (ret != HAL_OK)
return ret;
len -= n;
p += n;
}
return ret;
}
/*
* Reset the ICE40 while holding SPI_SS_B low to force a spi-slave configuration
*/
static int
ice40_reset(void)
{
int timeout;
gpio_low(ICE40_CRST);
gpio_low(ICE40_SPI_CS);
msec_delay(1);
gpio_high(ICE40_CRST);
timeout = 100;
while (gpio_ishigh(ICE40_CDONE)) {
if (--timeout == 0)
return TIMEOUT;
}
msec_delay(2);
return OK;
}
/*
* Wait for end of ICE40 configuration
*/
static int
ice40_configdone(void)
{
uint8_t b = 0;
for (int timeout = 100; !gpio_ishigh(ICE40_CDONE); timeout--) {
if (timeout == 0) {
uart_puts("CDONE not set\n");
return ICE_ERROR;
}
spi_write(&b, 1);
}
for (int i = 0; i < 7; i++)
spi_write(&b, 1);
uart_puts("Config done\n");
return OK;
}
/*
* Update bitstream checksum
*/
static void
crc_update(uint8_t b)
{
int c, v;
v = b << 8;
c = crc;
for (int i = 0; i < 8; i++) {
int x = 0;
if ((c ^ v) & 0x8000)
x = 0x1021;
c = (c << 1) ^ x;
v <<= 1;
}
crc = c;
}
/*
* Restart bitstream checksum
*/
static void
crc_reset(void)
{
crc = 0xFFFF;
}
/*
* Read a byte from uart, update checksum, and send it to spi3
*/
static int
rbyte_uart_send(uint8_t *b, uint16_t *crc)
{
if (uart_getc(b) != OK)
return -1;
spi_write(b, 1);
crc_update(*b);
return OK;
}
/*
* Read a byte from flash or RAM, and update checksum
*/
static int
rbyte_mem_check(uint8_t *b, uint16_t *crc)
{
if (memp >= endmem)
return -1;
*b = *memp++;
crc_update(*b);
return OK;
}
/*
* Read a byte from flash or RAM, update checksum, and send it to spi3
*/
static int
rbyte_mem_send(uint8_t *b, uint16_t *crc)
{
if (memp >= endmem)
return -1;
*b = *memp++;
spi_write(b, 1);
crc_update(*b);
return OK;
}
/*
* Read n bytes using the given reader
*/
static int
rbytes(Reader rbyte, int n, uint8_t *b, uint16_t *crc)
{
while (n-- > 0) {
if (rbyte(b, crc) < 0)
return -1;
}
return 0;
}
/*
* Read and parse a bitstream using the given reader
*/
static int
rbits(Reader rbyte, int firstb)
{
int preamble;
int crc_checked = 0;
uint8_t b;
int cmd, len, arg;
int width = 0, height = 0;
/* find the synchronising marker */
preamble = firstb;
while (preamble != 0x7EAA997E) {
if (rbyte(&b, &crc) < 0)
return -1;
preamble <<= 8;
preamble |= b;
}
/* parse the bitstream to find crc reset+check commands */
while (rbyte(&b, &crc) == 0) {
cmd = b >> 4;
len = b & 0xF;
arg = 0;
while (len-- > 0) {
if (rbyte(&b, &crc) < 0)
return -1;
arg <<= 8;
arg |= b;
}
switch (cmd) {
default: /* unknown */
return -1;
case 1: /* current bank */
case 5: /* frequency range */
case 8: /* offset of section */
case 9: /* warm boot */
break;
case 2: /* check crc */
if (crc != 0) {
return -1;
}
break;
case 6: /* width of section */
width = arg + 1;
break;
case 7: /* height of section */
height = arg;
break;
case 0:
switch (arg) {
default: /* unknown */
return -1;
case 1: /* CRAM data */
case 3: /* BRAM data */
if (rbytes(rbyte, height*width/8, &b, &crc) < 0)
return -1;
if (rbyte(&b, &crc) < 0 || b != 0)
return -1;
if (rbyte(&b, &crc) < 0 || b != 0)
return -1;
break;
case 5: /* crc reset */
crc_reset();
break;
case 6: /* wakeup */
if (!crc_checked)
return -1;
return OK;
}
}
crc_checked = (cmd == 2);
}
return -1;
}
/*
* Setup function (called once at powerup)
* - flush any input in uart buffer
* - if there's a bitstream in flash, send it to the ice40
*/
void
setup(void)
{
uint8_t b;
gpio_high(LED5);
memp = (uint8_t*)FLASH_ICE40_START;
endmem = (uint8_t*)FLASH_ICE40_END;
crc_reset();
if (rbits(rbyte_mem_check, 0) == OK) {
uart_puts("Programming from flash\n");
memp = (uint8_t*)FLASH_ICE40_START;
crc_reset();
gpio_low(LED5);
if (ice40_reset() != OK)
uart_puts("reset failed\n");
else if (rbits(rbyte_mem_send, 0) != OK)
uart_puts("rbits failed\n");
else if (ice40_configdone() != OK)
uart_puts("configdone failed\n");
gpio_high(LED5);
}
uart_puts("Setup done\n");
while (uart_trygetc(&b) == OK)
;
uart_startread();
}
/*
* Loop function (called repeatedly)
* - wait for the start of a bitstream to be received on uart1
* - receive the bitstream and pass it to the ice40
*/
void
loop(void)
{
uint8_t b = 0;
static int err = 0;
if (err) {
gpio_toggle(LED5);
HAL_Delay(100);
return;
}
uart_puts("Waiting for UART\n");
do {
uart_getc(&b);
} while (b != 0x7E);
gpio_low(LED5);
err = ice40_reset();
if (err)
return;
crc_reset();
spi_write(&b, 1);
crc_update(b);
if ((err = rbits(rbyte_uart_send, b)) != OK) {
uart_puts("rbits failed\n");
return;
}
err = ice40_configdone();
gpio_high(LED5);
}
This diff is collapsed.
# gnu makefile to build arm program to load ice40 bitstreams
# using STM32Cube library
#
# make elf - build executable (can be loaded by openocd)
# make hex - build intelhex file (can be loaded by stm32flash)
# make flash - build and load with stm32flash
# make oflash - build and load with openocd
# make dfu - build raw image and load with dfu-util
TARGET = iceboot
SRC = iceboot.c
BITSTREAM = bitmap.bin
OCDCONFIG = stlink.cfg
# default target
all: elf
# select STM32 SoC
MCU = STM32L476xx
ASM = startup_stm32l476xx.s
LDSCRIPT = ../Src/sdk_flash.ld
#MCU = STM32L433xx
#ASM = startup_stm32l433xx.s
#LDSCRIPT = blackice_flash.ld
# select ARM architecture
ARCH = -mcpu=cortex-m3
#ARCH = -mcpu=cortex-m4
# select floating-point support (cortex-m4 only)
#FPU = -mfloat-abi=softfp -mfpu=fpv4-sp-d16
#FPU = -mfloat-abi=hard -mfpu=fpv4-sp-d16
# uncomment to include debug symbols
DEBUG = -g
# directories
#OUTPUT_DIR = $(TARGET)_output
OUTPUT_DIR = output
OBJ_DIR = $(OUTPUT_DIR)/obj
DRIVERS = ../Drivers/STM32L4xx_HAL_Driver
CMSIS = ../Drivers/CMSIS/Device/ST/STM32L4xx
INCLUDE = \
-I../Inc \
-I../Drivers/CMSIS/Include \
-I$(DRIVERS)/Inc \
-I$(CMSIS)/Include \
VPATH = \
../Src \
$(DRIVERS)/Src \
$(CMSIS)/Source/Templates/gcc \
$(CMSIS)/Source/Templates
# source and object files
SRC += \
main.c \
stm32l4xx_it.c \
stm32l4xx_hal_msp.c \
system_stm32l4xx.c
LIB_SRC := $(wildcard $(DRIVERS)/Src/*.c)
LIB_OBJECTS := $(addprefix $(OBJ_DIR)/,$(notdir $(LIB_SRC:.c=.o)))
ASM_OBJECTS := $(addprefix $(OBJ_DIR)/,$(notdir $(ASM:.s=.o)))
SRC_OBJECTS := $(addprefix $(OBJ_DIR)/,$(notdir $(SRC:.c=.o)))
LIB = $(OUTPUT_DIR)/drivers.a
# toolchain definitions
CC = arm-none-eabi-gcc
AR = arm-none-eabi-ar
CPPFLAGS = -D$(MCU) $(INCLUDE)
CFLAGS = -ffunction-sections -fdata-sections -O0 -Werror --std=c11 -Wall
CFLAGS += -mthumb $(ARCH) $(FPU) $(DEBUG)
LDFLAGS = -Wl,--gc-sections -Wall
#LDFLAGS += -Wl,-Map=$(OUTPUT_DIR)/$(TARGET).map,--cref
LDFLAGS += -mthumb $(ARCH) $(FPU) $(DEBUG)
# toolchain recipes
$(OBJ_DIR)/%.o: %.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
$(OBJ_DIR)/%.o: %.s
$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
$(OUTPUT_DIR)/$(TARGET).elf : $(LIB) $(ASM_OBJECTS) $(SRC_OBJECTS) $(OBJ_DIR)/bitmap.o $(LDSCRIPT)
$(CC) $(LDFLAGS) $(ASM_OBJECTS) $(SRC_OBJECTS) $(LIB) $(OBJ_DIR)/bitmap.o -T$(LDSCRIPT) -o $@
# dependency rules
HEADERS := $(wildcard ../Inc/*.h \
../Drivers/CMSIS/Include/*.h \
../Drivers/CMSIS/Device/ST/STM32L4xx/Include/*.h \
../Drivers/STM32L4xx_HAL_Driver/Inc/*.h \
../Drivers/STM32L4xx_HAL_Driver/Inc/legacy/*.h)
$(SRC_OBJECTS) $(LIB_OBJECTS): $(HEADERS)
$(ASM_OBJECTS) $(SRC_OBJECTS) $(LIB_OBJECTS): | $(OBJ_DIR)
$(OBJ_DIR):
-mkdir -p $(OBJ_DIR)
$(LIB): $(LIB_OBJECTS)
-rm -f $@
$(AR) crv $@ $^
.INTERMEDIATE: $(LIB_OBJECTS)
$(OUTPUT_DIR)/$(TARGET).hex : $(OUTPUT_DIR)/$(TARGET).elf
arm-none-eabi-objcopy -O ihex $< $@
$(OUTPUT_DIR)/$(TARGET).raw: $(OUTPUT_DIR)/$(TARGET).elf
arm-none-eabi-objcopy -O binary $< $@
$(OBJ_DIR)/bitmap.o: bitmap.bin
arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata $< $@
ifneq ($(BITSTREAM), bitmap.bin)
bitmap.bin: $(BITSTREAM)
-cp $< $@
endif
# top-level targets
clean:
-rm -r $(OUTPUT_DIR)
elf: $(OUTPUT_DIR)/$(TARGET).elf
hex: $(OUTPUT_DIR)/$(TARGET).hex
raw: $(OUTPUT_DIR)/$(TARGET).raw
flash: hex
stm32flash -w $(OUTPUT_DIR)/$(TARGET).hex -v -g 0x0 /dev/ttyAMA0
oflash: elf
openocd -f $(OCDCONFIG) -c "program $(OUTPUT_DIR)/$(TARGET).elf verify reset exit"
dfu: raw
dfu-util -s 0x08000000:leave -a 0 -D $(OUTPUT_DIR)/$(TARGET).raw
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment