Add alternative way of handling USB Mode Protocol

Make read()/write() hide all the details of handling the USB Mode
Protocol.

Unfortunately this also introduces 4 bytes to the .bss segment which
was removed because of lack of FW_RAM, originally just 1 kB. We add
back support in start.S and the linker script.
This commit is contained in:
Michael Cardell Widerkrantz 2025-02-06 12:03:48 +01:00
parent 193d6a44cb
commit 108120ffbf
No known key found for this signature in database
GPG key ID: D3DB3DDF57E704E5
6 changed files with 114 additions and 67 deletions

View file

@ -241,9 +241,6 @@ check-binary-hashes:
sha256sum -c application_fpga.bin.sha256
%.bin: %.elf
$(SIZE) $<
@test "$$($(SIZE) $< | awk 'NR==2{print $$2, $$3}')" = "0 0" \
|| { printf "Non-empty data or bss section!\n"; false; }
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $< $@
chmod -x $@

View file

@ -8,7 +8,8 @@ ENTRY(_start)
MEMORY
{
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128 KB */
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* Pretend we have 128 kiB */
FWRAM (rwx) : ORIGIN = 0xd0000000, LENGTH = 0x800 /* 2 KB */
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}
@ -39,8 +40,6 @@ SECTIONS
_sidata = _etext;
} >ROM
/* XXX We don't allow any data or BSS - but they need be defined or linking will fail */
.data : AT (_etext)
{
. = ALIGN(4);
@ -52,7 +51,7 @@ SECTIONS
*(.sdata*) /* .sdata* sections */
. = ALIGN(4);
_edata = .;
} >ROM
} >FWRAM
/* Uninitialized data section */
.bss :
@ -67,5 +66,5 @@ SECTIONS
. = ALIGN(4);
_ebss = .;
} >ROM
} >FWRAM
}

View file

@ -420,9 +420,6 @@ int main(void)
/*@+mustfreeonly@*/
ctx.use_uss = FALSE;
uint8_t mode = 0;
uint8_t mode_bytes_left = 0;
scramble_ram();
#if defined(SIMULATION)
@ -432,8 +429,7 @@ int main(void)
for (;;) {
switch (state) {
case FW_STATE_INITIAL:
if (readcommand(&hdr, cmd, state, &mode,
&mode_bytes_left) == -1) {
if (readcommand(&hdr, cmd, state) == -1) {
state = FW_STATE_FAIL;
break;
}
@ -442,8 +438,7 @@ int main(void)
break;
case FW_STATE_LOADING:
if (readcommand(&hdr, cmd, state, &mode,
&mode_bytes_left) == -1) {
if (readcommand(&hdr, cmd, state) == -1) {
state = FW_STATE_FAIL;
break;
}

View file

@ -11,6 +11,16 @@
#include "state.h"
#include "types.h"
// USB Mode Protocol:
// 1 byte mode
// 1 byte length
//
// Our USB Mode Protocol packets has room for 255 bytes according to
// the header but we use a packet size of 62 so we limit transfers to
// 64 bytes (2 byte header + 62 byte data) to fit in a single USB
// frame.
#define USBMODE_PACKET_SIZE 64
// clang-format off
static volatile uint32_t *can_rx = (volatile uint32_t *)TK1_MMIO_UART_RX_STATUS;
static volatile uint32_t *rx = (volatile uint32_t *)TK1_MMIO_UART_RX_DATA;
@ -21,9 +31,10 @@ static volatile uint32_t *tx = (volatile uint32_t *)TK1_MMIO_UART_TX_DATA;
static uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status,
enum cmdlen len);
static int parseframe(uint8_t b, struct frame_header *hdr);
static void write(uint8_t *buf, size_t nbytes);
static int read(uint8_t *buf, size_t bufsize, size_t nbytes, uint8_t *mode,
uint8_t *mode_bytes_left);
static uint8_t readbyte(void);
static void write_with_header(const uint8_t *buf, size_t nbytes,
enum mode mode);
static void writebyte(uint8_t b);
static size_t bytelen(enum cmdlen cmdlen);
static uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status,
@ -32,13 +43,15 @@ static uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status,
return (id << 5) | (endpoint << 3) | (status << 2) | len;
}
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state,
uint8_t *mode, uint8_t *mode_bytes_left)
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state)
{
uint8_t in = 0;
set_led((state == FW_STATE_LOADING) ? LED_BLACK : LED_WHITE);
in = readbyte(mode, mode_bytes_left);
if (read(&in, 1, 1, MODE_CDC) == -1) {
return -1;
}
if (parseframe(in, hdr) == -1) {
htif_puts("Couldn't parse header\n");
@ -47,7 +60,7 @@ int readcommand(struct frame_header *hdr, uint8_t *cmd, int state,
(void)memset(cmd, 0, CMDLEN_MAXBYTES);
// Now we know the size of the cmd frame, read it all
if (read(cmd, CMDLEN_MAXBYTES, hdr->len, mode, mode_bytes_left) != 0) {
if (read(cmd, CMDLEN_MAXBYTES, hdr->len, MODE_CDC) != 0) {
htif_puts("read: buffer overrun\n");
return -1;
}
@ -80,12 +93,14 @@ static int parseframe(uint8_t b, struct frame_header *hdr)
return 0;
}
#define FWFRAMESIZE (2 + 128)
// Send a firmware reply with a frame header, response code rspcode and
// following data in buf
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf)
{
size_t nbytes = 0;
enum cmdlen len = 0; // length covering (rspcode + length of buf)
enum cmdlen len = 0; // length covering (rspcode + length of buf)
uint8_t frame[FWFRAMESIZE]; // Frame header + longest response
switch (rspcode) {
case FW_RSP_NAME_VERSION:
@ -117,33 +132,20 @@ void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf)
nbytes = bytelen(len);
// Mode Protocol Header
writebyte(MODE_CDC);
writebyte(2);
// Frame Protocol Header
writebyte(genhdr(hdr.id, hdr.endpoint, 0x0, len));
frame[0] = genhdr(hdr.id, hdr.endpoint, 0x0, len);
// FW protocol header
writebyte(rspcode);
nbytes--;
frame[1] = rspcode;
while (nbytes > 0) {
// Limit transfers to 64 bytes (2 byte header + 62 byte data) to
// fit in a single USB frame.
size_t tx_count = nbytes > 62 ? 62 : nbytes;
// Mode Protocol Header
writebyte(MODE_CDC);
writebyte(tx_count & 0xff);
// Payload
memcpy_s(&frame[2], FWFRAMESIZE, buf, nbytes);
// Data
write(buf, tx_count);
nbytes -= tx_count;
buf += tx_count;
}
// 1 byte framing header + length + payload
write(frame, 1 + nbytes, MODE_CDC);
}
void writebyte(uint8_t b)
static void writebyte(uint8_t b)
{
for (;;) {
if (*can_tx) {
@ -153,14 +155,39 @@ void writebyte(uint8_t b)
}
}
static void write(uint8_t *buf, size_t nbytes)
static void write_with_header(const uint8_t *buf, size_t nbytes, enum mode mode)
{
// Append USB Mode Protocol header:
// 1 byte mode
// 1 byte length
writebyte(mode);
writebyte(nbytes);
for (int i = 0; i < nbytes; i++) {
writebyte(buf[i]);
}
}
uint8_t readbyte_(void)
// write blockingly writes nbytes bytes of data from buf to the UART,
// framing the data in USB Mode Protocol with mode mode, either
// MODE_CDC or MODE_HID.
void write(const uint8_t *buf, size_t nbytes, enum mode mode)
{
while (nbytes > 0) {
// We split the data into chunks that will fit in the
// USB Mode Protocol with some spare change.
uint8_t len =
nbytes < USBMODE_PACKET_SIZE ? nbytes : USBMODE_PACKET_SIZE;
write_with_header((const uint8_t *)buf, len, mode);
buf += len;
nbytes -= len;
}
}
static uint8_t readbyte(void)
{
for (;;) {
if (*can_rx) {
@ -170,30 +197,48 @@ uint8_t readbyte_(void)
}
}
uint8_t readbyte(uint8_t *mode, uint8_t *mode_bytes_left)
// read blockingly reads nbytes bytes of data into buffer buf, a
// maximum bufsize bytes.
//
// Caller asks for the expected USB mode expect_mode: MODE_CDC or
// MODE_HID, which represents different endpoints on the USB
// controller.
//
// If data is readable but with another mode set, it is silently
// discarded and we keep on reading until nbytes bytes have appeared.
//
int read(uint8_t *buf, size_t bufsize, size_t nbytes, enum mode expect_mode)
{
if (*mode_bytes_left == 0) {
*mode = readbyte_();
if (*mode != MODE_CDC) {
htif_puts("We only support MODE_CDC\n");
} else {
*mode_bytes_left = readbyte_();
}
}
uint8_t b = readbyte_();
*mode_bytes_left -= 1;
return b;
}
static uint8_t mode = 0;
static uint8_t mode_bytes_left = 0;
static int read(uint8_t *buf, size_t bufsize, size_t nbytes, uint8_t *mode,
uint8_t *mode_bytes_left)
{
if (nbytes > bufsize) {
return -1;
}
for (int n = 0; n < nbytes; n++) {
buf[n] = readbyte(mode, mode_bytes_left);
int n = 0;
while (n < nbytes) {
if (mode_bytes_left == 0) {
// Read USB Mode Protocol header:
// 1 byte mode
// 1 byte length
mode = readbyte();
mode_bytes_left = readbyte();
}
if (mode == expect_mode) {
// Reading payload.
buf[n] = readbyte();
n++;
mode_bytes_left--;
} else {
// Not the USB mode caller asked for. Eat the rest.
for (int i = 0; i < mode_bytes_left; i++) {
(void)readbyte();
}
mode_bytes_left = 0;
}
}
return 0;

View file

@ -57,9 +57,8 @@ struct frame_header {
};
/*@ -exportlocal @*/
void writebyte(uint8_t b);
uint8_t readbyte(uint8_t *mode, uint8_t *mode_bytes_left);
int read(uint8_t *buf, size_t bufsize, size_t nbytes, enum mode expect_mode);
void write(const uint8_t *buf, size_t nbytes, enum mode mode);
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf);
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state,
uint8_t *mode, uint8_t *mode_bytes_left);
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state);
#endif

View file

@ -46,6 +46,18 @@ clear:
addi a0, a0, 4
blt a0, a1, clear
/* zero-init bss section */
la a0, _sbss
la a1, _ebss
bge a0, a1, end_init_bss
loop_init_bss:
sw zero, 0(a0)
addi a0, a0, 4
blt a0, a1, loop_init_bss
end_init_bss:
/*
* Init stack at top of fw_ram.
*/