diff --git a/firmware/Makefile b/firmware/Makefile index 4b118fbf8..95cbc1857 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -2,7 +2,7 @@ VID ?= 20b7 PID ?= 9db1 TARGET = glasgow -SOURCES = main leds fpga dac_ldo adc +SOURCES = main leds fpga dac_ldo adc fifo LIBRARIES = fx2 fx2isrs fx2usb CFLAGS = -DSYNCDELAYLEN=16 diff --git a/firmware/fifo.c b/firmware/fifo.c new file mode 100644 index 000000000..95788d937 --- /dev/null +++ b/firmware/fifo.c @@ -0,0 +1,121 @@ +#include +#include +#include "glasgow.h" + +void fifo_init() { + // Use newest chip features. + SYNCDELAY; + REVCTL = _ENH_PKT|_DYN_OUT; + + // Disable all FIFOs. + SYNCDELAY; + FIFORESET = _NAKALL; + + // Configure strobes and flags. + SYNCDELAY; + FIFOPINPOLAR = _PKTEND|_SLOE|_SLRD|_SLWR|_EF|_FF; + SYNCDELAY; + PINFLAGSAB = 0b10011000; // FLAGA = EP2 EF, FLAGB = EP4 EF + SYNCDELAY; + PINFLAGSCD = 0b11111110; // FLAGC = EP6 FF, FLAGD = EP8 FF + SYNCDELAY; + PORTACFG |= _FLAGD; // PA7 is FLAGD + + // Use 8-bit wide bus. + SYNCDELAY; + EP2FIFOCFG &= ~_WORDWIDE; + SYNCDELAY; + EP4FIFOCFG &= ~_WORDWIDE; + SYNCDELAY; + EP6FIFOCFG &= ~_WORDWIDE; + SYNCDELAY; + EP8FIFOCFG &= ~_WORDWIDE; + + // Drive 30 MHz IFCLK, sample on negative edge, use FIFO with external master + SYNCDELAY; + IFCONFIG = _IFCLKSRC|_IFCLKOE|_IFCLKPOL|_IFCFG1|_IFCFG0; +} + +void fifo_configure(bool two_ep) { + uint8_t ep26buf, ep48valid; + if(two_ep) { + ep26buf = 0; // quad buffered + ep48valid = 0; // invalid + } else { + ep26buf = _BUF1; // double buffered + ep48valid = _VALID; // valid + } + + // Disable all FIFOs. + SYNCDELAY; + FIFORESET = _NAKALL; + + // For the following code, note that for FIFORESET and OUTPKTEND to do anything, + // the endpoints *must* be in manual mode (_AUTOIN/_AUTOOUT bits cleared). + + // Configure EP2. + SYNCDELAY; + EP2CFG = _VALID|_TYPE1|ep26buf; // OUT BULK 512B + SYNCDELAY; + EP2FIFOCFG = 0; + SYNCDELAY; + FIFORESET = _NAKALL|2; + SYNCDELAY; + OUTPKTEND = _SKIP|2; + SYNCDELAY; + OUTPKTEND = _SKIP|2; + if(two_ep) { + SYNCDELAY; + OUTPKTEND = _SKIP|2; + SYNCDELAY; + OUTPKTEND = _SKIP|2; + } + SYNCDELAY; + EP2FIFOCFG = _AUTOOUT; + + // Configure EP4. + SYNCDELAY; + EP4CFG = ep48valid|_TYPE1; // OUT BULK 512B + SYNCDELAY; + EP4FIFOCFG = 0; + SYNCDELAY; + FIFORESET = _NAKALL|4; + SYNCDELAY; + OUTPKTEND = _SKIP|4; + SYNCDELAY; + OUTPKTEND = _SKIP|4; + SYNCDELAY; + EP4FIFOCFG = _AUTOOUT; + + // Configure EP6. + SYNCDELAY; + EP6CFG = _VALID|_DIR|_TYPE1|ep26buf; // IN BULK 512B + SYNCDELAY; + EP6AUTOINLENH = 512 >> 8; + SYNCDELAY; + EP6AUTOINLENL = 0; + SYNCDELAY; + EP6FIFOCFG = 0; + SYNCDELAY; + FIFORESET = _NAKALL|6; + SYNCDELAY; + EP6FIFOCFG = _AUTOIN|_ZEROLENIN; + + // Configure EP8. + SYNCDELAY; + EP8CFG = ep48valid|_DIR|_TYPE1; // IN BULK 512B + SYNCDELAY; + EP8AUTOINLENH = 512 >> 8; + SYNCDELAY; + EP8AUTOINLENL = 0; + SYNCDELAY; + EP8FIFOCFG = 0; + SYNCDELAY; + FIFORESET = _NAKALL|8; + SYNCDELAY; + EP8FIFOCFG = _AUTOIN|_ZEROLENIN; + + // Enable FIFOs. + SYNCDELAY; + FIFORESET = 0; +} diff --git a/firmware/glasgow.h b/firmware/glasgow.h index 465c72d79..e5b92ea83 100644 --- a/firmware/glasgow.h +++ b/firmware/glasgow.h @@ -78,4 +78,8 @@ bool iobuf_get_alert(uint8_t selector, bool iobuf_is_alerted(); bool iobuf_poll_alert(__xdata uint8_t *mask, bool clear); +// FIFO API +void fifo_init(); +void fifo_configure(bool two_ep); + #endif diff --git a/firmware/main.c b/firmware/main.c index 59ee21fea..3e6c579d7 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -21,7 +21,7 @@ usb_desc_device_c usb_device = { .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 0, - .bNumConfigurations = 1, + .bNumConfigurations = 2, }; usb_desc_configuration_c usb_configs[] = { @@ -29,32 +29,122 @@ usb_desc_configuration_c usb_configs[] = { .bLength = sizeof(struct usb_desc_configuration), .bDescriptorType = USB_DESC_CONFIGURATION, .wTotalLength = sizeof(struct usb_desc_configuration) + - sizeof(struct usb_desc_interface), + 2 * sizeof(struct usb_desc_interface) + + 4 * sizeof(struct usb_desc_endpoint), + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = 3, + .bmAttributes = USB_ATTR_RESERVED_1, + .bMaxPower = 250, + }, + { + .bLength = sizeof(struct usb_desc_configuration), + .bDescriptorType = USB_DESC_CONFIGURATION, + .wTotalLength = sizeof(struct usb_desc_configuration) + + 1 * sizeof(struct usb_desc_interface) + + 2 * sizeof(struct usb_desc_endpoint), .bNumInterfaces = 1, - .bConfigurationValue = 0, - .iConfiguration = 0, + .bConfigurationValue = 2, + .iConfiguration = 6, .bmAttributes = USB_ATTR_RESERVED_1, - .bMaxPower = 50, - } + .bMaxPower = 250, + }, }; usb_desc_interface_c usb_interfaces[] = { - { + { // EP2OUT + EP6IN + .bLength = sizeof(struct usb_desc_interface), + .bDescriptorType = USB_DESC_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 255, + .bInterfaceSubClass = 255, + .bInterfaceProtocol = 255, + .iInterface = 4, + }, + { // EP4OUT + EP8IN + .bLength = sizeof(struct usb_desc_interface), + .bDescriptorType = USB_DESC_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 255, + .bInterfaceSubClass = 255, + .bInterfaceProtocol = 255, + .iInterface = 5, + }, + { // EP2OUT + EP6IN .bLength = sizeof(struct usb_desc_interface), .bDescriptorType = USB_DESC_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, - .bNumEndpoints = 0, + .bNumEndpoints = 2, .bInterfaceClass = 255, .bInterfaceSubClass = 255, .bInterfaceProtocol = 255, - .iInterface = 0, + .iInterface = 6, } }; +usb_desc_endpoint_c usb_endpoints[] = { + { // EP2OUT + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 2, + .bmAttributes = USB_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + { // EP6IN + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 6|USB_DIR_IN, + .bmAttributes = USB_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + { // EP4OUT + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 4, + .bmAttributes = USB_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + { // EP8IN + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 8|USB_DIR_IN, + .bmAttributes = USB_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + { // EP2OUT + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 2, + .bmAttributes = USB_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + { // EP6IN + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 6|USB_DIR_IN, + .bmAttributes = USB_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, +}; + usb_ascii_string_c usb_strings[] = { - "whitequark research", - "Glasgow Debug Tool", + [0] = "whitequark research", + [1] = "Glasgow Debug Tool", + [2] = "Port A at {2x512B EP2OUT, 2x512B EP6IN}, B at {2x512B EP4OUT, 2x512B EP8IN}", + [3] = "Port A at {2x512B EP2OUT, 2x512B EP6IN}", + [4] = "Port B at {2x512B EP4OUT, 2x512B EP8IN}", + [5] = "Ports AB at {4x512B EP2OUT, 4x512B EP6IN}", }; usb_descriptor_set_c usb_descriptor_set = { @@ -63,6 +153,8 @@ usb_descriptor_set_c usb_descriptor_set = { .configs = usb_configs, .interface_count = ARRAYSIZE(usb_interfaces), .interfaces = usb_interfaces, + .endpoint_count = ARRAYSIZE(usb_endpoints), + .endpoints = usb_endpoints, .string_count = ARRAYSIZE(usb_strings), .strings = usb_strings, }; @@ -136,6 +228,20 @@ void handle_usb_setup(__xdata struct usb_req_setup *req) { } } +void handle_usb_set_configuration(uint8_t value) { + switch(value) { + case 0: break; + case 1: fifo_configure(/*two_ep=*/false); break; + case 2: fifo_configure(/*two_ep=*/true); break; + default: + STALL_EP0(); + return; + } + + usb_configuration = value; + ACK_EP0(); +} + // This monotonically increasing number ensures that we upload bitstream chunks // strictly in order. uint16_t bitstream_idx; @@ -392,23 +498,21 @@ void isr_TF2() __interrupt(_INT_TF2) { TF2 = false; } -static void pulse_led_act() { +static void isr_EPn() { led_act_set(true); - // Just let it run, at the maximum reload value we get a pulse width of around 16ms + // Just let it run, at the maximum reload value we get a pulse width of around 16ms. TR2 = true; -} - -void isr_EP0IN() __interrupt { - pulse_led_act(); + // Clear all EPn IRQs, since we don't really need this IRQ to be fine-grained. CLEAR_USB_IRQ(); - EPIRQ = _EP0IN; + EPIRQ = 0b11110011; //_EP0IN|_EP0OUT|_EP2|_EP4|_EP6|_EP8 } -void isr_EP0OUT() __interrupt { - pulse_led_act(); - CLEAR_USB_IRQ(); - EPIRQ = _EP0OUT; -} +void isr_EP0IN() __interrupt { isr_EPn(); } +void isr_EP0OUT() __interrupt { isr_EPn(); } +void isr_EP2() __interrupt { isr_EPn(); } +void isr_EP4() __interrupt { isr_EPn(); } +void isr_EP6() __interrupt { isr_EPn(); } +void isr_EP8() __interrupt { isr_EPn(); } int main() { // Run at 48 MHz, drive CLKOUT. @@ -416,50 +520,30 @@ int main() { // Initialize subsystems. leds_init(); - led_fpga_set(fpga_is_ready()); iobuf_init_dac_ldo(); iobuf_init_adc(); + fifo_init(); + + // Latch initial status bits. + if(fpga_is_ready()) + latch_status_bit(ST_FPGA_RDY); + + // Disable EP1IN/OUT + SYNCDELAY; + EP1INCFG = 0; + SYNCDELAY; + EP1OUTCFG = 0; // Use timer 2 in 16-bit timer mode for ACT LED. T2CON = _CPRL2; ET2 = true; // Set up endpoint interrupts for ACT LED. - EPIE |= _EP0IN|_EP0OUT; + EPIE |= 0b11110011; //_EP0IN|_EP0OUT|_EP2|_EP4|_EP6|_EP8 // Set up interrupt for ADC ALERT. EX0 = true; - // Configure FIFOs - SYNCDELAY(); - REVCTL = _ENH_PKT|_DYN_OUT; - SYNCDELAY(); - FIFOPINPOLAR = _PKTEND|_SLOE|_SLRD|_SLWR|_EF|_FF; - SYNCDELAY(); - EP1INCFG = 0; - SYNCDELAY(); - EP1OUTCFG = 0; - SYNCDELAY(); - EP2CFG = _VALID|_DIR|_TYPE1|_BUF1; // EP2 IN BULK - SYNCDELAY(); - EP2FIFOCFG = _INFM1|_OEP1|_AUTOOUT|_AUTOIN|_ZEROLENIN; - SYNCDELAY(); - EP4CFG = _VALID|_TYPE1|_BUF1; // EP4 OUT BULK - SYNCDELAY(); - EP4FIFOCFG = _INFM1|_OEP1|_AUTOOUT|_AUTOIN|_ZEROLENIN; - SYNCDELAY(); - EP6CFG = _VALID|_DIR|_TYPE1|_BUF1; // EP6 IN BULK - SYNCDELAY(); - EP6FIFOCFG = _INFM1|_OEP1|_AUTOOUT|_AUTOIN|_ZEROLENIN; - SYNCDELAY(); - EP8CFG = _VALID|_TYPE1|_BUF1; // EP2 OUT BULK - SYNCDELAY(); - EP8FIFOCFG = _INFM1|_OEP1|_AUTOOUT|_AUTOIN|_ZEROLENIN; - - // Drive 30 MHz IFCLK - SYNCDELAY(); - IFCONFIG = _IFCLKSRC|_IFCLKOE; - // Finally, enumerate. usb_init(/*reconnect=*/true); diff --git a/software/glasgow/cli.py b/software/glasgow/cli.py index d67ad132b..84a01cc75 100644 --- a/software/glasgow/cli.py +++ b/software/glasgow/cli.py @@ -5,7 +5,7 @@ from fx2 import FX2DeviceError from .device import * -from .gateware.target import TestToggleIO, TestExposeI2C +from .gateware.target import TestToggleIO, TestMirrorI2C def get_argparser(): @@ -43,9 +43,8 @@ def get_argparser(): p_test_toggle_io = test_subparsers.add_parser( "toggle-io", help="toggle all I/O pins at 3.3 V") - - p_test_expose_i2c = test_subparsers.add_parser( - "expose-i2c", help="mirror {SDA,SCL} on A[1:0] at 3.3 V") + p_test_mirror_i2c = test_subparsers.add_parser( + "mirror-i2c", help="mirror {SDA,SCL} on A[1:0] at 3.3 V") return parser @@ -89,8 +88,8 @@ def main(): if args.mode == "toggle-io": device.download_bitstream(TestToggleIO().get_bitstream(debug=True)) device.set_voltage("AB", 3.3) - if args.mode == "expose-i2c": - device.download_bitstream(TestExposeI2C().get_bitstream(debug=True)) + if args.mode == "mirror-i2c": + device.download_bitstream(TestMirrorI2C().get_bitstream(debug=True)) device.set_voltage("A", 3.3) except FX2DeviceError as e: diff --git a/vendor/libfx2 b/vendor/libfx2 index 28ea07bd1..a3d4211da 160000 --- a/vendor/libfx2 +++ b/vendor/libfx2 @@ -1 +1 @@ -Subproject commit 28ea07bd1084f41dcae03ddcb4910f163a100b8d +Subproject commit a3d4211da0646c2b6dec7f41543ddfe9dc9fad9b