diff --git a/build.sh b/build.sh index 10e5af8..51b58b7 100755 --- a/build.sh +++ b/build.sh @@ -6,9 +6,9 @@ PACKAGE_FILE_NAME="SC64" FILES=( "./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/primer.py" + "./sw/pc/requirements.txt" "./sw/pc/sc64.py" "./sw/update/sc64.upd" "./LICENSE" @@ -89,6 +89,7 @@ build_update () { --mcu ../controller/build/app/app.bin \ --fpga ../../fw/project/lcmxo2/impl1/sc64_impl1.jed \ --boot ../bootloader/build/bootloader.bin \ + --primer ../controller/build/primer/primer.bin \ sc64.upd popd > /dev/null @@ -113,7 +114,7 @@ print_usage () { echo "usage: ./build.sh [bootloader] [controller] [fpga] [update] [release] [-c] [--help]" echo "parameters:" echo " bootloader - compile N64 bootloader software" - echo " controller - compile ARM controller software" + echo " controller - compile MCU controller software" echo " fpga - compile FPGA design" echo " update - compile all software and designs" echo " release - collect and zip files for release (triggers 'update' build)" diff --git a/fw/project/lcmxo2/sc64.lpf b/fw/project/lcmxo2/sc64.lpf index dfc6bdd..ac7502d 100644 --- a/fw/project/lcmxo2/sc64.lpf +++ b/fw/project/lcmxo2/sc64.lpf @@ -200,7 +200,7 @@ LOCATE COMP "usb_miosi[6]" SITE "14" ; LOCATE COMP "usb_miosi[7]" SITE "13" ; LOCATE COMP "usb_miso" SITE "10" ; LOCATE COMP "usb_pwrsav" SITE "2" ; -SYSCONFIG SDM_PORT=DISABLE ; +SYSCONFIG SDM_PORT=DISABLE I2C_PORT=ENABLE ; VOLTAGE 3.300 V; FREQUENCY NET "clk" 100.000000 MHz PAR_ADJ 10.000000 ; BLOCK PATH TO PORT "mcu_int" ; diff --git a/sw/bootloader/Makefile b/sw/bootloader/Makefile index bdbb28a..18001b4 100644 --- a/sw/bootloader/Makefile +++ b/sw/bootloader/Makefile @@ -70,7 +70,7 @@ $(BUILD_DIR)/bootloader.elf: $(OBJS) N64.ld $(BUILD_DIR)/bootloader.bin: $(BUILD_DIR)/bootloader.elf @$(OBJCOPY) -O binary $< $@ @chksum64 $@ > /dev/null - @truncate --size=1028k $@ + @$(PYTHON) tools/strip.py $@ $(BUILD_DIR)/bootloader.hex: $(BUILD_DIR)/bootloader.bin @$(OBJCOPY) -I binary -O ihex $< $@ diff --git a/sw/bootloader/N64.ld b/sw/bootloader/N64.ld index 0d88ed9..a44e160 100644 --- a/sw/bootloader/N64.ld +++ b/sw/bootloader/N64.ld @@ -55,6 +55,13 @@ SECTIONS { . += __stack_size; _sp = .; + .fill : { + . = ALIGN(1024) - 4; + LONG(0xDEADBEEF); + FILL(0xFFFFFFFF); + . = ORIGIN(rom) + LENGTH(rom); + } > rom + /DISCARD/ : { *(.MIPS.*) } diff --git a/sw/bootloader/src/ipl2.S b/sw/bootloader/src/ipl2.S index abf4f4b..ed074f0 100644 --- a/sw/bootloader/src/ipl2.S +++ b/sw/bootloader/src/ipl2.S @@ -1,6 +1,7 @@ .set noat .set noreorder + .section .data.ipl2 ipl2: .global ipl2 diff --git a/sw/bootloader/tools/strip.py b/sw/bootloader/tools/strip.py new file mode 100644 index 0000000..0693835 --- /dev/null +++ b/sw/bootloader/tools/strip.py @@ -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) diff --git a/sw/controller/app.ld b/sw/controller/app.ld index f90c3fd..bea3e0c 100644 --- a/sw/controller/app.ld +++ b/sw/controller/app.ld @@ -1,6 +1,6 @@ MEMORY { loader (rx) : org = 0x08000000, len = 4k - rom (rx) : org = 0x08001000, len = 28k + code (rx) : org = 0x08001000, len = 28k ram (rwx) : org = 0x20000000, len = 8k } @@ -17,7 +17,7 @@ SECTIONS { . = ALIGN(4); KEEP(*(.isr_vector)) . = ALIGN(4); - } > rom + } > code .text : { . = ALIGN(4); @@ -27,18 +27,16 @@ SECTIONS { *(.glue_7t) . = ALIGN(4); _etext = .; - } > rom + } > code .bss : { . = ALIGN(4); _sbss = .; - __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; - __bss_end__ = _ebss; } > ram .data : { @@ -49,14 +47,14 @@ SECTIONS { *(.data*) . = ALIGN(4); _edata = .; - } > ram AT > rom + } > ram AT > code .rodata : { . = ALIGN(4); *(.rodata) *(.rodata*) . = ALIGN(4); - } > rom + } > code _estack = ORIGIN(ram) + LENGTH(ram); } diff --git a/sw/controller/app.mk b/sw/controller/app.mk index bf5870b..6e67cdc 100644 --- a/sw/controller/app.mk +++ b/sw/controller/app.mk @@ -1,6 +1,9 @@ EXE_NAME = app -LD_SCRIPT = app.ld BUILD_DIR = build/app + +LD_SCRIPT = app.ld +PAD_TO = 0x08008000 + SRC_FILES = \ app.S \ app.c \ @@ -24,7 +27,6 @@ SRC_FILES = \ update.c \ usb.c \ writeback.c -PAD_TO = 0x08008000 include common.mk diff --git a/sw/controller/build.sh b/sw/controller/build.sh index a969cec..a95a007 100755 --- a/sw/controller/build.sh +++ b/sw/controller/build.sh @@ -4,10 +4,12 @@ set -e case "$1" in 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 app.mk USER_FLAGS="$USER_FLAGS" ;; clean) + make clean -f primer.mk make clean -f loader.mk make clean -f app.mk ;; diff --git a/sw/controller/common.mk b/sw/controller/common.mk index 8f714e9..5e6fdc3 100644 --- a/sw/controller/common.mk +++ b/sw/controller/common.mk @@ -14,7 +14,6 @@ SRC_DIR = src SRCS = $(addprefix $(SRC_DIR)/, $(SRC_FILES)) OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(patsubst %,%.o,$(SRCS)))) DEPS = $(OBJS:.o=.d) - VPATH = $(SRC_DIR) $(@info $(shell mkdir -p ./$(BUILD_DIR) &> /dev/null)) diff --git a/sw/controller/loader.ld b/sw/controller/loader.ld index 096e772..97f2f37 100644 --- a/sw/controller/loader.ld +++ b/sw/controller/loader.ld @@ -1,5 +1,5 @@ MEMORY { - rom (rx) : org = 0x08000000, len = 4k + code (rx) : org = 0x08000000, len = 4k ram (rwx) : org = 0x20000000, len = 8k } @@ -10,13 +10,13 @@ SECTIONS { . = ALIGN(4); KEEP(*(.isr_vector)) . = ALIGN(4); - } > rom + } > code .startup : { . = ALIGN(4); *(.text.Reset_Handler) . = ALIGN(4); - } > rom + } > code .text : { _sitext = LOADADDR(.text); @@ -28,18 +28,16 @@ SECTIONS { *(.glue_7t) . = ALIGN(4); _etext = .; - } > ram AT > rom + } > ram AT > code .bss : { . = ALIGN(4); _sbss = .; - __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; - __bss_end__ = _ebss; } > ram .data : { @@ -50,7 +48,7 @@ SECTIONS { *(.data*) . = ALIGN(4); _edata = .; - } > ram AT > rom + } > ram AT > code .rodata : { _sirodata = LOADADDR(.rodata); @@ -60,9 +58,9 @@ SECTIONS { *(.rodata*) . = ALIGN(4); _erodata = .; - } > ram AT > rom + } > ram AT > code - _app_header = ORIGIN(rom) + LENGTH(rom); + _app_header = ORIGIN(code) + LENGTH(code); _app_magic = _app_header + 16; _estack = ORIGIN(ram) + LENGTH(ram); } diff --git a/sw/controller/loader.mk b/sw/controller/loader.mk index 32917db..0637f73 100644 --- a/sw/controller/loader.mk +++ b/sw/controller/loader.mk @@ -1,6 +1,9 @@ EXE_NAME = loader -LD_SCRIPT = loader.ld BUILD_DIR = build/loader + +LD_SCRIPT = loader.ld +PAD_TO = 0x08001000 + SRC_FILES = \ loader.S \ flash.c \ @@ -9,6 +12,5 @@ SRC_FILES = \ lcmxo2.c \ loader.c \ update.c -PAD_TO = 0x08001000 include common.mk diff --git a/sw/controller/primer.ld b/sw/controller/primer.ld new file mode 100644 index 0000000..7225e46 --- /dev/null +++ b/sw/controller/primer.ld @@ -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); +} diff --git a/sw/controller/primer.mk b/sw/controller/primer.mk new file mode 100644 index 0000000..1abc92f --- /dev/null +++ b/sw/controller/primer.mk @@ -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 diff --git a/sw/controller/src/app.S b/sw/controller/src/app.S index 0f2b160..5150fde 100644 --- a/sw/controller/src/app.S +++ b/sw/controller/src/app.S @@ -3,6 +3,7 @@ .fpu softvfp .thumb + .section .loader, "a", %progbits .type loader, %object loader: @@ -13,7 +14,6 @@ loader: .type Reset_Handler, %function Reset_Handler: .global Reset_Handler - cpsid i init_data: ldr r0, =_sdata diff --git a/sw/controller/src/hw.c b/sw/controller/src/hw.c index cff7c0e..fb6b189 100644 --- a/sw/controller/src/hw.c +++ b/sw/controller/src/hw.c @@ -1,7 +1,11 @@ +#include #include #include "hw.h" +#define UART_BAUD (115200) + + typedef enum { GPIO_INPUT = 0b00, GPIO_OUTPUT = 0b01, @@ -108,6 +112,13 @@ void hw_gpio_reset (gpio_id_t id) { 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) { for (int i = 0; i < length; i++) { 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) { 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) { - 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) { @@ -311,16 +365,18 @@ hw_flash_t hw_flash_read (uint32_t offset) { return *(uint64_t *) (FLASH_BASE + offset); } -void hw_loader_reset (loader_parameters_t *parameters) { - RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN; - PWR->CR1 |= PWR_CR1_DBP; - TAMP->BKP0R = parameters->magic; - TAMP->BKP1R = parameters->flags; - TAMP->BKP2R = parameters->mcu_address; - TAMP->BKP3R = parameters->fpga_address; - TAMP->BKP4R = parameters->bootloader_address; - PWR->CR1 &= ~(PWR_CR1_DBP); - RCC->APBENR1 &= ~(RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN); +void hw_reset (loader_parameters_t *parameters) { + if (parameters != NULL) { + RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN; + PWR->CR1 |= PWR_CR1_DBP; + TAMP->BKP0R = parameters->magic; + TAMP->BKP1R = parameters->flags; + TAMP->BKP2R = parameters->mcu_address; + TAMP->BKP3R = parameters->fpga_address; + TAMP->BKP4R = parameters->bootloader_address; + PWR->CR1 &= ~(PWR_CR1_DBP); + RCC->APBENR1 &= ~(RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN); + } 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_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_FPGA_INT, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0); } static void hw_init_i2c (void) { 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); 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_RTC_MFP, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0); } 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); - USART1->BRR = (64000000UL) / 1000000; - USART1->CR1 = USART_CR1_FIFOEN | USART_CR1_TE | USART_CR1_UE; + hw_gpio_init(GPIO_ID_UART_TX, GPIO_ALT, GPIO_PP, GPIO_SPEED_LOW, GPIO_PULL_UP, GPIO_AF_1, 0); + 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); - hw_gpio_init(GPIO_ID_UART_RX, GPIO_ALT, GPIO_PP, GPIO_SPEED_LOW, GPIO_PULL_NONE, GPIO_AF_1, 0); + USART1->BRR = (64000000UL) / UART_BAUD; + 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) { @@ -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; 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_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_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) { @@ -461,6 +524,7 @@ void hw_init (void) { hw_init_i2c(); hw_init_uart(); hw_init_tim(); + hw_init_crc(); hw_init_misc(); NVIC_SetPriority(EXTI0_1_IRQn, 0); @@ -489,6 +553,14 @@ void hw_loader_init (void) { 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) { for (int i = 0; i <= 1; i++) { diff --git a/sw/controller/src/hw.h b/sw/controller/src/hw.h index 07658f1..6daaaea 100644 --- a/sw/controller/src/hw.h +++ b/sw/controller/src/hw.h @@ -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); void hw_gpio_set (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_wait_busy (void); void hw_spi_start (void); void hw_spi_stop (void); 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_write (uint8_t i2c_address, uint8_t address, uint8_t *data, uint8_t length, void (*callback)(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_enable_irq (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_program (uint32_t offset, hw_flash_t value); 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_set_vector_table (uint32_t offset); void hw_init (void); void hw_loader_init (void); +void hw_primer_init (void); #endif diff --git a/sw/controller/src/lcmxo2.c b/sw/controller/src/lcmxo2.c index 4990444..de8535c 100644 --- a/sw/controller/src/lcmxo2.c +++ b/sw/controller/src/lcmxo2.c @@ -1,253 +1,451 @@ -#include -#include "fpga.h" -#include "vendor.h" - - -#define VENDOR_SCR_START (1 << 0) -#define VENDOR_SCR_BUSY (1 << 0) -#define VENDOR_SCR_WRITE (1 << 1) -#define VENDOR_SCR_LENGTH_BIT (2) -#define VENDOR_SCR_DELAY (1 << 4) -#define VENDOR_SCR_ADDRESS_BIT (8) - -#define LCMXO2_CFGCR (0x70) -#define LCMXO2_CFGTXDR (0x71) -#define LCMXO2_CFGRXDR (0x73) - -#define CFGCR_RSTE (1 << 6) -#define CFGCR_WBCE (1 << 7) - -#define ISC_ERASE (0x0E) -#define ISC_DISABLE (0x26) -#define LSC_READ_STATUS (0x3C) -#define LSC_INIT_ADDRESS (0x46) -#define ISC_PROGRAM_DONE (0x5E) -#define LSC_PROG_INCR_NV (0x70) -#define LSC_READ_INCR_NV (0x73) -#define ISC_ENABLE_X (0x74) -#define LSC_REFRESH (0x79) -#define ISC_NOOP (0xFF) - -#define ISC_ERASE_CFG (1 << 18) -#define ISC_ERASE_UFM (1 << 19) - -#define LSC_STATUS_1_BUSY (1 << 4) -#define LSC_STATUS_1_FAIL (1 << 5) - -#define FLASH_PAGE_SIZE (16) -#define FLASH_NUM_PAGES (11260) - - -typedef enum { - CMD_NORMAL, - CMD_DELAYED, - CMD_TWO_OP, -} cmd_type_t; - - -static void lcmxo2_reg_set (uint8_t reg, uint8_t value) { - fpga_reg_set(REG_VENDOR_DATA, value << 24); - fpga_reg_set(REG_VENDOR_SCR, - (reg << VENDOR_SCR_ADDRESS_BIT) | - (0 << VENDOR_SCR_LENGTH_BIT) | - VENDOR_SCR_WRITE | - VENDOR_SCR_START - ); - while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); -} - -static void lcmxo2_reset_bus (void) { - lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_RSTE); - lcmxo2_reg_set(LCMXO2_CFGCR, 0); -} - -static void lcmxo2_execute_cmd (uint8_t cmd, uint32_t arg, cmd_type_t type) { - lcmxo2_reg_set(LCMXO2_CFGCR, 0); - uint32_t data = (cmd << 24) | (arg & 0x00FFFFFF); - lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_WBCE); - fpga_reg_set(REG_VENDOR_DATA, data); - fpga_reg_set(REG_VENDOR_SCR, - (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) | - (type == CMD_DELAYED ? VENDOR_SCR_DELAY : 0) | - ((type == CMD_TWO_OP ? 2 : 3) << VENDOR_SCR_LENGTH_BIT) | - VENDOR_SCR_WRITE | - VENDOR_SCR_START - ); - while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); -} - -static void lcmxo2_cleanup (void) { - lcmxo2_reg_set(LCMXO2_CFGCR, 0); -} - -static void lcmxo2_read_data (uint8_t *buffer, uint32_t length) { - while (length > 0) { - uint32_t block_size = (length > 4) ? 4 : length; - fpga_reg_set(REG_VENDOR_SCR, - (LCMXO2_CFGRXDR << VENDOR_SCR_ADDRESS_BIT) | - ((block_size - 1) << VENDOR_SCR_LENGTH_BIT) | - 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); - for (int i = 0; i < block_size; i++) { - *buffer++ = ((data >> 24) & 0xFF); - data <<= 8; - length -= 1; - } - } -} - -static void lcmxo2_write_data (uint8_t *buffer, uint32_t length) { - while (length > 0) { - uint32_t block_size = (length > 4) ? 4 : length; - uint32_t data = 0; - for (int i = 0; i < block_size; i++) { - data = ((data << 8) | *buffer++); - length -= 1; - } - data <<= ((4 - block_size) * 8); - fpga_reg_set(REG_VENDOR_DATA, data); - fpga_reg_set(REG_VENDOR_SCR, - (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) | - ((block_size - 1) << VENDOR_SCR_LENGTH_BIT) | - VENDOR_SCR_WRITE | - VENDOR_SCR_START - ); - while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); - } -} - -static bool lcmxo2_wait_busy (void) { - uint8_t status[4]; - do { - lcmxo2_execute_cmd(LSC_READ_STATUS, 0, CMD_NORMAL); - lcmxo2_read_data(status, 4); - } while(status[2] & LSC_STATUS_1_BUSY); - return status[2] & LSC_STATUS_1_FAIL; -} - -static bool lcmxo2_enable_flash (void) { - lcmxo2_execute_cmd(ISC_ENABLE_X, 0x080000, CMD_NORMAL); - return lcmxo2_wait_busy(); -} - -static void lcmxo2_disable_flash (void) { - lcmxo2_wait_busy(); - lcmxo2_execute_cmd(ISC_DISABLE, 0, CMD_TWO_OP); - lcmxo2_execute_cmd(ISC_NOOP, 0xFFFFFF, CMD_NORMAL); -} - -static bool lcmxo2_erase_flash (void) { - lcmxo2_execute_cmd(ISC_ERASE, ISC_ERASE_UFM | ISC_ERASE_CFG, CMD_NORMAL); - return lcmxo2_wait_busy(); -} - -static void lcmxo2_reset_flash_address (void) { - lcmxo2_execute_cmd(LSC_INIT_ADDRESS, 0, CMD_NORMAL); -} - -static bool lcmxo2_write_flash_page (uint8_t *buffer) { - lcmxo2_execute_cmd(LSC_PROG_INCR_NV, 1, CMD_NORMAL); - lcmxo2_write_data(buffer, FLASH_PAGE_SIZE); - return lcmxo2_wait_busy(); -} - -static void lcmxo2_read_flash_page (uint8_t *buffer) { - lcmxo2_execute_cmd(LSC_READ_INCR_NV, 1, CMD_DELAYED); - lcmxo2_read_data(buffer, FLASH_PAGE_SIZE); -} - -static void lcmxo2_program_done (void) { - lcmxo2_execute_cmd(ISC_PROGRAM_DONE, 0, CMD_NORMAL); - lcmxo2_wait_busy(); -} - -static void lcmxo2_refresh (void) { - lcmxo2_execute_cmd(LSC_REFRESH, 0, CMD_TWO_OP); -} - -static vendor_error_t lcmxo2_fail (vendor_error_t error) { - lcmxo2_disable_flash(); - lcmxo2_cleanup(); - return error; -} - -uint32_t vendor_flash_size (void) { - return (FLASH_PAGE_SIZE * FLASH_NUM_PAGES); -} - -vendor_error_t vendor_backup (uint32_t address, uint32_t *length) { - uint8_t buffer[FLASH_PAGE_SIZE]; - - *length = 0; - - lcmxo2_reset_bus(); - if (lcmxo2_enable_flash()) { - return lcmxo2_fail(VENDOR_ERROR_INIT); - } - lcmxo2_reset_flash_address(); - 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(); - lcmxo2_cleanup(); - - 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); - } - } - lcmxo2_program_done(); - 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(); - lcmxo2_cleanup(); - - return VENDOR_OK; -} - -vendor_error_t vendor_reconfigure (void) { - lcmxo2_reset_bus(); - lcmxo2_refresh(); - lcmxo2_cleanup(); - - return VENDOR_OK; -} +#include +#include "fpga.h" +#include "hw.h" +#include "vendor.h" + + +#define VENDOR_SCR_START (1 << 0) +#define VENDOR_SCR_BUSY (1 << 0) +#define VENDOR_SCR_WRITE (1 << 1) +#define VENDOR_SCR_LENGTH_BIT (2) +#define VENDOR_SCR_DELAY (1 << 4) +#define VENDOR_SCR_ADDRESS_BIT (8) + +#define LCMXO2_I2C_ADDR_CFG (0x80) +#define LCMXO2_I2C_ADDR_RESET (0x86) + +#define LCMXO2_CFGCR (0x70) +#define LCMXO2_CFGTXDR (0x71) +#define LCMXO2_CFGRXDR (0x73) + +#define CFGCR_RSTE (1 << 6) +#define CFGCR_WBCE (1 << 7) + +#define ISC_ERASE (0x0E) +#define ISC_DISABLE (0x26) +#define LSC_READ_STATUS (0x3C) +#define LSC_INIT_ADDRESS (0x46) +#define ISC_PROGRAM_DONE (0x5E) +#define LSC_PROG_INCR_NV (0x70) +#define LSC_READ_INCR_NV (0x73) +#define ISC_ENABLE_X (0x74) +#define LSC_REFRESH (0x79) +#define ISC_ENABLE (0xC6) +#define IDCODE_PUB (0xE0) +#define LSC_PROG_FEABITS (0xF8) +#define LSC_READ_FEABITS (0xFB) +#define ISC_NOOP (0xFF) + +#define ISC_ERASE_FEATURE (1 << 17) +#define ISC_ERASE_CFG (1 << 18) +#define ISC_ERASE_UFM (1 << 19) + +#define LSC_STATUS_CFG_ENABLE (1 << 9) +#define LSC_STATUS_BUSY (1 << 12) +#define LSC_STATUS_FAIL (1 << 13) + +#define DEVICE_ID_SIZE (4) + +#define FLASH_PAGE_SIZE (16) +#define FLASH_NUM_PAGES (11260) + +#define FEATBITS_SIZE (2) + + +typedef enum { + CMD_NORMAL, + CMD_DELAYED, + CMD_TWO_OP, +} cmd_type_t; + + +#ifndef LCMXO2_I2C +static void lcmxo2_reg_set (uint8_t reg, uint8_t value) { + fpga_reg_set(REG_VENDOR_DATA, value << 24); + fpga_reg_set(REG_VENDOR_SCR, + (reg << VENDOR_SCR_ADDRESS_BIT) | + (0 << VENDOR_SCR_LENGTH_BIT) | + VENDOR_SCR_WRITE | + VENDOR_SCR_START + ); + while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); +} + +static void lcmxo2_read_data (uint8_t *buffer, uint32_t length) { + while (length > 0) { + uint32_t block_size = (length > 4) ? 4 : length; + fpga_reg_set(REG_VENDOR_SCR, + (LCMXO2_CFGRXDR << VENDOR_SCR_ADDRESS_BIT) | + ((block_size - 1) << VENDOR_SCR_LENGTH_BIT) | + 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); + for (int i = 0; i < block_size; i++) { + *buffer++ = ((data >> 24) & 0xFF); + data <<= 8; + length -= 1; + } + } +} + +static void lcmxo2_write_data (uint8_t *buffer, uint32_t length) { + while (length > 0) { + uint32_t block_size = (length > 4) ? 4 : length; + uint32_t data = 0; + for (int i = 0; i < block_size; i++) { + data = ((data << 8) | *buffer++); + length -= 1; + } + data <<= ((4 - block_size) * 8); + fpga_reg_set(REG_VENDOR_DATA, data); + fpga_reg_set(REG_VENDOR_SCR, + (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) | + ((block_size - 1) << VENDOR_SCR_LENGTH_BIT) | + VENDOR_SCR_WRITE | + VENDOR_SCR_START + ); + while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); + } +} +#endif + +static void lcmxo2_reset_bus (void) { +#ifdef LCMXO2_I2C + uint8_t reset_data = 0; + hw_i2c_raw(LCMXO2_I2C_ADDR_RESET, &reset_data, sizeof(reset_data), NULL, 0); +#else + lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_RSTE); + lcmxo2_reg_set(LCMXO2_CFGCR, 0); +#endif +} + +static void lcmxo2_execute_cmd (uint8_t cmd, uint32_t arg, cmd_type_t type, uint8_t *buffer, uint8_t length, bool write) { +#ifdef LCMXO2_I2C + uint8_t packet[20] = { cmd, ((arg >> 16) & 0xFF), ((arg >> 8) & 0xFF), (arg & 0xFF) }; + int packet_length = ((type == CMD_TWO_OP) ? 3 : 4); + if (write) { + for (int i = 0; i < length; i++) { + packet[packet_length + i] = buffer[i]; + } + packet_length += length; + } + hw_i2c_raw(LCMXO2_I2C_ADDR_CFG, packet, packet_length, buffer, (write ? 0 : length)); +#else + uint32_t data = (cmd << 24) | (arg & 0x00FFFFFF); + lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_WBCE); + fpga_reg_set(REG_VENDOR_DATA, data); + fpga_reg_set(REG_VENDOR_SCR, + (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) | + (type == CMD_DELAYED ? VENDOR_SCR_DELAY : 0) | + ((type == CMD_TWO_OP ? 2 : 3) << VENDOR_SCR_LENGTH_BIT) | + VENDOR_SCR_WRITE | + VENDOR_SCR_START + ); + while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY); + if (length > 0) { + if (write) { + lcmxo2_write_data(buffer, length); + } else { + lcmxo2_read_data(buffer, length); + } + } + lcmxo2_reg_set(LCMXO2_CFGCR, 0); +#endif +} + +static void lcmxo2_read_device_id (uint8_t *id) { + lcmxo2_execute_cmd(IDCODE_PUB, 0, CMD_NORMAL, id, DEVICE_ID_SIZE, false); +} + +static uint32_t lcmxo2_read_status (void) { + uint32_t status = 0; + lcmxo2_execute_cmd(LSC_READ_STATUS, 0, CMD_NORMAL, (uint8_t *) (&status), 4, false); + return SWAP32(status); +} + +static bool lcmxo2_wait_busy (void) { + uint32_t status; + do { + status = lcmxo2_read_status(); + } while(status & LSC_STATUS_BUSY); + return (status & LSC_STATUS_FAIL); +} + +static bool lcmxo2_enable_flash (void) { +#ifdef LCMXO2_I2C + 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); +#endif + return lcmxo2_wait_busy(); +} + +static void lcmxo2_disable_flash (void) { + lcmxo2_wait_busy(); + lcmxo2_execute_cmd(ISC_DISABLE, 0, CMD_TWO_OP, NULL, 0, false); + lcmxo2_execute_cmd(ISC_NOOP, 0xFFFFFF, CMD_NORMAL, NULL, 0, false); +} + +static bool lcmxo2_erase_featbits (void) { + lcmxo2_execute_cmd(ISC_ERASE, ISC_ERASE_FEATURE, CMD_NORMAL, NULL, 0, false); + return lcmxo2_wait_busy(); +} + +static bool lcmxo2_erase_flash (void) { + lcmxo2_execute_cmd(ISC_ERASE, (ISC_ERASE_UFM | ISC_ERASE_CFG), CMD_NORMAL, NULL, 0, false); + return lcmxo2_wait_busy(); +} + +static void lcmxo2_reset_flash_address (void) { + lcmxo2_execute_cmd(LSC_INIT_ADDRESS, 0, CMD_NORMAL, NULL, 0, false); +} + +static bool lcmxo2_write_flash_page (uint8_t *buffer) { + lcmxo2_execute_cmd(LSC_PROG_INCR_NV, 1, CMD_NORMAL, buffer, FLASH_PAGE_SIZE, true); + return lcmxo2_wait_busy(); +} + +static void lcmxo2_read_flash_page (uint8_t *buffer) { + lcmxo2_execute_cmd(LSC_READ_INCR_NV, 1, CMD_DELAYED, buffer, FLASH_PAGE_SIZE, false); +} + +static bool lcmxo2_program_done (void) { + lcmxo2_execute_cmd(ISC_PROGRAM_DONE, 0, CMD_NORMAL, NULL, 0, false); + return lcmxo2_wait_busy(); +} + +static bool lcmxo2_write_featbits (uint8_t *buffer) { + lcmxo2_execute_cmd(LSC_PROG_FEABITS, 0, CMD_NORMAL, buffer, FEATBITS_SIZE, true); + return lcmxo2_wait_busy(); +} + +static void lcmxo2_read_featbits (uint8_t *buffer) { + lcmxo2_execute_cmd(LSC_READ_FEABITS, 0, CMD_NORMAL, buffer, FEATBITS_SIZE, false); +} + +static void lcmxo2_refresh (void) { + lcmxo2_execute_cmd(LSC_REFRESH, 0, CMD_TWO_OP, NULL, 0, false); +} + +static vendor_error_t lcmxo2_fail (vendor_error_t error) { + lcmxo2_disable_flash(); + return error; +} + + +uint32_t vendor_flash_size (void) { + return (FLASH_PAGE_SIZE * FLASH_NUM_PAGES); +} + +vendor_error_t vendor_backup (uint32_t address, uint32_t *length) { + uint8_t buffer[FLASH_PAGE_SIZE]; + + *length = 0; + + lcmxo2_reset_bus(); + if (lcmxo2_enable_flash()) { + return lcmxo2_fail(VENDOR_ERROR_INIT); + } + lcmxo2_reset_flash_address(); + 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); + } +} diff --git a/sw/controller/src/loader.S b/sw/controller/src/loader.S index da493c8..059e702 100644 --- a/sw/controller/src/loader.S +++ b/sw/controller/src/loader.S @@ -3,11 +3,11 @@ .fpu softvfp .thumb + .section .text.Reset_Handler .type Reset_Handler, %function Reset_Handler: .global Reset_Handler - cpsid i init_text: ldr r0, =_stext @@ -51,8 +51,9 @@ boot: ldr r0, =_app_header push {r0} - bl set_vector_table_offset + bl hw_set_vector_table pop {r0} + ldr r1, [r0, #0] msr MSP, r1 ldr r1, [r0, #4] diff --git a/sw/controller/src/loader.c b/sw/controller/src/loader.c index 9a10708..be5e67f 100644 --- a/sw/controller/src/loader.c +++ b/sw/controller/src/loader.c @@ -4,17 +4,14 @@ #include "update.h" +void no_valid_image (void) { + hw_loader_init(); + hw_gpio_set(GPIO_ID_LED); +} + void loader (void) { if (update_check()) { hw_loader_init(); 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); -} diff --git a/sw/controller/src/primer.S b/sw/controller/src/primer.S new file mode 100644 index 0000000..ab6c0ba --- /dev/null +++ b/sw/controller/src/primer.S @@ -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 diff --git a/sw/controller/src/primer.c b/sw/controller/src/primer.c new file mode 100644 index 0000000..94e22a3 --- /dev/null +++ b/sw/controller/src/primer.c @@ -0,0 +1,75 @@ +#include +#include +#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); +} diff --git a/sw/controller/src/update.c b/sw/controller/src/update.c index 90aa404..52549cf 100644 --- a/sw/controller/src/update.c +++ b/sw/controller/src/update.c @@ -24,6 +24,7 @@ typedef enum { CHUNK_ID_MCU_DATA = 2, CHUNK_ID_FPGA_DATA = 3, CHUNK_ID_BOOTLOADER_DATA = 4, + CHUNK_ID_PRIMER_DATA = 5, } chunk_id_t; @@ -264,6 +265,9 @@ update_error_t update_prepare (uint32_t address, uint32_t length) { parameters.bootloader_address = data_address; break; + case CHUNK_ID_PRIMER_DATA: + break; + default: return UPDATE_ERROR_UNKNOWN_CHUNK; } @@ -274,7 +278,7 @@ update_error_t update_prepare (uint32_t address, uint32_t length) { void update_start (void) { parameters.magic = UPDATE_MAGIC_START; - hw_loader_reset(¶meters); + hw_reset(¶meters); } bool update_check (void) { @@ -317,5 +321,5 @@ void update_perform (void) { vendor_reconfigure(); parameters.magic = 0; - hw_loader_reset(¶meters); + hw_reset(¶meters); } diff --git a/sw/controller/src/vendor.h b/sw/controller/src/vendor.h index 016fac6..e9e6672 100644 --- a/sw/controller/src/vendor.h +++ b/sw/controller/src/vendor.h @@ -2,6 +2,7 @@ #define VENDOR_H__ +#include #include @@ -14,11 +15,16 @@ typedef enum { VENDOR_ERROR_VERIFY, } 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); 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_reconfigure (void); +void vendor_initial_configuration (vendor_get_cmd_t get_cmd, vendor_send_response_t send_response); + #endif diff --git a/sw/pc/primer.py b/sw/pc/primer.py new file mode 100644 index 0000000..d56422b --- /dev/null +++ b/sw/pc/primer.py @@ -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() diff --git a/sw/update/update.py b/sw/update/update.py index 5bd7c5c..2889056 100755 --- a/sw/update/update.py +++ b/sw/update/update.py @@ -19,6 +19,7 @@ class JedecFile: __fuse_length: int = 0 __fuse_offset: int = 0 __fuse_data: bytes = b'' + __fuse_ignore: bool = False __byte_buffer: int = 0 def __handle_q_field(self, f: BufferedRandom) -> None: @@ -77,6 +78,19 @@ class JedecFile: else: 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: data = None while (data != b'*'): @@ -88,6 +102,7 @@ class JedecFile: self.__fuse_length = 0 self.__fuse_offset = 0 self.__fuse_data = b'' + self.__fuse_ignore = False self.__byte_buffer = 0 field = None @@ -104,8 +119,10 @@ class JedecFile: field = f.read(1) if (field == b'Q'): self.__handle_q_field(f) - elif (field == b'L'): + elif (field == b'L' and not self.__fuse_ignore): self.__handle_l_field(f) + elif (field == b'N'): + self.__handle_n_field(f) elif (field == b'\r' or field == b'\n'): pass elif (field == b'\x03'): @@ -134,6 +151,7 @@ class SC64UpdateData: __CHUNK_ID_MCU_DATA = 2 __CHUNK_ID_FPGA_DATA = 3 __CHUNK_ID_BOOTLOADER_DATA = 4 + __CHUNK_ID_PRIMER_DATA = 5 __data = b'' @@ -172,6 +190,9 @@ class SC64UpdateData: def add_bootloader_data(self, data: bytes) -> None: 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: 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('--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('--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') if (len(sys.argv) <= 1): @@ -218,6 +240,10 @@ if __name__ == "__main__": with open(args.boot, 'rb+') as f: 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: f.write(update.get_update_data()) except JedecError as e: