update stuff done

This commit is contained in:
Polprzewodnikowy 2022-08-18 23:12:04 +02:00
parent ab0c72ce26
commit eb7443d95d
12 changed files with 336 additions and 38 deletions

View File

@ -6,13 +6,8 @@ PACKAGE_FILE_NAME="SC64"
FILES=(
"./fw/ft232h_config.xml"
"./fw/project/lcmxo2/impl1/sc64_impl1.bit"
"./fw/project/lcmxo2/impl1/sc64_impl1.jed"
"./sw/bootloader/build/bootloader.bin"
"./sw/controller/build/controller.bin"
"./sw/controller/build/controller.elf"
"./sw/pc/helpers.py"
"./sw/pc/sc64.py"
"./sw/update/sc64.upd"
"./LICENSE"
"./README.md"
)
@ -75,6 +70,19 @@ build_update () {
build_controller
build_fpga
pushd sw/update > /dev/null
INFO=""
if [ ! -z "${GIT_BRANCH+x}" ]; then INFO+=" $GIT_BRANCH"; fi
if [ ! -z "${GIT_TAG+x}" ]; then INFO+=" $GIT_TAG"; fi
if [ ! -z "${GIT_SHA+x}" ]; then INFO+=" $GIT_SHA"; fi
python3 update.py \
--info "$INFO" \
--mcu ../controller/build/app/app.bin \
--fpga ../../fw/project/lcmxo2/impl1/sc64_impl1.jed \
--boot ../bootloader/build/bootloader.bin \
sc64.upd
popd > /dev/null
BUILT_UPDATE=true
}

View File

@ -1,3 +1,2 @@
prj_project open sc64.ldf
prj_run Export -impl impl1 -task Bitgen
prj_run Export -impl impl1 -task Jedecgen

View File

@ -19,6 +19,7 @@ SRC_FILES = \
task.c \
update.c \
usb.c
PAD_TO = 0x08008000
include common.mk

View File

@ -30,7 +30,7 @@ $(BUILD_DIR)/$(EXE_NAME).elf: $(OBJS) $(LD_SCRIPT)
@$(OBJDUMP) -S -D $@ > $(BUILD_DIR)/$(EXE_NAME).lst
$(BUILD_DIR)/$(EXE_NAME).bin: $(BUILD_DIR)/$(EXE_NAME).elf
$(OBJCOPY) -O binary $< $@
$(OBJCOPY) -O binary --gap-fill 0xFF --pad-to $(PAD_TO) $< $@
$(BUILD_DIR)/$(EXE_NAME).hex: $(BUILD_DIR)/$(EXE_NAME).bin
@$(OBJCOPY) -I binary -O ihex $< $@

View File

@ -3,10 +3,12 @@ LD_SCRIPT = loader.ld
BUILD_DIR = build/loader
SRC_FILES = \
loader.S \
flash.c \
fpga.c \
hw.c \
lcmxo2.c \
loader.c \
update.c
PAD_TO = 0x08001000
include common.mk

View File

@ -5,6 +5,9 @@
#include <stdint.h>
#define FLASH_ERASE_BLOCK_SIZE (128 * 1024)
void flash_erase_block (uint32_t offset);

View File

@ -40,9 +40,13 @@ void fpga_reg_set (fpga_reg_t reg, uint32_t value) {
void fpga_mem_read (uint32_t address, size_t length, uint8_t *buffer) {
fpga_cmd_t cmd = CMD_MEM_READ;
uint8_t buffer_address = 0;
size_t dma_length = length;
if ((dma_length % 2) != 0) {
dma_length += 1;
}
fpga_reg_set(REG_MEM_ADDRESS, address);
fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START);
fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START);
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
hw_spi_start();
@ -55,6 +59,10 @@ void fpga_mem_read (uint32_t address, size_t length, uint8_t *buffer) {
void fpga_mem_write (uint32_t address, size_t length, uint8_t *buffer) {
fpga_cmd_t cmd = CMD_MEM_WRITE;
uint8_t buffer_address = 0;
size_t dma_length = length;
if ((dma_length % 2) != 0) {
dma_length += 1;
}
hw_spi_start();
hw_spi_trx((uint8_t *) (&cmd), 1, SPI_TX);
@ -63,17 +71,22 @@ void fpga_mem_write (uint32_t address, size_t length, uint8_t *buffer) {
hw_spi_stop();
fpga_reg_set(REG_MEM_ADDRESS, address);
fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START);
fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START);
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
}
void fpga_mem_copy (uint32_t src, uint32_t dst, size_t length) {
size_t dma_length = length;
if ((dma_length % 2) != 0) {
dma_length += 1;
}
fpga_reg_set(REG_MEM_ADDRESS, src);
fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START);
fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START);
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
fpga_reg_set(REG_MEM_ADDRESS, dst);
fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START);
fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START);
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
}

View File

@ -313,10 +313,10 @@ 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->mcu_address;
TAMP->BKP2R = parameters->mcu_length;
TAMP->BKP1R = parameters->flags;
TAMP->BKP2R = parameters->mcu_address;
TAMP->BKP3R = parameters->fpga_address;
TAMP->BKP4R = parameters->fpga_length;
TAMP->BKP4R = parameters->bootloader_address;
PWR->CR1 &= ~(PWR_CR1_DBP);
RCC->APBENR1 &= ~(RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN);
NVIC_SystemReset();
@ -325,10 +325,10 @@ void hw_loader_reset (loader_parameters_t *parameters) {
void hw_loader_get_parameters (loader_parameters_t *parameters) {
RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN;
parameters->magic = TAMP->BKP0R;
parameters->mcu_address = TAMP->BKP1R;
parameters->mcu_length = TAMP->BKP2R;
parameters->flags = TAMP->BKP1R;
parameters->mcu_address = TAMP->BKP2R;
parameters->fpga_address = TAMP->BKP3R;
parameters->fpga_length = TAMP->BKP4R;
parameters->bootloader_address = TAMP->BKP4R;
PWR->CR1 |= PWR_CR1_DBP;
TAMP->BKP0R = 0;
TAMP->BKP1R = 0;

View File

@ -43,12 +43,18 @@ typedef enum {
typedef uint64_t hw_flash_t;
typedef enum {
LOADER_FLAGS_UPDATE_MCU = (1 << 0),
LOADER_FLAGS_UPDATE_FPGA = (1 << 1),
LOADER_FLAGS_UPDATE_BOOTLOADER = (1 << 2),
} loader_parameters_flags_t;
typedef struct {
uint32_t magic;
loader_parameters_flags_t flags;
uint32_t mcu_address;
uint32_t mcu_length;
uint32_t fpga_address;
uint32_t fpga_length;
uint32_t bootloader_address;
} loader_parameters_t;

View File

@ -1,3 +1,4 @@
#include "flash.h"
#include "fpga.h"
#include "hw.h"
#include "update.h"
@ -5,12 +6,15 @@
#include "vendor.h"
#define UPDATE_MAGIC_START (0x54535055)
#define UPDATE_MAGIC_START (0x54535055UL)
#define BOOTLOADER_ADDRESS (0x04E00000UL)
#define BOOTLOADER_LENGTH (0x001E0000UL)
typedef enum {
UPDATE_STATUS_MCU = 1,
UPDATE_STATUS_FPGA = 2,
UPDATE_STATUS_BOOTLOADER = 3,
UPDATE_STATUS_DONE = 0x80,
UPDATE_STATUS_ERROR = 0xFF,
} update_status_t;
@ -19,6 +23,7 @@ typedef enum {
CHUNK_ID_UPDATE_INFO = 1,
CHUNK_ID_MCU_DATA = 2,
CHUNK_ID_FPGA_DATA = 3,
CHUNK_ID_BOOTLOADER_DATA = 4,
} chunk_id_t;
@ -31,6 +36,13 @@ static uint8_t status_data[12] = {
};
static uint32_t update_align (uint32_t value) {
if ((value % 16) != 0) {
value += (16 - (value % 16));
}
return value;
}
static uint32_t update_checksum (uint32_t address, uint32_t length) {
uint8_t buffer[32];
uint32_t block_size;
@ -55,16 +67,21 @@ static uint32_t update_write_token (uint32_t *address) {
static uint32_t update_prepare_chunk (uint32_t *address, chunk_id_t chunk_id) {
uint32_t id = (uint32_t) (chunk_id);
uint32_t length = sizeof(id) + (2 * sizeof(uint32_t));
uint32_t length = (4 * sizeof(uint32_t));
fpga_mem_write(*address, sizeof(id), (uint8_t *) (&id));
*address += length;
return length;
}
static uint32_t update_finalize_chunk (uint32_t *address, uint32_t length) {
uint32_t chunk_length = ((4 * sizeof(uint32_t)) + length);
uint32_t aligned_chunk_length = update_align(chunk_length);
uint32_t aligned_length = aligned_chunk_length - (2 * sizeof(uint32_t));
uint32_t checksum = update_checksum(*address, length);
fpga_mem_write(*address - (2 * sizeof(uint32_t)), sizeof(length), (uint8_t *) (&length));
fpga_mem_write(*address - sizeof(uint32_t), sizeof(checksum), (uint8_t *) (&checksum));
fpga_mem_write(*address - (3 * sizeof(uint32_t)), sizeof(aligned_length), (uint8_t *) (&aligned_length));
fpga_mem_write(*address - (2 * sizeof(uint32_t)), sizeof(checksum), (uint8_t *) (&checksum));
fpga_mem_write(*address - sizeof(uint32_t), sizeof(length), (uint8_t *) (&length));
length += (aligned_chunk_length - chunk_length);
*address += length;
return length;
}
@ -83,16 +100,19 @@ static bool update_check_token (uint32_t *address) {
static bool update_get_chunk (uint32_t *address, chunk_id_t *chunk_id, uint32_t *data_address, uint32_t *data_length) {
uint32_t id;
uint32_t chunk_length;
uint32_t checksum;
fpga_mem_read(*address, sizeof(id), (uint8_t *) (&id));
*chunk_id = (chunk_id_t) (id);
*address += sizeof(id);
fpga_mem_read(*address, sizeof(*data_length), (uint8_t *) (data_length));
*address += sizeof(*data_length);
fpga_mem_read(*address, sizeof(chunk_length), (uint8_t *) (&chunk_length));
*address += sizeof(chunk_length);
fpga_mem_read(*address, sizeof(checksum), (uint8_t *) (&checksum));
*address += sizeof(checksum);
fpga_mem_read(*address, sizeof(*data_length), (uint8_t *) (data_length));
*address += sizeof(*data_length);
*data_address = *address;
*address += *data_length;
*address += (chunk_length - (2 * sizeof(uint32_t)));
if (checksum != update_checksum(*data_address, *data_length)) {
return true;
}
@ -127,16 +147,16 @@ static void update_status_notify (update_status_t status) {
update_error_t update_backup (uint32_t address, uint32_t *length) {
hw_flash_t buffer;
uint32_t mcu_length;
uint32_t fpga_length;
uint32_t bootloader_length;
*length = update_write_token(&address);
*length += update_prepare_chunk(&address, CHUNK_ID_MCU_DATA);
mcu_length = hw_flash_size();
for (uint32_t offset = 0; offset < mcu_length; offset += sizeof(hw_flash_t)) {
buffer = hw_flash_read(offset);
hw_flash_t buffer = hw_flash_read(offset);
fpga_mem_write(address + offset, sizeof(hw_flash_t), (uint8_t *) (&buffer));
}
*length += update_finalize_chunk(&address, mcu_length);
@ -147,6 +167,13 @@ update_error_t update_backup (uint32_t address, uint32_t *length) {
}
*length += update_finalize_chunk(&address, fpga_length);
*length += update_prepare_chunk(&address, CHUNK_ID_BOOTLOADER_DATA);
bootloader_length = BOOTLOADER_LENGTH;
for (uint32_t offset = 0; offset < bootloader_length; offset += FPGA_MAX_MEM_TRANSFER) {
fpga_mem_copy(BOOTLOADER_ADDRESS + offset, address + offset, FPGA_MAX_MEM_TRANSFER);
}
*length += update_finalize_chunk(&address, bootloader_length);
return UPDATE_OK;
}
@ -160,10 +187,10 @@ update_error_t update_prepare (uint32_t address, uint32_t length) {
return UPDATE_ERROR_TOKEN;
}
parameters.flags = 0;
parameters.mcu_address = 0;
parameters.mcu_length = 0;
parameters.fpga_address = 0;
parameters.fpga_length = 0;
parameters.bootloader_address = 0;
while (address < end_address) {
if (update_get_chunk(&address, &id, &data_address, &data_length)) {
@ -178,16 +205,24 @@ update_error_t update_prepare (uint32_t address, uint32_t length) {
if (data_length > hw_flash_size()) {
return UPDATE_ERROR_SIZE;
}
parameters.flags |= LOADER_FLAGS_UPDATE_MCU;
parameters.mcu_address = data_address;
parameters.mcu_length = data_length;
break;
case CHUNK_ID_FPGA_DATA:
if (data_length > vendor_flash_size()) {
return UPDATE_ERROR_SIZE;
}
parameters.flags |= LOADER_FLAGS_UPDATE_FPGA;
parameters.fpga_address = data_address;
parameters.fpga_length = data_length;
break;
case CHUNK_ID_BOOTLOADER_DATA:
if (data_length > BOOTLOADER_LENGTH) {
return UPDATE_ERROR_SIZE;
}
parameters.flags |= LOADER_FLAGS_UPDATE_BOOTLOADER;
parameters.bootloader_address = data_address;
break;
default:
@ -209,12 +244,14 @@ bool update_check (void) {
}
void update_perform (void) {
hw_flash_t buffer;
uint32_t length;
if (parameters.mcu_length != 0) {
if (parameters.flags & LOADER_FLAGS_UPDATE_MCU) {
hw_flash_t buffer;
update_status_notify(UPDATE_STATUS_MCU);
fpga_mem_read(parameters.mcu_address - 4, sizeof(length), (uint8_t *) (&length));
hw_flash_erase();
for (uint32_t offset = 0; offset < parameters.mcu_length; offset += sizeof(hw_flash_t)) {
for (uint32_t offset = 0; offset < length; offset += sizeof(hw_flash_t)) {
fpga_mem_read(parameters.mcu_address + offset, sizeof(hw_flash_t), (uint8_t *) (&buffer));
hw_flash_program(offset, buffer);
if (hw_flash_read(offset) != buffer) {
@ -224,14 +261,26 @@ void update_perform (void) {
}
}
if (parameters.fpga_length != 0) {
if (parameters.flags & LOADER_FLAGS_UPDATE_FPGA) {
update_status_notify(UPDATE_STATUS_FPGA);
if (vendor_update(parameters.fpga_address, parameters.fpga_length) != VENDOR_OK) {
fpga_mem_read(parameters.fpga_address - 4, sizeof(length), (uint8_t *) (&length));
if (vendor_update(parameters.fpga_address, length) != VENDOR_OK) {
update_status_notify(UPDATE_STATUS_ERROR);
while (1);
}
}
if (parameters.flags & LOADER_FLAGS_UPDATE_BOOTLOADER) {
update_status_notify(UPDATE_STATUS_BOOTLOADER);
fpga_mem_read(parameters.bootloader_address - 4, sizeof(length), (uint8_t *) (&length));
for (uint32_t offset = 0; offset < BOOTLOADER_LENGTH; offset += FLASH_ERASE_BLOCK_SIZE) {
flash_erase_block(BOOTLOADER_ADDRESS + offset);
}
for (uint32_t offset = 0; offset < length; offset += FPGA_MAX_MEM_TRANSFER) {
fpga_mem_copy(parameters.bootloader_address + offset, BOOTLOADER_ADDRESS + offset, FPGA_MAX_MEM_TRANSFER);
}
}
update_status_notify(UPDATE_STATUS_DONE);
vendor_reconfigure();

1
sw/update/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.upd

216
sw/update/update.py Normal file
View File

@ -0,0 +1,216 @@
import argparse
import math
import os
import sys
from binascii import crc32
from datetime import datetime
from io import BufferedRandom
class JedecError(Exception):
pass
class JedecFile:
__fuse_length: int = 0
__fuse_offset: int = 0
__fuse_data: bytes = b''
__byte_buffer: int = 0
def __handle_q_field(self, f: BufferedRandom) -> None:
type = f.read(1)
if (type == b'F'):
value = b''
while (True):
data = f.read(1)
if (data == b'*'):
value = value.decode('ascii', errors='backslashreplace')
if (not value.isdecimal()):
raise JedecError('Invalid Q field data')
self.__fuse_length = int(value)
break
else:
value += data
else:
self.__ignore_field(f)
def __handle_l_field(self, f: BufferedRandom) -> None:
if (self.__fuse_length <= 0):
raise JedecError('Found fuse data before declaring fuse count')
offset = b''
while (True):
data = f.read(1)
if (data >= b'0' and data <= b'9'):
offset += data
elif (data == b'\r' or data == b'\n'):
offset = offset.decode('ascii', errors='backslashreplace')
if (not offset.isdecimal()):
raise JedecError('Invalid L field offset data')
offset = int(offset)
if (offset != self.__fuse_offset):
raise JedecError('Fuse data is not continuous')
break
else:
raise JedecError('Unexpected byte inside L field offset data')
data = b''
while (True):
data = f.read(1)
if (data == b'0' or data == b'1'):
shift = (7 - (self.__fuse_offset % 8))
self.__byte_buffer |= (1 if data == b'1' else 0) << shift
if (((self.__fuse_offset % 8) == 7) or (self.__fuse_offset == (self.__fuse_length - 1))):
self.__fuse_data += int.to_bytes(self.__byte_buffer, 1, byteorder='little')
self.__byte_buffer = 0
self.__fuse_offset += 1
elif (data == b'\r' or data == b'\n'):
pass
elif (data == b'*'):
break
elif (data == b''):
raise JedecError('Unexpected end of file')
else:
raise JedecError('Unexpected byte inside L field fuse data')
def __ignore_field(self, f: BufferedRandom) -> None:
data = None
while (data != b'*'):
data = f.read(1)
if (data == b''):
raise JedecError('Unexpected end of file')
def parse(self, path: str) -> bytes:
self.__fuse_length = 0
self.__fuse_offset = 0
self.__fuse_data = b''
self.__byte_buffer = 0
field = None
with open(path, 'rb+') as f:
while (True):
field = f.read(1)
if (field == b'\x02'):
f.seek(-1, os.SEEK_CUR)
break
elif (field == b''):
raise JedecError('Unexpected end of file')
while (True):
field = f.read(1)
if (field == b'Q'):
self.__handle_q_field(f)
elif (field == b'L'):
self.__handle_l_field(f)
elif (field == b'\r' or field == b'\n'):
pass
elif (field == b'\x03'):
break
elif (field == b''):
raise JedecError('Unexpected end of file')
else:
self.__ignore_field(f)
if (self.__fuse_length <= 0):
raise JedecError('No fuse data found')
if (self.__fuse_offset != self.__fuse_length):
raise JedecError('Missing fuse data inside JEDEC file')
if (len(self.__fuse_data) != math.ceil(self.__fuse_length / 8)):
raise JedecError('Missing fuse data inside JEDEC file')
return self.__fuse_data
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
__data = b''
def __int_to_bytes(self, value: int) -> bytes:
return value.to_bytes(4, byteorder='little')
def __align(self, value: int) -> int:
if (value % 16 != 0):
value += (16 - (value % 16))
return value
def __add_chunk(self, id: int, data: bytes) -> None:
chunk = b''
chunk_length = (16 + len(data))
aligned_length = self.__align(chunk_length)
chunk += self.__int_to_bytes(id)
chunk += self.__int_to_bytes(aligned_length - 8)
chunk += self.__int_to_bytes(crc32(data))
chunk += self.__int_to_bytes(len(data))
chunk += data
chunk += bytes([0] * (aligned_length - chunk_length))
self.__data += chunk
def create_update_data(self) -> None:
self.__data = self.__UPDATE_TOKEN
def add_update_info(self, data: bytes) -> None:
self.__add_chunk(self.__CHUNK_ID_UPDATE_INFO, data)
def add_mcu_data(self, data: bytes) -> None:
self.__add_chunk(self.__CHUNK_ID_MCU_DATA, data)
def add_fpga_data(self, data: bytes) -> None:
self.__add_chunk(self.__CHUNK_ID_FPGA_DATA, data)
def add_bootloader_data(self, data: bytes) -> None:
self.__add_chunk(self.__CHUNK_ID_BOOTLOADER_DATA, data)
def get_update_data(self) -> bytes:
return self.__data
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='SC64 update file generator')
parser.add_argument('--info', metavar='info_text', required=False, help='text to embed as update info')
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('output', metavar='output_path', help='path to final update data')
if (len(sys.argv) <= 1):
parser.print_help()
parser.exit()
args = parser.parse_args()
try:
update = SC64UpdateData()
update.create_update_data()
creation_time = datetime.now().strftime('%d.%m.%Y %H:%M:%S')
info_text = args.info or ''
update_info = f'{creation_time}{info_text}'
print(f'Update info text: {update_info}')
update.add_update_info(update_info.encode())
if (args.mcu):
with open(args.mcu, 'rb+') as f:
update.add_mcu_data(f.read())
if (args.fpga):
update.add_fpga_data(JedecFile().parse(args.fpga))
if (args.boot):
with open(args.boot, 'rb+') as f:
update.add_bootloader_data(f.read())
with open(args.output, 'wb+') as f:
f.write(update.get_update_data())
except JedecError as e:
print(f'Error while parsing FPGA update data: {e}')
exit(-1)
except IOError as e:
print(f'IOError: {e}')
exit(-1)