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.
This commit is contained in:
Florent Kermarrec 2021-09-29 18:33:59 +02:00
parent 5a35aa9df6
commit 80cb53fb04
1 changed files with 97 additions and 47 deletions

View File

@ -1,4 +1,4 @@
// This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr> // This file is Copyright (c) 2014-2021 Florent Kermarrec <florent@enjoy-digital.fr>
// This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk> // This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
// This file is Copyright (c) 2018 Ewen McNeill <ewen@naos.co.nz> // This file is Copyright (c) 2018 Ewen McNeill <ewen@naos.co.nz>
// This file is Copyright (c) 2018 Felix Held <felix-github@felixheld.de> // This file is Copyright (c) 2018 Felix Held <felix-github@felixheld.de>
@ -104,20 +104,27 @@ void romboot(void)
#ifdef CSR_UART_BASE #ifdef CSR_UART_BASE
static int check_ack(void) #define ACK_TIMEOUT_DELAY CONFIG_CLOCK_FREQUENCY/4
{ #define CMD_TIMEOUT_DELAY CONFIG_CLOCK_FREQUENCY/16
int recognized;
static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK;
static void timer0_load(unsigned int value) {
timer0_en_write(0); timer0_en_write(0);
timer0_reload_write(0); timer0_reload_write(0);
#ifndef CONFIG_DISABLE_DELAYS #ifndef CONFIG_DISABLE_DELAYS
timer0_load_write(CONFIG_CLOCK_FREQUENCY/4); timer0_load_write(value);
#else #else
timer0_load_write(0); timer0_load_write(0);
#endif #endif
timer0_en_write(1); timer0_en_write(1);
timer0_update_value_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; recognized = 0;
while(timer0_value_read()) { while(timer0_value_read()) {
if(uart_read_nonblock()) { if(uart_read_nonblock()) {
@ -149,13 +156,13 @@ static uint32_t get_uint32(unsigned char* data)
(uint32_t) data[3]; (uint32_t) data[3];
} }
#define MAX_FAILED 5 #define MAX_FAILURES 256
/* Returns 1 if other boot methods should be tried */ /* Returns 1 if other boot methods should be tried */
int serialboot(void) int serialboot(void)
{ {
struct sfl_frame frame; struct sfl_frame frame;
int failed; int failures;
static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ; static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ;
const char *c; const char *c;
int ack_status; int ack_status;
@ -163,7 +170,7 @@ int serialboot(void)
printf("Booting from serial...\n"); printf("Booting from serial...\n");
printf("Press Q or ESC to abort boot completely.\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; c = str;
while(*c) { while(*c) {
uart_write(*c); uart_write(*c);
@ -178,70 +185,113 @@ int serialboot(void)
printf("Cancelled\n"); printf("Cancelled\n");
return 0; return 0;
} }
/* Assume ACK_OK */
failed = 0; /* Assume ACK_OK */
failures = 0;
while(1) { while(1) {
int i; int i;
int actualcrc; int timeout;
int goodcrc; int computed_crc;
int received_crc;
/* Get one Frame */ /* Get one Frame */
i = 0;
timeout = 1;
while((i == 0) || timer0_value_read()) {
if (uart_read_nonblock()) {
if (i == 0) {
timer0_load(CMD_TIMEOUT_DELAY);
frame.payload_length = uart_read(); frame.payload_length = uart_read();
frame.crc[0] = uart_read(); }
frame.crc[1] = uart_read(); if (i == 1) frame.crc[0] = uart_read();
frame.cmd = uart_read(); if (i == 2) frame.crc[1] = uart_read();
for(i=0;i<frame.payload_length;i++) if (i == 3) frame.cmd = uart_read();
frame.payload[i] = uart_read(); if (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) */ /* Check Timeout */
actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1]; if (timeout) {
goodcrc = crc16(&frame.cmd, frame.payload_length+1); /* Acknowledge the Timeout and continue with a new frame */
if(actualcrc != goodcrc) { uart_write(SFL_ACK_ERROR);
/* Clear out the RX buffer */ continue;
while (uart_read_nonblock()) uart_read(); }
failed++;
if(failed == MAX_FAILED) { /* 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"); printf("Too many consecutive errors, aborting");
return 1; return 1;
} }
uart_write(SFL_ACK_CRCERROR);
continue; continue;
} }
/* Execute Frame CMD */ /* Execute Frame CMD */
switch(frame.cmd) { switch(frame.cmd) {
/* On SFL_CMD_ABORT ... */
case SFL_CMD_ABORT: case SFL_CMD_ABORT:
failed = 0; /* Reset failures */
failures = 0;
/* Acknowledge and exit */
uart_write(SFL_ACK_SUCCESS); uart_write(SFL_ACK_SUCCESS);
return 1; return 1;
case SFL_CMD_LOAD: {
char *writepointer;
failed = 0; /* On SFL_CMD_LOAD... */
writepointer = (char *)(uintptr_t) get_uint32(&frame.payload[0]); case SFL_CMD_LOAD: {
for(i=4;i<frame.payload_length;i++) char *load_addr;
*(writepointer++) = frame.payload[i];
if (frame.cmd == SFL_CMD_LOAD) /* Reset failures */
failures = 0;
/* Copy payload */
load_addr = (char *)(uintptr_t) get_uint32(&frame.payload[0]);
memcpy(load_addr, &frame.payload[4], frame.payload_length);
/* Acknowledge and continue */
uart_write(SFL_ACK_SUCCESS); uart_write(SFL_ACK_SUCCESS);
break; break;
} }
/* On SFL_CMD_ABORT ... */
case SFL_CMD_JUMP: { case SFL_CMD_JUMP: {
uint32_t addr; uint32_t jump_addr;
failed = 0; /* Reset failures */
addr = get_uint32(&frame.payload[0]); failures = 0;
/* Acknowledge and jump */
uart_write(SFL_ACK_SUCCESS); uart_write(SFL_ACK_SUCCESS);
boot(0, 0, 0, addr); jump_addr = get_uint32(&frame.payload[0]);
boot(0, 0, 0, jump_addr);
break; break;
} }
default: default:
failed++; /* Increment failures */
if(failed == MAX_FAILED) { failures++;
/* Acknowledge the UNKNOWN cmd */
uart_write(SFL_ACK_UNKNOWN);
/* Increment failures and exit when max is reached */
if(failures == MAX_FAILURES) {
printf("Too many consecutive errors, aborting"); printf("Too many consecutive errors, aborting");
return 1; return 1;
} }
uart_write(SFL_ACK_UNKNOWN);
break; break;
} }
} }