mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-12-25 12:31:57 +01:00
[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:
parent
4f6c65c770
commit
5b85b0f661
7
build.sh
7
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)"
|
||||
|
@ -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" ;
|
||||
|
@ -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 $< $@
|
||||
|
@ -55,6 +55,13 @@ SECTIONS {
|
||||
. += __stack_size;
|
||||
_sp = .;
|
||||
|
||||
.fill : {
|
||||
. = ALIGN(1024) - 4;
|
||||
LONG(0xDEADBEEF);
|
||||
FILL(0xFFFFFFFF);
|
||||
. = ORIGIN(rom) + LENGTH(rom);
|
||||
} > rom
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.MIPS.*)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
.set noat
|
||||
.set noreorder
|
||||
|
||||
|
||||
.section .data.ipl2
|
||||
ipl2:
|
||||
.global ipl2
|
||||
|
25
sw/bootloader/tools/strip.py
Normal file
25
sw/bootloader/tools/strip.py
Normal 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)
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
;;
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
50
sw/controller/primer.ld
Normal file
50
sw/controller/primer.ld
Normal 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
16
sw/controller/primer.mk
Normal 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
|
@ -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
|
||||
|
@ -1,7 +1,11 @@
|
||||
#include <stddef.h>
|
||||
#include <stm32g0xx.h>
|
||||
#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++) {
|
||||
|
@ -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
|
||||
|
@ -1,253 +1,451 @@
|
||||
#include <stdbool.h>
|
||||
#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 <stdbool.h>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
}
|
||||
|
53
sw/controller/src/primer.S
Normal file
53
sw/controller/src/primer.S
Normal 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
|
75
sw/controller/src/primer.c
Normal file
75
sw/controller/src/primer.c
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define VENDOR_H__
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@ -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
|
||||
|
512
sw/pc/primer.py
Normal file
512
sw/pc/primer.py
Normal 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()
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user