From aee495e7859d0f19b52ba12cafaad2628d84f8e1 Mon Sep 17 00:00:00 2001 From: Ahmed BOUDJELIDA Date: Wed, 16 Aug 2023 17:05:05 +0200 Subject: [PATCH] contrib/firmware: add new i2c bit-banging feature to angie's firmware add new i2c bit-banging feature, we can now connect in JTAG with the SoC target and in i2c with the main board components at the same time. Change-Id: I8e4516fe1ad5238e0373444f1c3c9bc0814d0f52 Signed-off-by: Ahmed BOUDJELIDA Reviewed-on: https://review.openocd.org/c/openocd/+/7796 Tested-by: jenkins Reviewed-by: Antonio Borneo --- contrib/firmware/angie/c/Makefile | 7 +- contrib/firmware/angie/c/README | 4 +- contrib/firmware/angie/c/include/i2c.h | 28 +++ contrib/firmware/angie/c/include/io.h | 16 +- contrib/firmware/angie/c/include/usb.h | 120 +++++----- contrib/firmware/angie/c/src/i2c.c | 127 ++++++++++ contrib/firmware/angie/c/src/protocol.c | 13 +- contrib/firmware/angie/c/src/usb.c | 217 ++++++++++++++---- contrib/firmware/angie/hdl/Makefile | 2 +- ...{angie_openocd.ucf => angie_bitstream.ucf} | 16 +- ...{angie_openocd.vhd => angie_bitstream.vhd} | 64 ++++-- doc/usb_adapters/angie/584e_424e_angie.txt | 59 ++++- src/jtag/drivers/angie/angie_bitstream.bit | Bin 340702 -> 340704 bytes src/jtag/drivers/angie/angie_firmware.bin | Bin 9298 -> 10158 bytes 14 files changed, 534 insertions(+), 139 deletions(-) create mode 100644 contrib/firmware/angie/c/include/i2c.h create mode 100644 contrib/firmware/angie/c/src/i2c.c rename contrib/firmware/angie/hdl/src/{angie_openocd.ucf => angie_bitstream.ucf} (83%) rename contrib/firmware/angie/hdl/src/{angie_openocd.vhd => angie_bitstream.vhd} (55%) diff --git a/contrib/firmware/angie/c/Makefile b/contrib/firmware/angie/c/Makefile index 80e8cbe57..e919cd011 100644 --- a/contrib/firmware/angie/c/Makefile +++ b/contrib/firmware/angie/c/Makefile @@ -38,7 +38,7 @@ LDFLAGS = --code-loc 0x0000 --code-size $(CODE_SIZE) --xram-loc $(XRAM_LOC) \ --xram-size $(XRAM_SIZE) --iram-size 256 --model-small # list of base object files -OBJECTS = main.rel usb.rel protocol.rel jtag.rel delay.rel USBJmpTb.rel serial.rel gpif.rel +OBJECTS = main.rel usb.rel protocol.rel jtag.rel delay.rel USBJmpTb.rel serial.rel gpif.rel i2c.rel HEADERS = $(INCLUDE_DIR)/usb.h \ $(INCLUDE_DIR)/protocol.h \ $(INCLUDE_DIR)/jtag.h \ @@ -47,7 +47,8 @@ HEADERS = $(INCLUDE_DIR)/usb.h \ $(INCLUDE_DIR)/io.h \ $(INCLUDE_DIR)/serial.h \ $(INCLUDE_DIR)/fx2macros.h \ - $(INCLUDE_DIR)/msgtypes.h + $(INCLUDE_DIR)/msgtypes.h \ + $(INCLUDE_DIR)/i2c.h # Disable all built-in rules. .SUFFIXES: @@ -61,7 +62,7 @@ all: angie_firmware.ihx angie_firmware.ihx: $(OBJECTS) $(CC) -mmcs51 $(LDFLAGS) -o $@ $^ -# Rebuild every C module (there are only 5 of them) if any header changes. +# Rebuild every C module (there are only 8 of them) if any header changes. %.rel: $(SRC_DIR)/%.c $(HEADERS) $(CC) -c $(CFLAGS) -mmcs51 -I$(INCLUDE_DIR) -o $@ $< diff --git a/contrib/firmware/angie/c/README b/contrib/firmware/angie/c/README index 04ed0be2a..2d41da7a4 100644 --- a/contrib/firmware/angie/c/README +++ b/contrib/firmware/angie/c/README @@ -12,8 +12,8 @@ To compile the firmware, the SDCC compiler package is required. Most Linux distributions include SDCC in their official package repositories. The SDCC source code can be found at http://sdcc.sourceforge.net/ -Simply type "make hex" in the ANGIE directory to compile the firmware. -"make clean" will remove all generated files except the Intel HEX file +Simply type "make bin" in the ANGIE directory to compile the firmware. +"make clean" will remove all generated files except the BIN file required for downloading the firmware to ANGIE. Note that the EZ-USB FX2 microcontroller does not have on-chip flash, diff --git a/contrib/firmware/angie/c/include/i2c.h b/contrib/firmware/angie/c/include/i2c.h new file mode 100644 index 000000000..06185efb4 --- /dev/null +++ b/contrib/firmware/angie/c/include/i2c.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/**************************************************************************** + File : i2c.h * + Contents : i2c bit-bang library * + Copyright 2023, Ahmed Errached BOUDJELIDA, NanoXplore SAS. * + * + * +*****************************************************************************/ + +#ifndef __I2C_H +#define __I2C_H + +#include +#include +#include + +void start_cd(void); +void repeated_start(void); +void stop_cd(void); +void clock_cd(void); +void send_ack(void); +bool get_ack(void); + +uint8_t get_address(uint8_t adr, uint8_t rdwr); + +void send_byte(uint8_t input); +uint8_t receive_byte(void); +#endif diff --git a/contrib/firmware/angie/c/include/io.h b/contrib/firmware/angie/c/include/io.h index 35afa626f..af488f4ed 100644 --- a/contrib/firmware/angie/c/include/io.h +++ b/contrib/firmware/angie/c/include/io.h @@ -45,14 +45,14 @@ #define PIN_TDI IOB3 #define PIN_TDO IOB4 #define PIN_SRST IOB5 -/* PA6 Not Connected */ -/* PA7 Not Connected */ +/* PB6 Not Connected */ +/* PB7 Not Connected */ /* JTAG Signals with direction 'OUT' on port B */ /* PIN_TDI - PIN_TCK - PIN_TMS - PIN_TRST - PIN_SRST */ #define MASK_PORTB_DIRECTION_OUT (bmbit0 | bmbit1 | bmbit2 | bmbit3 | bmbit5) -/* PORT C */ // Debug: +/* PORT C */ #define PIN_T0 IOC0 #define PIN_T1 IOC1 #define PIN_T2 IOC2 @@ -62,4 +62,14 @@ /* PC6 Not Connected */ /* PC7 Not Connected */ +/* PORT D */ +#define PIN_SDA IOD0 +#define PIN_SCL IOD1 +#define PIN_SDA_DIR IOD2 +/* PD3 Not Connected */ +/* PD4 Not Connected */ +/* PD5 Not Connected */ +/* PD6 Not Connected */ +/* PD7 Not Connected */ + #endif diff --git a/contrib/firmware/angie/c/include/usb.h b/contrib/firmware/angie/c/include/usb.h index 0450d1d1c..07cb12ae6 100644 --- a/contrib/firmware/angie/c/include/usb.h +++ b/contrib/firmware/angie/c/include/usb.h @@ -24,18 +24,19 @@ #define STALL_EP0() (EP0CS |= EPSTALL) #define CLEAR_IRQ() (USBINT = 0) -/*********** USB descriptors. See section 9.5 of the USB 1.1 spec **********/ +/*********** USB descriptors. See USB 2.0 Spec **********/ -/* USB Descriptor Types. See USB 1.1 spec, page 187, table 9-5 */ -#define DESCRIPTOR_TYPE_DEVICE 0x01 -#define DESCRIPTOR_TYPE_CONFIGURATION 0x02 -#define DESCRIPTOR_TYPE_STRING 0x03 -#define DESCRIPTOR_TYPE_INTERFACE 0x04 -#define DESCRIPTOR_TYPE_ENDPOINT 0x05 +/* USB Descriptor Types. See USB 2.0 Spec */ +#define DESCRIPTOR_TYPE_DEVICE 0x01 +#define DESCRIPTOR_TYPE_CONFIGURATION 0x02 +#define DESCRIPTOR_TYPE_STRING 0x03 +#define DESCRIPTOR_TYPE_INTERFACE 0x04 +#define DESCRIPTOR_TYPE_ENDPOINT 0x05 +#define DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION 0x0B #define STR_DESCR(len, ...) { (len) * 2 + 2, DESCRIPTOR_TYPE_STRING, { __VA_ARGS__ } } -/** USB Device Descriptor. See USB 1.1 spec, pp. 196 - 198 */ +/** USB Device Descriptor. See USB 2.0 Spec */ struct usb_device_descriptor { uint8_t blength; /**< Size of this descriptor in bytes. */ uint8_t bdescriptortype; /**< DEVICE Descriptor Type. */ @@ -53,7 +54,7 @@ struct usb_device_descriptor { uint8_t bnumconfigurations; /**< Number of possible configurations. */ }; -/** USB Configuration Descriptor. See USB 1.1 spec, pp. 199 - 200 */ +/** USB Configuration Descriptor. See USB 2.0 Spec */ struct usb_config_descriptor { uint8_t blength; /**< Size of this descriptor in bytes. */ uint8_t bdescriptortype; /**< CONFIGURATION descriptor type. */ @@ -65,7 +66,19 @@ struct usb_config_descriptor { uint8_t maxpower; /**< Maximum power consumption in 2 mA units. */ }; -/** USB Interface Descriptor. See USB 1.1 spec, pp. 201 - 203 */ +/** USB Interface association Descriptor. See USB 2.0 Spec */ +struct usb_interface_association_descriptor { + uint8_t blength; + uint8_t bdescriptortype; + uint8_t bfirstinterface; + uint8_t binterfacecount; + uint8_t bfunctionclass; + uint8_t bfunctionsubclass; + uint8_t bfunctionprotocol; + uint8_t ifunction; +}; + +/** USB Interface Descriptor. See USB 2.0 Spec */ struct usb_interface_descriptor { uint8_t blength; /**< Size of this descriptor in bytes. */ uint8_t bdescriptortype; /**< INTERFACE descriptor type. */ @@ -78,7 +91,7 @@ struct usb_interface_descriptor { uint8_t iinterface; /**< Index of interface string descriptor. */ }; -/** USB Endpoint Descriptor. See USB 1.1 spec, pp. 203 - 204 */ +/** USB Endpoint Descriptor. See USB 2.0 Spec */ struct usb_endpoint_descriptor { uint8_t blength; /**< Size of this descriptor in bytes. */ uint8_t bdescriptortype; /**< ENDPOINT descriptor type. */ @@ -88,14 +101,14 @@ struct usb_endpoint_descriptor { uint8_t binterval; /**< Polling interval (in ms) for this endpoint. */ }; -/** USB Language Descriptor. See USB 1.1 spec, pp. 204 - 205 */ +/** USB Language Descriptor. See USB 2.0 Spec */ struct usb_language_descriptor { uint8_t blength; /**< Size of this descriptor in bytes. */ uint8_t bdescriptortype; /**< STRING descriptor type. */ uint16_t wlangid[]; /**< LANGID codes. */ }; -/** USB String Descriptor. See USB 1.1 spec, pp. 204 - 205 */ +/** USB String Descriptor. See USB 2.0 Spec */ struct usb_string_descriptor { uint8_t blength; /**< Size of this descriptor in bytes. */ uint8_t bdescriptortype; /**< STRING descriptor type. */ @@ -104,7 +117,7 @@ struct usb_string_descriptor { /********************** USB Control Endpoint 0 related *********************/ -/** USB Control Setup Data. See USB 1.1 spec, pp. 183 - 185 */ +/** USB Control Setup Data. See USB 2.0 Spec */ struct setup_data { uint8_t bmrequesttype; /**< Characteristics of a request. */ uint8_t brequest; /**< Specific request. */ @@ -121,66 +134,66 @@ extern volatile bool ep1_in; extern volatile __xdata __at 0xE6B8 struct setup_data setup_data; /* - * USB Request Types (bmRequestType): See USB 1.1 spec, page 183, table 9-2 + * USB Request Types (bmRequestType): See USB 2.0 Spec * * Bit 7: Data transfer direction - * 0 = Host-to-device - * 1 = Device-to-host + * 0 = Host-to-device + * 1 = Device-to-host * Bit 6...5: Type - * 0 = Standard - * 1 = Class - * 2 = Vendor - * 3 = Reserved + * 0 = Standard + * 1 = Class + * 2 = Vendor + * 3 = Reserved * Bit 4...0: Recipient - * 0 = Device - * 1 = Interface - * 2 = Endpoint - * 3 = Other - * 4...31 = Reserved + * 0 = Device + * 1 = Interface + * 2 = Endpoint + * 3 = Other + * 4...31 = Reserved */ -#define USB_DIR_OUT 0x00 -#define USB_DIR_IN 0x80 +#define USB_DIR_OUT 0x00 +#define USB_DIR_IN 0x80 #define USB_REQ_TYPE_STANDARD (0x00 << 5) #define USB_REQ_TYPE_CLASS (0x01 << 5) #define USB_REQ_TYPE_VENDOR (0x02 << 5) #define USB_REQ_TYPE_RESERVED (0x03 << 5) -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 /* Clear Interface Request */ -#define CF_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define CF_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) #define CF_INTERFACE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_INTERFACE) #define CF_ENDPOINT (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_ENDPOINT) /* Get Configuration Request */ -#define GC_DEVICE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define GC_DEVICE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) /* Get Descriptor Request */ -#define GD_DEVICE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define GD_DEVICE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) /* Get Interface Request */ #define GI_INTERFACE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_INTERFACE) /* Get Status Request: See USB 1.1 spec, page 190 */ -#define GS_DEVICE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define GS_DEVICE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) #define GS_INTERFACE (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_INTERFACE) #define GS_ENDPOINT (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_ENDPOINT) /* Set Address Request is handled by EZ-USB core */ /* Set Configuration Request */ -#define SC_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define SC_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) /* Set Descriptor Request */ -#define SD_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define SD_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) /* Set Feature Request */ -#define SF_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) +#define SF_DEVICE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_DEVICE) #define SF_INTERFACE (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_INTERFACE) #define SF_ENDPOINT (USB_DIR_OUT | USB_REQ_TYPE_STANDARD | USB_RECIP_ENDPOINT) @@ -190,27 +203,27 @@ extern volatile __xdata __at 0xE6B8 struct setup_data setup_data; /* Synch Frame Request */ #define SY_ENDPOINT (USB_DIR_IN | USB_REQ_TYPE_STANDARD | USB_RECIP_ENDPOINT) -/* USB Requests (bRequest): See USB 1.1 spec, table 9-4 on page 187 */ -#define GET_STATUS 0 -#define CLEAR_FEATURE 1 +/* USB Requests (bRequest): See USB 2.0 Spec */ +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 /* Value '2' is reserved for future use */ -#define SET_FEATURE 3 +#define SET_FEATURE 3 /* Value '4' is reserved for future use */ -#define SET_ADDRESS 5 -#define GET_DESCRIPTOR 6 -#define SET_DESCRIPTOR 7 -#define GET_CONFIGURATION 8 -#define SET_CONFIGURATION 9 +#define SET_ADDRESS 5 +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 #define GET_INTERFACE 10 #define SET_INTERFACE 11 #define SYNCH_FRAME 12 -/* Standard Feature Selectors: See USB 1.1 spec, table 9-6 on page 188 */ -#define DEVICE_REMOTE_WAKEUP 1 -#define ENDPOINT_HALT 0 +/* Standard Feature Selectors: See USB 2.0 Spec */ +#define DEVICE_REMOTE_WAKEUP 1 +#define ENDPOINT_HALT 0 /************************** EZ-USB specific stuff **************************/ -/** USB Interrupts. See AN2131-TRM, page 9-4 for details */ +/** USB Interrupts. See EZ-USB FX2-TRM, for details */ enum usb_isr { SUDAV_ISR = 13, SOF_ISR, @@ -265,7 +278,10 @@ bool usb_handle_set_feature(void); bool usb_handle_get_descriptor(void); void usb_handle_set_interface(void); void usb_handle_setup_data(void); +void usb_handle_i2c_in(void); +void usb_handle_i2c_out(void); +void i2c_recieve(void); void ep_init(void); void interrupt_init(void); void io_init(void); diff --git a/contrib/firmware/angie/c/src/i2c.c b/contrib/firmware/angie/c/src/i2c.c new file mode 100644 index 000000000..a7004bfac --- /dev/null +++ b/contrib/firmware/angie/c/src/i2c.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + File : i2c.cpp * + Contents : i2c bit-bang library * + Copyright 2023, Ahmed Errached BOUDJELIDA, NanoXplore SAS. * + * + * +*****************************************************************************/ + +#include "i2c.h" +#include "io.h" +#include "delay.h" +#include "reg_ezusb.h" + +void start_cd(void) +{ + PIN_SDA = 0; //SDA = 1; + delay_us(1); + PIN_SCL = 0; //SCL = 1; + delay_us(1); +} + +void repeated_start(void) +{ + PIN_SDA = 1; + delay_us(1); + PIN_SCL = 1; + delay_us(1); + PIN_SDA = 0; + delay_us(1); + PIN_SCL = 0; + delay_us(1); +} + +void stop_cd(void) +{ + PIN_SDA = 0; + delay_us(1); + PIN_SCL = 1; + delay_us(1); + PIN_SDA = 1; + delay_us(1); +} + +void clock_cd(void) +{ + PIN_SCL = 1; + delay_us(1); + PIN_SCL = 0; + delay_us(1); +} + +void send_ack(void) +{ + PIN_SDA = 0; + delay_us(1); + PIN_SCL = 1; + delay_us(1); + PIN_SCL = 0; + delay_us(1); +} + +bool get_ack(void) +{ + PIN_SDA_DIR = 1; + delay_us(1); + OED = 0xFE; + PIN_SCL = 1; + delay_us(1); + bool ack = PIN_SDA; + PIN_SCL = 0; + delay_us(1); + OED = 0xFF; + PIN_SDA_DIR = 0; + delay_us(1); + return ack; +} + +/* here address(8 bits) = adr (7 bits) + type (1 bit) */ +uint8_t get_address(uint8_t adr, uint8_t rdwr) +{ + adr &= 0x7F; + adr = adr << 1; + adr |= (rdwr & 0x01); + return adr; +} + +/* here send bit after bit and clocking scl with each bit */ +void send_byte(uint8_t input) +{ + for (uint8_t i = 0; i < 8; i++) { + if ((input & 0x80)) { + PIN_SDA = 1; + delay_us(1); + clock_cd(); + } else { + PIN_SDA = 0; + delay_us(1); + clock_cd(); + } + input = input << 1; + } +} + +/* here receive bit after bit and clocking scl with each bit */ + +uint8_t receive_byte(void) +{ + PIN_SDA_DIR = 1; //FX2 <-- FPGA + OED = 0xFE; + uint8_t input = 0x00; + for (uint8_t i = 0; i < 8; i++) { + PIN_SCL = 1; + delay_us(1); + input = input << 1; + if (PIN_SDA == 1) + input |= 0x01; + else + input |= 0X00; + + PIN_SCL = 0; + delay_us(1); + } + OED = 0xFF; + PIN_SDA_DIR = 0; + return input; +} diff --git a/contrib/firmware/angie/c/src/protocol.c b/contrib/firmware/angie/c/src/protocol.c index 3f3aaaf36..d84534bf2 100644 --- a/contrib/firmware/angie/c/src/protocol.c +++ b/contrib/firmware/angie/c/src/protocol.c @@ -139,15 +139,12 @@ bool execute_command(void) payload_index_in += usb_in_bytecount; /* Determine if this was the last command */ - if ((cmd_id_index + usb_out_bytecount + 1) >= EP1OUTBC) { + if ((cmd_id_index + usb_out_bytecount + 1) >= EP1OUTBC) return true; - /* Line between return and else required by checkpatch: */ - uint8_t a = 0; - } else { - /* Not the last command, update cmd_id_index */ - cmd_id_index += (usb_out_bytecount + 1); - return false; - } + + /* Not the last command, update cmd_id_index */ + cmd_id_index += (usb_out_bytecount + 1); + return false; } /** diff --git a/contrib/firmware/angie/c/src/usb.c b/contrib/firmware/angie/c/src/usb.c index 8fd4de637..747fef124 100644 --- a/contrib/firmware/angie/c/src/usb.c +++ b/contrib/firmware/angie/c/src/usb.c @@ -18,6 +18,7 @@ #include #include #include +#include "i2c.h" /* Also update external declarations in "include/usb.h" if making changes to * these variables! @@ -36,9 +37,9 @@ __code struct usb_device_descriptor device_descriptor = { .blength = sizeof(struct usb_device_descriptor), .bdescriptortype = DESCRIPTOR_TYPE_DEVICE, .bcdusb = 0x0200, /* BCD: 02.00 (Version 2.0 USB spec) */ - .bdeviceclass = 0xFF, /* 0xFF = vendor-specific */ - .bdevicesubclass = 0xFF, - .bdeviceprotocol = 0xFF, + .bdeviceclass = 0xEF, + .bdevicesubclass = 0x02, + .bdeviceprotocol = 0x01, .bmaxpacketsize0 = 64, .idvendor = 0x584e, .idproduct = 0x424e, @@ -55,25 +56,36 @@ __code struct usb_config_descriptor config_descriptor = { .blength = sizeof(struct usb_config_descriptor), .bdescriptortype = DESCRIPTOR_TYPE_CONFIGURATION, .wtotallength = sizeof(struct usb_config_descriptor) + - sizeof(struct usb_interface_descriptor) + - (NUM_ENDPOINTS * sizeof(struct usb_endpoint_descriptor)), - .bnuminterfaces = 1, + 3 * sizeof(struct usb_interface_descriptor) + + ((NUM_ENDPOINTS + 2) * sizeof(struct usb_endpoint_descriptor)), + .bnuminterfaces = 2, .bconfigurationvalue = 1, - .iconfiguration = 4, /* String describing this configuration */ + .iconfiguration = 1, /* String describing this configuration */ .bmattributes = 0x80, /* Only MSB set according to USB spec */ .maxpower = 50 /* 100 mA */ }; +__code struct usb_interface_association_descriptor interface_association_descriptor = { + .blength = sizeof(struct usb_interface_association_descriptor), + .bdescriptortype = DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, + .bfirstinterface = 0x01, + .binterfacecount = 0x02, + .bfunctionclass = 0x02, + .bfunctionsubclass = 0x00, + .bfunctionprotocol = 0x00, + .ifunction = 0x00 +}; + __code struct usb_interface_descriptor interface_descriptor00 = { .blength = sizeof(struct usb_interface_descriptor), .bdescriptortype = DESCRIPTOR_TYPE_INTERFACE, .binterfacenumber = 0, .balternatesetting = 0, .bnumendpoints = NUM_ENDPOINTS, - .binterfaceclass = 0xFF, - .binterfacesubclass = 0xFF, - .binterfaceprotocol = 0xFF, - .iinterface = 0 + .binterfaceclass = 0XFF, + .binterfacesubclass = 0x00, + .binterfaceprotocol = 0x00, + .iinterface = 4 }; __code struct usb_endpoint_descriptor bulk_ep1_out_endpoint_descriptor = { @@ -103,16 +115,19 @@ __code struct usb_endpoint_descriptor bulk_ep2_endpoint_descriptor = { .binterval = 0 }; -__code struct usb_endpoint_descriptor bulk_ep4_endpoint_descriptor = { - .blength = sizeof(struct usb_endpoint_descriptor), - .bdescriptortype = 0x05, - .bendpointaddress = (4 | USB_DIR_IN), - .bmattributes = 0x02, - .wmaxpacketsize = 512, - .binterval = 0 +__code struct usb_interface_descriptor interface_descriptor01 = { + .blength = sizeof(struct usb_interface_descriptor), + .bdescriptortype = DESCRIPTOR_TYPE_INTERFACE, + .binterfacenumber = 1, + .balternatesetting = 0, + .bnumendpoints = 2, + .binterfaceclass = 0x0A, + .binterfacesubclass = 0x00, + .binterfaceprotocol = 0x00, + .iinterface = 0x00 }; -__code struct usb_endpoint_descriptor bulk_ep6_endpoint_descriptor = { +__code struct usb_endpoint_descriptor bulk_ep6_out_endpoint_descriptor = { .blength = sizeof(struct usb_endpoint_descriptor), .bdescriptortype = 0x05, .bendpointaddress = (6 | USB_DIR_OUT), @@ -121,19 +136,18 @@ __code struct usb_endpoint_descriptor bulk_ep6_endpoint_descriptor = { .binterval = 0 }; -__code struct usb_endpoint_descriptor bulk_ep8_endpoint_descriptor = { +__code struct usb_endpoint_descriptor bulk_ep8_in_endpoint_descriptor = { .blength = sizeof(struct usb_endpoint_descriptor), .bdescriptortype = 0x05, - .bendpointaddress = (8 | USB_DIR_OUT), + .bendpointaddress = (8 | USB_DIR_IN), .bmattributes = 0x02, .wmaxpacketsize = 512, .binterval = 0 }; - __code struct usb_language_descriptor language_descriptor = { .blength = 4, .bdescriptortype = DESCRIPTOR_TYPE_STRING, - .wlangid = {0x0409 /* US English */} + .wlangid = {0x0409} /* US English */ }; __code struct usb_string_descriptor strmanufacturer = @@ -212,9 +226,15 @@ void ep4_isr(void)__interrupt EP4_ISR } void ep6_isr(void)__interrupt EP6_ISR { + i2c_recieve(); + EXIF &= ~0x10; /* Clear USBINT: Main global interrupt */ + EPIRQ = 0x40; /* Clear individual EP6OUT IRQ */ + } void ep8_isr(void)__interrupt EP8_ISR { + EXIF &= ~0x10; /* Clear USBINT: Main global interrupt */ + EPIRQ = 0x80; /* Clear individual EP8IN IRQ */ } void ibn_isr(void)__interrupt IBN_ISR { @@ -411,21 +431,21 @@ bool usb_handle_clear_feature(void) switch (setup_data.bmrequesttype) { case CF_DEVICE: /* Clear remote wakeup not supported: stall EP0 */ - STALL_EP0(); - break; + STALL_EP0(); + break; case CF_ENDPOINT: - if (setup_data.wvalue == 0) { + if (setup_data.wvalue == 0) { /* Unstall the endpoint specified in wIndex */ - ep_cs = usb_get_endpoint_cs_reg(setup_data.windex); - if (!ep_cs) - return false; - *ep_cs &= ~EPSTALL; + ep_cs = usb_get_endpoint_cs_reg(setup_data.windex); + if (!ep_cs) + return false; + *ep_cs &= ~EPSTALL; } else { /* Unsupported feature, stall EP0 */ - STALL_EP0(); - } - break; - default: + STALL_EP0(); + } + break; + default: /* Vendor commands... */ break; } @@ -597,7 +617,6 @@ bool usb_handle_send_bitstream(void) /* wait until GPIF transaction has been completed */ while ((GPIFTRIG & BMGPIFDONE) == 0) { if (ix-- == 0) { - printf("GPIF done time out\n"); break; } delay_ms(1); @@ -697,9 +716,9 @@ void ep_init(void) syncdelay(3); EP4CFG = 0x00; syncdelay(3); - EP6CFG = 0x00; + EP6CFG = 0xA2; syncdelay(3); - EP8CFG = 0x00; + EP8CFG = 0xE2; syncdelay(3); /* arm EP1-OUT */ @@ -714,6 +733,12 @@ void ep_init(void) EP1INBC = 0; syncdelay(3); + /* arm EP6-OUT */ + EP6BCL = 0x80; + syncdelay(3); + EP6BCL = 0x80; + syncdelay(3); + /* Standard procedure to reset FIFOs */ FIFORESET = BMNAKALL; /* NAK all transfers during the reset */ syncdelay(3); @@ -727,9 +752,110 @@ void ep_init(void) syncdelay(3); } +void i2c_recieve(void) +{ + PIN_SDA_DIR = 0; + if (EP6FIFOBUF[0] == 1) { + uint8_t rdwr = EP6FIFOBUF[0]; //read + uint8_t reg_adr_check = EP6FIFOBUF[1]; + uint8_t count = EP6FIFOBUF[2]; //request data count + uint8_t adr = EP6FIFOBUF[3]; //address + uint8_t reg_adr = EP6FIFOBUF[4]; + uint8_t address = get_address(adr, rdwr); //address byte (read command) + uint8_t address_2 = get_address(adr, 0); //address byte 2 (write command) + + printf("%d\n", address); + + /* start: */ + start_cd(); + /* address: */ + send_byte(address_2); //write + /* ack: */ + uint8_t ack = get_ack(); + + delay_us(10); + + /* send data */ + if (reg_adr_check) { //if there is a byte reg + send_byte(reg_adr); + /* ack(): */ + ack = get_ack(); + } + + delay_us(10); + + /* repeated start: */ + repeated_start(); + /* address: */ + send_byte(address); + /* get ack: */ + ack = get_ack(); + + delay_us(10); + + /* receive data */ + for (uint8_t i = 0; i < count; i++) { + EP8FIFOBUF[i] = receive_byte(); + + /* send ack: */ + send_ack(); + } + + delay_ms(1); + + /* stop */ + stop_cd(); + + delay_us(10); + + EP8BCH = 0; //EP8 + syncdelay(3); + EP8BCL = count; //EP8 + + EP6BCL = 0x80; //EP6 + syncdelay(3); + EP6BCL = 0x80; //EP6 + } else { + uint8_t rdwr = EP6FIFOBUF[0]; //write + uint8_t count = EP6FIFOBUF[1]; //data count + uint8_t adr = EP6FIFOBUF[2]; //address + uint8_t address = get_address(adr, rdwr); //address byte (read command) + uint8_t ack_cnt = 0; + +/* start(): */ + start_cd(); +/* address: */ + send_byte(address); //write +/* ack(): */ + if (!get_ack()) + ack_cnt++; +/* send data */ + for (uint8_t i = 0; i < count; i++) { + send_byte(EP6FIFOBUF[i + 3]); + + /* get ack: */ + if (!get_ack()) + ack_cnt++; + } + +/* stop */ + stop_cd(); + + EP8FIFOBUF[0] = ack_cnt; + + EP8BCH = 0; //EP8 + syncdelay(3); + EP8BCL = 1; //EP8 + + EP6BCL = 0x80; //EP6 + syncdelay(3); + EP6BCL = 0x80; //EP6 + } +} + /** * Interrupt initialization. Configures USB interrupts. - */ + **/ void interrupt_init(void) { /* Enable Interrupts */ @@ -742,11 +868,11 @@ void interrupt_init(void) /* Enable INT 2 & 4 Autovectoring */ INTSETUP |= (AV2EN | AV4EN); - /* Enable individual EP1OUT&IN interrupts */ - EPIE |= 0x0C; + /* Enable individual EP1OUT&IN & EP6&8 interrupts */ + EPIE |= 0xCC; /* Clear individual USB interrupt IRQ */ - EPIRQ = 0x0C; + EPIRQ = 0xCC; /* Enable SUDAV interrupt */ USBIEN |= SUDAVI; @@ -777,8 +903,15 @@ void io_init(void) PIN_TDI = 0; PIN_SRST = 1; + + /* PORT C */ PORTCCFG = 0x00; /* 0: normal ou 1: alternate function (each bit) */ - OEC = 0xEF; + OEC = 0xFF; IOC = 0xFF; + + /* PORT D */ + OED = 0xFF; + IOD = 0xFF; + PIN_SDA_DIR = 0; } diff --git a/contrib/firmware/angie/hdl/Makefile b/contrib/firmware/angie/hdl/Makefile index c2c74a0b8..b28b65089 100644 --- a/contrib/firmware/angie/hdl/Makefile +++ b/contrib/firmware/angie/hdl/Makefile @@ -2,7 +2,7 @@ # Copyright (C) 2023 by NanoXplore, France - all rights reserved # Needed by timing test -export PROJECT := angie_openocd +export PROJECT := angie_bitstream TARGET_PART := xc6slx9-2tqg144 export TOPLEVEL := S609 diff --git a/contrib/firmware/angie/hdl/src/angie_openocd.ucf b/contrib/firmware/angie/hdl/src/angie_bitstream.ucf similarity index 83% rename from contrib/firmware/angie/hdl/src/angie_openocd.ucf rename to contrib/firmware/angie/hdl/src/angie_bitstream.ucf index fda3cdaaf..92a89c99e 100644 --- a/contrib/firmware/angie/hdl/src/angie_openocd.ucf +++ b/contrib/firmware/angie/hdl/src/angie_bitstream.ucf @@ -14,17 +14,31 @@ net TRST LOC = 'P48' ; net TMS LOC = 'P43' ; net TCK LOC = 'P44' ; net TDI LOC = 'P45' ; -net TDO LOC = 'P46' ; +net TDO LOC = 'P46' ; net SRST LOC = 'P61' ; + +net SDA LOC = 'P50' ; +net SCL LOC = 'P51' ; +net SDA_DIR LOC = 'P56' ; + net SI_TDO LOC = 'P16' ; net SO_TRST LOC = 'P32' ; net SO_TMS LOC = 'P27' ; net SO_TCK LOC = 'P30' ; net SO_TDI LOC = 'P26' ; net SO_SRST LOC = 'P12' ; + +net SO_SDA_OUT LOC = 'P140' ; +net SO_SDA_IN LOC = 'P1' ; +net SO_SCL LOC = 'P137'; + net ST_0 LOC = 'P29' ; net ST_1 LOC = 'P21' ; net ST_2 LOC = 'P11' ; + +net ST_4 LOC = 'P134' ; +net ST_5 LOC = 'P139' ; + net FTP<0> LOC = 'P121' ; net FTP<1> LOC = 'P120' ; net FTP<2> LOC = 'P119' ; diff --git a/contrib/firmware/angie/hdl/src/angie_openocd.vhd b/contrib/firmware/angie/hdl/src/angie_bitstream.vhd similarity index 55% rename from contrib/firmware/angie/hdl/src/angie_openocd.vhd rename to contrib/firmware/angie/hdl/src/angie_bitstream.vhd index d79c0fece..21ddb844a 100644 --- a/contrib/firmware/angie/hdl/src/angie_openocd.vhd +++ b/contrib/firmware/angie/hdl/src/angie_bitstream.vhd @@ -16,22 +16,35 @@ library UNISIM; use UNISIM.VComponents.all; entity S609 is port( - TRST : in std_logic; - TMS : in std_logic; - TCK : in std_logic; - TDI : in std_logic; - TDO : out std_logic; - SRST : in std_logic; - FTP : out std_logic_vector(7 downto 0); -- Test points + TRST : in std_logic; + TMS : in std_logic; + TCK : in std_logic; + TDI : in std_logic; + TDO : out std_logic; + SRST : in std_logic; + + SDA : inout std_logic; + SDA_DIR : in std_logic; + SCL : in std_logic; + + FTP : out std_logic_vector(7 downto 0):=(others => '1'); -- Test points SI_TDO : in std_logic; - ST_0 : out std_logic; - ST_1 : out std_logic; - ST_2 : out std_logic; + ST_0 : out std_logic; + ST_1 : out std_logic; + ST_2 : out std_logic; + + ST_4 : out std_logic; + ST_5 : out std_logic; + + SO_SDA_OUT : out std_logic; + SO_SDA_IN : in std_logic; + SO_SCL : out std_logic; + SO_TRST : out std_logic; - SO_TMS : out std_logic; - SO_TCK : out std_logic; + SO_TMS : out std_logic; + SO_TCK : out std_logic; SO_TDI : out std_logic; - SO_SRST :out std_logic + SO_SRST : out std_logic ); end S609; @@ -42,6 +55,8 @@ begin ST_0 <= '0'; ST_1 <= '1'; +ST_4 <= '0'; + --TDO: TDO <= not SI_TDO; @@ -53,11 +68,26 @@ SO_TDI <= TDI; ST_2 <= SRST; SO_SRST <= '0'; +SO_SCL <= SCL; + +SDA <= not(SO_SDA_IN) when (SDA_DIR = '1') else 'Z'; +SO_SDA_OUT <= SDA; + +process(SDA_DIR) +begin + if(SDA_DIR = '1') then + ST_5 <= '1'; + else + ST_5 <= '0'; + end if; +end process; + + --Points de test: -FTP(0) <= TRST; -FTP(1) <= TMS; -FTP(2) <= TCK; -FTP(3) <= TDI; +FTP(0) <= SDA; +FTP(1) <= SCL; +FTP(2) <= not(SO_SDA_IN); +FTP(3) <= SDA_DIR; FTP(5) <= SRST; FTP(4) <= SI_TDO; FTP(6) <= '1'; diff --git a/doc/usb_adapters/angie/584e_424e_angie.txt b/doc/usb_adapters/angie/584e_424e_angie.txt index 8162cbad5..d68657a98 100644 --- a/doc/usb_adapters/angie/584e_424e_angie.txt +++ b/doc/usb_adapters/angie/584e_424e_angie.txt @@ -1,13 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-or-later OR GFDL-1.2-no-invariants-or-later -Bus 001 Device 056: ID 584e:424e NanoXplore, SAS. ANGIE Adapter +Bus 001 Device 029: ID 584e:424e NanoXplore, SAS. ANGIE Adapter Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 - bDeviceClass 255 Vendor Specific Class - bDeviceSubClass 255 Vendor Specific Subclass - bDeviceProtocol 255 Vendor Specific Protocol + bDeviceClass 239 Miscellaneous Device + bDeviceSubClass 2 + bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x584e idProduct 0x424e @@ -19,13 +19,22 @@ Device Descriptor: Configuration Descriptor: bLength 9 bDescriptorType 2 - wTotalLength 0x0027 - bNumInterfaces 1 + wTotalLength 0x0047 + bNumInterfaces 2 bConfigurationValue 1 - iConfiguration 4 (error) + iConfiguration 1 NanoXplore, SAS. bmAttributes 0x80 (Bus Powered) MaxPower 100mA + Interface Association: + bLength 8 + bDescriptorType 11 + bFirstInterface 1 + bInterfaceCount 2 + bFunctionClass 2 Communications + bFunctionSubClass 0 + bFunctionProtocol 0 + iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 @@ -33,9 +42,9 @@ Device Descriptor: bAlternateSetting 0 bNumEndpoints 3 bInterfaceClass 255 Vendor Specific Class - bInterfaceSubClass 255 Vendor Specific Subclass - bInterfaceProtocol 255 Vendor Specific Protocol - iInterface 0 + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 4 JTAG Adapter Endpoint Descriptor: bLength 7 bDescriptorType 5 @@ -66,3 +75,33 @@ Device Descriptor: Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x06 EP 6 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x88 EP 8 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 diff --git a/src/jtag/drivers/angie/angie_bitstream.bit b/src/jtag/drivers/angie/angie_bitstream.bit index 9e83e6b69d92a880881baf8d68ab171fb1a3f09e..aebd3700b3c36cf4d1d83d7a3939a94eebcf1d41 100644 GIT binary patch delta 1559 zcmZuxU1%It6rOYM-pz@2CLZ`+?8L?*T*k>HWC(fmGxsIw z?@O2Dr4JsE;JMuu-|havVNl+ysx7?8c%_u(kMEG+&s$5Ni7F%XnL~08y!rM~d^inz ziK|N!_C4i)xcl1_FMnz!_!S@)W*V&36v(OgD~rRx!kmzdbi61u>5!nmJ}y&A*p8Qi zQ!t6&q~UQ&Q2-GwJAA59kg_~81dl?n;yil$Co(Sa>X&kY9H<1<68oBhq@RMS1 z2nZr0Rv8zCHk(M$EtK%8_{YFsHpC|Bt?4}lY?z+i2hyp@%Wm?=?OBTHsUu30#<>I) ze5s9e{1sF^!336%*3H1uMp9*c1p4Cy9J2{jmMn<_e~hntSn#Xr-XON7UPjCW3@f=% z13&M-BZ}cS#jOeAyxzb${}&vFJy%-~gP3AUSH`IO*UT#H)iTD-wSeg*%2L>5`greN+mTfO!Ik6dBhQd@6Ii)j(zC)int zX_9wNopoPj+SmA7Yhd&V7MEi27x@tm6@yzKYzbA_8HwtkD`WktS-(j>-w;jPq>8K@ zi9~A~rf2}~3py4ubB+wVlvEMbn0|Ue?zZXnxDFI)1WMubi1tcK%5t>w}TN_ z1sXmDd0c&;T&0>601jl}Eo!i>^y0D{jM4I-BA5vS-ig6i*gaE_A43arT5<6fe$fms zYijkUf{K0f_Iz_flZa~kG@TYYF{-5l&vhx+Ho-8Ab|Y~b6W!_ne{=N3dsLr*B38Rp z5g#fk$0AJd!=qc(GZ^nyk5PtQyqD4aCDkS5*26B2mR0BUyoK+URhzW;-^8!VD*rEU CAn)-2 delta 1144 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 delta 2705 zcmaJ@4R9036~5a$NtP^O=`6{TY)kS+ZVRXnZ5>a-kP6|Ll+cnYOq1!P?L>4ekxpYvX{*o%g8KF( zBxfem^L+b#@9o=tyKnb&KC|KKDkWg!C*x8FgQ%UmslTtJI?A7-6#vHO_HU}SU*mL6 zuIxijH_7QzN=fnnpB&M@bCv$%gCN-r-&HzRMzXbdW@N2*r)i?#ku$)v_>_J~X*p9!>)@#JHB>3?6*!k_o zfP^??R1(9GP)Qsk%jhSWk-hztbUJh-v>5Ix4fPy?yGW;a4*mIyjzSOf)wtCCSl-SH zj@b_#>+-h3FZpl^E7zApHTRQ}_OWD}P*a7Ee|ZODzs9kn_yJv6ket6+7|Iuko25&q z@@{KE+6uD&OXXm}J4>>Hn4gW!U$|vTf-QtQX36Yf!7i?jUj3-)PhKUl9ZIH${j~ub zf!hTm)*0>zCIdqmK1jvQez zXy=5h7-T33^Pty+eW1S?_Cq@mu7>vAa1Dcst>Ic|*M;Xo`;+iI1{rn8Slp{QZ8l9W zIvBe(?Z@RtgFS9$(~@kdAs3f39*AnA5N5&_V=2B_Y|@uB7d&oUU#!)0rkv-1$&TMK z+0FfuI@WeJ?_XMMYt0$G8kov@C(C>_()4~|&TJO*PYWKuzH}r%(-$+u^ZuoJn>fp` z?<*Kq`)fI)mkJ^^p}iKbH_eA_<(AClmTYgl6G>KCim#X+LG}%}Va~QiMeb2a{4{Rq zC{+@JaZBz3#PuDw0(uHw{=#|ZB|blAZ?WyH@X15U&8Mj2wRq6%#GNHaknJpH%_6eJ z@iXR+2?-XNIB!}m5}M%l5$CGXPYB&>eHNT!Z(FrA>3r7~BHnM=Wn`_#f3=?iYpkzq z0gW8T@EZ;XvOkI6cg#ihT?d@oEzSip=a5UCvehNq$~nhnyxS=R%U+4<2H}u6BF>u( z0m+avdZ|Hafc&`A!VKKYw2(Us|aeSCksWp2(P=p0*1Z?TuJ8mEeJOe zmq2)kcpHS5h|3_Vi1-}{o`~Or@DXt(HHIp5w7YC9TE@%LHaXNPr`o2{52v@Jx5|*z zc^J9+aBZcx)Onb5^+D9A6xk4Ii9EnLKf_xp8=TJRxRjZEup~_?jCLM}5N zseGc6KYmM~>(bk%mZe(<+dj^8Gl<`Dz8Zf&y!xAd6P&eL<#NEOQ)jTRRseD3MN!WcuXeR72 z39AUhysLBmH931N8QvmnP~EqM7L`{Bn~3}S!h^Y}hX{KZVYA9bwZfKc2l7~Y$zB2- zYWoOjpH}5}NQ;f$xh2l&D2g)Kk0A0a!cE?Vi0{Ea@NPrSY5bm-CzboJx1LyEshVf@ zEP6BEbMT3{aBv6I5C%M2<>Nec0m(b7P!RikRlC(W9gvoCq-{{!A3}?Z`I-vXyh2iW zqcm%zYaU+5-%XN*_@(UqflYpVfvmaD4m8z%PF9}Swe_HVC~D2HcLggw`Qly<-5