icE1usb fw: Expose error conditions from E1 driver
This will allow the USB interface code to report the errors to the
host PC.
Related: OS#4674
Change-Id: Iba3e00a2b28a2fef6dbd986bfc706c1619c3a3ed
diff --git a/firmware/ice40-riscv/icE1usb/e1.c b/firmware/ice40-riscv/icE1usb/e1.c
index 16c8c80..5e57480 100644
--- a/firmware/ice40-riscv/icE1usb/e1.c
+++ b/firmware/ice40-riscv/icE1usb/e1.c
@@ -232,6 +232,7 @@
int in_flight;
enum e1_pipe_state state;
} tx;
+ struct e1_error_count errors;
} g_e1;
@@ -360,6 +361,12 @@
return e1f_valid_frames(&g_e1.rx.fifo);
}
+const struct e1_error_count *
+e1_get_error_count(void)
+{
+ return &g_e1.errors;
+}
+
void
e1_poll(void)
{
@@ -374,10 +381,12 @@
if (e1_regs->rx.csr & E1_RX_SR_ALIGNED) {
e1_platform_led_set(0, E1P_LED_GREEN, E1P_LED_ST_ON);
led_color(0, 48, 0);
+ g_e1.errors.flags &= ~(E1_ERR_F_LOS|E1_ERR_F_ALIGN_ERR);
} else {
e1_platform_led_set(0, E1P_LED_GREEN, E1P_LED_ST_BLINK);
- /* TODO: completely off if rx tick counter not incrementing */
led_color(48, 0, 0);
+ g_e1.errors.flags |= E1_ERR_F_ALIGN_ERR;
+ /* TODO: completely off if rx tick counter not incrementing */
}
/* Recover any done TX BD */
@@ -390,8 +399,10 @@
while ( (bd = e1_regs->rx.bd) & E1_BD_VALID ) {
/* FIXME: CRC status ? */
e1f_multiframe_write_commit(&g_e1.rx.fifo);
- if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1))
+ if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) {
printf("b: %03x\n", bd);
+ g_e1.errors.crc++;
+ }
g_e1.rx.in_flight--;
}
@@ -410,6 +421,7 @@
if (!(e1_regs->rx.csr & E1_RX_SR_ALIGNED)) {
printf("[!] E1 rx misalign\n");
g_e1.rx.state = RECOVER;
+ g_e1.errors.align++;
}
}
@@ -418,6 +430,7 @@
if (e1_regs->rx.csr & E1_RX_SR_OVFL) {
printf("[!] E1 overflow %d\n", g_e1.rx.in_flight);
g_e1.rx.state = RECOVER;
+ g_e1.errors.ovfl++;
}
}
@@ -449,6 +462,7 @@
if (e1_regs->tx.csr & E1_TX_SR_UNFL) {
printf("[!] E1 underflow %d\n", g_e1.tx.in_flight);
g_e1.tx.state = RECOVER;
+ g_e1.errors.unfl++;
}
}
diff --git a/firmware/ice40-riscv/icE1usb/e1.h b/firmware/ice40-riscv/icE1usb/e1.h
index fcd4284..8ba9838 100644
--- a/firmware/ice40-riscv/icE1usb/e1.h
+++ b/firmware/ice40-riscv/icE1usb/e1.h
@@ -14,6 +14,19 @@
void e1_tx_config(uint16_t cr);
void e1_rx_config(uint16_t cr);
+#define E1_ERR_F_ALIGN_ERR 0x01
+#define E1_ERR_F_LOS 0x02
+
+struct e1_error_count {
+ uint16_t crc;
+ uint16_t align;
+ uint16_t ovfl;
+ uint16_t unfl;
+ uint8_t flags;
+};
+
+const struct e1_error_count *e1_get_error_count(void);
+
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/icE1usb/ice1usb_proto.h b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
index 61e12ad..71f7236 100644
--- a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
+++ b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
@@ -75,3 +75,32 @@
struct ice1usb_rx_config {
uint8_t mode; /*!< enum ice1usb_rx_mode */
} __attribute__((packed));
+
+
+/***********************************************************************
+ * Interrupt Endpoint
+ ***********************************************************************/
+
+enum ice1usb_irq_type {
+ ICE1USB_IRQ_T_ERRCNT = 1,
+};
+
+/* Ensue ro keep those in sync with e1.h */
+#define ICE1USB_ERR_F_ALIGN_ERR 0x01
+#define ICE1USB_ERR_F_LOS 0x02
+
+struct ice1usb_irq_err {
+ /* 16-bit little-endian counters */
+ uint16_t crc;
+ uint16_t align;
+ uint16_t ovfl;
+ uint16_t unfl;
+ uint8_t flags;
+} __attribute__((packed));
+
+struct ice1usb_irq {
+ uint8_t type; /*!< enum ice1usb_irq_type */
+ union {
+ struct ice1usb_irq_err errors;
+ } u;
+} __attribute__((packed));
diff --git a/firmware/ice40-riscv/icE1usb/usb_desc_app.c b/firmware/ice40-riscv/icE1usb/usb_desc_app.c
index 9fe3968..153d70e 100644
--- a/firmware/ice40-riscv/icE1usb/usb_desc_app.c
+++ b/firmware/ice40-riscv/icE1usb/usb_desc_app.c
@@ -28,12 +28,14 @@
struct usb_ep_desc ep_data_in;
struct usb_ep_desc ep_data_out;
struct usb_ep_desc ep_fb;
+ struct usb_ep_desc ep_interrupt;
} __attribute__ ((packed)) off;
struct {
struct usb_intf_desc intf;
struct usb_ep_desc ep_data_in;
struct usb_ep_desc ep_data_out;
struct usb_ep_desc ep_fb;
+ struct usb_ep_desc ep_interrupt;
} __attribute__ ((packed)) on;
} __attribute__ ((packed)) e1;
@@ -79,7 +81,7 @@
.bDescriptorType = USB_DT_INTF,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
- .bNumEndpoints = 3,
+ .bNumEndpoints = 4,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xe1,
.bInterfaceProtocol = 0x00,
@@ -109,6 +111,14 @@
.wMaxPacketSize = 0,
.bInterval = 3,
},
+ .ep_interrupt = {
+ .bLength = sizeof(struct usb_ep_desc),
+ .bDescriptorType = USB_DT_EP,
+ .bEndpointAddress = 0x83,
+ .bmAttributes = 0x03,
+ .wMaxPacketSize = 10,
+ .bInterval = 3,
+ },
},
.on = {
.intf = {
@@ -116,7 +126,7 @@
.bDescriptorType = USB_DT_INTF,
.bInterfaceNumber = 0,
.bAlternateSetting = 1,
- .bNumEndpoints = 3,
+ .bNumEndpoints = 4,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xe1,
.bInterfaceProtocol = 0x00,
@@ -146,6 +156,14 @@
.wMaxPacketSize = 8,
.bInterval = 3,
},
+ .ep_interrupt = {
+ .bLength = sizeof(struct usb_ep_desc),
+ .bDescriptorType = USB_DT_EP,
+ .bEndpointAddress = 0x83,
+ .bmAttributes = 0x03,
+ .wMaxPacketSize = 10,
+ .bInterval = 3,
+ },
},
},
#if 0
diff --git a/firmware/ice40-riscv/icE1usb/usb_e1.c b/firmware/ice40-riscv/icE1usb/usb_e1.c
index 0698de5..bbbb24e 100644
--- a/firmware/ice40-riscv/icE1usb/usb_e1.c
+++ b/firmware/ice40-riscv/icE1usb/usb_e1.c
@@ -25,6 +25,7 @@
int in_bdi; /* buffer descriptor index for IN EP */
struct ice1usb_tx_config tx_cfg;
struct ice1usb_rx_config rx_cfg;
+ struct e1_error_count last_err;
} g_usb_e1;
/* default configuration at power-up */
@@ -87,6 +88,29 @@
if (!g_usb_e1.running)
return;
+ /* EP3 IRQ */
+ if ((usb_ep_regs[3].in.bd[0].csr & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA) {
+ const struct e1_error_count *cur_err = e1_get_error_count();
+ if (memcmp(cur_err, &g_usb_e1.last_err, sizeof(*cur_err))) {
+ struct ice1usb_irq errmsg = {
+ .type = ICE1USB_IRQ_T_ERRCNT,
+ .u = {
+ .errors = {
+ .crc = cur_err->crc,
+ .align = cur_err->align,
+ .ovfl = cur_err->ovfl,
+ .unfl = cur_err->unfl,
+ .flags = cur_err->flags,
+ }
+ }
+ };
+ printf("E");
+ usb_data_write(usb_ep_regs[3].in.bd[0].ptr, &errmsg, sizeof(errmsg));
+ usb_ep_regs[3].in.bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(sizeof(errmsg));
+ g_usb_e1.last_err = *cur_err;
+ }
+ }
+
/* EP2 IN */
bdi = g_usb_e1.in_bdi;
@@ -213,6 +237,7 @@
usb_ep_boot(intf, 0x01, true);
usb_ep_boot(intf, 0x81, true);
usb_ep_boot(intf, 0x82, true);
+ usb_ep_boot(intf, 0x83, true);
return USB_FND_SUCCESS;
}
@@ -274,6 +299,11 @@
/* EP1 IN: Queue buffer */
_usb_fill_feedback_ep();
+ /* EP3 IN: Interrupt */
+ usb_ep_regs[3].in.status = USB_EP_TYPE_INT;
+ usb_ep_regs[3].in.bd[0].ptr = 68;
+ usb_ep_regs[3].in.bd[0].csr = 0;
+
return USB_FND_SUCCESS;
}