From 80cb53fb04060763152cc4fd5f33bfdef8cff90f Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 29 Sep 2021 18:33:59 +0200 Subject: [PATCH] software/bios/boot: Allow frame reception to time out during serial boot and do some cleanup/add comments. Allowing the serial boot to time out during frame reception allows doing test on the Host side to calibrate the minimum inter-frame delay and maximum payload length. In the future, we should probably compute the CRC directly during frame reception and do the mempcpy of frame N during the reception of frame N+1 to avoid these inter-frame constraints. --- litex/soc/software/bios/boot.c | 144 ++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 47 deletions(-) diff --git a/litex/soc/software/bios/boot.c b/litex/soc/software/bios/boot.c index d802a1abc..6bf970dda 100644 --- a/litex/soc/software/bios/boot.c +++ b/litex/soc/software/bios/boot.c @@ -1,4 +1,4 @@ -// This file is Copyright (c) 2014-2020 Florent Kermarrec +// This file is Copyright (c) 2014-2021 Florent Kermarrec // This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq // This file is Copyright (c) 2018 Ewen McNeill // This file is Copyright (c) 2018 Felix Held @@ -104,20 +104,27 @@ void romboot(void) #ifdef CSR_UART_BASE -static int check_ack(void) -{ - int recognized; - static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK; +#define ACK_TIMEOUT_DELAY CONFIG_CLOCK_FREQUENCY/4 +#define CMD_TIMEOUT_DELAY CONFIG_CLOCK_FREQUENCY/16 +static void timer0_load(unsigned int value) { timer0_en_write(0); timer0_reload_write(0); #ifndef CONFIG_DISABLE_DELAYS - timer0_load_write(CONFIG_CLOCK_FREQUENCY/4); + timer0_load_write(value); #else timer0_load_write(0); #endif timer0_en_write(1); timer0_update_value_write(1); +} + +static int check_ack(void) +{ + int recognized; + static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK; + + timer0_load(ACK_TIMEOUT_DELAY); recognized = 0; while(timer0_value_read()) { if(uart_read_nonblock()) { @@ -144,18 +151,18 @@ static int check_ack(void) static uint32_t get_uint32(unsigned char* data) { return ((uint32_t) data[0] << 24) | - ((uint32_t) data[1] << 16) | - ((uint32_t) data[2] << 8) | - (uint32_t) data[3]; + ((uint32_t) data[1] << 16) | + ((uint32_t) data[2] << 8) | + (uint32_t) data[3]; } -#define MAX_FAILED 5 +#define MAX_FAILURES 256 /* Returns 1 if other boot methods should be tried */ int serialboot(void) { struct sfl_frame frame; - int failed; + int failures; static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ; const char *c; int ack_status; @@ -163,7 +170,7 @@ int serialboot(void) printf("Booting from serial...\n"); printf("Press Q or ESC to abort boot completely.\n"); - /* Send the serialboot "magic" request to Host */ + /* Send the serialboot "magic" request to Host and wait for ACK_OK */ c = str; while(*c) { uart_write(*c); @@ -178,70 +185,113 @@ int serialboot(void) printf("Cancelled\n"); return 0; } - /* Assume ACK_OK */ - failed = 0; + /* Assume ACK_OK */ + failures = 0; while(1) { int i; - int actualcrc; - int goodcrc; + int timeout; + int computed_crc; + int received_crc; /* Get one Frame */ - frame.payload_length = uart_read(); - frame.crc[0] = uart_read(); - frame.crc[1] = uart_read(); - frame.cmd = uart_read(); - for(i=0;i= 4) { + frame.payload[i-4] = uart_read(); + if (i == (frame.payload_length + 4 - 1)) { + timeout = 0; + break; + } + } + i++; + } + timer0_update_value_write(1); + } - /* Check Frame CRC (if CMD has a CRC) */ - actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1]; - goodcrc = crc16(&frame.cmd, frame.payload_length+1); - if(actualcrc != goodcrc) { - /* Clear out the RX buffer */ - while (uart_read_nonblock()) uart_read(); - failed++; - if(failed == MAX_FAILED) { + /* Check Timeout */ + if (timeout) { + /* Acknowledge the Timeout and continue with a new frame */ + uart_write(SFL_ACK_ERROR); + continue; + } + + /* Check Frame CRC */ + received_crc = ((int)frame.crc[0] << 8)|(int)frame.crc[1]; + computed_crc = crc16(&frame.cmd, frame.payload_length+1); + if(computed_crc != received_crc) { + /* Acknowledge the CRC error */ + uart_write(SFL_ACK_CRCERROR); + + /* Increment failures and exit when max is reached */ + failures++; + if(failures == MAX_FAILURES) { printf("Too many consecutive errors, aborting"); return 1; } - uart_write(SFL_ACK_CRCERROR); continue; } /* Execute Frame CMD */ switch(frame.cmd) { + /* On SFL_CMD_ABORT ... */ case SFL_CMD_ABORT: - failed = 0; + /* Reset failures */ + failures = 0; + /* Acknowledge and exit */ uart_write(SFL_ACK_SUCCESS); return 1; - case SFL_CMD_LOAD: { - char *writepointer; - failed = 0; - writepointer = (char *)(uintptr_t) get_uint32(&frame.payload[0]); - for(i=4;i