[SC64][SW] Added board bring-up via UART header (#20)

* [SC64][SW] Added board bring-up via UART header

* [SC64][SW] Made I2C in primer stable

* [SC64][SW] LCMXO2 primer fixes

* [SC64][SW] SC64 primer PC software

* [SC64][SW] Added primer.py to release package

* [SC64][SW] Fixed FPGA refresh

* [SC64][SW] Changed release package contents
This commit is contained in:
Mateusz Faderewski 2023-01-21 04:08:15 +01:00 committed by GitHub
parent 4f6c65c770
commit 5b85b0f661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1363 additions and 313 deletions

View File

@ -6,9 +6,9 @@ PACKAGE_FILE_NAME="SC64"
FILES=( FILES=(
"./fw/ftdi/ft232h_config.xml" "./fw/ftdi/ft232h_config.xml"
"./fw/project/lcmxo2/impl1/sc64_impl1.mrp"
"./fw/project/lcmxo2/impl1/sc64_impl1.twr"
"./sw/pc/dd64.py" "./sw/pc/dd64.py"
"./sw/pc/primer.py"
"./sw/pc/requirements.txt"
"./sw/pc/sc64.py" "./sw/pc/sc64.py"
"./sw/update/sc64.upd" "./sw/update/sc64.upd"
"./LICENSE" "./LICENSE"
@ -89,6 +89,7 @@ build_update () {
--mcu ../controller/build/app/app.bin \ --mcu ../controller/build/app/app.bin \
--fpga ../../fw/project/lcmxo2/impl1/sc64_impl1.jed \ --fpga ../../fw/project/lcmxo2/impl1/sc64_impl1.jed \
--boot ../bootloader/build/bootloader.bin \ --boot ../bootloader/build/bootloader.bin \
--primer ../controller/build/primer/primer.bin \
sc64.upd sc64.upd
popd > /dev/null popd > /dev/null
@ -113,7 +114,7 @@ print_usage () {
echo "usage: ./build.sh [bootloader] [controller] [fpga] [update] [release] [-c] [--help]" echo "usage: ./build.sh [bootloader] [controller] [fpga] [update] [release] [-c] [--help]"
echo "parameters:" echo "parameters:"
echo " bootloader - compile N64 bootloader software" echo " bootloader - compile N64 bootloader software"
echo " controller - compile ARM controller software" echo " controller - compile MCU controller software"
echo " fpga - compile FPGA design" echo " fpga - compile FPGA design"
echo " update - compile all software and designs" echo " update - compile all software and designs"
echo " release - collect and zip files for release (triggers 'update' build)" echo " release - collect and zip files for release (triggers 'update' build)"

View File

@ -200,7 +200,7 @@ LOCATE COMP "usb_miosi[6]" SITE "14" ;
LOCATE COMP "usb_miosi[7]" SITE "13" ; LOCATE COMP "usb_miosi[7]" SITE "13" ;
LOCATE COMP "usb_miso" SITE "10" ; LOCATE COMP "usb_miso" SITE "10" ;
LOCATE COMP "usb_pwrsav" SITE "2" ; LOCATE COMP "usb_pwrsav" SITE "2" ;
SYSCONFIG SDM_PORT=DISABLE ; SYSCONFIG SDM_PORT=DISABLE I2C_PORT=ENABLE ;
VOLTAGE 3.300 V; VOLTAGE 3.300 V;
FREQUENCY NET "clk" 100.000000 MHz PAR_ADJ 10.000000 ; FREQUENCY NET "clk" 100.000000 MHz PAR_ADJ 10.000000 ;
BLOCK PATH TO PORT "mcu_int" ; BLOCK PATH TO PORT "mcu_int" ;

View File

@ -70,7 +70,7 @@ $(BUILD_DIR)/bootloader.elf: $(OBJS) N64.ld
$(BUILD_DIR)/bootloader.bin: $(BUILD_DIR)/bootloader.elf $(BUILD_DIR)/bootloader.bin: $(BUILD_DIR)/bootloader.elf
@$(OBJCOPY) -O binary $< $@ @$(OBJCOPY) -O binary $< $@
@chksum64 $@ > /dev/null @chksum64 $@ > /dev/null
@truncate --size=1028k $@ @$(PYTHON) tools/strip.py $@
$(BUILD_DIR)/bootloader.hex: $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/bootloader.hex: $(BUILD_DIR)/bootloader.bin
@$(OBJCOPY) -I binary -O ihex $< $@ @$(OBJCOPY) -I binary -O ihex $< $@

View File

@ -55,6 +55,13 @@ SECTIONS {
. += __stack_size; . += __stack_size;
_sp = .; _sp = .;
.fill : {
. = ALIGN(1024) - 4;
LONG(0xDEADBEEF);
FILL(0xFFFFFFFF);
. = ORIGIN(rom) + LENGTH(rom);
} > rom
/DISCARD/ : { /DISCARD/ : {
*(.MIPS.*) *(.MIPS.*)
} }

View File

@ -1,6 +1,7 @@
.set noat .set noat
.set noreorder .set noreorder
.section .data.ipl2 .section .data.ipl2
ipl2: ipl2:
.global ipl2 .global ipl2

View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
import sys
if __name__ == "__main__":
if (len(sys.argv) != 2):
print(f"Usage: python {sys.argv[0]} file_path")
sys.exit(1)
file = sys.argv[1]
try:
data = b''
with open(file, 'rb') as f:
data = f.read()
with open(file, 'wb') as f:
f.write(data.strip(b'\xFF'))
except FileNotFoundError:
print(f"Couldn't open file \"{file}\"")
sys.exit(2)
except Exception as e:
print(e)
sys.exit(3)

View File

@ -1,6 +1,6 @@
MEMORY { MEMORY {
loader (rx) : org = 0x08000000, len = 4k loader (rx) : org = 0x08000000, len = 4k
rom (rx) : org = 0x08001000, len = 28k code (rx) : org = 0x08001000, len = 28k
ram (rwx) : org = 0x20000000, len = 8k ram (rwx) : org = 0x20000000, len = 8k
} }
@ -17,7 +17,7 @@ SECTIONS {
. = ALIGN(4); . = ALIGN(4);
KEEP(*(.isr_vector)) KEEP(*(.isr_vector))
. = ALIGN(4); . = ALIGN(4);
} > rom } > code
.text : { .text : {
. = ALIGN(4); . = ALIGN(4);
@ -27,18 +27,16 @@ SECTIONS {
*(.glue_7t) *(.glue_7t)
. = ALIGN(4); . = ALIGN(4);
_etext = .; _etext = .;
} > rom } > code
.bss : { .bss : {
. = ALIGN(4); . = ALIGN(4);
_sbss = .; _sbss = .;
__bss_start__ = _sbss;
*(.bss) *(.bss)
*(.bss*) *(.bss*)
*(COMMON) *(COMMON)
. = ALIGN(4); . = ALIGN(4);
_ebss = .; _ebss = .;
__bss_end__ = _ebss;
} > ram } > ram
.data : { .data : {
@ -49,14 +47,14 @@ SECTIONS {
*(.data*) *(.data*)
. = ALIGN(4); . = ALIGN(4);
_edata = .; _edata = .;
} > ram AT > rom } > ram AT > code
.rodata : { .rodata : {
. = ALIGN(4); . = ALIGN(4);
*(.rodata) *(.rodata)
*(.rodata*) *(.rodata*)
. = ALIGN(4); . = ALIGN(4);
} > rom } > code
_estack = ORIGIN(ram) + LENGTH(ram); _estack = ORIGIN(ram) + LENGTH(ram);
} }

View File

@ -1,6 +1,9 @@
EXE_NAME = app EXE_NAME = app
LD_SCRIPT = app.ld
BUILD_DIR = build/app BUILD_DIR = build/app
LD_SCRIPT = app.ld
PAD_TO = 0x08008000
SRC_FILES = \ SRC_FILES = \
app.S \ app.S \
app.c \ app.c \
@ -24,7 +27,6 @@ SRC_FILES = \
update.c \ update.c \
usb.c \ usb.c \
writeback.c writeback.c
PAD_TO = 0x08008000
include common.mk include common.mk

View File

@ -4,10 +4,12 @@ set -e
case "$1" in case "$1" in
all) all)
make all -j -f primer.mk USER_FLAGS="$USER_FLAGS"
make all -j -f loader.mk USER_FLAGS="$USER_FLAGS" make all -j -f loader.mk USER_FLAGS="$USER_FLAGS"
make all -j -f app.mk USER_FLAGS="$USER_FLAGS" make all -j -f app.mk USER_FLAGS="$USER_FLAGS"
;; ;;
clean) clean)
make clean -f primer.mk
make clean -f loader.mk make clean -f loader.mk
make clean -f app.mk make clean -f app.mk
;; ;;

View File

@ -14,7 +14,6 @@ SRC_DIR = src
SRCS = $(addprefix $(SRC_DIR)/, $(SRC_FILES)) SRCS = $(addprefix $(SRC_DIR)/, $(SRC_FILES))
OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(patsubst %,%.o,$(SRCS)))) OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(patsubst %,%.o,$(SRCS))))
DEPS = $(OBJS:.o=.d) DEPS = $(OBJS:.o=.d)
VPATH = $(SRC_DIR) VPATH = $(SRC_DIR)
$(@info $(shell mkdir -p ./$(BUILD_DIR) &> /dev/null)) $(@info $(shell mkdir -p ./$(BUILD_DIR) &> /dev/null))

View File

@ -1,5 +1,5 @@
MEMORY { MEMORY {
rom (rx) : org = 0x08000000, len = 4k code (rx) : org = 0x08000000, len = 4k
ram (rwx) : org = 0x20000000, len = 8k ram (rwx) : org = 0x20000000, len = 8k
} }
@ -10,13 +10,13 @@ SECTIONS {
. = ALIGN(4); . = ALIGN(4);
KEEP(*(.isr_vector)) KEEP(*(.isr_vector))
. = ALIGN(4); . = ALIGN(4);
} > rom } > code
.startup : { .startup : {
. = ALIGN(4); . = ALIGN(4);
*(.text.Reset_Handler) *(.text.Reset_Handler)
. = ALIGN(4); . = ALIGN(4);
} > rom } > code
.text : { .text : {
_sitext = LOADADDR(.text); _sitext = LOADADDR(.text);
@ -28,18 +28,16 @@ SECTIONS {
*(.glue_7t) *(.glue_7t)
. = ALIGN(4); . = ALIGN(4);
_etext = .; _etext = .;
} > ram AT > rom } > ram AT > code
.bss : { .bss : {
. = ALIGN(4); . = ALIGN(4);
_sbss = .; _sbss = .;
__bss_start__ = _sbss;
*(.bss) *(.bss)
*(.bss*) *(.bss*)
*(COMMON) *(COMMON)
. = ALIGN(4); . = ALIGN(4);
_ebss = .; _ebss = .;
__bss_end__ = _ebss;
} > ram } > ram
.data : { .data : {
@ -50,7 +48,7 @@ SECTIONS {
*(.data*) *(.data*)
. = ALIGN(4); . = ALIGN(4);
_edata = .; _edata = .;
} > ram AT > rom } > ram AT > code
.rodata : { .rodata : {
_sirodata = LOADADDR(.rodata); _sirodata = LOADADDR(.rodata);
@ -60,9 +58,9 @@ SECTIONS {
*(.rodata*) *(.rodata*)
. = ALIGN(4); . = ALIGN(4);
_erodata = .; _erodata = .;
} > ram AT > rom } > ram AT > code
_app_header = ORIGIN(rom) + LENGTH(rom); _app_header = ORIGIN(code) + LENGTH(code);
_app_magic = _app_header + 16; _app_magic = _app_header + 16;
_estack = ORIGIN(ram) + LENGTH(ram); _estack = ORIGIN(ram) + LENGTH(ram);
} }

View File

@ -1,6 +1,9 @@
EXE_NAME = loader EXE_NAME = loader
LD_SCRIPT = loader.ld
BUILD_DIR = build/loader BUILD_DIR = build/loader
LD_SCRIPT = loader.ld
PAD_TO = 0x08001000
SRC_FILES = \ SRC_FILES = \
loader.S \ loader.S \
flash.c \ flash.c \
@ -9,6 +12,5 @@ SRC_FILES = \
lcmxo2.c \ lcmxo2.c \
loader.c \ loader.c \
update.c update.c
PAD_TO = 0x08001000
include common.mk include common.mk

50
sw/controller/primer.ld Normal file
View File

@ -0,0 +1,50 @@
MEMORY {
ram (rwx) : org = 0x20000000, len = 4k
code (rwx) : org = 0x20001000, len = 4k
}
ENTRY(Reset_Handler)
SECTIONS {
.isr_vector : {
. = ALIGN(4);
_header = .;
KEEP(*(.isr_vector))
. = ALIGN(4);
} > code
.text : {
. = ALIGN(4);
*(.text)
*(.text*)
*(.glue_7)
*(.glue_7t)
. = ALIGN(4);
} > code
.bss : {
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > ram
.data : {
. = ALIGN(4);
*(.data)
*(.data*)
. = ALIGN(4);
} > code
.rodata : {
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > code
_estack = ORIGIN(ram) + LENGTH(ram);
}

16
sw/controller/primer.mk Normal file
View File

@ -0,0 +1,16 @@
EXE_NAME = primer
BUILD_DIR = build/primer
LD_SCRIPT = primer.ld
PAD_TO = 0x20002000
SRC_FILES = \
primer.S \
fpga.c \
hw.c \
lcmxo2.c \
primer.c
include common.mk
FLAGS += -DLCMXO2_I2C

View File

@ -3,6 +3,7 @@
.fpu softvfp .fpu softvfp
.thumb .thumb
.section .loader, "a", %progbits .section .loader, "a", %progbits
.type loader, %object .type loader, %object
loader: loader:
@ -13,7 +14,6 @@ loader:
.type Reset_Handler, %function .type Reset_Handler, %function
Reset_Handler: Reset_Handler:
.global Reset_Handler .global Reset_Handler
cpsid i
init_data: init_data:
ldr r0, =_sdata ldr r0, =_sdata

View File

@ -1,7 +1,11 @@
#include <stddef.h>
#include <stm32g0xx.h> #include <stm32g0xx.h>
#include "hw.h" #include "hw.h"
#define UART_BAUD (115200)
typedef enum { typedef enum {
GPIO_INPUT = 0b00, GPIO_INPUT = 0b00,
GPIO_OUTPUT = 0b01, GPIO_OUTPUT = 0b01,
@ -108,6 +112,13 @@ void hw_gpio_reset (gpio_id_t id) {
gpio->BSRR = (GPIO_BSRR_BR0 << pin); gpio->BSRR = (GPIO_BSRR_BR0 << pin);
} }
void hw_uart_read (uint8_t *data, int length) {
for (int i = 0; i < length; i++) {
while (!(USART1->ISR & USART_ISR_RXNE_RXFNE));
*data++ = (uint8_t) (USART1->RDR);
}
}
void hw_uart_write (uint8_t *data, int length) { void hw_uart_write (uint8_t *data, int length) {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
while (!(USART1->ISR & USART_ISR_TXE_TXFNF)); while (!(USART1->ISR & USART_ISR_TXE_TXFNF));
@ -115,6 +126,10 @@ void hw_uart_write (uint8_t *data, int length) {
} }
} }
void hw_uart_wait_busy (void) {
while (!(USART1->ISR & USART_ISR_TC));
}
void hw_spi_start (void) { void hw_spi_start (void) {
hw_gpio_reset(GPIO_ID_SPI_CS); hw_gpio_reset(GPIO_ID_SPI_CS);
} }
@ -181,7 +196,46 @@ void hw_i2c_write (uint8_t i2c_address, uint8_t address, uint8_t *data, uint8_t
} }
uint32_t hw_i2c_get_error (void) { uint32_t hw_i2c_get_error (void) {
return I2C1->ISR & I2C_ISR_NACKF; return (I2C1->ISR & I2C_ISR_NACKF);
}
void hw_i2c_raw (uint8_t i2c_address, uint8_t *tx_data, uint8_t tx_length, uint8_t *rx_data, uint8_t rx_length) {
while (I2C1->ISR & I2C_ISR_BUSY);
if (tx_length > 0) {
I2C1->ICR = I2C_ICR_NACKCF;
I2C1->CR2 = (
((rx_length == 0) ? I2C_CR2_AUTOEND : 0) |
(tx_length << I2C_CR2_NBYTES_Pos) |
I2C_CR2_START |
(i2c_address << I2C_CR2_SADD_Pos)
);
for (int i = 0; i < tx_length; i++) {
while (!(I2C1->ISR & I2C_ISR_TXIS));
I2C1->TXDR = *tx_data++;
}
if (!(I2C1->CR2 & I2C_CR2_AUTOEND)) {
while (!(I2C1->ISR & (I2C_ISR_NACKF | I2C_ISR_TC)));
}
}
if (rx_length > 0) {
I2C1->CR2 = (
I2C_CR2_AUTOEND |
(rx_length << I2C_CR2_NBYTES_Pos) |
I2C_CR2_START |
I2C_CR2_RD_WRN |
(i2c_address << I2C_CR2_SADD_Pos)
);
for (int i = 0; i < rx_length; i++) {
while (!(I2C1->ISR & I2C_ISR_RXNE));
*rx_data++ = I2C1->RXDR;
}
}
if ((tx_length > 0) || (rx_length > 0)) {
while (!(I2C1->ISR & I2C_ISR_STOPF));
}
} }
void hw_i2c_disable_irq (void) { void hw_i2c_disable_irq (void) {
@ -311,16 +365,18 @@ hw_flash_t hw_flash_read (uint32_t offset) {
return *(uint64_t *) (FLASH_BASE + offset); return *(uint64_t *) (FLASH_BASE + offset);
} }
void hw_loader_reset (loader_parameters_t *parameters) { void hw_reset (loader_parameters_t *parameters) {
RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN; if (parameters != NULL) {
PWR->CR1 |= PWR_CR1_DBP; RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN;
TAMP->BKP0R = parameters->magic; PWR->CR1 |= PWR_CR1_DBP;
TAMP->BKP1R = parameters->flags; TAMP->BKP0R = parameters->magic;
TAMP->BKP2R = parameters->mcu_address; TAMP->BKP1R = parameters->flags;
TAMP->BKP3R = parameters->fpga_address; TAMP->BKP2R = parameters->mcu_address;
TAMP->BKP4R = parameters->bootloader_address; TAMP->BKP3R = parameters->fpga_address;
PWR->CR1 &= ~(PWR_CR1_DBP); TAMP->BKP4R = parameters->bootloader_address;
RCC->APBENR1 &= ~(RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN); PWR->CR1 &= ~(PWR_CR1_DBP);
RCC->APBENR1 &= ~(RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN);
}
NVIC_SystemReset(); NVIC_SystemReset();
} }
@ -397,18 +453,16 @@ static void hw_init_spi (void) {
hw_gpio_init(GPIO_ID_SPI_CLK, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_SPI_CLK, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_SPI_MISO, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_DOWN, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_SPI_MISO, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_DOWN, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_SPI_MOSI, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_SPI_MOSI, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_FPGA_INT, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0);
} }
static void hw_init_i2c (void) { static void hw_init_i2c (void) {
RCC->APBENR1 |= RCC_APBENR1_I2C1EN; RCC->APBENR1 |= RCC_APBENR1_I2C1EN;
I2C1->TIMINGR = 0x10B17DB5UL; I2C1->TIMINGR = 0x00C12166UL;
I2C1->CR1 |= (I2C_CR1_TCIE | I2C_CR1_STOPIE | I2C_CR1_RXIE | I2C_CR1_TXIE | I2C_CR1_PE); I2C1->CR1 |= (I2C_CR1_TCIE | I2C_CR1_STOPIE | I2C_CR1_RXIE | I2C_CR1_TXIE | I2C_CR1_PE);
hw_gpio_init(GPIO_ID_I2C_SCL, GPIO_ALT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_NONE, GPIO_AF_6, 0); hw_gpio_init(GPIO_ID_I2C_SCL, GPIO_ALT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_NONE, GPIO_AF_6, 0);
hw_gpio_init(GPIO_ID_I2C_SDA, GPIO_ALT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_NONE, GPIO_AF_6, 0); hw_gpio_init(GPIO_ID_I2C_SDA, GPIO_ALT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_NONE, GPIO_AF_6, 0);
hw_gpio_init(GPIO_ID_RTC_MFP, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0);
} }
static void hw_init_uart (void) { static void hw_init_uart (void) {
@ -416,11 +470,12 @@ static void hw_init_uart (void) {
SYSCFG->CFGR1 |= (SYSCFG_CFGR1_PA12_RMP | SYSCFG_CFGR1_PA11_RMP); SYSCFG->CFGR1 |= (SYSCFG_CFGR1_PA12_RMP | SYSCFG_CFGR1_PA11_RMP);
USART1->BRR = (64000000UL) / 1000000; hw_gpio_init(GPIO_ID_UART_TX, GPIO_ALT, GPIO_PP, GPIO_SPEED_LOW, GPIO_PULL_UP, GPIO_AF_1, 0);
USART1->CR1 = USART_CR1_FIFOEN | USART_CR1_TE | USART_CR1_UE; hw_gpio_init(GPIO_ID_UART_RX, GPIO_ALT, GPIO_PP, GPIO_SPEED_LOW, GPIO_PULL_UP, GPIO_AF_1, 0);
hw_gpio_init(GPIO_ID_UART_TX, GPIO_ALT, GPIO_PP, GPIO_SPEED_LOW, GPIO_PULL_NONE, GPIO_AF_1, 0); USART1->BRR = (64000000UL) / UART_BAUD;
hw_gpio_init(GPIO_ID_UART_RX, GPIO_ALT, GPIO_PP, GPIO_SPEED_LOW, GPIO_PULL_NONE, GPIO_AF_1, 0); USART1->RQR = USART_RQR_TXFRQ | USART_RQR_RXFRQ;
USART1->CR1 = USART_CR1_FIFOEN | USART_CR1_M0 | USART_CR1_PCE | USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
} }
static void hw_init_tim (void) { static void hw_init_tim (void) {
@ -445,14 +500,22 @@ static void hw_init_tim (void) {
); );
} }
static void hw_init_misc (void) { static void hw_init_crc (void) {
RCC->AHBENR |= RCC_AHBENR_CRCEN; RCC->AHBENR |= RCC_AHBENR_CRCEN;
CRC->CR = (CRC_CR_REV_OUT | CRC_CR_REV_IN_0); CRC->CR = (CRC_CR_REV_OUT | CRC_CR_REV_IN_0);
}
static void hw_init_misc (void) {
hw_gpio_init(GPIO_ID_N64_RESET, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_DOWN, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_N64_RESET, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_DOWN, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_N64_CIC_CLK, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_DOWN, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_N64_CIC_CLK, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_DOWN, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_N64_CIC_DQ, GPIO_OUTPUT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 1); hw_gpio_init(GPIO_ID_N64_CIC_DQ, GPIO_OUTPUT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 1);
hw_gpio_init(GPIO_ID_FPGA_INT, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_RTC_MFP, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0);
}
void hw_set_vector_table (uint32_t offset) {
SCB->VTOR = (__IOM uint32_t) (offset);
} }
void hw_init (void) { void hw_init (void) {
@ -461,6 +524,7 @@ void hw_init (void) {
hw_init_i2c(); hw_init_i2c();
hw_init_uart(); hw_init_uart();
hw_init_tim(); hw_init_tim();
hw_init_crc();
hw_init_misc(); hw_init_misc();
NVIC_SetPriority(EXTI0_1_IRQn, 0); NVIC_SetPriority(EXTI0_1_IRQn, 0);
@ -489,6 +553,14 @@ void hw_loader_init (void) {
hw_init_spi(); hw_init_spi();
} }
void hw_primer_init (void) {
hw_init_mcu();
hw_init_spi();
hw_init_i2c();
hw_init_uart();
hw_init_crc();
}
void EXTI0_1_IRQHandler (void) { void EXTI0_1_IRQHandler (void) {
for (int i = 0; i <= 1; i++) { for (int i = 0; i <= 1; i++) {

View File

@ -63,13 +63,16 @@ void hw_gpio_irq_setup (gpio_id_t id, gpio_irq_t irq, void (*callback)(void));
uint32_t hw_gpio_get (gpio_id_t id); uint32_t hw_gpio_get (gpio_id_t id);
void hw_gpio_set (gpio_id_t id); void hw_gpio_set (gpio_id_t id);
void hw_gpio_reset (gpio_id_t id); void hw_gpio_reset (gpio_id_t id);
void hw_uart_read (uint8_t *data, int length);
void hw_uart_write (uint8_t *data, int length); void hw_uart_write (uint8_t *data, int length);
void hw_uart_wait_busy (void);
void hw_spi_start (void); void hw_spi_start (void);
void hw_spi_stop (void); void hw_spi_stop (void);
void hw_spi_trx (uint8_t *data, int length, spi_direction_t direction); void hw_spi_trx (uint8_t *data, int length, spi_direction_t direction);
void hw_i2c_read (uint8_t i2c_address, uint8_t address, uint8_t *data, uint8_t length, void (*callback)(void)); void hw_i2c_read (uint8_t i2c_address, uint8_t address, uint8_t *data, uint8_t length, void (*callback)(void));
void hw_i2c_write (uint8_t i2c_address, uint8_t address, uint8_t *data, uint8_t length, void (*callback)(void)); void hw_i2c_write (uint8_t i2c_address, uint8_t address, uint8_t *data, uint8_t length, void (*callback)(void));
uint32_t hw_i2c_get_error (void); uint32_t hw_i2c_get_error (void);
void hw_i2c_raw (uint8_t i2c_address, uint8_t *tx_data, uint8_t tx_length, uint8_t *rx_data, uint8_t rx_length);
void hw_i2c_disable_irq (void); void hw_i2c_disable_irq (void);
void hw_i2c_enable_irq (void); void hw_i2c_enable_irq (void);
void hw_tim_setup (tim_id_t id, uint16_t delay, void (*callback)(void)); void hw_tim_setup (tim_id_t id, uint16_t delay, void (*callback)(void));
@ -83,10 +86,12 @@ uint32_t hw_flash_size (void);
void hw_flash_erase (void); void hw_flash_erase (void);
void hw_flash_program (uint32_t offset, hw_flash_t value); void hw_flash_program (uint32_t offset, hw_flash_t value);
hw_flash_t hw_flash_read (uint32_t offset); hw_flash_t hw_flash_read (uint32_t offset);
void hw_loader_reset (loader_parameters_t *parameters); void hw_reset (loader_parameters_t *parameters);
void hw_loader_get_parameters (loader_parameters_t *parameters); void hw_loader_get_parameters (loader_parameters_t *parameters);
void hw_set_vector_table (uint32_t offset);
void hw_init (void); void hw_init (void);
void hw_loader_init (void); void hw_loader_init (void);
void hw_primer_init (void);
#endif #endif

View File

@ -1,253 +1,451 @@
#include <stdbool.h> #include <stdbool.h>
#include "fpga.h" #include "fpga.h"
#include "vendor.h" #include "hw.h"
#include "vendor.h"
#define VENDOR_SCR_START (1 << 0)
#define VENDOR_SCR_BUSY (1 << 0) #define VENDOR_SCR_START (1 << 0)
#define VENDOR_SCR_WRITE (1 << 1) #define VENDOR_SCR_BUSY (1 << 0)
#define VENDOR_SCR_LENGTH_BIT (2) #define VENDOR_SCR_WRITE (1 << 1)
#define VENDOR_SCR_DELAY (1 << 4) #define VENDOR_SCR_LENGTH_BIT (2)
#define VENDOR_SCR_ADDRESS_BIT (8) #define VENDOR_SCR_DELAY (1 << 4)
#define VENDOR_SCR_ADDRESS_BIT (8)
#define LCMXO2_CFGCR (0x70)
#define LCMXO2_CFGTXDR (0x71) #define LCMXO2_I2C_ADDR_CFG (0x80)
#define LCMXO2_CFGRXDR (0x73) #define LCMXO2_I2C_ADDR_RESET (0x86)
#define CFGCR_RSTE (1 << 6) #define LCMXO2_CFGCR (0x70)
#define CFGCR_WBCE (1 << 7) #define LCMXO2_CFGTXDR (0x71)
#define LCMXO2_CFGRXDR (0x73)
#define ISC_ERASE (0x0E)
#define ISC_DISABLE (0x26) #define CFGCR_RSTE (1 << 6)
#define LSC_READ_STATUS (0x3C) #define CFGCR_WBCE (1 << 7)
#define LSC_INIT_ADDRESS (0x46)
#define ISC_PROGRAM_DONE (0x5E) #define ISC_ERASE (0x0E)
#define LSC_PROG_INCR_NV (0x70) #define ISC_DISABLE (0x26)
#define LSC_READ_INCR_NV (0x73) #define LSC_READ_STATUS (0x3C)
#define ISC_ENABLE_X (0x74) #define LSC_INIT_ADDRESS (0x46)
#define LSC_REFRESH (0x79) #define ISC_PROGRAM_DONE (0x5E)
#define ISC_NOOP (0xFF) #define LSC_PROG_INCR_NV (0x70)
#define LSC_READ_INCR_NV (0x73)
#define ISC_ERASE_CFG (1 << 18) #define ISC_ENABLE_X (0x74)
#define ISC_ERASE_UFM (1 << 19) #define LSC_REFRESH (0x79)
#define ISC_ENABLE (0xC6)
#define LSC_STATUS_1_BUSY (1 << 4) #define IDCODE_PUB (0xE0)
#define LSC_STATUS_1_FAIL (1 << 5) #define LSC_PROG_FEABITS (0xF8)
#define LSC_READ_FEABITS (0xFB)
#define FLASH_PAGE_SIZE (16) #define ISC_NOOP (0xFF)
#define FLASH_NUM_PAGES (11260)
#define ISC_ERASE_FEATURE (1 << 17)
#define ISC_ERASE_CFG (1 << 18)
typedef enum { #define ISC_ERASE_UFM (1 << 19)
CMD_NORMAL,
CMD_DELAYED, #define LSC_STATUS_CFG_ENABLE (1 << 9)
CMD_TWO_OP, #define LSC_STATUS_BUSY (1 << 12)
} cmd_type_t; #define LSC_STATUS_FAIL (1 << 13)
#define DEVICE_ID_SIZE (4)
static void lcmxo2_reg_set (uint8_t reg, uint8_t value) {
fpga_reg_set(REG_VENDOR_DATA, value << 24); #define FLASH_PAGE_SIZE (16)
fpga_reg_set(REG_VENDOR_SCR, #define FLASH_NUM_PAGES (11260)
(reg << VENDOR_SCR_ADDRESS_BIT) |
(0 << VENDOR_SCR_LENGTH_BIT) | #define FEATBITS_SIZE (2)
VENDOR_SCR_WRITE |
VENDOR_SCR_START
); typedef enum {
while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); CMD_NORMAL,
} CMD_DELAYED,
CMD_TWO_OP,
static void lcmxo2_reset_bus (void) { } cmd_type_t;
lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_RSTE);
lcmxo2_reg_set(LCMXO2_CFGCR, 0);
} #ifndef LCMXO2_I2C
static void lcmxo2_reg_set (uint8_t reg, uint8_t value) {
static void lcmxo2_execute_cmd (uint8_t cmd, uint32_t arg, cmd_type_t type) { fpga_reg_set(REG_VENDOR_DATA, value << 24);
lcmxo2_reg_set(LCMXO2_CFGCR, 0); fpga_reg_set(REG_VENDOR_SCR,
uint32_t data = (cmd << 24) | (arg & 0x00FFFFFF); (reg << VENDOR_SCR_ADDRESS_BIT) |
lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_WBCE); (0 << VENDOR_SCR_LENGTH_BIT) |
fpga_reg_set(REG_VENDOR_DATA, data); VENDOR_SCR_WRITE |
fpga_reg_set(REG_VENDOR_SCR, VENDOR_SCR_START
(LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) | );
(type == CMD_DELAYED ? VENDOR_SCR_DELAY : 0) | while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
((type == CMD_TWO_OP ? 2 : 3) << VENDOR_SCR_LENGTH_BIT) | }
VENDOR_SCR_WRITE |
VENDOR_SCR_START static void lcmxo2_read_data (uint8_t *buffer, uint32_t length) {
); while (length > 0) {
while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); uint32_t block_size = (length > 4) ? 4 : length;
} fpga_reg_set(REG_VENDOR_SCR,
(LCMXO2_CFGRXDR << VENDOR_SCR_ADDRESS_BIT) |
static void lcmxo2_cleanup (void) { ((block_size - 1) << VENDOR_SCR_LENGTH_BIT) |
lcmxo2_reg_set(LCMXO2_CFGCR, 0); VENDOR_SCR_START
} );
while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
static void lcmxo2_read_data (uint8_t *buffer, uint32_t length) { uint32_t data = fpga_reg_get(REG_VENDOR_DATA);
while (length > 0) { data <<= ((4 - block_size) * 8);
uint32_t block_size = (length > 4) ? 4 : length; for (int i = 0; i < block_size; i++) {
fpga_reg_set(REG_VENDOR_SCR, *buffer++ = ((data >> 24) & 0xFF);
(LCMXO2_CFGRXDR << VENDOR_SCR_ADDRESS_BIT) | data <<= 8;
((block_size - 1) << VENDOR_SCR_LENGTH_BIT) | length -= 1;
VENDOR_SCR_START }
); }
while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); }
uint32_t data = fpga_reg_get(REG_VENDOR_DATA);
data <<= ((4 - block_size) * 8); static void lcmxo2_write_data (uint8_t *buffer, uint32_t length) {
for (int i = 0; i < block_size; i++) { while (length > 0) {
*buffer++ = ((data >> 24) & 0xFF); uint32_t block_size = (length > 4) ? 4 : length;
data <<= 8; uint32_t data = 0;
length -= 1; for (int i = 0; i < block_size; i++) {
} data = ((data << 8) | *buffer++);
} length -= 1;
} }
data <<= ((4 - block_size) * 8);
static void lcmxo2_write_data (uint8_t *buffer, uint32_t length) { fpga_reg_set(REG_VENDOR_DATA, data);
while (length > 0) { fpga_reg_set(REG_VENDOR_SCR,
uint32_t block_size = (length > 4) ? 4 : length; (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) |
uint32_t data = 0; ((block_size - 1) << VENDOR_SCR_LENGTH_BIT) |
for (int i = 0; i < block_size; i++) { VENDOR_SCR_WRITE |
data = ((data << 8) | *buffer++); VENDOR_SCR_START
length -= 1; );
} while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
data <<= ((4 - block_size) * 8); }
fpga_reg_set(REG_VENDOR_DATA, data); }
fpga_reg_set(REG_VENDOR_SCR, #endif
(LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) |
((block_size - 1) << VENDOR_SCR_LENGTH_BIT) | static void lcmxo2_reset_bus (void) {
VENDOR_SCR_WRITE | #ifdef LCMXO2_I2C
VENDOR_SCR_START uint8_t reset_data = 0;
); hw_i2c_raw(LCMXO2_I2C_ADDR_RESET, &reset_data, sizeof(reset_data), NULL, 0);
while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); #else
} lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_RSTE);
} lcmxo2_reg_set(LCMXO2_CFGCR, 0);
#endif
static bool lcmxo2_wait_busy (void) { }
uint8_t status[4];
do { static void lcmxo2_execute_cmd (uint8_t cmd, uint32_t arg, cmd_type_t type, uint8_t *buffer, uint8_t length, bool write) {
lcmxo2_execute_cmd(LSC_READ_STATUS, 0, CMD_NORMAL); #ifdef LCMXO2_I2C
lcmxo2_read_data(status, 4); uint8_t packet[20] = { cmd, ((arg >> 16) & 0xFF), ((arg >> 8) & 0xFF), (arg & 0xFF) };
} while(status[2] & LSC_STATUS_1_BUSY); int packet_length = ((type == CMD_TWO_OP) ? 3 : 4);
return status[2] & LSC_STATUS_1_FAIL; if (write) {
} for (int i = 0; i < length; i++) {
packet[packet_length + i] = buffer[i];
static bool lcmxo2_enable_flash (void) { }
lcmxo2_execute_cmd(ISC_ENABLE_X, 0x080000, CMD_NORMAL); packet_length += length;
return lcmxo2_wait_busy(); }
} hw_i2c_raw(LCMXO2_I2C_ADDR_CFG, packet, packet_length, buffer, (write ? 0 : length));
#else
static void lcmxo2_disable_flash (void) { uint32_t data = (cmd << 24) | (arg & 0x00FFFFFF);
lcmxo2_wait_busy(); lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_WBCE);
lcmxo2_execute_cmd(ISC_DISABLE, 0, CMD_TWO_OP); fpga_reg_set(REG_VENDOR_DATA, data);
lcmxo2_execute_cmd(ISC_NOOP, 0xFFFFFF, CMD_NORMAL); fpga_reg_set(REG_VENDOR_SCR,
} (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) |
(type == CMD_DELAYED ? VENDOR_SCR_DELAY : 0) |
static bool lcmxo2_erase_flash (void) { ((type == CMD_TWO_OP ? 2 : 3) << VENDOR_SCR_LENGTH_BIT) |
lcmxo2_execute_cmd(ISC_ERASE, ISC_ERASE_UFM | ISC_ERASE_CFG, CMD_NORMAL); VENDOR_SCR_WRITE |
return lcmxo2_wait_busy(); VENDOR_SCR_START
} );
while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
static void lcmxo2_reset_flash_address (void) { if (length > 0) {
lcmxo2_execute_cmd(LSC_INIT_ADDRESS, 0, CMD_NORMAL); if (write) {
} lcmxo2_write_data(buffer, length);
} else {
static bool lcmxo2_write_flash_page (uint8_t *buffer) { lcmxo2_read_data(buffer, length);
lcmxo2_execute_cmd(LSC_PROG_INCR_NV, 1, CMD_NORMAL); }
lcmxo2_write_data(buffer, FLASH_PAGE_SIZE); }
return lcmxo2_wait_busy(); lcmxo2_reg_set(LCMXO2_CFGCR, 0);
} #endif
}
static void lcmxo2_read_flash_page (uint8_t *buffer) {
lcmxo2_execute_cmd(LSC_READ_INCR_NV, 1, CMD_DELAYED); static void lcmxo2_read_device_id (uint8_t *id) {
lcmxo2_read_data(buffer, FLASH_PAGE_SIZE); lcmxo2_execute_cmd(IDCODE_PUB, 0, CMD_NORMAL, id, DEVICE_ID_SIZE, false);
} }
static void lcmxo2_program_done (void) { static uint32_t lcmxo2_read_status (void) {
lcmxo2_execute_cmd(ISC_PROGRAM_DONE, 0, CMD_NORMAL); uint32_t status = 0;
lcmxo2_wait_busy(); lcmxo2_execute_cmd(LSC_READ_STATUS, 0, CMD_NORMAL, (uint8_t *) (&status), 4, false);
} return SWAP32(status);
}
static void lcmxo2_refresh (void) {
lcmxo2_execute_cmd(LSC_REFRESH, 0, CMD_TWO_OP); static bool lcmxo2_wait_busy (void) {
} uint32_t status;
do {
static vendor_error_t lcmxo2_fail (vendor_error_t error) { status = lcmxo2_read_status();
lcmxo2_disable_flash(); } while(status & LSC_STATUS_BUSY);
lcmxo2_cleanup(); return (status & LSC_STATUS_FAIL);
return error; }
}
static bool lcmxo2_enable_flash (void) {
uint32_t vendor_flash_size (void) { #ifdef LCMXO2_I2C
return (FLASH_PAGE_SIZE * FLASH_NUM_PAGES); lcmxo2_execute_cmd(ISC_ENABLE, 0x080000, CMD_TWO_OP, NULL, 0, false);
} #else
lcmxo2_execute_cmd(ISC_ENABLE_X, 0x080000, CMD_NORMAL, NULL, 0, false);
vendor_error_t vendor_backup (uint32_t address, uint32_t *length) { #endif
uint8_t buffer[FLASH_PAGE_SIZE]; return lcmxo2_wait_busy();
}
*length = 0;
static void lcmxo2_disable_flash (void) {
lcmxo2_reset_bus(); lcmxo2_wait_busy();
if (lcmxo2_enable_flash()) { lcmxo2_execute_cmd(ISC_DISABLE, 0, CMD_TWO_OP, NULL, 0, false);
return lcmxo2_fail(VENDOR_ERROR_INIT); lcmxo2_execute_cmd(ISC_NOOP, 0xFFFFFF, CMD_NORMAL, NULL, 0, false);
} }
lcmxo2_reset_flash_address();
for (int i = 0; i < (FLASH_PAGE_SIZE * FLASH_NUM_PAGES); i += FLASH_PAGE_SIZE) { static bool lcmxo2_erase_featbits (void) {
lcmxo2_read_flash_page(buffer); lcmxo2_execute_cmd(ISC_ERASE, ISC_ERASE_FEATURE, CMD_NORMAL, NULL, 0, false);
fpga_mem_write(address + i, FLASH_PAGE_SIZE, buffer); return lcmxo2_wait_busy();
*length += FLASH_PAGE_SIZE; }
}
lcmxo2_disable_flash(); static bool lcmxo2_erase_flash (void) {
lcmxo2_cleanup(); lcmxo2_execute_cmd(ISC_ERASE, (ISC_ERASE_UFM | ISC_ERASE_CFG), CMD_NORMAL, NULL, 0, false);
return lcmxo2_wait_busy();
return VENDOR_OK; }
}
static void lcmxo2_reset_flash_address (void) {
vendor_error_t vendor_update (uint32_t address, uint32_t length) { lcmxo2_execute_cmd(LSC_INIT_ADDRESS, 0, CMD_NORMAL, NULL, 0, false);
uint8_t buffer[FLASH_PAGE_SIZE]; }
uint8_t verify_buffer[FLASH_PAGE_SIZE];
static bool lcmxo2_write_flash_page (uint8_t *buffer) {
if (length == 0) { lcmxo2_execute_cmd(LSC_PROG_INCR_NV, 1, CMD_NORMAL, buffer, FLASH_PAGE_SIZE, true);
return VENDOR_ERROR_ARGS; return lcmxo2_wait_busy();
} }
if ((length % FLASH_PAGE_SIZE) != 0) {
return VENDOR_ERROR_ARGS; static void lcmxo2_read_flash_page (uint8_t *buffer) {
} lcmxo2_execute_cmd(LSC_READ_INCR_NV, 1, CMD_DELAYED, buffer, FLASH_PAGE_SIZE, false);
if (length > (FLASH_PAGE_SIZE * FLASH_NUM_PAGES)) { }
return VENDOR_ERROR_ARGS;
} static bool lcmxo2_program_done (void) {
lcmxo2_execute_cmd(ISC_PROGRAM_DONE, 0, CMD_NORMAL, NULL, 0, false);
lcmxo2_reset_bus(); return lcmxo2_wait_busy();
if (lcmxo2_enable_flash()) { }
return lcmxo2_fail(VENDOR_ERROR_INIT);
} static bool lcmxo2_write_featbits (uint8_t *buffer) {
if (lcmxo2_erase_flash()) { lcmxo2_execute_cmd(LSC_PROG_FEABITS, 0, CMD_NORMAL, buffer, FEATBITS_SIZE, true);
return lcmxo2_fail(VENDOR_ERROR_ERASE); return lcmxo2_wait_busy();
} }
lcmxo2_reset_flash_address();
for (int i = 0; i < length; i += FLASH_PAGE_SIZE) { static void lcmxo2_read_featbits (uint8_t *buffer) {
fpga_mem_read(address + i, FLASH_PAGE_SIZE, buffer); lcmxo2_execute_cmd(LSC_READ_FEABITS, 0, CMD_NORMAL, buffer, FEATBITS_SIZE, false);
if (lcmxo2_write_flash_page(buffer)) { }
return lcmxo2_fail(VENDOR_ERROR_PROGRAM);
} static void lcmxo2_refresh (void) {
} lcmxo2_execute_cmd(LSC_REFRESH, 0, CMD_TWO_OP, NULL, 0, false);
lcmxo2_program_done(); }
lcmxo2_reset_flash_address();
for (int i = 0; i < length; i += FLASH_PAGE_SIZE) { static vendor_error_t lcmxo2_fail (vendor_error_t error) {
lcmxo2_read_flash_page(buffer); lcmxo2_disable_flash();
fpga_mem_read(address + i, FLASH_PAGE_SIZE, verify_buffer); return error;
for (int x = 0; x < FLASH_PAGE_SIZE; x++) { }
if (buffer[x] != verify_buffer[x]) {
return lcmxo2_fail(VENDOR_ERROR_VERIFY);
} uint32_t vendor_flash_size (void) {
} return (FLASH_PAGE_SIZE * FLASH_NUM_PAGES);
} }
lcmxo2_disable_flash();
lcmxo2_cleanup(); vendor_error_t vendor_backup (uint32_t address, uint32_t *length) {
uint8_t buffer[FLASH_PAGE_SIZE];
return VENDOR_OK;
} *length = 0;
vendor_error_t vendor_reconfigure (void) { lcmxo2_reset_bus();
lcmxo2_reset_bus(); if (lcmxo2_enable_flash()) {
lcmxo2_refresh(); return lcmxo2_fail(VENDOR_ERROR_INIT);
lcmxo2_cleanup(); }
lcmxo2_reset_flash_address();
return VENDOR_OK; for (int i = 0; i < (FLASH_PAGE_SIZE * FLASH_NUM_PAGES); i += FLASH_PAGE_SIZE) {
} lcmxo2_read_flash_page(buffer);
fpga_mem_write(address + i, FLASH_PAGE_SIZE, buffer);
*length += FLASH_PAGE_SIZE;
}
lcmxo2_disable_flash();
return VENDOR_OK;
}
vendor_error_t vendor_update (uint32_t address, uint32_t length) {
uint8_t buffer[FLASH_PAGE_SIZE];
uint8_t verify_buffer[FLASH_PAGE_SIZE];
if (length == 0) {
return VENDOR_ERROR_ARGS;
}
if ((length % FLASH_PAGE_SIZE) != 0) {
return VENDOR_ERROR_ARGS;
}
if (length > (FLASH_PAGE_SIZE * FLASH_NUM_PAGES)) {
return VENDOR_ERROR_ARGS;
}
lcmxo2_reset_bus();
if (lcmxo2_enable_flash()) {
return lcmxo2_fail(VENDOR_ERROR_INIT);
}
if (lcmxo2_erase_flash()) {
return lcmxo2_fail(VENDOR_ERROR_ERASE);
}
lcmxo2_reset_flash_address();
for (int i = 0; i < length; i += FLASH_PAGE_SIZE) {
fpga_mem_read(address + i, FLASH_PAGE_SIZE, buffer);
if (lcmxo2_write_flash_page(buffer)) {
return lcmxo2_fail(VENDOR_ERROR_PROGRAM);
}
}
if (lcmxo2_program_done()) {
return lcmxo2_fail(VENDOR_ERROR_PROGRAM);
}
lcmxo2_reset_flash_address();
for (int i = 0; i < length; i += FLASH_PAGE_SIZE) {
lcmxo2_read_flash_page(buffer);
fpga_mem_read(address + i, FLASH_PAGE_SIZE, verify_buffer);
for (int x = 0; x < FLASH_PAGE_SIZE; x++) {
if (buffer[x] != verify_buffer[x]) {
return lcmxo2_fail(VENDOR_ERROR_VERIFY);
}
}
}
lcmxo2_disable_flash();
return VENDOR_OK;
}
vendor_error_t vendor_reconfigure (void) {
lcmxo2_reset_bus();
lcmxo2_refresh();
return VENDOR_OK;
}
#define FEATBITS_0_SPI_OFF (1 << 1)
#define FEATBITS_1_PROGRAMN_OFF (1 << 5)
typedef enum {
CMD_GET_PRIMER_ID = '?',
CMD_PROBE_FPGA = '#',
CMD_RESTART = '$',
CMD_GET_DEVICE_ID = 'I',
CMD_ENABLE_FLASH = 'E',
CMD_ERASE_FLASH = 'X',
CMD_RESET_ADDRESS = 'A',
CMD_WRITE_PAGE = 'W',
CMD_READ_PAGE = 'R',
CMD_PROGRAM_DONE = 'F',
CMD_INIT_FEATBITS = 'Q',
CMD_REFRESH = 'B',
} primer_cmd_e;
static bool primer_check_rx_length (primer_cmd_e cmd, size_t rx_length) {
switch (cmd) {
case CMD_WRITE_PAGE:
return (rx_length != FLASH_PAGE_SIZE);
default:
return (rx_length != 0);
}
return true;
}
static bool lcmxo2_init_featbits (void) {
uint8_t programmed[2] = { 0x00, 0x00 };
uint8_t target[2] = { FEATBITS_0_SPI_OFF, FEATBITS_1_PROGRAMN_OFF };
lcmxo2_read_featbits(programmed);
if ((programmed[0] == target[0]) && (programmed[1] == target[1])) {
return false;
}
if (lcmxo2_erase_featbits()) {
return true;
}
if (lcmxo2_write_featbits(target)) {
return true;
}
lcmxo2_read_featbits(programmed);
if ((programmed[0] != target[0]) || (programmed[1] != target[1])) {
return true;
}
return false;
}
void vendor_initial_configuration (vendor_get_cmd_t get_cmd, vendor_send_response_t send_response) {
bool runninng = true;
primer_cmd_e cmd;
uint8_t buffer[256];
uint8_t rx_length;
uint8_t tx_length;
bool error;
lcmxo2_reset_bus();
while (runninng) {
cmd = get_cmd(buffer, &rx_length);
tx_length = 0;
error = false;
if (primer_check_rx_length(cmd, rx_length)) {
send_response(cmd, NULL, 0, true);
continue;
}
switch (cmd) {
case CMD_GET_PRIMER_ID:
buffer[0] = 'M';
buffer[1] = 'X';
buffer[2] = 'O';
buffer[3] = '2';
tx_length = 4;
break;
case CMD_PROBE_FPGA:
buffer[0] = fpga_id_get();
tx_length = 1;
break;
case CMD_RESTART:
runninng = false;
break;
case CMD_GET_DEVICE_ID:
lcmxo2_read_device_id(buffer);
tx_length = 4;
break;
case CMD_ENABLE_FLASH:
error = lcmxo2_enable_flash();
break;
case CMD_ERASE_FLASH:
error = lcmxo2_erase_flash();
break;
case CMD_RESET_ADDRESS:
lcmxo2_reset_flash_address();
break;
case CMD_WRITE_PAGE:
error = lcmxo2_write_flash_page(buffer);
break;
case CMD_READ_PAGE:
lcmxo2_read_flash_page(buffer);
tx_length = FLASH_PAGE_SIZE;
break;
case CMD_PROGRAM_DONE:
error = lcmxo2_program_done();
break;
case CMD_INIT_FEATBITS:
error = lcmxo2_init_featbits();
break;
case CMD_REFRESH:
lcmxo2_refresh();
hw_delay_ms(200);
break;
default:
error = true;
break;
}
send_response(cmd, buffer, tx_length, error);
}
}

View File

@ -3,11 +3,11 @@
.fpu softvfp .fpu softvfp
.thumb .thumb
.section .text.Reset_Handler .section .text.Reset_Handler
.type Reset_Handler, %function .type Reset_Handler, %function
Reset_Handler: Reset_Handler:
.global Reset_Handler .global Reset_Handler
cpsid i
init_text: init_text:
ldr r0, =_stext ldr r0, =_stext
@ -51,8 +51,9 @@ boot:
ldr r0, =_app_header ldr r0, =_app_header
push {r0} push {r0}
bl set_vector_table_offset bl hw_set_vector_table
pop {r0} pop {r0}
ldr r1, [r0, #0] ldr r1, [r0, #0]
msr MSP, r1 msr MSP, r1
ldr r1, [r0, #4] ldr r1, [r0, #4]

View File

@ -4,17 +4,14 @@
#include "update.h" #include "update.h"
void no_valid_image (void) {
hw_loader_init();
hw_gpio_set(GPIO_ID_LED);
}
void loader (void) { void loader (void) {
if (update_check()) { if (update_check()) {
hw_loader_init(); hw_loader_init();
update_perform(); update_perform();
} }
} }
void no_valid_image (void) {
hw_gpio_set(GPIO_ID_LED);
}
void set_vector_table_offset (uint32_t offset) {
SCB->VTOR = (__IOM uint32_t) (offset);
}

View File

@ -0,0 +1,53 @@
.syntax unified
.cpu cortex-m0plus
.fpu softvfp
.thumb
.section .text.Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
.global Reset_Handler
ldr r0, =_estack
msr msp, r0
init_bss:
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b 2f
1:
str r3, [r2]
adds r2, r2, #4
2:
cmp r2, r4
bcc 1b
run:
ldr r0, =_header
bl hw_set_vector_table
bl primer
loop:
b loop
.section .text.Default_Handler, "ax", %progbits
Default_Handler:
.global Default_Handler
b Default_Handler
.section .isr_vector, "a", %progbits
.type g_pfnVectors, %object
g_pfnVectors:
.global g_pfnVectors
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.weak NMI_Handler
.thumb_set NMI_Handler, Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler, Default_Handler

View File

@ -0,0 +1,75 @@
#include <stdbool.h>
#include <string.h>
#include "hw.h"
#include "vendor.h"
static const uint8_t cmd_token[3] = { 'C', 'M', 'D' };
static const uint8_t rsp_token[3] = { 'R', 'S', 'P' };
static const uint8_t err_token[3] = { 'E', 'R', 'R' };
static void primer_get_and_calculate_crc32 (uint8_t *buffer, uint8_t rx_length, uint32_t *crc32) {
hw_uart_read(buffer, rx_length);
*crc32 = hw_crc32_calculate(buffer, rx_length);
}
static uint8_t primer_get_command (uint8_t *buffer, uint8_t *rx_length) {
uint32_t calculated_crc32;
uint32_t received_crc32;
uint8_t token[4];
while (1) {
hw_crc32_reset();
primer_get_and_calculate_crc32(token, 4, &calculated_crc32);
if (memcmp(token, cmd_token, 3) != 0) {
continue;
}
primer_get_and_calculate_crc32(rx_length, 1, &calculated_crc32);
if (*rx_length > 0) {
primer_get_and_calculate_crc32(buffer, *rx_length, &calculated_crc32);
}
hw_uart_read((uint8_t *) (&received_crc32), sizeof(received_crc32));
if (calculated_crc32 == received_crc32) {
break;
}
}
return token[3];
}
static void primer_send_and_calculate_crc32 (uint8_t *buffer, uint8_t tx_length, uint32_t *crc32) {
hw_uart_write(buffer, tx_length);
*crc32 = hw_crc32_calculate(buffer, tx_length);
}
static void primer_send_response (uint8_t cmd, uint8_t *buffer, uint8_t tx_length, bool error) {
uint32_t calculated_crc32;
uint8_t *token = (uint8_t *) (error ? err_token : rsp_token);
uint8_t length = (error ? 0 : tx_length);
hw_crc32_reset();
primer_send_and_calculate_crc32(token, 3, &calculated_crc32);
primer_send_and_calculate_crc32(&cmd, 1, &calculated_crc32);
primer_send_and_calculate_crc32(&length, 1, &calculated_crc32);
if ((!error) && (tx_length > 0)) {
primer_send_and_calculate_crc32(buffer, tx_length, &calculated_crc32);
}
hw_uart_write((uint8_t *) (&calculated_crc32), sizeof(calculated_crc32));
}
void primer (void) {
hw_primer_init();
vendor_initial_configuration(primer_get_command, primer_send_response);
hw_uart_wait_busy();
hw_reset(NULL);
}

View File

@ -24,6 +24,7 @@ typedef enum {
CHUNK_ID_MCU_DATA = 2, CHUNK_ID_MCU_DATA = 2,
CHUNK_ID_FPGA_DATA = 3, CHUNK_ID_FPGA_DATA = 3,
CHUNK_ID_BOOTLOADER_DATA = 4, CHUNK_ID_BOOTLOADER_DATA = 4,
CHUNK_ID_PRIMER_DATA = 5,
} chunk_id_t; } chunk_id_t;
@ -264,6 +265,9 @@ update_error_t update_prepare (uint32_t address, uint32_t length) {
parameters.bootloader_address = data_address; parameters.bootloader_address = data_address;
break; break;
case CHUNK_ID_PRIMER_DATA:
break;
default: default:
return UPDATE_ERROR_UNKNOWN_CHUNK; return UPDATE_ERROR_UNKNOWN_CHUNK;
} }
@ -274,7 +278,7 @@ update_error_t update_prepare (uint32_t address, uint32_t length) {
void update_start (void) { void update_start (void) {
parameters.magic = UPDATE_MAGIC_START; parameters.magic = UPDATE_MAGIC_START;
hw_loader_reset(&parameters); hw_reset(&parameters);
} }
bool update_check (void) { bool update_check (void) {
@ -317,5 +321,5 @@ void update_perform (void) {
vendor_reconfigure(); vendor_reconfigure();
parameters.magic = 0; parameters.magic = 0;
hw_loader_reset(&parameters); hw_reset(&parameters);
} }

View File

@ -2,6 +2,7 @@
#define VENDOR_H__ #define VENDOR_H__
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -14,11 +15,16 @@ typedef enum {
VENDOR_ERROR_VERIFY, VENDOR_ERROR_VERIFY,
} vendor_error_t; } vendor_error_t;
typedef uint8_t vendor_get_cmd_t (uint8_t *buffer, uint8_t *rx_length);
typedef void vendor_send_response_t (uint8_t cmd, uint8_t *buffer, uint8_t tx_length, bool error);
uint32_t vendor_flash_size (void); uint32_t vendor_flash_size (void);
vendor_error_t vendor_backup (uint32_t address, uint32_t *length); vendor_error_t vendor_backup (uint32_t address, uint32_t *length);
vendor_error_t vendor_update (uint32_t address, uint32_t length); vendor_error_t vendor_update (uint32_t address, uint32_t length);
vendor_error_t vendor_reconfigure (void); vendor_error_t vendor_reconfigure (void);
void vendor_initial_configuration (vendor_get_cmd_t get_cmd, vendor_send_response_t send_response);
#endif #endif

512
sw/pc/primer.py Normal file
View File

@ -0,0 +1,512 @@
#!/usr/bin/env python3
import io
import os
import serial
import signal
import sys
import time
from binascii import crc32
from sc64 import SC64
from typing import Callable, Optional
class Utils:
__progress_active = False
@staticmethod
def log(message: str='') -> None:
print(message)
@staticmethod
def log_no_end(message: str='') -> None:
print(message, end='', flush=True)
@staticmethod
def info(message: str='') -> None:
print(f'\033[92m{message}\033[0m')
@staticmethod
def warning(message: str='') -> None:
print(f'\033[93m{message}\033[0m')
@staticmethod
def die(reason: str) -> None:
print(f'\033[91m{reason}\033[0m')
exit(-1)
@property
def get_progress_active(self):
return self.__progress_active
def progress(self, length: int, position: int, description: str) -> None:
value = ((position / length) * 100.0)
if (position == 0):
self.__progress_active = True
Utils.log_no_end(f'\r{value:5.1f}%: [{description}]')
if (position == length):
Utils.log()
self.__progress_active = False
def exit_warning(self):
if (self.__progress_active):
Utils.log()
Utils.warning('Ctrl-C is prohibited during bring-up procedure')
class SC64UpdateDataException(Exception):
pass
class SC64UpdateData:
__UPDATE_TOKEN = b'SC64 Update v2.0'
__CHUNK_ID_UPDATE_INFO = 1
__CHUNK_ID_MCU_DATA = 2
__CHUNK_ID_FPGA_DATA = 3
__CHUNK_ID_BOOTLOADER_DATA = 4
__CHUNK_ID_PRIMER_DATA = 5
__update_info: Optional[str]
__mcu_data: Optional[bytes]
__fpga_data: Optional[bytes]
__bootloader_data: Optional[bytes]
__primer_data: Optional[bytes]
def __load_int(self, f: io.BufferedReader) -> int:
try:
data = f.read(4)
if (len(data) != 4):
raise ValueError('Read size did not match requested amount')
value = int.from_bytes(data, byteorder='little')
except ValueError as e:
raise SC64UpdateDataException(f'Error while reading chunk header: {e}')
return value
def __load_chunk(self, f: io.BufferedReader) -> tuple[int, bytes]:
id = self.__load_int(f)
aligned_length = self.__load_int(f)
checksum = self.__load_int(f)
data_length = self.__load_int(f)
data = f.read(data_length)
align = (aligned_length - 4 - 4 - data_length)
f.seek(align, io.SEEK_CUR)
if (crc32(data) != checksum):
raise SC64UpdateDataException(f'Invalid checksum for chunk id [{id}] inside update file')
return (id, data)
def load(self, path: str, require_all: bool=False) -> None:
try:
with open(path, 'rb') as f:
if (f.read(len(self.__UPDATE_TOKEN)) != self.__UPDATE_TOKEN):
raise SC64UpdateDataException('Invalid update file header')
while (f.peek(1) != b''):
(id, data) = self.__load_chunk(f)
if (id == self.__CHUNK_ID_UPDATE_INFO):
self.__update_info = data.decode('ascii')
elif (id == self.__CHUNK_ID_MCU_DATA):
self.__mcu_data = data
elif (id == self.__CHUNK_ID_FPGA_DATA):
self.__fpga_data = data
elif (id == self.__CHUNK_ID_BOOTLOADER_DATA):
self.__bootloader_data = data
elif (id == self.__CHUNK_ID_PRIMER_DATA):
self.__primer_data = data
else:
raise SC64UpdateDataException('Unknown chunk inside update file')
if (require_all):
if (not self.__update_info):
raise SC64UpdateDataException('No update info inside update file')
if (not self.__mcu_data):
raise SC64UpdateDataException('No MCU data inside update file')
if (not self.__fpga_data):
raise SC64UpdateDataException('No FPGA data inside update file')
if (not self.__bootloader_data):
raise SC64UpdateDataException('No bootloader data inside update file')
if (not self.__primer_data):
raise SC64UpdateDataException('No primer data inside update file')
except IOError as e:
raise SC64UpdateDataException(f'IO error while loading update data: {e}')
def get_update_info(self) -> Optional[str]:
return self.__update_info
def get_mcu_data(self) -> Optional[bytes]:
return self.__mcu_data
def get_fpga_data(self) -> Optional[bytes]:
return self.__fpga_data
def get_bootloader_data(self) -> Optional[bytes]:
return self.__bootloader_data
def get_primer_data(self) -> Optional[bytes]:
return self.__primer_data
class STM32BootloaderException(Exception):
pass
class STM32Bootloader:
__INIT = b'\x7F'
__ACK = b'\x79'
__NACK = b'\x1F'
__MEMORY_RW_MAX_SIZE = 256
__FLASH_LOAD_ADDRESS = 0x08000000
__FLASH_MAX_LOAD_SIZE = 0x8000
__RAM_LOAD_ADDRESS = 0x20001000
__RAM_MAX_LOAD_SIZE = 0x1000
DEV_ID_STM32G030XX = b'\x04\x66'
__connected = False
def __init__(self, write: Callable[[bytes], None], read: Callable[[int], bytes], progress: Callable[[int, int, str], None]):
self.__write = write
self.__read = read
self.__progress = progress
def __append_xor(self, data: bytes) -> bytes:
xor = (0xFF if (len(data) == 1) else 0x00)
for b in data:
xor ^= b
return bytes([*data, xor])
def __check_ack(self) -> None:
response = self.__read(1)
if (response == None):
raise STM32BootloaderException('No ACK/NACK byte received')
if (response == self.__NACK):
raise STM32BootloaderException('NACK byte received')
if (response != self.__ACK):
raise STM32BootloaderException('Unknown ACK/NACK byte received')
def __cmd_send(self, cmd: bytes) -> None:
if (len(cmd) != 1):
raise ValueError('Command must contain only one byte')
self.__write(self.__append_xor(cmd))
self.__check_ack()
def __data_write(self, data: bytes) -> None:
self.__write(self.__append_xor(data))
self.__check_ack()
def __data_read(self) -> bytes:
length = self.__read(1)
if (len(length) != 1):
raise STM32BootloaderException('Did not receive length byte')
length = length[0]
data = self.__read(length + 1)
self.__check_ack()
return data
def __get_id(self) -> bytes:
self.__cmd_send(b'\x02')
return self.__data_read()
def __read_memory(self, address: int, length: int) -> bytes:
if (length == 0 or length > self.__MEMORY_RW_MAX_SIZE):
raise ValueError('Wrong data size for read memory command')
self.__cmd_send(b'\x11')
self.__data_write(address.to_bytes(4, byteorder='big'))
self.__data_write(bytes([length - 1]))
return self.__read(length)
def __go(self, address: int) -> None:
self.__cmd_send(b'\x21')
self.__data_write(address.to_bytes(4, byteorder='big'))
self.__connected = False
def __write_memory(self, address: int, data: bytes) -> None:
length = len(data)
if (length == 0 or length > self.__MEMORY_RW_MAX_SIZE):
raise ValueError('Wrong data size for write memory command')
if (length % 4):
raise ValueError('Write memory command requires 4 byte alignment')
self.__cmd_send(b'\x31')
self.__data_write(address.to_bytes(4, byteorder='big'))
self.__data_write(bytes([length - 1, *data]))
def __mass_erase(self) -> None:
self.__cmd_send(b'\x44')
self.__data_write(b'\xFF\xFF')
def __load_memory(self, address: int, data: bytes, description: str='') -> None:
length = len(data)
self.__progress(length, 0, description)
for offset in range(0, length, self.__MEMORY_RW_MAX_SIZE):
chunk = data[offset:offset + self.__MEMORY_RW_MAX_SIZE]
self.__write_memory(address + offset, chunk)
verify = self.__read_memory(address + offset, len(chunk))
if (chunk != verify):
raise STM32BootloaderException('Memory verify failed')
self.__progress(length, offset, description)
self.__progress(length, length, description)
def connect(self, id: int) -> None:
if (not self.__connected):
self.__write(self.__INIT)
self.__check_ack()
self.__connected = True
dev_id = self.__get_id()
if (dev_id != id):
raise STM32BootloaderException('Unknown chip detected')
def load_ram_and_run(self, data: bytes, description: str='') -> None:
if (len(data) > self.__RAM_MAX_LOAD_SIZE):
raise STM32BootloaderException('RAM image too big')
self.__load_memory(self.__RAM_LOAD_ADDRESS, data, description)
self.__go(self.__RAM_LOAD_ADDRESS)
def load_flash_and_run(self, data: bytes, description: str='') -> None:
if (len(data) > self.__FLASH_MAX_LOAD_SIZE):
raise STM32BootloaderException('Flash image too big')
self.__mass_erase()
try:
self.__load_memory(self.__FLASH_LOAD_ADDRESS, data, description)
self.__go(self.__FLASH_LOAD_ADDRESS)
except STM32BootloaderException as e:
self.__mass_erase()
raise STM32BootloaderException(e)
class LCMXO2PrimerException(Exception):
pass
class LCMXO2Primer:
__PRIMER_ID_LCMXO2 = b'MXO2'
__CMD_GET_PRIMER_ID = b'?'
__CMD_PROBE_FPGA = b'#'
__CMD_RESTART = b'$'
__CMD_GET_DEVICE_ID = b'I'
__CMD_ENABLE_FLASH = b'E'
__CMD_ERASE_FLASH = b'X'
__CMD_RESET_ADDRESS = b'A'
__CMD_WRITE_PAGE = b'W'
__CMD_READ_PAGE = b'R'
__CMD_PROGRAM_DONE = b'F'
__CMD_INIT_FEATBITS = b'Q'
__CMD_REFRESH = b'B'
__FLASH_PAGE_SIZE = 16
__FLASH_NUM_PAGES = 11260
__FPGA_PROBE_VALUE = b'\x64'
DEV_ID_LCMXO2_7000HC = b'\x01\x2B\xD0\x43'
def __init__(self, write: Callable[[bytes], None], read: Callable[[int], bytes], progress: Callable[[int, int, str], None]):
self.__write = write
self.__read = read
self.__progress = progress
def __cmd_execute(self, cmd: bytes, data: bytes=b'') -> bytes:
if (len(cmd) != 1):
raise ValueError('Command must contain only one byte')
if (len(data) >= 256):
raise ValueError('Data size too big')
packet = b'CMD' + cmd
packet += len(data).to_bytes(1, byteorder='little')
packet += data
packet += crc32(packet).to_bytes(4, byteorder='little')
self.__write(packet)
response = self.__read(5)
if (len(response) != 5):
raise LCMXO2PrimerException(f'No response received [{cmd}]')
length = int.from_bytes(response[4:5], byteorder='little')
response += self.__read(length)
calculated_checksum = crc32(response)
received_checksum = int.from_bytes(self.__read(4), byteorder='little')
if (response[0:3] != b'RSP'):
raise LCMXO2PrimerException(f'Invalid response token [{response[0:3]} / {cmd}]')
if (response[3:4] != cmd):
raise LCMXO2PrimerException(f'Invalid response command [{cmd} / {response[3]}]')
if (calculated_checksum != received_checksum):
raise LCMXO2PrimerException(f'Invalid response checksum [{cmd}]')
return response[5:]
def connect(self, id: bytes) -> None:
primer_id = self.__cmd_execute(self.__CMD_GET_PRIMER_ID)
if (primer_id != self.__PRIMER_ID_LCMXO2):
raise LCMXO2PrimerException('Invalid primer ID received')
dev_id = self.__cmd_execute(self.__CMD_GET_DEVICE_ID)
if (dev_id != id):
raise LCMXO2PrimerException('Invalid FPGA device id received')
def load_flash_and_run(self, data: bytes, description: str) -> None:
erase_description = f'{description} / Erase'
program_description = f'{description} / Program'
verify_description = f'{description} / Verify'
length = len(data)
if (length > (self.__FLASH_PAGE_SIZE * self.__FLASH_NUM_PAGES)):
raise LCMXO2PrimerException('FPGA data size too big')
self.__cmd_execute(self.__CMD_ENABLE_FLASH)
self.__progress(length, 0, erase_description)
self.__cmd_execute(self.__CMD_ERASE_FLASH)
self.__progress(length, length, erase_description)
try:
self.__cmd_execute(self.__CMD_RESET_ADDRESS)
self.__progress(length, 0, program_description)
for offset in range(0, length, self.__FLASH_PAGE_SIZE):
page_data = data[offset:(offset + self.__FLASH_PAGE_SIZE)]
self.__cmd_execute(self.__CMD_WRITE_PAGE, page_data)
self.__progress(length, offset, program_description)
self.__progress(length, length, program_description)
self.__cmd_execute(self.__CMD_RESET_ADDRESS)
self.__progress(length, 0, verify_description)
for offset in range(0, length, self.__FLASH_PAGE_SIZE):
page_data = data[offset:(offset + self.__FLASH_PAGE_SIZE)]
verify_data = self.__cmd_execute(self.__CMD_READ_PAGE)
self.__progress(length, offset, verify_description)
if (page_data != verify_data):
raise LCMXO2PrimerException('FPGA verification error')
self.__progress(length, length, verify_description)
self.__cmd_execute(self.__CMD_INIT_FEATBITS)
self.__cmd_execute(self.__CMD_PROGRAM_DONE)
self.__cmd_execute(self.__CMD_REFRESH)
if (self.__cmd_execute(self.__CMD_PROBE_FPGA) != self.__FPGA_PROBE_VALUE):
raise LCMXO2PrimerException('Invalid FPGA ID value received')
except LCMXO2PrimerException as e:
self.__cmd_execute(self.__CMD_ENABLE_FLASH)
self.__cmd_execute(self.__CMD_ERASE_FLASH)
self.__cmd_execute(self.__CMD_REFRESH)
self.__cmd_execute(self.__CMD_RESTART)
raise LCMXO2PrimerException(e)
self.__cmd_execute(self.__CMD_RESTART)
class SC64BringUp:
__SERIAL_BAUD: int = 115200
__SERIAL_TIMEOUT: float = 6.0
__INTERVAL_TIME: float = 0.5
def __init__(self, progress: Callable[[int, int, str], None]) -> None:
self.__progress = progress
def load_update_data(self, path: str) -> None:
self.__sc64_update_data = SC64UpdateData()
self.__sc64_update_data.load(path, require_all=True)
def get_update_info(self) -> str:
return self.__sc64_update_data.get_update_info()
def start_bring_up(self, port: str) -> None:
link = None
try:
link = serial.Serial(
port,
baudrate=self.__SERIAL_BAUD,
parity=serial.PARITY_EVEN,
timeout=self.__SERIAL_TIMEOUT,
write_timeout=self.__SERIAL_TIMEOUT
)
stm32_bootloader = STM32Bootloader(link.write, link.read, self.__progress)
lcmxo2_primer = LCMXO2Primer(link.write, link.read, self.__progress)
stm32_bootloader.connect(stm32_bootloader.DEV_ID_STM32G030XX)
stm32_bootloader.load_ram_and_run(self.__sc64_update_data.get_primer_data(), 'FPGA primer -> STM32 RAM')
time.sleep(self.__INTERVAL_TIME)
link.read_all()
lcmxo2_primer.connect(lcmxo2_primer.DEV_ID_LCMXO2_7000HC)
lcmxo2_primer.load_flash_and_run(self.__sc64_update_data.get_fpga_data(), 'FPGA configuration -> LCMXO2 FLASH')
time.sleep(self.__INTERVAL_TIME)
link.read_all()
stm32_bootloader.connect(stm32_bootloader.DEV_ID_STM32G030XX)
stm32_bootloader.load_flash_and_run(self.__sc64_update_data.get_mcu_data(), 'MCU software -> STM32 FLASH')
time.sleep(self.__INTERVAL_TIME)
link.read_all()
sc64 = SC64()
bootloader_description = 'Bootloader -> SC64 FLASH'
bootloader_data = self.__sc64_update_data.get_bootloader_data()
bootloader_length = len(bootloader_data)
self.__progress(bootloader_length, 0, bootloader_description)
sc64.upload_bootloader(bootloader_data)
self.__progress(bootloader_length, bootloader_length, bootloader_description)
finally:
if (link and link.is_open):
link.close()
if __name__ == '__main__':
if (len(sys.argv) != 3):
Utils.die(f'Usage: {sys.argv[0]} serial_port update_file')
port = sys.argv[1]
update_data_path = sys.argv[2]
utils = Utils()
sc64_bring_up = SC64BringUp(progress=utils.progress)
Utils.log()
Utils.info('[ Welcome to SC64 flashcart board bring-up! ]')
Utils.log()
Utils.log(f'Serial port: {port}')
Utils.log(f'Update data path: {os.path.abspath(update_data_path)}')
try:
sc64_bring_up.load_update_data(update_data_path)
except SC64UpdateDataException as e:
Utils.die(f'Provided \'{update_data_path}\' file is invalid: {e}')
Utils.log_no_end('Update info: ')
Utils.log(sc64_bring_up.get_update_info())
Utils.log()
Utils.warning('[ CAUTION ]')
Utils.warning('Configure FTDI chip with provided ft232h_config.xml before continuing')
Utils.warning('Connect SC64 USB port to the same computer you\'re running this script')
Utils.warning('Make sure SC64 USB port is recognized in system before continuing')
Utils.log()
Utils.warning('[ IMPORTANT ]')
Utils.warning('Unplug board from power and reconnect it before proceeding')
Utils.log()
try:
if (input('Type YES to continue: ') != 'YES'):
Utils.die('No confirmation received. Exiting')
Utils.log()
except KeyboardInterrupt:
Utils.log()
Utils.die('Aborted')
original_sigint_handler = signal.getsignal(signal.SIGINT)
try:
signal.signal(signal.SIGINT, lambda *kwargs: utils.exit_warning())
sc64_bring_up.start_bring_up(port)
except (serial.SerialException, STM32BootloaderException, LCMXO2PrimerException) as e:
if (utils.get_progress_active):
Utils.log()
Utils.die(f'Error while running bring-up: {e}')
finally:
signal.signal(signal.SIGINT, original_sigint_handler)
Utils.log()
Utils.info('[ SC64 flashcart board bring-up finished successfully! ]')
Utils.log()

View File

@ -19,6 +19,7 @@ class JedecFile:
__fuse_length: int = 0 __fuse_length: int = 0
__fuse_offset: int = 0 __fuse_offset: int = 0
__fuse_data: bytes = b'' __fuse_data: bytes = b''
__fuse_ignore: bool = False
__byte_buffer: int = 0 __byte_buffer: int = 0
def __handle_q_field(self, f: BufferedRandom) -> None: def __handle_q_field(self, f: BufferedRandom) -> None:
@ -77,6 +78,19 @@ class JedecFile:
else: else:
raise JedecError('Unexpected byte inside L field fuse data') raise JedecError('Unexpected byte inside L field fuse data')
def __handle_n_field(self, f: BufferedRandom) -> None:
data = b''
buffer = b''
while (buffer != b'*'):
buffer = f.read(1)
if (buffer == b''):
raise JedecError('Unexpected end of file')
if (buffer != b'*'):
data += buffer
if (data == b'OTE END CONFIG DATA'):
self.__fuse_length = self.__fuse_offset
self.__fuse_ignore = True
def __ignore_field(self, f: BufferedRandom) -> None: def __ignore_field(self, f: BufferedRandom) -> None:
data = None data = None
while (data != b'*'): while (data != b'*'):
@ -88,6 +102,7 @@ class JedecFile:
self.__fuse_length = 0 self.__fuse_length = 0
self.__fuse_offset = 0 self.__fuse_offset = 0
self.__fuse_data = b'' self.__fuse_data = b''
self.__fuse_ignore = False
self.__byte_buffer = 0 self.__byte_buffer = 0
field = None field = None
@ -104,8 +119,10 @@ class JedecFile:
field = f.read(1) field = f.read(1)
if (field == b'Q'): if (field == b'Q'):
self.__handle_q_field(f) self.__handle_q_field(f)
elif (field == b'L'): elif (field == b'L' and not self.__fuse_ignore):
self.__handle_l_field(f) self.__handle_l_field(f)
elif (field == b'N'):
self.__handle_n_field(f)
elif (field == b'\r' or field == b'\n'): elif (field == b'\r' or field == b'\n'):
pass pass
elif (field == b'\x03'): elif (field == b'\x03'):
@ -134,6 +151,7 @@ class SC64UpdateData:
__CHUNK_ID_MCU_DATA = 2 __CHUNK_ID_MCU_DATA = 2
__CHUNK_ID_FPGA_DATA = 3 __CHUNK_ID_FPGA_DATA = 3
__CHUNK_ID_BOOTLOADER_DATA = 4 __CHUNK_ID_BOOTLOADER_DATA = 4
__CHUNK_ID_PRIMER_DATA = 5
__data = b'' __data = b''
@ -172,6 +190,9 @@ class SC64UpdateData:
def add_bootloader_data(self, data: bytes) -> None: def add_bootloader_data(self, data: bytes) -> None:
self.__add_chunk(self.__CHUNK_ID_BOOTLOADER_DATA, data) self.__add_chunk(self.__CHUNK_ID_BOOTLOADER_DATA, data)
def add_primer_data(self, data: bytes) -> None:
self.__add_chunk(self.__CHUNK_ID_PRIMER_DATA, data)
def get_update_data(self) -> bytes: def get_update_data(self) -> bytes:
return self.__data return self.__data
@ -183,6 +204,7 @@ if __name__ == "__main__":
parser.add_argument('--mcu', metavar='mcu_path', required=False, help='path to MCU update data') parser.add_argument('--mcu', metavar='mcu_path', required=False, help='path to MCU update data')
parser.add_argument('--fpga', metavar='fpga_path', required=False, help='path to FPGA update data') parser.add_argument('--fpga', metavar='fpga_path', required=False, help='path to FPGA update data')
parser.add_argument('--boot', metavar='bootloader_path', required=False, help='path to N64 bootloader update data') parser.add_argument('--boot', metavar='bootloader_path', required=False, help='path to N64 bootloader update data')
parser.add_argument('--primer', metavar='primer_path', required=False, help='path to MCU board bring-up data')
parser.add_argument('output', metavar='output_path', help='path to final update data') parser.add_argument('output', metavar='output_path', help='path to final update data')
if (len(sys.argv) <= 1): if (len(sys.argv) <= 1):
@ -218,6 +240,10 @@ if __name__ == "__main__":
with open(args.boot, 'rb+') as f: with open(args.boot, 'rb+') as f:
update.add_bootloader_data(f.read()) update.add_bootloader_data(f.read())
if (args.primer):
with open(args.primer, 'rb+') as f:
update.add_primer_data(f.read())
with open(args.output, 'wb+') as f: with open(args.output, 'wb+') as f:
f.write(update.get_update_data()) f.write(update.get_update_data())
except JedecError as e: except JedecError as e: