From f5d7bf248011ee179abc53ce4156b01051831998 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Mon, 14 Sep 2020 10:23:50 +0200 Subject: [PATCH] fw/e1-tracer: Import firmware for the E1 tracer board Some more code could probably be unified with the "normal" usb E1 adapter to both reduce code duplication but also offer 'sniff' function to the E1 adapter. Signed-off-by: Sylvain Munaut --- firmware/ice40-riscv/e1-tracer/.gitignore | 5 + firmware/ice40-riscv/e1-tracer/Makefile | 82 +++ firmware/ice40-riscv/e1-tracer/config.h | 19 + firmware/ice40-riscv/e1-tracer/e1.c | 513 ++++++++++++++++++ firmware/ice40-riscv/e1-tracer/e1.h | 19 + firmware/ice40-riscv/e1-tracer/fw_app.c | 224 ++++++++ firmware/ice40-riscv/e1-tracer/misc.c | 35 ++ firmware/ice40-riscv/e1-tracer/misc.h | 15 + firmware/ice40-riscv/e1-tracer/usb_desc_app.c | 253 +++++++++ firmware/ice40-riscv/e1-tracer/usb_e1.c | 230 ++++++++ .../ice40-riscv/e1-tracer/usb_str_app.txt | 8 + 11 files changed, 1403 insertions(+) create mode 100644 firmware/ice40-riscv/e1-tracer/.gitignore create mode 100644 firmware/ice40-riscv/e1-tracer/Makefile create mode 100644 firmware/ice40-riscv/e1-tracer/config.h create mode 100644 firmware/ice40-riscv/e1-tracer/e1.c create mode 100644 firmware/ice40-riscv/e1-tracer/e1.h create mode 100644 firmware/ice40-riscv/e1-tracer/fw_app.c create mode 100644 firmware/ice40-riscv/e1-tracer/misc.c create mode 100644 firmware/ice40-riscv/e1-tracer/misc.h create mode 100644 firmware/ice40-riscv/e1-tracer/usb_desc_app.c create mode 100644 firmware/ice40-riscv/e1-tracer/usb_e1.c create mode 100644 firmware/ice40-riscv/e1-tracer/usb_str_app.txt diff --git a/firmware/ice40-riscv/e1-tracer/.gitignore b/firmware/ice40-riscv/e1-tracer/.gitignore new file mode 100644 index 0000000..5d5607c --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/.gitignore @@ -0,0 +1,5 @@ +*.elf +*.hex +*.bin +*.o +*.gen.h diff --git a/firmware/ice40-riscv/e1-tracer/Makefile b/firmware/ice40-riscv/e1-tracer/Makefile new file mode 100644 index 0000000..b190478 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/Makefile @@ -0,0 +1,82 @@ +#ice1usb +#ice1usb-proto-icebreaker +#ice1usb-proto-bitsty +#e1-tracer +BOARD ?= e1-tracer +CROSS ?= riscv-none-embed- +CC = $(CROSS)gcc +OBJCOPY = $(CROSS)objcopy +ICEPROG = iceprog +DFU_UTIL = dfu-util + +BOARD_DEFINE=BOARD_$(shell echo $(BOARD) | tr a-z\- A-Z_) +CFLAGS=-Wall -Os -march=rv32i -mabi=ilp32 -ffreestanding -flto -nostartfiles -fomit-frame-pointer -Wl,--gc-section --specs=nano.specs -D$(BOARD_DEFINE) -I. -I../common + +NO2USB_FW_VERSION=0 +include ../../../gateware/cores/no2usb/fw/fw.mk +CFLAGS += $(INC_no2usb) + +LNK=../common/lnk-app.lds + +HEADERS_common := $(addprefix ../common/, \ + console.h \ + dma.h \ + led.h \ + mini-printf.h \ + spi.h \ + utils.h \ +) + +SOURCES_common := $(addprefix ../common/, \ + ../common/start.S \ + console.c \ + dma.c \ + led.c \ + mini-printf.c \ + spi.c \ + utils.c \ +) + +HEADERS_common += $(HEADERS_no2usb) +SOURCES_common += $(SOURCES_no2usb) + +HEADERS_app=\ + config.h \ + e1.h \ + misc.h \ + usb_str_app.gen.h \ + $(NULL) + +SOURCES_app=\ + e1.c \ + fw_app.c \ + misc.c \ + usb_desc_app.c \ + usb_e1.c \ + $(NULL) + + +all: fw_app.bin + + +fw_app.elf: $(LNK) $(HEADERS_app) $(SOURCES_app) $(HEADERS_common) $(SOURCES_common) + $(CC) $(CFLAGS) -Wl,-Bstatic,-T,$(LNK),--strip-debug -o $@ $(SOURCES_common) $(SOURCES_app) + + +%.hex: %.bin + ../common/bin2hex.py $< $@ + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +prog: fw_app.bin + $(ICEPROG) -o 640k $< + +dfuprog: fw_app.bin + $(DFU_UTIL) -R -a 1 -D $< + + +clean: + rm -f *.bin *.hex *.elf *.o *.gen.h + +.PHONY: prog dfuprog clean diff --git a/firmware/ice40-riscv/e1-tracer/config.h b/firmware/ice40-riscv/e1-tracer/config.h new file mode 100644 index 0000000..d292a4f --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/config.h @@ -0,0 +1,19 @@ +/* + * config.h + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#define SPI_FLASH_BASE 0x80000000 +#define UART_BASE 0x81000000 +#define LED_BASE 0x82000000 +#define USB_CORE_BASE 0x83000000 +#define USB_DATA_BASE 0x84000000 +#define E1_DATA_BASE 0x85000000 +#define DMA_BASE 0x86000000 +#define E1_CORE_BASE 0x87000000 +#define MISC_BASE 0x88000000 +#define SPI_LIU_BASE 0x89000000 diff --git a/firmware/ice40-riscv/e1-tracer/e1.c b/firmware/ice40-riscv/e1-tracer/e1.c new file mode 100644 index 0000000..989d2c8 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/e1.c @@ -0,0 +1,513 @@ +/* + * e1.c + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +#include + +#include "config.h" +#include "console.h" +#include "e1.h" + +#include "dma.h" +#include "led.h" // FIXME + + +// Hardware +// -------- + +struct e1_chan { + uint32_t csr; + uint32_t bd; + uint32_t _rsvd[2]; +} __attribute__((packed,aligned(4))); + +struct e1_core { + struct e1_chan rx[2]; +} __attribute__((packed,aligned(4))); + +#define E1_RX_CR_ENABLE (1 << 0) +#define E1_RX_CR_MODE_TRSP (0 << 1) +#define E1_RX_CR_MODE_BYTE (1 << 1) +#define E1_RX_CR_MODE_BFA (2 << 1) +#define E1_RX_CR_MODE_MFA (3 << 1) +#define E1_RX_CR_OVFL_CLR (1 << 12) +#define E1_RX_SR_ENABLED (1 << 0) +#define E1_RX_SR_ALIGNED (1 << 1) +#define E1_RX_SR_BD_IN_EMPTY (1 << 8) +#define E1_RX_SR_BD_IN_FULL (1 << 9) +#define E1_RX_SR_BD_OUT_EMPTY (1 << 10) +#define E1_RX_SR_BD_OUT_FULL (1 << 11) +#define E1_RX_SR_OVFL (1 << 12) + +#define E1_BD_VALID (1 << 15) +#define E1_BD_CRC1 (1 << 14) +#define E1_BD_CRC0 (1 << 13) +#define E1_BD_ADDR(x) ((x) & 0x7f) +#define E1_BD_ADDR_MSK 0x7f +#define E1_BD_ADDR_SHFT 0 + + +static volatile struct e1_core * const e1_regs = (void *)(E1_CORE_BASE); +static volatile uint8_t * const e1_data = (void *)(E1_DATA_BASE); + + +volatile uint8_t * +e1_data_ptr(int mf, int frame, int ts) +{ + return &e1_data[(mf << 9) | (frame << 5) | ts]; +} + +unsigned int +e1_data_ofs(int mf, int frame, int ts) +{ + return (mf << 9) | (frame << 5) | ts; +} + + +// FIFOs +// ----- +/* Note: FIFO works at 'frame' level (i.e. 32 bytes) */ + +struct e1_fifo { + /* Buffer zone associated with the FIFO */ + unsigned int base; + unsigned int mask; + + /* Pointers / Levels */ + unsigned int wptr[2]; /* 0=committed 1=allocated */ + unsigned int rptr[2]; /* 0=discared 1=peeked */ +}; + + /* Utils */ +static void +e1f_reset(struct e1_fifo *fifo, unsigned int base, unsigned int len) +{ + memset(fifo, 0x00, sizeof(struct e1_fifo)); + fifo->base = base; + fifo->mask = len - 1; +} + +static unsigned int +e1f_allocd_frames(struct e1_fifo *fifo) +{ + /* Number of frames that are allocated (i.e. where we can't write to) */ + return (fifo->wptr[1] - fifo->rptr[0]) & fifo->mask; +} + +static unsigned int +e1f_valid_frames(struct e1_fifo *fifo) +{ + /* Number of valid frames */ + return (fifo->wptr[0] - fifo->rptr[0]) & fifo->mask; +} + +static unsigned int +e1f_unseen_frames(struct e1_fifo *fifo) +{ + /* Number of valid frames that haven't been peeked yet */ + return (fifo->wptr[0] - fifo->rptr[1]) & fifo->mask; +} + +static unsigned int +e1f_free_frames(struct e1_fifo *fifo) +{ + /* Number of frames that aren't allocated */ + return (fifo->rptr[0] - fifo->wptr[1] - 1) & fifo->mask; +} + +static unsigned int +e1f_ofs_to_dma(unsigned int ofs) +{ + /* DMA address are 32-bits word address. Offsets are 32 byte address */ + return (ofs << 3); +} + +static unsigned int +e1f_ofs_to_mf(unsigned int ofs) +{ + /* E1 Buffer Descriptors are always multiframe aligned */ + return (ofs >> 4); +} + + + /* Debug */ +static void +e1f_debug(struct e1_fifo *fifo, const char *name) +{ + unsigned int la, lv, lu, lf; + + la = e1f_allocd_frames(fifo); + lv = e1f_valid_frames(fifo); + lu = e1f_unseen_frames(fifo); + lf = e1f_free_frames(fifo); + + printf("%s: R: %u / %u | W: %u / %u | A:%u V:%u U:%u F:%u\n", + name, + fifo->rptr[0], fifo->rptr[1], fifo->wptr[0], fifo->wptr[1], + la, lv, lu, lf + ); +} + + /* Frame level read/write */ +static unsigned int +e1f_frame_write(struct e1_fifo *fifo, unsigned int *ofs, unsigned int max_frames) +{ + unsigned int lf, le; + + lf = e1f_free_frames(fifo); + le = fifo->mask - fifo->wptr[0] + 1; + + if (max_frames > le) + max_frames = le; + if (max_frames > lf) + max_frames = lf; + + *ofs = fifo->base + fifo->wptr[0]; + fifo->wptr[1] = fifo->wptr[0] = (fifo->wptr[0] + max_frames) & fifo->mask; + + return max_frames; +} + +static unsigned int +e1f_frame_read(struct e1_fifo *fifo, unsigned int *ofs, int max_frames) +{ + unsigned int lu, le; + + lu = e1f_unseen_frames(fifo); + le = fifo->mask - fifo->rptr[1] + 1; + + if (max_frames > le) + max_frames = le; + if (max_frames > lu) + max_frames = lu; + + *ofs = fifo->base + fifo->rptr[1]; + fifo->rptr[0] = fifo->rptr[1] = (fifo->rptr[1] + max_frames) & fifo->mask; + + return max_frames; +} + + + /* MultiFrame level split read/write */ +static bool +e1f_multiframe_write_prepare(struct e1_fifo *fifo, unsigned int *ofs) +{ + unsigned int lf; + + lf = e1f_free_frames(fifo); + if (lf < 16) + return false; + + *ofs = fifo->base + fifo->wptr[1]; + fifo->wptr[1] = (fifo->wptr[1] + 16) & fifo->mask; + + return true; +} + +static void +e1f_multiframe_write_commit(struct e1_fifo *fifo) +{ + fifo->wptr[0] = (fifo->wptr[0] + 16) & fifo->mask; +} + +static bool +e1f_multiframe_read_peek(struct e1_fifo *fifo, unsigned int *ofs) +{ + unsigned int lu; + + lu = e1f_unseen_frames(fifo); + if (lu < 16) + return false; + + *ofs = fifo->base + fifo->rptr[1]; + fifo->rptr[1] = (fifo->rptr[1] + 16) & fifo->mask; + + return true; +} + +static void +e1f_multiframe_read_discard(struct e1_fifo *fifo) +{ + fifo->rptr[0] = (fifo->rptr[0] + 16) & fifo->mask; +} + +static void +e1f_multiframe_empty(struct e1_fifo *fifo) +{ + fifo->rptr[0] = fifo->rptr[1] = (fifo->wptr[0] & ~15); +} + + + +// Main logic +// ---------- + +enum e1_pipe_state { + IDLE = 0, + BOOT = 1, + RUN = 2, + RECOVER = 3, +}; + +static struct { + struct { + uint32_t cr; + struct e1_fifo fifo; + short in_flight; + enum e1_pipe_state state; + uint8_t flags; + } rx[2]; + uint32_t error; +} g_e1; + + +void +e1_init(void) +{ + /* Global state init */ + memset(&g_e1, 0x00, sizeof(g_e1)); +} + +void +e1_start(void) +{ + /* Reset FIFOs */ +#ifdef BIGBUF + e1f_reset(&g_e1.rx[0].fifo, 0, 1024); + e1f_reset(&g_e1.rx[1].fifo, 1024, 1024); +#else + e1f_reset(&g_e1.rx[0].fifo, 0, 128); + e1f_reset(&g_e1.rx[1].fifo, 128, 128); +#endif + + /* Enable Rx0 */ + g_e1.rx[0].cr = E1_RX_CR_OVFL_CLR | + E1_RX_CR_MODE_MFA | + E1_RX_CR_ENABLE; + e1_regs->rx[0].csr = g_e1.rx[0].cr; + + /* Enable Rx1 */ + g_e1.rx[1].cr = E1_RX_CR_OVFL_CLR | + E1_RX_CR_MODE_MFA | + E1_RX_CR_ENABLE; + e1_regs->rx[1].csr = g_e1.rx[1].cr; + + /* State */ + g_e1.rx[0].state = BOOT; + g_e1.rx[0].in_flight = 0; + g_e1.rx[0].flags = 0; + + g_e1.rx[1].state = BOOT; + g_e1.rx[1].in_flight = 0; + g_e1.rx[1].flags = 0; +} + +void +e1_stop() +{ + /* Disable RX0 */ + g_e1.rx[0].cr = 0; + e1_regs->rx[0].csr = g_e1.rx[0].cr; + + /* Disable RX1 */ + g_e1.rx[1].cr = 0; + e1_regs->rx[1].csr = g_e1.rx[1].cr; + + /* State */ + g_e1.rx[0].state = IDLE; + g_e1.rx[1].state = IDLE; +} + + +#include "dma.h" + +unsigned int +e1_rx_need_data(int chan, unsigned int usb_addr, unsigned int max_frames, unsigned int *pos) +{ + unsigned int ofs; + int tot_frames = 0; + int n_frames; + + while (max_frames) { + /* Get some data from the FIFO */ + n_frames = e1f_frame_read(&g_e1.rx[chan].fifo, &ofs, max_frames); + if (!n_frames) + break; + + /* Give pos */ + if (pos) { + *pos = ofs & g_e1.rx[chan].fifo.mask; + pos = NULL; + } + + /* Copy from FIFO to USB */ + dma_exec(e1f_ofs_to_dma(ofs), usb_addr, n_frames * (32 / 4), false, NULL, NULL); + + /* Prepare Next */ + usb_addr += n_frames * (32 / 4); + max_frames -= n_frames; + tot_frames += n_frames; + + /* Wait for DMA completion */ + while (dma_poll()); + } + + return tot_frames; +} + +unsigned int +e1_rx_level(int chan) +{ + return e1f_valid_frames(&g_e1.rx[chan].fifo); +} + +uint8_t +e1_get_pending_flags(int chan) +{ + uint8_t f = g_e1.rx[chan].flags; + g_e1.rx[chan].flags = 0; + return f; +} + + +#define ERR_TIME 1000 + +void +e1_poll(void) +{ + uint32_t bd; + unsigned int ofs; + int chan; + bool error = false; + + /* HACK: LED link status */ + if ((g_e1.rx[0].state == IDLE) && (g_e1.rx[1].state == IDLE)) + { + /* Static dim red */ + led_color(32, 0, 0); + led_blink(false, 0, 0); + } else { + uint32_t csr[2]; + + csr[0] = e1_regs->rx[0].csr; + csr[1] = e1_regs->rx[1].csr; + + if (!((csr[0] & csr[1]) & E1_RX_SR_ALIGNED)) + error = true; + + /* Color is current SYNC status */ + led_color( + error ? 1 : 0, + csr[0] & E1_RX_SR_ALIGNED ? 48 : 0, + csr[1] & E1_RX_SR_ALIGNED ? 112 : 0 + ); + } + + /* Active ? */ + if ((g_e1.rx[0].state == IDLE) && (g_e1.rx[1].state == IDLE)) + return; + + /* Recover any done RX BD */ + for (chan=0; chan<2; chan++) + { + while ( (bd = e1_regs->rx[chan].bd) & E1_BD_VALID ) { + /* FIXME: CRC status ? */ + e1f_multiframe_write_commit(&g_e1.rx[chan].fifo); + if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) { + printf("b: %03x\n", bd); + g_e1.rx[chan].flags |= 4; + error = true; + } + g_e1.rx[chan].in_flight--; + } + } + + /* Handle RX */ + for (chan=0; chan<2; chan++) + { + /* Misalign ? */ + if (g_e1.rx[chan].state == RUN) { + if (!(e1_regs->rx[chan].csr & E1_RX_SR_ALIGNED)) { + printf("[!] E1 rx misalign\n"); + g_e1.rx[chan].state = RECOVER; + g_e1.rx[chan].flags |= 1; + error = true; + } + } + + /* Overflow ? */ + if (g_e1.rx[chan].state == RUN) { + if (e1_regs->rx[chan].csr & E1_RX_SR_OVFL) { + printf("[!] E1 overflow %d\n", g_e1.rx[chan].in_flight); + g_e1.rx[chan].state = RECOVER; + g_e1.rx[chan].flags |= 2; + } + } + + /* Recover ready ? */ + if (g_e1.rx[chan].state == RECOVER) { + if (g_e1.rx[chan].in_flight != 0) + continue; + e1f_multiframe_empty(&g_e1.rx[chan].fifo); + } + + /* Fill new RX BD */ + while (g_e1.rx[chan].in_flight < 4) { + if (!e1f_multiframe_write_prepare(&g_e1.rx[chan].fifo, &ofs)) + break; + e1_regs->rx[chan].bd = e1f_ofs_to_mf(ofs); + g_e1.rx[chan].in_flight++; + } + + /* Clear overflow if needed */ + if (g_e1.rx[chan].state != RUN) { + e1_regs->rx[chan].csr = g_e1.rx[chan].cr | E1_RX_CR_OVFL_CLR; + g_e1.rx[chan].state = RUN; + } + } + + /* Error tracking */ + if (error) { + if (!g_e1.error) { + printf("Error LED\n"); + led_blink(true, 150, 150); + } + g_e1.error = usb_get_tick() + ERR_TIME; + } else if (g_e1.error && (g_e1.error < usb_get_tick())) { + g_e1.error = 0; + led_blink(false, 0, 0); + printf("No error\n"); + } + +} + +void +e1_debug_print(bool data) +{ + volatile uint8_t *p; + + puts("E1\n"); + printf("CSR: Rx0 %04x / Rx1 %04x\n", e1_regs->rx[0].csr, e1_regs->rx[1].csr); + printf("InF: Rx0 %d / Rx1 %d\n", g_e1.rx[0].in_flight, g_e1.rx[1].in_flight); + printf("Sta: Rx0 %d / Rx1 %d\n", g_e1.rx[0].state, g_e1.rx[1].state); + + e1f_debug(&g_e1.rx[0].fifo, "Rx0 FIFO"); + e1f_debug(&g_e1.rx[1].fifo, "Rx1 FIFO"); + + if (data) { + puts("\nE1 Data\n"); + for (int f=0; f<16; f++) { + p = e1_data_ptr(0, f, 0); + for (int ts=0; ts<32; ts++) + printf(" %02x", p[ts]); + printf("\n"); + } + } +} diff --git a/firmware/ice40-riscv/e1-tracer/e1.h b/firmware/ice40-riscv/e1-tracer/e1.h new file mode 100644 index 0000000..b99c682 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/e1.h @@ -0,0 +1,19 @@ +/* + * e1.h + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +void e1_init(); +void e1_start(); +void e1_stop(); + +void e1_poll(void); + +void e1_debug_print(bool data); + +volatile uint8_t *e1_data_ptr(int mf, int frame, int ts); +unsigned int e1_data_ofs(int mf, int frame, int ts); diff --git a/firmware/ice40-riscv/e1-tracer/fw_app.c b/firmware/ice40-riscv/e1-tracer/fw_app.c new file mode 100644 index 0000000..80f88bf --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/fw_app.c @@ -0,0 +1,224 @@ +/* + * fw_app.c + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +#include +#include + +#include "console.h" +#include "e1.h" +#include "led.h" +#include "misc.h" +#include "mini-printf.h" +#include "spi.h" +#include "utils.h" + + +extern const struct usb_stack_descriptors app_stack_desc; + +void usb_e1_init(void); +void usb_e1_run(void); + + +static void +serial_no_init() +{ + uint8_t buf[8]; + char *id, *desc; + int i; + + flash_manuf_id(buf); + printf("Flash Manufacturer : %s\n", hexstr(buf, 3, true)); + + flash_unique_id(buf); + printf("Flash Unique ID : %s\n", hexstr(buf, 8, true)); + + /* Overwrite descriptor string */ + /* In theory in rodata ... but nothing is ro here */ + id = hexstr(buf, 8, false); + desc = (char*)app_stack_desc.str[1]; + for (i=0; i<16; i++) + desc[2 + (i << 1)] = id[i]; +} + +static void +boot_dfu(void) +{ + /* Force re-enumeration */ + usb_disconnect(); + + /* Boot firmware */ + volatile uint32_t *boot = (void*)0x80000000; + *boot = (1 << 2) | (1 << 0); +} + +void +usb_dfu_rt_cb_reboot(void) +{ + boot_dfu(); +} + + + +static uint8_t +liu_read_reg(unsigned chan, uint8_t reg) +{ + uint8_t cmd = reg | 0x20; + uint8_t rv; + struct spi_xfer_chunk xfer[2] = { + { .data = (void*)&cmd, .len = 1, .read = false, .write = true, }, + { .data = (void*)&rv, .len = 1, .read = true, .write = false, }, + }; + spi_xfer(SPI_CS_LIU(chan), xfer, 2); + return rv; +} + +static void +liu_write_reg(unsigned chan, uint8_t reg, uint8_t val) +{ + uint8_t cmd[2] = { reg, val }; + struct spi_xfer_chunk xfer[2] = { + { .data = (void*)cmd, .len = 2, .read = false, .write = true, }, + }; + spi_xfer(SPI_CS_LIU(chan), xfer, 1); +} + + + +#define USB_RT_LIU_REG_WRITE ((1 << 8) | 0x42) +#define USB_RT_LIU_REG_READ ((2 << 8) | 0xc2) + + +static bool +_liu_reg_write_done_cb(struct usb_xfer *xfer) +{ + struct usb_ctrl_req *req = xfer->cb_ctx; + unsigned chan = req->wIndex - 0x81; + + liu_write_reg(chan, req->wValue, xfer->data[0]); + + return true; +} + +static enum usb_fnd_resp +_liu_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer) +{ + unsigned chan; + + /* If this a vendor request for the E1 endpoints */ + if (USB_REQ_RCPT(req) != USB_REQ_RCPT_EP) + return USB_FND_CONTINUE; + + if (req->wIndex < 0x81 || req->wIndex > 0x82) + return USB_FND_CONTINUE; + + if (USB_REQ_TYPE(req) != USB_REQ_TYPE_VENDOR) + return USB_FND_CONTINUE; + + chan = req->wIndex - 0x81; + + /* */ + switch (req->wRequestAndType) + { + case USB_RT_LIU_REG_WRITE: + xfer->len = 1; + xfer->cb_done = _liu_reg_write_done_cb; + xfer->cb_ctx = req; + break; + + case USB_RT_LIU_REG_READ: + xfer->len = 1; + xfer->data[0] = liu_read_reg(chan, req->wValue); + break; + + default: + goto error; + } + + return USB_FND_SUCCESS; + +error: + return USB_FND_ERROR; +} + +static struct usb_fn_drv _liu_drv = { + .ctrl_req = _liu_ctrl_req, +}; + + + +void main() +{ + int cmd = 0; + + /* Init console IO */ + console_init(); + puts("Booting App image..\n"); + + /* LED */ + led_init(); + led_state(true); + + /* SPI */ + spi_init(); + + puts("XXX\n"); + + /* Enable USB directly */ + serial_no_init(); + usb_init(&app_stack_desc); + usb_dfu_rt_init(); + usb_register_function_driver(&_liu_drv); + usb_e1_init(); + + usb_connect(); + e1_init(); + + /* Main loop */ + while (1) + { + /* Prompt ? */ + if (cmd >= 0) + printf("Command> "); + + /* Poll for command */ + cmd = getchar_nowait(); + + if (cmd >= 0) { + if (cmd > 32 && cmd < 127) { + putchar(cmd); + putchar('\r'); + putchar('\n'); + } + + switch (cmd) + { + case 'u': + usb_debug_print(); + break; + case 'e': + e1_debug_print(false); + break; + case 'E': + e1_debug_print(true); + break; + default: + break; + } + } + + /* USB poll */ + usb_poll(); + + /* E1 poll */ + e1_poll(); + usb_e1_run(); + } +} diff --git a/firmware/ice40-riscv/e1-tracer/misc.c b/firmware/ice40-riscv/e1-tracer/misc.c new file mode 100644 index 0000000..6b100bd --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/misc.c @@ -0,0 +1,35 @@ +/* + * misc.c + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include + +#include "config.h" +#include "misc.h" + + +struct misc { + uint32_t warmboot; + uint32_t _rsvd0[3];; + struct { + uint16_t tx; + uint16_t rx; + } e1_tick[2]; + uint32_t _rsvd1; + uint32_t time; +} __attribute__((packed,aligned(4))); + +static volatile struct misc * const misc_regs = (void*)(MISC_BASE); + + +void +e1_tick_read(uint16_t *ticks) +{ + uint32_t v = misc_regs->e1_tick; + ticks[0] = (v ) & 0xffff; + ticks[1] = (v >> 16) & 0xffff; +} diff --git a/firmware/ice40-riscv/e1-tracer/misc.h b/firmware/ice40-riscv/e1-tracer/misc.h new file mode 100644 index 0000000..31e8f80 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/misc.h @@ -0,0 +1,15 @@ +/* + * misc.h + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +void vio_set(unsigned value); + +void e1_tick_read(uint16_t *ticks); diff --git a/firmware/ice40-riscv/e1-tracer/usb_desc_app.c b/firmware/ice40-riscv/e1-tracer/usb_desc_app.c new file mode 100644 index 0000000..176ec12 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/usb_desc_app.c @@ -0,0 +1,253 @@ +/* + * usb_desc_app.c + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include + +#define NULL ((void*)0) +#define num_elem(a) (sizeof(a) / sizeof(a[0])) + + +static const struct { + /* Configuration */ + struct usb_conf_desc conf; + + /* E1 */ + struct { + struct { + struct usb_intf_desc intf; + struct usb_ep_desc ep_data_in0; + struct usb_ep_desc ep_data_in1; + } __attribute__ ((packed)) off; + struct { + struct usb_intf_desc intf; + struct usb_ep_desc ep_data_in0; + struct usb_ep_desc ep_data_in1; + } __attribute__ ((packed)) on; + } __attribute__ ((packed)) e1; + + /* CDC */ +#if 0 + struct { + struct usb_intf_desc intf_ctl; + struct usb_cs_intf_hdr_desc cs_intf_hdr; + struct usb_cs_intf_acm_desc cs_intf_acm; + struct usb_cs_intf_union_desc cs_intf_union; + uint8_t cs_intf_union_slave; + struct usb_ep_desc ep_ctl; + struct usb_intf_desc intf_data; + struct usb_ep_desc ep_data_out; + struct usb_ep_desc ep_data_in; + } __attribute__ ((packed)) cdc; +#endif + + /* DFU Runtime */ + struct { + struct usb_intf_desc intf; + struct usb_dfu_desc func; + } __attribute__ ((packed)) dfu; +} __attribute__ ((packed)) _app_conf_desc = { + .conf = { + .bLength = sizeof(struct usb_conf_desc), + .bDescriptorType = USB_DT_CONF, + .wTotalLength = sizeof(_app_conf_desc), +#if 0 + .bNumInterfaces = 4, +#else + .bNumInterfaces = 2, +#endif + .bConfigurationValue = 1, + .iConfiguration = 4, + .bmAttributes = 0x80, + .bMaxPower = 0x32, /* 100 mA */ + }, + .e1 = { + .off = { + .intf = { + .bLength = sizeof(struct usb_intf_desc), + .bDescriptorType = USB_DT_INTF, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xe1, + .bInterfaceProtocol = 0x00, + .iInterface = 5, + }, + .ep_data_in0 = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x81, + .bmAttributes = 0x05, + .wMaxPacketSize = 0, + .bInterval = 1, + }, + .ep_data_in1 = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x82, + .bmAttributes = 0x05, + .wMaxPacketSize = 0, + .bInterval = 1, + }, + }, + .on = { + .intf = { + .bLength = sizeof(struct usb_intf_desc), + .bDescriptorType = USB_DT_INTF, + .bInterfaceNumber = 0, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xe1, + .bInterfaceProtocol = 0x00, + .iInterface = 5, + }, + .ep_data_in0 = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x81, + .bmAttributes = 0x05, + .wMaxPacketSize = 388, + .bInterval = 1, + }, + .ep_data_in1 = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x82, + .bmAttributes = 0x05, + .wMaxPacketSize = 388, + .bInterval = 1, + }, + }, + }, +#if 0 + .cdc = { + .intf_ctl = { + .bLength = sizeof(struct usb_intf_desc), + .bDescriptorType = USB_DT_INTF, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = 0x02, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 6, + }, + .cs_intf_hdr = { + .bLength = sizeof(struct usb_cs_intf_hdr_desc), + .bDescriptorType = USB_DT_CS_INTF, + .bDescriptorsubtype = 0x00, + .bcdCDC = 0x0110, + }, + .cs_intf_acm = { + .bLength = sizeof(struct usb_cs_intf_acm_desc), + .bDescriptorType = USB_DT_CS_INTF, + .bDescriptorsubtype = 0x02, + .bmCapabilities = 0x00, + }, + .cs_intf_union = { + .bLength = sizeof(struct usb_cs_intf_union_desc) + 1, + .bDescriptorType = USB_DT_CS_INTF, + .bDescriptorsubtype = 0x06, + .bMasterInterface = 1, + }, + .cs_intf_union_slave = 2, + .ep_ctl = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x84, + .bmAttributes = 0x03, + .wMaxPacketSize = 64, + .bInterval = 0xff, + }, + .intf_data = { + .bLength = sizeof(struct usb_intf_desc), + .bDescriptorType = USB_DT_INTF, + .bInterfaceNumber = 2, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0x0a, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .iInterface = 7, + }, + .ep_data_out = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x05, + .bmAttributes = 0x02, + .wMaxPacketSize = 64, + .bInterval = 0x00, + }, + .ep_data_in = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x85, + .bmAttributes = 0x02, + .wMaxPacketSize = 64, + .bInterval = 0x01, + }, + }, +#endif + .dfu = { + .intf = { + .bLength = sizeof(struct usb_intf_desc), + .bDescriptorType = USB_DT_INTF, +#if 0 + .bInterfaceNumber = 3, +#else + .bInterfaceNumber = 1, +#endif + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xfe, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x01, + .iInterface = 8, + }, + .func = { + .bLength = sizeof(struct usb_dfu_desc), + .bDescriptorType = USB_DT_DFU, + .bmAttributes = 0x0d, + .wDetachTimeOut = 1000, + .wTransferSize = 4096, + .bcdDFUVersion = 0x0101, + }, + }, +}; + +static const struct usb_conf_desc * const _conf_desc_array[] = { + &_app_conf_desc.conf, +}; + +static const struct usb_dev_desc _dev_desc = { + .bLength = sizeof(struct usb_dev_desc), + .bDescriptorType = USB_DT_DEV, + .bcdUSB = 0x0200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1d50, + .idProduct = 0x6151, + .bcdDevice = 2, + .iManufacturer = 2, + .iProduct = 3, + .iSerialNumber = 1, + .bNumConfigurations = num_elem(_conf_desc_array), +}; + +#include "usb_str_app.gen.h" + +const struct usb_stack_descriptors app_stack_desc = { + .dev = &_dev_desc, + .conf = _conf_desc_array, + .n_conf = num_elem(_conf_desc_array), + .str = _str_desc_array, + .n_str = num_elem(_str_desc_array), +}; diff --git a/firmware/ice40-riscv/e1-tracer/usb_e1.c b/firmware/ice40-riscv/e1-tracer/usb_e1.c new file mode 100644 index 0000000..421305b --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/usb_e1.c @@ -0,0 +1,230 @@ +/* + * usb_e1.c + * + * Copyright (C) 2019-2020 Sylvain Munaut + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +#include +#include + +#include "console.h" +#include "e1.h" +#include "misc.h" + +struct { + bool running; + int in_bdi[2]; +} g_usb_e1; + + +/* Hack */ +unsigned int e1_rx_need_data(int chan, unsigned int usb_addr, unsigned int max_len, unsigned int *pos); +unsigned int e1_rx_level(int chan); +uint8_t e1_get_pending_flags(int chan); +/* ---- */ + +bool +usb_ep_boot(const struct usb_intf_desc *intf, uint8_t ep_addr, bool dual_bd); + + +void +usb_e1_run(void) +{ + int chan; + int bdi; + + if (!g_usb_e1.running) + return; + + /* EP[1-2] IN */ + for (chan=0; chan<2; chan++) + { + bdi = g_usb_e1.in_bdi[chan]; + + while ((usb_ep_regs[1+chan].in.bd[bdi].csr & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA) + { + uint32_t ptr = usb_ep_regs[1+chan].in.bd[bdi].ptr; + uint32_t hdr; + unsigned int pos; + + /* Error check */ + if ((usb_ep_regs[1+chan].in.bd[bdi].csr & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_ERR) + printf("Err EP%d IN\n", 1+chan); + + /* Get some data from E1 */ + int n = e1_rx_level(chan); + +// if (n > 64) +// n = 12; +// else if (n > 32) +// n = 10; +// else if (n > 8) +// n = 8; + if (n > 12) + n = 12; + else if (!n) + break; + + n = e1_rx_need_data(chan, (ptr >> 2) + 1, n, &pos); + + /* Write header */ + /* [31:12] (reserved) */ + /* [11:10] CRC results (first new multiframe present in packet) */ + /* [ 9: 8] CRC results (second new multiframe present in packet) */ + /* [ 7: 5] Multiframe sequence number (first frame of packet) */ + /* [ 4: 0] Position in multi-frame (first frame of packet) */ + hdr = (pos & 0xff) | (e1_get_pending_flags(chan) << 24); + usb_data_write(ptr, &hdr, 4); + usb_ep_regs[1+chan].in.bd[bdi].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN((n * 32) + 4); + + /* Next BDI */ + bdi ^= 1; + g_usb_e1.in_bdi[chan] = bdi; + } + } +} + +static const struct usb_intf_desc * +_find_intf(const struct usb_conf_desc *conf, uint8_t idx) +{ + const struct usb_intf_desc *intf = NULL; + const void *sod, *eod; + + if (!conf) + return NULL; + + sod = conf; + eod = sod + conf->wTotalLength; + + while (1) { + sod = usb_desc_find(sod, eod, USB_DT_INTF); + if (!sod) + break; + + intf = (void*)sod; + if (intf->bInterfaceNumber == idx) + return intf; + + sod = usb_desc_next(sod); + } + + return NULL; +} +enum usb_fnd_resp +_e1_set_conf(const struct usb_conf_desc *conf) +{ + const struct usb_intf_desc *intf; + + printf("e1 set_conf %08x\n", conf); + if (!conf) + return USB_FND_SUCCESS; + + intf = _find_intf(conf, 0); + if (!intf) + return USB_FND_ERROR; + + printf("e1 set_conf %08x\n", intf); + + usb_ep_boot(intf, 0x81, true); + usb_ep_boot(intf, 0x82, true); + + return USB_FND_SUCCESS; +} + +enum usb_fnd_resp +_e1_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel) +{ + if (base->bInterfaceNumber != 0) + return USB_FND_CONTINUE; + + if (sel->bAlternateSetting == 0) + { + /* Already stopped ? */ + if (!g_usb_e1.running) + return USB_FND_SUCCESS; + + /* Update state */ + g_usb_e1.running = false; + + /* Stop E1 */ + e1_stop(); + + /* Disable end-points */ + usb_ep_regs[1].in.status = 0; + usb_ep_regs[2].in.status = 0; + } + else if (sel->bAlternateSetting == 1) + { + /* Already running ? */ + if (g_usb_e1.running) + return USB_FND_SUCCESS; + + /* Update state */ + g_usb_e1.running = true; + + /* Reset buffer pointers */ + g_usb_e1.in_bdi[0] = 0; + g_usb_e1.in_bdi[1] = 0; + + /* Configure EP1 IN / EP2 IN */ + usb_ep_regs[1].in.status = USB_EP_TYPE_ISOC | USB_EP_BD_DUAL; /* Type=Isochronous, dual buffered */ + usb_ep_regs[2].in.status = USB_EP_TYPE_ISOC | USB_EP_BD_DUAL; /* Type=Isochronous, dual buffered */ + + /* EP1 IN: Prepare two buffers */ + usb_ep_regs[1].in.bd[0].ptr = 256 + 0 * 388; + usb_ep_regs[1].in.bd[0].csr = 0; + + usb_ep_regs[1].in.bd[1].ptr = 256 + 1 * 388; + usb_ep_regs[1].in.bd[1].csr = 0; + + /* EP2 IN: Prepare two buffers */ + usb_ep_regs[2].in.bd[0].ptr = 256 + 2 * 388; + usb_ep_regs[2].in.bd[0].csr = 0; + + usb_ep_regs[2].in.bd[1].ptr = 256 + 3 * 388; + usb_ep_regs[2].in.bd[1].csr = 0; + + /* Start E1 */ + e1_start(); + } + else + { + /* Unknown */ + return USB_FND_ERROR; + } + + + return USB_FND_SUCCESS; +} + +enum usb_fnd_resp +_e1_get_intf(const struct usb_intf_desc *base, uint8_t *alt) +{ + if (base->bInterfaceNumber != 0) + return USB_FND_CONTINUE; + + *alt = g_usb_e1.running ? 1 : 0; + + return USB_FND_SUCCESS; +} + +static struct usb_fn_drv _e1_drv = { + .set_conf = _e1_set_conf, + .set_intf = _e1_set_intf, + .get_intf = _e1_get_intf, +}; + +void +usb_e1_init(void) +{ + /* Clear state */ + memset(&g_usb_e1, 0x00, sizeof(g_usb_e1)); + + /* Install driver */ + usb_register_function_driver(&_e1_drv); +} diff --git a/firmware/ice40-riscv/e1-tracer/usb_str_app.txt b/firmware/ice40-riscv/e1-tracer/usb_str_app.txt new file mode 100644 index 0000000..7f325e6 --- /dev/null +++ b/firmware/ice40-riscv/e1-tracer/usb_str_app.txt @@ -0,0 +1,8 @@ +0000000000000000 +osmocom +e1-tracer +Main +E1 +Console (control) +Console (data) +DFU runtime