diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9f19d8d..171290d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,7 +116,7 @@ jobs: - name: test baremetal run: make fpga-run RUN_LINUX=0 BOARD=AES-KU040-DB-G - name: test linux - run: make test-linux-fpga-connect BOARD=AES-KU040-DB-G + run: make test-linux-fpga-connect BOARD=AES-KU040-DB-G BOOT_FLOW=FLASH_TO_EXTMEM ## doc: ## runs-on: self-hosted diff --git a/Makefile b/Makefile index dcfdb4e5..1befe9d7 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ include $(LIB_DIR)/setup.mk INIT_MEM ?= 0 RUN_LINUX ?= 1 USE_EXTMEM := 1 +BOOT_FLOW ?= CONSOLE_TO_EXTMEM ifeq ($(INIT_MEM),1) SETUP_ARGS += INIT_MEM @@ -39,12 +40,12 @@ sim-test-spi: fpga-run: nix-shell --run 'make clean setup INIT_MEM=$(INIT_MEM) USE_EXTMEM=$(USE_EXTMEM)' nix-shell --run 'make -C ../$(CORE)_V*/ fpga-fw-build BOARD=$(BOARD) RUN_LINUX=$(RUN_LINUX)' - make -C ../$(CORE)_V*/ fpga-run BOARD=$(BOARD) RUN_LINUX=$(RUN_LINUX) + make -C ../$(CORE)_V*/ fpga-run BOARD=$(BOARD) RUN_LINUX=$(RUN_LINUX) BOOT_FLOW=$(BOOT_FLOW) fpga-connect: nix-shell --run 'make -C ../$(CORE)_V*/ fpga-fw-build BOARD=$(BOARD) RUN_LINUX=$(RUN_LINUX)' # Should run under 'bash', running with 'fish' as a shell gives an error - make -C ../$(CORE)_V*/ fpga-run BOARD=$(BOARD) RUN_LINUX=$(RUN_LINUX) + make -C ../$(CORE)_V*/ fpga-run BOARD=$(BOARD) RUN_LINUX=$(RUN_LINUX) BOOT_FLOW=$(BOOT_FLOW) fpga-test: make clean setup fpga-run INIT_MEM=0 @@ -58,6 +59,6 @@ test-all: test-linux-fpga-connect: build_dir_name -rm $(BUILD_DIR)/hardware/fpga/test.log -ln -s minicom_test1.txt $(BUILD_DIR)/hardware/fpga/minicom_linux_script.txt - make fpga-connect RUN_LINUX=1 + make fpga-connect RUN_LINUX=1 BOOT_FLOW=$(BOOT_FLOW) .PHONY: sim-test fpga-test test-all test-linux-fpga-connect diff --git a/hardware/simulation/sim_build.mk b/hardware/simulation/sim_build.mk index 3244fd40..60803ab2 100644 --- a/hardware/simulation/sim_build.mk +++ b/hardware/simulation/sim_build.mk @@ -1,5 +1,6 @@ # Add iob_soc_opencryptolinux software as a build dependency HEX+=iob_soc_opencryptolinux_boot.hex iob_soc_opencryptolinux_firmware.hex +HEX+=boot_flow ROOT_DIR :=../.. include $(ROOT_DIR)/software/sw_build.mk @@ -23,3 +24,5 @@ GRAB_TIMEOUT ?= 7200 TEST_LIST+=test1 test1: make -C ../../ fw-clean SIMULATOR=$(SIMULATOR) && make -C ../../ sim-clean SIMULATOR=$(SIMULATOR) && make run SIMULATOR=$(SIMULATOR) + +.PHONY: boot_flow diff --git a/iob_soc_opencryptolinux.py b/iob_soc_opencryptolinux.py index 05fba2c2..f2251fc9 100755 --- a/iob_soc_opencryptolinux.py +++ b/iob_soc_opencryptolinux.py @@ -201,10 +201,12 @@ def _post_setup(cls): # Set custom ethernet CONSOLE_CMD contents.append( f""" +RUN_DEPS+=boot_flow GRAB_TIMEOUT = 600 ### Launch minicom if running Linux # pass CI variable over ssh commands UFLAGS+=CI=$(CI) +UFLAGS+=BOOT_FLOW=$(BOOT_FLOW) ifeq ($(shell grep -o rootfs.cpio.gz ../{cls.name}_mem.config),rootfs.cpio.gz) ifneq ($(wildcard minicom_linux_script.txt),) SCRIPT_STR:=-S minicom_linux_script.txt diff --git a/software/src/iob_soc_opencryptolinux_boot.c b/software/src/iob_soc_opencryptolinux_boot.c index 60b84030..98d9a6fc 100644 --- a/software/src/iob_soc_opencryptolinux_boot.c +++ b/software/src/iob_soc_opencryptolinux_boot.c @@ -9,6 +9,7 @@ #include "iob_soc_opencryptolinux_periphs.h" #include "iob_soc_opencryptolinux_system.h" #include "printf.h" +#include #include #define PROGNAME "IOb-Bootloader" @@ -16,23 +17,8 @@ #define DC1 17 // Device Control 1 (used to indicate end of bootloader) #define EXT_MEM 0x80000000 -#define NSAMPLES 16 - -// Set SPI Support: -// Simulation: Enabled, except for Verilator -// FPGA: Disabled, except for AMD -#ifdef SIMULATION -# ifdef VERILATOR - // no SPI support -# else -# define SPI_SUPPORT -# endif // ifndef VERILATOR -#else // ifdef SIMULATION: FPGA case -# ifdef AMD -# define SPI_SUPPORT -# endif // ifdef AMD - // no SPI support -#endif // ifdef SIMULATION +#define FLASH_FILE_SIZE_OFFSET 0x0 // sector 0, subsector 0 +#define FLASH_FIRMWARE_OFFSET 0x1000 // sector 0, subsector 1 // Ethernet utility functions // NOTE: These functions are not compatible with malloc() and free(). @@ -75,6 +61,132 @@ uint32_t uart_recvfile_ethernet(char *file_name) { return file_size; } +int compute_mem_load_txt(char file_name_array[4][50], + long int file_address_array[4], char *file_start_addr, + uint32_t file_size) { + int state = 0; + int file_name_count = 0; + int file_count = 0; + char hexChar = 0; + int hexDecimal = 0; + int i = 0; + for (i = 0; i < file_size; i++) { + hexChar = *(file_start_addr + i); + // uart16550_puts(&hexChar); /* Used for debugging. */ + if (state == 0) { + if (hexChar == ' ') { + file_name_array[file_count][file_name_count] = '\0'; + file_name_count = 0; + file_address_array[file_count] = 0; + file_count = file_count + 1; + state = 1; + } else { + file_name_array[file_count][file_name_count] = hexChar; + file_name_count = file_name_count + 1; + } + } else if (state == 1) { + if (hexChar == '\n') { + state = 0; + } else { + if ('0' <= hexChar && hexChar <= '9') { + hexDecimal = hexChar - '0'; + } else if ('a' <= hexChar && hexChar <= 'f') { + hexDecimal = 10 + hexChar - 'a'; + } else if ('A' <= hexChar && hexChar <= 'F') { + hexDecimal = 10 + hexChar - 'A'; + } else { + uart16550_puts(PROGNAME); + uart16550_puts(": invalid hexadecimal character.\n"); + } + file_address_array[file_count - 1] = + file_address_array[file_count - 1] * 16 + hexDecimal; + } + } + } + + return file_count; +} + +void console_get_files(int file_count, long int file_address_array[4], + char *file_start_addr, char file_name_array[4][50], + int file_sizes[4]) { + int i = 0; + char *file_addr = NULL; + for (i = 0; i < file_count; i++) { + file_addr = (char *)(file_start_addr + file_address_array[i]); + // Receive data from console via Ethernet +#ifndef SIMULATION + file_sizes[i] = uart_recvfile_ethernet(file_name_array[i]); + eth_rcv_file(file_addr, file_sizes[i]); +#else + file_sizes[i] = uart16550_recvfile(file_name_array[i], file_addr); +#endif + } +} + +void program_flash(int file_count, long int file_address_array[4], + char *file_start_addr, int file_sizes[4]) { + int i = 0; + unsigned int flash_addr = 0x0; + char *prog_data = NULL; + int next_subsector = 0; + + // erase SPI Flash + flash_addr = FLASH_FILE_SIZE_OFFSET; + spiflash_erase_address_range(flash_addr, 4 * file_count); + + // store file sizes + prog_data = (char *)file_sizes; + spiflash_memProgram(prog_data, 4 * file_count, flash_addr); + + for (i = 0; i < file_count; i++) { + flash_addr = FLASH_FIRMWARE_OFFSET + (next_subsector*SUBSECTOR_SIZE); + prog_data = file_start_addr + file_address_array[i]; + spiflash_erase_address_range(flash_addr, file_sizes[i]); + printf("Program %d: addr: %p\tflash: %x\tsize: %d\n", i, prog_data, + flash_addr, file_sizes[i]); + spiflash_memProgram(prog_data, file_sizes[i], flash_addr); + printf("Program %d: complete\n", i); + next_subsector += (((file_sizes[i]+SUBSECTOR_SIZE-1)/SUBSECTOR_SIZE)); + } +} + +void read_flash(int file_count, long int file_address_array[4], + char *file_start_addr) { + int i = 0; + int sample = 0; + unsigned int *read_array = NULL; + unsigned int spi_data = 0; + unsigned int flash_file_start = 0; + int read_cnt = 0, read_total = 0; + int file_sizes[4] = {0}; + int next_subsector = 0; + + // get file sizes + for (sample = 0; sample < 4 * file_count; sample = sample + 4) { + file_sizes[sample >> 2] = spiflash_readmem(FLASH_FILE_SIZE_OFFSET + sample); + } + + for (i = 0; i < file_count; i++) { + read_array = (unsigned int *)(file_start_addr + file_address_array[i]); + flash_file_start = FLASH_FIRMWARE_OFFSET + (next_subsector*SUBSECTOR_SIZE); + read_total = file_sizes[i] / 4; + read_cnt = 0; + + printf("Read %d: flash: %x\tmem: %p\tsize: %d\n", i, flash_file_start, + read_array, file_sizes[i]); + for (sample = 0; sample < file_sizes[i]; sample = sample + 4) { + read_array[sample >> 2] = spiflash_readmem(flash_file_start + sample); + // progress every 10% + if (read_cnt % (read_total / 10) == 0) { + printf("\tRead %d: %d%%\n", i, read_cnt * 100 / read_total); + } + read_cnt++; + } + next_subsector += (((file_sizes[i]+SUBSECTOR_SIZE-1)/SUBSECTOR_SIZE)); + } +} + int main() { int run_linux = 0; int file_size; @@ -104,81 +216,10 @@ int main() { uart16550_puts(": Waiting for Console ACK.\n"); } - #ifndef IOB_SOC_OPENCRYPTOLINUX_INIT_MEM // Init ethernet and printf (for ethernet) printf_init(&uart16550_putc); -#ifdef SPI_SUPPORT - // init spit flash controller - spiflash_init(SPI0_BASE); - printf("\nResetting flash registers...\n"); - spiflash_resetmem(); - - printf("Testing program flash\n"); - char prog_data[NSAMPLES] = {0}; - char *char_data = NULL; - unsigned int read_data[NSAMPLES] = {0}; - unsigned int flash_addr = 0x0; - int sample = 0; - int flash_failed = 0; - - // set samples to write - for (sample = 0; sample < NSAMPLES; sample++) { - prog_data[sample] = sample; - } - - // Flash data before erase - printf("\nFlash data before erase:\n"); - for (sample = 0; sample < NSAMPLES; sample = sample + 4) { - read_data[sample >> 2] = spiflash_readmem(flash_addr + sample); - } - char_data = (char *)read_data; - for (sample = 0; sample < NSAMPLES; sample++) { - printf("\tflash[%x] = %02x\n", flash_addr + sample, char_data[sample]); - } - - spiflash_erase_address_range(flash_addr, NSAMPLES); - - // Flash data after erase - printf("\nFlash data after erase:\n"); - for (sample = 0; sample < NSAMPLES; sample = sample + 4) { - read_data[sample >> 2] = spiflash_readmem(flash_addr + sample); - } - char_data = (char *)read_data; - for (sample = 0; sample < NSAMPLES; sample++) { - printf("\tflash[%x] = %02x\n", flash_addr + sample, char_data[sample]); - if (char_data[sample] != 0xFF){ - printf("Error: flash[%x] = %02x != 0xFF\n", flash_addr + sample, char_data[sample]); - flash_failed = 1; - } - } - - spiflash_memProgram(prog_data, NSAMPLES, 0x0); - - printf("\nFlash data after program:\n"); - for (sample = 0; sample < NSAMPLES; sample = sample + 4) { - read_data[sample >> 2] = spiflash_readmem(0x0 + sample); - } - char_data = (char *)read_data; - for (sample = 0; sample < NSAMPLES; sample++) { - if (prog_data[sample] != char_data[sample]) { - printf("Error: expected[%x] = %02x != flash[%x] = %02x\n", flash_addr + sample, - prog_data[sample], flash_addr + sample, char_data[sample]); - flash_failed = 1; - } else { - printf("Valid: expected[%x] = %02x == flash[%x] = %02x\n", flash_addr + sample, - prog_data[sample], flash_addr + sample, char_data[sample]); - } - } - - if (flash_failed) { - printf("ERROR: Flash test failed!\n"); - } else { - printf("SUCCESS: Flash test passed!\n"); - } -#endif // ifdef SPI_SUPPORT - eth_init(ETH0_BASE, &clear_cache); // Use custom memory alloc/free functions to ensure it allocates in external // memory @@ -186,64 +227,49 @@ int main() { // Wait for PHY reset to finish eth_wait_phy_rst(); + char boot_flow[20] = {0}; + + file_size = uart16550_recvfile("boot.flow", boot_flow); + if (file_size > 20) { + printf("Error: boot.flow file size is too large\n"); + return -1; + } + file_size = uart16550_recvfile("../iob_soc_opencryptolinux_mem.config", prog_start_addr); + // compute_mem_load_txt - int state = 0; - int file_name_count = 0; - int file_count = 0; char file_name_array[4][50]; long int file_address_array[4]; - char hexChar = 0; - int hexDecimal = 0; - int i = 0; - for (i = 0; i < file_size; i++) { - hexChar = *(prog_start_addr + i); - // uart16550_puts(&hexChar); /* Used for debugging. */ - if (state == 0) { - if (hexChar == ' ') { - file_name_array[file_count][file_name_count] = '\0'; - file_name_count = 0; - file_address_array[file_count] = 0; - file_count = file_count + 1; - state = 1; - } else { - file_name_array[file_count][file_name_count] = hexChar; - file_name_count = file_name_count + 1; - } - } else if (state == 1) { - if (hexChar == '\n') { - state = 0; - } else { - if ('0' <= hexChar && hexChar <= '9') { - hexDecimal = hexChar - '0'; - } else if ('a' <= hexChar && hexChar <= 'f') { - hexDecimal = 10 + hexChar - 'a'; - } else if ('A' <= hexChar && hexChar <= 'F') { - hexDecimal = 10 + hexChar - 'A'; - } else { - uart16550_puts(PROGNAME); - uart16550_puts(": invalid hexadecimal character.\n"); - } - file_address_array[file_count - 1] = - file_address_array[file_count - 1] * 16 + hexDecimal; - } - } - } + int file_sizes[4] = {0}; + int file_count = compute_mem_load_txt(file_name_array, file_address_array, + prog_start_addr, file_size); - for (i = 0; i < file_count; i++) { - prog_start_addr = (char *)(EXT_MEM + file_address_array[i]); - // Receive data from console via Ethernet -#ifndef SIMULATION - file_size = uart_recvfile_ethernet(file_name_array[i]); - eth_rcv_file(prog_start_addr, file_size); -#else - file_size = uart16550_recvfile(file_name_array[i], prog_start_addr); -#endif + if (!strcmp(boot_flow, "CONSOLE_TO_FLASH")) { + uart16550_puts(PROGNAME); + uart16550_puts(": CONSOLE_TO_FLASH\n"); + // init spi flash controller + spiflash_init(SPI0_BASE); + // Read files from console + console_get_files(file_count, file_address_array, prog_start_addr, + file_name_array, file_sizes); + program_flash(file_count, file_address_array, prog_start_addr, file_sizes); + } else if (!strcmp(boot_flow, "FLASH_TO_EXTMEM")) { + uart16550_puts(PROGNAME); + uart16550_puts(": FLASH_TO_EXTMEM\n"); + // init spi flash controller + spiflash_init(SPI0_BASE); + read_flash(file_count, file_address_array, prog_start_addr); + } else { + uart16550_puts(PROGNAME); + uart16550_puts(": CONSOLE_TO_EXTMEM\n"); + // Read files from console to external memory + console_get_files(file_count, file_address_array, prog_start_addr, + file_name_array, file_sizes); } // Check if running Linux - for (i = 0; i < file_count; i++) { + for (int i = 0; i < file_count; i++) { if (!strcmp(file_name_array[i], "rootfs.cpio.gz")) { #ifdef SIMULATION // Running Linux: setup required dependencies diff --git a/software/sw_build.mk b/software/sw_build.mk index ed188480..3a423cbe 100644 --- a/software/sw_build.mk +++ b/software/sw_build.mk @@ -5,6 +5,17 @@ ROOT_DIR ?=.. # Local embedded makefile settings for custom bootloader and firmware targets. +# Bootloader flow options: +# 1. CONSOLE_TO_EXTMEM: default: load firmware from console to external memory +# 2. CONSOLE_TO_FLASH: program flash with firmware +# 3. FLASH_TO_EXTMEM: load firmware from flash to external memory +BOOT_FLOW ?= CONSOLE_TO_EXTMEM +UTARGETS += boot_flow + +boot_flow: + echo -n "$(BOOT_FLOW)" > boot.flow + # -n to avoid newline + #Function to obtain parameter named $(1) in verilog header file located in $(2) #Usage: $(call GET_MACRO,,) GET_MACRO = $(shell grep "define $(1)" $(2) | rev | cut -d" " -f1 | rev) @@ -56,7 +67,7 @@ fw_jump.bin iob_soc.dtb: cp $(FPGA_TOOL)/$(BOARD)/$@ .;\ fi # Set targets as PHONY to ensure that they are copied even if $(BOARD) is changed -.PHONY: fw_jump.bin iob_soc.dtb +.PHONY: fw_jump.bin iob_soc.dtb boot_flow ../../software/%.bin: make -C ../../ fw-build diff --git a/submodules/SPI b/submodules/SPI index 4a7d3677..ad98d928 160000 --- a/submodules/SPI +++ b/submodules/SPI @@ -1 +1 @@ -Subproject commit 4a7d3677541ab0c400200e40cc6611f33ddc39f0 +Subproject commit ad98d928485cff1421ce069a916a34a87e5f7d97