├── .gitignore ├── software ├── generated │ └── .gitignore ├── windows │ ├── FTD3XXLibrary │ │ ├── FTD3XX.h │ │ ├── x64 │ │ │ ├── FTD3XX.dll │ │ │ ├── FTD3XX.lib │ │ │ └── Static │ │ │ │ └── FTD3XX.lib │ │ └── Win32 │ │ │ ├── FTD3XX.dll │ │ │ ├── FTD3XX.lib │ │ │ └── Static │ │ │ └── FTD3XX.lib │ ├── ft601.h │ ├── compat.c │ └── ft601.c ├── common │ ├── testsuite.h │ ├── etherbone.h │ ├── ulpi.h │ ├── test_hw.c │ ├── testsuite.c │ ├── ulpi.c │ └── etherbone.c ├── Makefile ├── sdram_init.py ├── hw │ ├── flags.h │ └── common.h ├── linux │ └── compat.c ├── etherbone.py └── sniff.py ├── openocd ├── bscan_spi_xc7a35t.bit ├── bscan_spi_xc7a50t.bit └── openocd.cfg ├── README.md ├── gateware ├── clocker.py ├── storage.py ├── flash.py ├── spi.py ├── wrapper.py ├── ulpi.py ├── ft601.py ├── usb.py ├── dramfifo.py ├── iti.py └── etherbone.py ├── test ├── test_analyzer.py └── test_sdram.py ├── usbblink.py └── usbsniffer.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | build/ 3 | -------------------------------------------------------------------------------- /software/generated/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /openocd/bscan_spi_xc7a35t.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/openocd/bscan_spi_xc7a35t.bit -------------------------------------------------------------------------------- /openocd/bscan_spi_xc7a50t.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/openocd/bscan_spi_xc7a50t.bit -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/FTD3XX.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/FTD3XX.h -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/x64/FTD3XX.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/x64/FTD3XX.dll -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/x64/FTD3XX.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/x64/FTD3XX.lib -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/Win32/FTD3XX.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/Win32/FTD3XX.dll -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/Win32/FTD3XX.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/Win32/FTD3XX.lib -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/Win32/Static/FTD3XX.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/Win32/Static/FTD3XX.lib -------------------------------------------------------------------------------- /software/windows/FTD3XXLibrary/x64/Static/FTD3XX.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaconcept/usbsniffer/HEAD/software/windows/FTD3XXLibrary/x64/Static/FTD3XX.lib -------------------------------------------------------------------------------- /software/common/testsuite.h: -------------------------------------------------------------------------------- 1 | #ifndef __TESTSUITE_H_ 2 | #define __TESTSUITE_H_ 3 | 4 | int check_soc_identifier(int fd); 5 | int check_ulpi_scratch(int fd, int num); 6 | int check_sdram(int fd); 7 | int check_leds(int fd, int num); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /software/windows/ft601.h: -------------------------------------------------------------------------------- 1 | #include "FTD3XX.h" 2 | 3 | int FT601_Open(FT_HANDLE *ftHandle); 4 | int FT601_Close(FT_HANDLE ftHandle); 5 | int FT601_Read(FT_HANDLE ftHandle, void *buf, size_t len); 6 | int FT601_Write(FT_HANDLE ftHandle, void *buf, size_t len); 7 | -------------------------------------------------------------------------------- /software/common/etherbone.h: -------------------------------------------------------------------------------- 1 | #ifndef __ETHERBONE_H_ 2 | #define __ETHERBONE_H_ 3 | 4 | int eb_make_read_pkt( uint32_t addr, uint32_t r_count, char **buf, size_t *len); 5 | int eb_make_write_pkt( uint32_t addr, uint32_t *data, uint32_t w_count, char **buf, size_t *len); 6 | int eb_decode_rcv_pkt(char *buf, int blen, uint32_t **data, size_t *len); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /software/common/ulpi.h: -------------------------------------------------------------------------------- 1 | #ifndef __ULPI_H_ 2 | #define __ULPI_H_ 3 | 4 | #include 5 | 6 | #define ULPI_REG_SCRATCH 0x16 7 | 8 | uint8_t ulpi_read_reg(int fd, uint8_t addr, int num); 9 | void ulpi_write_reg(int fd, uint8_t addr, uint8_t val, int num); 10 | void ulpi_reset(int fd, uint32_t val, int num); 11 | void ulpi_dump(int fd, int num); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # usbsniffer 2 | 3 | ### Setup 4 | 5 | * Install Python3 and Xilinx's Vivado software. 6 | * Obtain LiteX and install it: 7 | ``` 8 | git clone https://github.com/enjoy-digital/litex --recursive 9 | cd litex 10 | python3 setup.py install 11 | ``` 12 | 13 | ### Build 14 | 15 | ``` 16 | python3 usbsniffer.py 17 | ``` 18 | 19 | ### Program FPGA Flash 20 | 21 | ``` 22 | openocd -f openocd/openocd.cfg 23 | ``` 24 | -------------------------------------------------------------------------------- /gateware/clocker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # Copyright (C) 2019 / LambdaConcept / po@lambdaconcept.com 4 | 5 | from migen import * 6 | 7 | class TuneClocker(Module): 8 | def __init__(self, tuning_word): 9 | self.en = Signal() 10 | 11 | # # # 12 | 13 | acc = Signal(32) 14 | 15 | self.sync += [ 16 | Cat(acc, self.en).eq(acc + tuning_word), 17 | ] 18 | -------------------------------------------------------------------------------- /openocd/openocd.cfg: -------------------------------------------------------------------------------- 1 | interface ftdi 2 | ftdi_vid_pid 0x0403 0x6011 3 | ftdi_channel 0 4 | ftdi_layout_init 0x0098 0x008b 5 | reset_config none 6 | 7 | source [find cpld/xilinx-xc7.cfg] 8 | source [find cpld/jtagspi.cfg] 9 | adapter_khz 10000 10 | 11 | proc fpga_program {} { 12 | global _CHIPNAME 13 | xc7_program $_CHIPNAME.tap 14 | } 15 | 16 | proc flash_program {} { 17 | init 18 | jtagspi_init 0 openocd/bscan_spi_xc7a35t.bit 19 | jtagspi_program build/gateware/top.bin 0x0 20 | } 21 | 22 | flash_program 23 | fpga_program 24 | 25 | exit 26 | -------------------------------------------------------------------------------- /software/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDES=-I. 2 | 3 | CC_WIN=i686-w64-mingw32.static-gcc 4 | INCLUDES_WIN=-I./windows/FTD3XXLibrary 5 | LIBS_WIN=windows/FTD3XXLibrary/Win32/FTD3XX.lib 6 | 7 | CC_LINUX=gcc 8 | 9 | all: build_windows build_linux 10 | 11 | build_windows: build_windows_test 12 | 13 | build_linux: build_linux_test 14 | 15 | build_windows_test: 16 | $(CC_WIN) windows/*.c common/*.c -o test_hw.exe $(INCLUDES) $(INCLUDES_WIN) -lws2_32 $(LIBS_WIN) 17 | cp windows/FTD3XXLibrary/Win32/FTD3XX.dll FTD3XX.dll 18 | 19 | build_linux_test: 20 | $(CC_LINUX) linux/*.c common/*.c -o test_hw $(INCLUDES) 21 | -------------------------------------------------------------------------------- /gateware/storage.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 / EnjoyDigital / florent@enjoy-digital.fr 2 | from migen import * 3 | 4 | from litex.soc.interconnect.csr import * 5 | from litex.soc.interconnect import stream 6 | 7 | class OverflowMeter(Module, AutoCSR): 8 | def __init__(self, description): 9 | self.sink = sink = stream.Endpoint(description) 10 | self.source = source = stream.Endpoint(description) 11 | 12 | self.reset = CSR() 13 | self.count = CSRStatus(32) 14 | 15 | # # # 16 | 17 | self.comb += sink.connect(source) 18 | self.sync += [ 19 | If(self.reset.re, 20 | self.count.status.eq(0) 21 | ).Elif(self.sink.valid & ~self.sink.ready, 22 | self.count.status.eq(self.count.status + 1) 23 | ) 24 | ] 25 | -------------------------------------------------------------------------------- /test/test_analyzer.py: -------------------------------------------------------------------------------- 1 | from litex import RemoteClient 2 | from litescope.software.driver.analyzer import LiteScopeAnalyzerDriver 3 | 4 | wb = RemoteClient(port=1234) 5 | wb.open() 6 | 7 | identifier = "" 8 | for i in range(0, 32): 9 | identifier += "%c" %wb.read(wb.bases.identifier_mem + 4*i) 10 | print("\nSoC identifier: " + identifier) 11 | print() 12 | 13 | # # # 14 | 15 | analyzer = LiteScopeAnalyzerDriver(wb.regs, "analyzer", debug=True) 16 | analyzer.configure_trigger(cond={ 17 | # "soc_sender_source_source_valid": 1, 18 | # "soc_sender_source_source_ready": 1, 19 | # "soc_sender_source_source_payload_data": 0 20 | "soc_ulpi_core0_source_source_valid": 1, 21 | "soc_ulpi_core0_source_source_ready": 1, 22 | }) 23 | # analyzer.configure_trigger() 24 | analyzer.configure_subsampler(1) 25 | analyzer.run(offset=1, length=1024) 26 | analyzer.wait_done() 27 | analyzer.upload() 28 | analyzer.save("dump.vcd") 29 | 30 | # # # 31 | 32 | wb.close() 33 | -------------------------------------------------------------------------------- /software/sdram_init.py: -------------------------------------------------------------------------------- 1 | dfii_control_sel = 0x01 2 | dfii_control_cke = 0x02 3 | dfii_control_odt = 0x04 4 | dfii_control_reset_n = 0x08 5 | 6 | dfii_command_cs = 0x01 7 | dfii_command_we = 0x02 8 | dfii_command_cas = 0x04 9 | dfii_command_ras = 0x08 10 | dfii_command_wrdata = 0x10 11 | dfii_command_rddata = 0x20 12 | 13 | init_sequence = [ 14 | ("Release reset", 0, 0, dfii_control_odt|dfii_control_reset_n, 50000), 15 | ("Bring CKE high", 0, 0, dfii_control_cke|dfii_control_odt|dfii_control_reset_n, 10000), 16 | ("Load Mode Register 2, CWL=5", 512, 2, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, 0), 17 | ("Load Mode Register 3", 0, 3, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, 0), 18 | ("Load Mode Register 1", 6, 1, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, 0), 19 | ("Load Mode Register 0, CL=6, BL=8", 2336, 0, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, 200), 20 | ("ZQ Calibration", 1024, 0, dfii_command_we|dfii_command_cs, 200), 21 | ] 22 | -------------------------------------------------------------------------------- /gateware/flash.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 / EnjoyDigital / florent@enjoy-digital.fr 2 | from migen import * 3 | 4 | from litex.soc.interconnect.csr import * 5 | 6 | from gateware.spi import SPIMaster 7 | 8 | 9 | class Flash(Module, AutoCSR): 10 | def __init__(self, pads, div=4): 11 | pads_i = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) 12 | self.submodules.spi = SPIMaster(pads_i, width=40, div=div) 13 | 14 | pads.vpp.reset = 1 15 | pads.hold.reset = 1 16 | 17 | self.comb += [ 18 | pads.cs_n.eq(pads_i.cs_n), 19 | pads.mosi.eq(pads_i.mosi), 20 | pads_i.miso.eq(pads.miso) 21 | ] 22 | 23 | # we need to use STARTUPE2 to drive clk on 7-series 24 | self.specials += \ 25 | Instance("STARTUPE2", 26 | i_CLK=0, 27 | i_GSR=0, 28 | i_GTS=0, 29 | i_KEYCLEARB=0, 30 | i_PACK=0, 31 | i_USRCCLKO=pads_i.clk, 32 | i_USRCCLKTS=0, 33 | i_USRDONEO=1, 34 | i_USRDONETS=1) 35 | -------------------------------------------------------------------------------- /software/hw/flags.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_FLAGS_H 2 | #define __HW_FLAGS_H 3 | 4 | #define UART_EV_TX 0x1 5 | #define UART_EV_RX 0x2 6 | 7 | #define DFII_CONTROL_SEL 0x01 8 | #define DFII_CONTROL_CKE 0x02 9 | #define DFII_CONTROL_ODT 0x04 10 | #define DFII_CONTROL_RESET_N 0x08 11 | 12 | #define DFII_COMMAND_CS 0x01 13 | #define DFII_COMMAND_WE 0x02 14 | #define DFII_COMMAND_CAS 0x04 15 | #define DFII_COMMAND_RAS 0x08 16 | #define DFII_COMMAND_WRDATA 0x10 17 | #define DFII_COMMAND_RDDATA 0x20 18 | 19 | #define ETHMAC_EV_SRAM_WRITER 0x1 20 | #define ETHMAC_EV_SRAM_READER 0x1 21 | 22 | #define CLKGEN_STATUS_BUSY 0x1 23 | #define CLKGEN_STATUS_PROGDONE 0x2 24 | #define CLKGEN_STATUS_LOCKED 0x4 25 | 26 | #define DVISAMPLER_TOO_LATE 0x1 27 | #define DVISAMPLER_TOO_EARLY 0x2 28 | 29 | #define DVISAMPLER_DELAY_MASTER_CAL 0x01 30 | #define DVISAMPLER_DELAY_MASTER_RST 0x02 31 | #define DVISAMPLER_DELAY_SLAVE_CAL 0x04 32 | #define DVISAMPLER_DELAY_SLAVE_RST 0x08 33 | #define DVISAMPLER_DELAY_INC 0x10 34 | #define DVISAMPLER_DELAY_DEC 0x20 35 | 36 | #define DVISAMPLER_SLOT_EMPTY 0 37 | #define DVISAMPLER_SLOT_LOADED 1 38 | #define DVISAMPLER_SLOT_PENDING 2 39 | 40 | #endif /* __HW_FLAGS_H */ 41 | -------------------------------------------------------------------------------- /software/hw/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_COMMON_H 2 | #define __HW_COMMON_H 3 | 4 | #include 5 | 6 | /* To overwrite CSR accessors, define extern, non-inlined versions 7 | * of csr_read[bwl]() and csr_write[bwl](), and define 8 | * CSR_ACCESSORS_DEFINED. 9 | */ 10 | 11 | #ifndef CSR_ACCESSORS_DEFINED 12 | #define CSR_ACCESSORS_DEFINED 13 | 14 | #ifdef __ASSEMBLER__ 15 | #define MMPTR(x) x 16 | #else /* ! __ASSEMBLER__ */ 17 | #define MMPTR(x) (*((volatile unsigned int *)(x))) 18 | 19 | static inline void csr_writeb(uint8_t value, uint32_t addr) 20 | { 21 | *((volatile uint8_t *)addr) = value; 22 | } 23 | 24 | static inline uint8_t csr_readb(uint32_t addr) 25 | { 26 | return *(volatile uint8_t *)addr; 27 | } 28 | 29 | static inline void csr_writew(uint16_t value, uint32_t addr) 30 | { 31 | *((volatile uint16_t *)addr) = value; 32 | } 33 | 34 | static inline uint16_t csr_readw(uint32_t addr) 35 | { 36 | return *(volatile uint16_t *)addr; 37 | } 38 | 39 | static inline void csr_writel(uint32_t value, uint32_t addr) 40 | { 41 | *((volatile uint32_t *)addr) = value; 42 | } 43 | 44 | static inline uint32_t csr_readl(uint32_t addr) 45 | { 46 | return *(volatile uint32_t *)addr; 47 | } 48 | #endif /* ! __ASSEMBLER__ */ 49 | 50 | #endif /* ! CSR_ACCESSORS_DEFINED */ 51 | 52 | #endif /* __HW_COMMON_H */ 53 | -------------------------------------------------------------------------------- /software/common/test_hw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef WIN32 8 | #include "FTD3XX.h" 9 | #include "windows/ft601.h" 10 | #endif 11 | 12 | #include "common/testsuite.h" 13 | 14 | void cdelay(int val) 15 | { 16 | usleep(val); 17 | } 18 | 19 | /* global handle used by csr read/write */ 20 | #ifdef WIN32 21 | static FT_HANDLE _gfd; 22 | #else 23 | static int _gfd; 24 | #endif 25 | 26 | extern uint32_t eb_read_reg32(int fd, uint32_t addr); 27 | extern void eb_write_reg32(int fd, uint32_t addr, uint32_t val); 28 | 29 | void csr_writel(uint32_t value, uint32_t addr) 30 | { 31 | eb_write_reg32(_gfd, addr, value); 32 | } 33 | 34 | uint32_t csr_readl(uint32_t addr) 35 | { 36 | return eb_read_reg32(_gfd, addr); 37 | } 38 | 39 | int main(int argc, char **argv) 40 | { 41 | int i; 42 | int ret; 43 | 44 | #ifdef WIN32 45 | WSADATA wsa_data; 46 | WSAStartup(0x0201, &wsa_data); 47 | #endif 48 | 49 | printf("USBSniffer Hardware Testsuite\n\n"); 50 | 51 | #ifdef WIN32 52 | ret = FT601_Open(&_gfd); 53 | #else 54 | if (argc < 2) { 55 | printf("usage: %s /dev/ft60xx\n", argv[0]); 56 | exit(1); 57 | } 58 | _gfd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 59 | ret = !(_gfd < 0); 60 | #endif 61 | if (!ret) { 62 | printf("Open failed: device not found\n"); 63 | return ret; 64 | } 65 | 66 | /* Check BUS */ 67 | check_soc_identifier(_gfd); 68 | 69 | /* Check both ULPI chips */ 70 | for (i=0; i<2; i++) { 71 | check_ulpi_scratch(_gfd, i); 72 | } 73 | 74 | /* Check SDRAM */ 75 | check_sdram(_gfd); 76 | 77 | /* Check LEDs */ 78 | for (i=0; i<2; i++) { 79 | check_leds(_gfd, i); 80 | } 81 | 82 | #ifdef WIN32 83 | FT601_Close(_gfd); 84 | #else 85 | close(_gfd); 86 | #endif 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /software/common/testsuite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "ulpi.h" 6 | 7 | #define CSR_ACCESSORS_DEFINED 8 | #include "generated/csr.h" 9 | #include "generated/sdram_phy.h" 10 | 11 | extern uint32_t eb_read_reg32(int fd, uint32_t addr); 12 | extern void eb_write_reg32(int fd, uint32_t addr, uint32_t val); 13 | 14 | int check_soc_identifier(int fd) 15 | { 16 | int i; 17 | char id[32 + 1]; 18 | 19 | printf("SoC identifier:\n"); 20 | for (i=0; i<32; i++) { 21 | id[i] = eb_read_reg32(fd, CSR_IDENTIFIER_MEM_BASE + 4*i); 22 | } 23 | id[i] = '\0'; 24 | printf("\t%s\n\n", id); 25 | 26 | return 0; 27 | } 28 | 29 | int check_ulpi_scratch(int fd, int num) 30 | { 31 | uint8_t reg; 32 | 33 | printf("ULPI %d scratch test:\n", num); 34 | 35 | /* reset ulpi chip */ 36 | ulpi_reset(fd, 1, num); 37 | usleep(100000); 38 | 39 | ulpi_reset(fd, 0, num); 40 | usleep(100000); 41 | 42 | /* write some value */ 43 | ulpi_write_reg(fd, ULPI_REG_SCRATCH, 0xc3, num); 44 | 45 | /* read our written value, must match */ 46 | reg = ulpi_read_reg(fd, ULPI_REG_SCRATCH, num); 47 | if(reg != 0xc3) 48 | goto error; 49 | 50 | printf("\t[OK]\n\n"); 51 | return 0; 52 | error: 53 | printf("\t[ERROR]\n\n"); 54 | return 1; 55 | } 56 | 57 | #define MAIN_RAM_BASE 0x40000000 58 | 59 | int check_sdram(int fd) 60 | { 61 | uint32_t i; 62 | uint32_t val; 63 | 64 | printf("Testing SDRAM write/read:\n"); 65 | 66 | /* initialize sdram registers */ 67 | init_sequence(); 68 | 69 | /* calibrate */ 70 | // XXX 71 | 72 | /* check write/read */ 73 | for (i=0; i<32; i++) { 74 | eb_write_reg32(fd, MAIN_RAM_BASE + 4*i, i); 75 | val = eb_read_reg32(fd, MAIN_RAM_BASE + 4*i); 76 | if (val != i) 77 | goto error; 78 | } 79 | 80 | printf("\t[OK]\n\n"); 81 | return 0; 82 | error: 83 | printf("\t[ERROR]\n\n"); 84 | return 1; 85 | } 86 | 87 | int check_leds(int fd, int num) 88 | { 89 | uint32_t reg; 90 | 91 | printf("LED %d blink test:\n", num); 92 | 93 | /* Force LED blink */ 94 | if (num) 95 | blinker1_forceblink_write(1); 96 | else 97 | blinker0_forceblink_write(1); 98 | 99 | printf("\t[Check LEDS]\n\n"); 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /software/common/ulpi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define CSR_ACCESSORS_DEFINED 6 | #include "generated/csr.h" 7 | 8 | extern uint32_t eb_read_reg32(int fd, uint32_t addr); 9 | extern void eb_write_reg32(int fd, uint32_t addr, uint32_t val); 10 | 11 | uint8_t ulpi0_read_reg(int fd, uint8_t addr) 12 | { 13 | eb_write_reg32(fd, CSR_ULPI_CORE0_REG_ADR_ADDR, addr); 14 | eb_write_reg32(fd, CSR_ULPI_CORE0_REG_READ_ADDR, 1); 15 | while(!eb_read_reg32(fd, CSR_ULPI_CORE0_REG_DONE_ADDR)); 16 | return eb_read_reg32(fd, CSR_ULPI_CORE0_REG_DAT_R_ADDR); 17 | } 18 | 19 | uint8_t ulpi1_read_reg(int fd, uint8_t addr) 20 | { 21 | eb_write_reg32(fd, CSR_ULPI_CORE1_REG_ADR_ADDR, addr); 22 | eb_write_reg32(fd, CSR_ULPI_CORE1_REG_READ_ADDR, 1); 23 | while(!eb_read_reg32(fd, CSR_ULPI_CORE1_REG_DONE_ADDR)); 24 | return eb_read_reg32(fd, CSR_ULPI_CORE1_REG_DAT_R_ADDR); 25 | } 26 | 27 | uint8_t ulpi_read_reg(int fd, uint8_t addr, int num) 28 | { 29 | if (num) 30 | return ulpi1_read_reg(fd, addr); 31 | else 32 | return ulpi0_read_reg(fd, addr); 33 | } 34 | 35 | void ulpi0_write_reg(int fd, uint8_t addr, uint8_t val) 36 | { 37 | eb_write_reg32(fd, CSR_ULPI_CORE0_REG_ADR_ADDR, addr); 38 | eb_write_reg32(fd, CSR_ULPI_CORE0_REG_DAT_W_ADDR, val); 39 | eb_write_reg32(fd, CSR_ULPI_CORE0_REG_WRITE_ADDR, 1); 40 | while(!eb_read_reg32(fd, CSR_ULPI_CORE0_REG_DONE_ADDR)); 41 | } 42 | 43 | void ulpi1_write_reg(int fd, uint8_t addr, uint8_t val) 44 | { 45 | eb_write_reg32(fd, CSR_ULPI_CORE1_REG_ADR_ADDR, addr); 46 | eb_write_reg32(fd, CSR_ULPI_CORE1_REG_DAT_W_ADDR, val); 47 | eb_write_reg32(fd, CSR_ULPI_CORE1_REG_WRITE_ADDR, 1); 48 | while(!eb_read_reg32(fd, CSR_ULPI_CORE1_REG_DONE_ADDR)); 49 | } 50 | 51 | void ulpi_write_reg(int fd, uint8_t addr, uint8_t val, int num) 52 | { 53 | if (num) 54 | ulpi1_write_reg(fd, addr, val); 55 | else 56 | ulpi0_write_reg(fd, addr, val); 57 | } 58 | 59 | void ulpi0_reset(int fd, uint32_t val) 60 | { 61 | eb_write_reg32(fd, CSR_ULPI_PHY0_ULPI_PHY_RESET_ADDR, val); 62 | } 63 | 64 | void ulpi1_reset(int fd, uint32_t val) 65 | { 66 | eb_write_reg32(fd, CSR_ULPI_PHY1_ULPI_PHY_RESET_ADDR, val); 67 | } 68 | 69 | void ulpi_reset(int fd, uint32_t val, int num) 70 | { 71 | if (num) 72 | ulpi1_reset(fd, val); 73 | else 74 | ulpi0_reset(fd, val); 75 | } 76 | 77 | void ulpi_dump(int fd, int num) 78 | { 79 | int i; 80 | printf("Registers:\n"); 81 | for(i=0; i< 0x19; i++) 82 | printf("Reg %02x -> %02x\n", i, ulpi_read_reg(fd, i, num)); 83 | printf("\n"); 84 | } 85 | -------------------------------------------------------------------------------- /software/linux/compat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/etherbone.h" 9 | 10 | #define FT_STREAM_PREAMBLE 0x5aa55aa5 11 | #define FT_STREAM_HEADER_SIZE 12 12 | #define FT_STREAM_PORTS 256 13 | 14 | // #define DEBUG 15 | 16 | size_t readft(int fd, void *buf, size_t len) 17 | { 18 | size_t toread=len; 19 | unsigned char *pnt=(unsigned char*)buf; 20 | size_t rdl; 21 | 22 | while(toread){ 23 | // printf("try reading length: %d\n", toread); 24 | rdl = read(fd, pnt, toread); 25 | if(rdl > toread) 26 | exit(0); 27 | 28 | #ifdef DEBUG 29 | int i; 30 | printf("recv: ", rdl); 31 | for(i = 0; i < rdl; i++) 32 | printf("%02x", pnt[i]); 33 | printf("\n"); 34 | #endif 35 | 36 | pnt += rdl; 37 | toread-=rdl; 38 | } 39 | return len; 40 | } 41 | 42 | struct xbar_s { 43 | uint32_t magic; 44 | uint32_t streamid; 45 | uint32_t len; 46 | }__attribute__((packed)); 47 | 48 | int ubar_send_packet(int fd, char *buf, size_t len, int streamid) 49 | { 50 | unsigned char *tosend; 51 | uint32_t *val; 52 | int i; 53 | 54 | tosend = malloc(len + 12); 55 | val = (uint32_t*)tosend; 56 | *(val++) = 0x5aa55aa5;// 0xa55aa55a; 57 | *(val++) = streamid; 58 | *(val++) = len; 59 | memcpy(tosend+12, buf, len); 60 | 61 | #ifdef DEBUG 62 | printf("send: ", len+12); 63 | for(i = 0; i < len+12; i++) 64 | printf("%02x", tosend[i]); 65 | printf("\n"); 66 | #endif 67 | 68 | write(fd, tosend, len+12); 69 | return 0; 70 | } 71 | 72 | int ubar_recv_packet(int fd, char **buf, size_t *len) 73 | { 74 | struct xbar_s xbar; 75 | int i; 76 | char *tmp; 77 | int rdl; 78 | uint32_t header; 79 | 80 | do { 81 | readft(fd, &header, 4); 82 | // printf("magic header: %08x\n", header); 83 | } while(header != 0x5aa55aa5); 84 | xbar.magic = 0x5aa55aa5; 85 | readft(fd, (unsigned char*)&xbar + 4, 8); 86 | if(xbar.len > 32768) 87 | { 88 | exit(1); 89 | } 90 | tmp = malloc(xbar.len); 91 | 92 | readft(fd, tmp, xbar.len); 93 | 94 | *buf = tmp; 95 | *len = xbar.len; 96 | return xbar.streamid; 97 | } 98 | 99 | uint32_t eb_read_reg32(int fd, uint32_t addr) 100 | { 101 | char *buf; 102 | size_t len; 103 | uint32_t *data; 104 | size_t dlen; 105 | uint32_t ret; 106 | 107 | eb_make_read_pkt(addr, 1, &buf, &len); 108 | ubar_send_packet(fd, buf, len, 0); 109 | free(buf); 110 | 111 | ubar_recv_packet(fd, &buf, &len); 112 | eb_decode_rcv_pkt(buf, len, &data, &dlen); 113 | ret = data[0]; 114 | free(data); 115 | return ret; 116 | } 117 | 118 | void eb_write_reg32(int fd, uint32_t addr, uint32_t val) 119 | { 120 | char *buf; 121 | size_t len; 122 | uint32_t *data; 123 | size_t dlen; 124 | uint32_t ret; 125 | 126 | eb_make_write_pkt(addr, &val, 1, &buf, &len); 127 | ubar_send_packet(fd, buf, len, 0); 128 | free(buf); 129 | } 130 | -------------------------------------------------------------------------------- /software/windows/compat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "FTD3XX.h" 5 | #include "ft601.h" 6 | 7 | #include "common/etherbone.h" 8 | 9 | #define FT_STREAM_PREAMBLE 0x5aa55aa5 10 | #define FT_STREAM_HEADER_SIZE 12 11 | #define FT_STREAM_PORTS 256 12 | 13 | // #define DEBUG 14 | 15 | size_t readft(FT_HANDLE fd, void *buf, size_t len) 16 | { 17 | size_t toread=len; 18 | unsigned char *pnt=(unsigned char*)buf; 19 | size_t rdl; 20 | 21 | while(toread){ 22 | // printf("try reading length: %d\n", toread); 23 | rdl = FT601_Read(fd, pnt, toread); 24 | if(rdl > toread) 25 | exit(0); 26 | 27 | #ifdef DEBUG 28 | int i; 29 | printf("recv: ", rdl); 30 | for(i = 0; i < rdl; i++) 31 | printf("%02x", pnt[i]); 32 | printf("\n"); 33 | #endif 34 | 35 | pnt += rdl; 36 | toread-=rdl; 37 | } 38 | return len; 39 | } 40 | 41 | struct xbar_s { 42 | uint32_t magic; 43 | uint32_t streamid; 44 | uint32_t len; 45 | }__attribute__((packed)); 46 | 47 | int ubar_send_packet(FT_HANDLE fd, char *buf, size_t len, int streamid) 48 | { 49 | unsigned char *tosend; 50 | uint32_t *val; 51 | int i; 52 | 53 | tosend = malloc(len + 12); 54 | val = (uint32_t*)tosend; 55 | *(val++) = 0x5aa55aa5;// 0xa55aa55a; 56 | *(val++) = streamid; 57 | *(val++) = len; 58 | memcpy(tosend+12, buf, len); 59 | 60 | #ifdef DEBUG 61 | printf("send: ", len+12); 62 | for(i = 0; i < len+12; i++) 63 | printf("%02x", tosend[i]); 64 | printf("\n"); 65 | #endif 66 | 67 | FT601_Write(fd, tosend, len+12); 68 | return 0; 69 | } 70 | 71 | int ubar_recv_packet(FT_HANDLE fd, char **buf, size_t *len) 72 | { 73 | struct xbar_s xbar; 74 | int i; 75 | char *tmp; 76 | int rdl; 77 | uint32_t header; 78 | 79 | do { 80 | readft(fd, &header, 4); 81 | // printf("magic header: %08x\n", header); 82 | } while(header != 0x5aa55aa5); 83 | xbar.magic = 0x5aa55aa5; 84 | readft(fd, (unsigned char*)&xbar + 4, 8); 85 | if(xbar.len > 32768) 86 | { 87 | exit(1); 88 | } 89 | tmp = malloc(xbar.len); 90 | 91 | readft(fd, tmp, xbar.len); 92 | 93 | *buf = tmp; 94 | *len = xbar.len; 95 | return xbar.streamid; 96 | } 97 | 98 | uint32_t eb_read_reg32(FT_HANDLE fd, uint32_t addr) 99 | { 100 | char *buf; 101 | size_t len; 102 | uint32_t *data; 103 | size_t dlen; 104 | uint32_t ret; 105 | 106 | eb_make_read_pkt(addr, 1, &buf, &len); 107 | ubar_send_packet(fd, buf, len, 0); 108 | free(buf); 109 | 110 | ubar_recv_packet(fd, &buf, &len); 111 | eb_decode_rcv_pkt(buf, len, &data, &dlen); 112 | ret = data[0]; 113 | free(data); 114 | return ret; 115 | } 116 | 117 | void eb_write_reg32(FT_HANDLE fd, uint32_t addr, uint32_t val) 118 | { 119 | char *buf; 120 | size_t len; 121 | uint32_t *data; 122 | size_t dlen; 123 | uint32_t ret; 124 | 125 | eb_make_write_pkt(addr, &val, 1, &buf, &len); 126 | ubar_send_packet(fd, buf, len, 0); 127 | free(buf); 128 | } 129 | -------------------------------------------------------------------------------- /software/etherbone.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | from litex.soc.tools.remote.etherbone import * 4 | from litex.soc.tools.remote.csr_builder import CSRBuilder 5 | 6 | class USBMux(): 7 | def __init__(self, path): 8 | self.f = open(path, "r+b") 9 | self.magic = 0x5aa55aa5 10 | 11 | def send(self, streamid, packet): 12 | length = len(packet) 13 | header = struct.pack("III", self.magic, streamid, length) 14 | data = header + packet 15 | # print("send:", data.hex()) 16 | self.f.write(data) 17 | 18 | def recv(self, streamid): 19 | magic = 0 20 | while magic != self.magic: 21 | data = self.f.read(4) 22 | magic, = struct.unpack("I", data) 23 | # print("Magic:", data.hex()) 24 | try: 25 | assert(magic == self.magic) 26 | except AssertionError as e: 27 | print("ASSERT ERROR!") 28 | for k in range(64): 29 | data = self.f.read(4) 30 | print(data.hex()) 31 | raise e 32 | data = self.f.read(8) 33 | sid, length = struct.unpack("II", data) 34 | # print("Header:", hex(magic), sid, length) 35 | # print(data.hex()) 36 | packet = self.f.read(length) 37 | # print("Packet:", packet.hex()) 38 | if sid != streamid: 39 | print("Not our stream, drop packet") 40 | return None 41 | return packet 42 | 43 | class Etherbone(CSRBuilder): 44 | def __init__(self, io, streamid, csr_csv=None, csr_data_width=32, debug=False): 45 | self.io = io 46 | self.streamid = streamid 47 | if csr_csv is not None: 48 | CSRBuilder.__init__(self, self, csr_csv, csr_data_width) 49 | self.debug = debug 50 | 51 | def open(self): 52 | pass 53 | 54 | def close(self): 55 | pass 56 | 57 | def read(self, addr, length=None): 58 | length_int = 1 if length is None else length 59 | datas = [] 60 | for i in range(length_int): 61 | record = EtherboneRecord() 62 | record.reads = EtherboneReads(addrs=[addr + 4*i]) 63 | record.rcount = 1 64 | 65 | packet = EtherbonePacket() 66 | packet.records = [record] 67 | packet.encode() 68 | 69 | self.io.send(self.streamid, bytes(packet)) 70 | data = None 71 | while data is None: 72 | data = self.io.recv(self.streamid) 73 | 74 | packet = EtherbonePacket(data) 75 | packet.decode() 76 | datas.append(packet.records.pop().writes.get_datas()[0]) 77 | if self.debug: 78 | for i, data in enumerate(datas): 79 | print("read {:08x} @ {:08x}".format(data, addr + 4*i)) 80 | return datas[0] if length is None else datas 81 | 82 | def write(self, addr, datas): 83 | datas = datas if isinstance(datas, list) else [datas] 84 | for i, data in enumerate(datas): 85 | record = EtherboneRecord() 86 | record.writes = EtherboneWrites(base_addr=addr + 4*i, datas=[data]) 87 | record.wcount = 1 88 | 89 | packet = EtherbonePacket() 90 | packet.records = [record] 91 | packet.encode() 92 | 93 | self.io.send(self.streamid, bytes(packet)) 94 | 95 | if self.debug: 96 | print("write {:08x} @ {:08x}".format(data, addr + 4*i)) 97 | -------------------------------------------------------------------------------- /gateware/spi.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 / EnjoyDigital / florent@enjoy-digital.fr 2 | from migen import * 3 | 4 | from litex.soc.interconnect.csr import * 5 | 6 | 7 | class SPIMaster(Module, AutoCSR): 8 | def __init__(self, pads, width, div): 9 | self.pads = pads 10 | 11 | self._ctrl = CSR(16) 12 | self._status = CSRStatus(4) 13 | self._mosi = CSRStorage(width) 14 | self._miso = CSRStatus(width) 15 | 16 | self.irq = Signal() 17 | 18 | # # # 19 | 20 | # ctrl 21 | start = Signal() 22 | length = Signal(8) 23 | enable_cs = Signal() 24 | enable_shift = Signal() 25 | done = Signal() 26 | 27 | self.comb += [ 28 | start.eq(self._ctrl.re & self._ctrl.r[0]), 29 | self._status.status.eq(done) 30 | ] 31 | self.sync += \ 32 | If(self._ctrl.re, length.eq(self._ctrl.r[8:16])) 33 | 34 | 35 | # clk 36 | i = Signal(max=div) 37 | clk_en = Signal() 38 | set_clk = Signal() 39 | clr_clk = Signal() 40 | self.sync += [ 41 | If(set_clk, 42 | pads.clk.eq(enable_cs) 43 | ), 44 | If(clr_clk, 45 | pads.clk.eq(0), 46 | i.eq(0) 47 | ).Else( 48 | i.eq(i + 1), 49 | ) 50 | ] 51 | 52 | self.comb += [ 53 | set_clk.eq(i == div//2-1), 54 | clr_clk.eq(i == div-1) 55 | ] 56 | 57 | # fsm 58 | count = Signal(8) 59 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 60 | fsm.act("IDLE", 61 | If(start, 62 | NextValue(count, 0), 63 | NextState("WAIT_CLK") 64 | ), 65 | done.eq(1) 66 | ) 67 | fsm.act("WAIT_CLK", 68 | If(clr_clk, 69 | NextState("SHIFT") 70 | ) 71 | ) 72 | fsm.act("SHIFT", 73 | If(count == length, 74 | NextState("END") 75 | ).Elif(clr_clk, 76 | NextValue(count, count + 1) 77 | ), 78 | enable_cs.eq(1), 79 | enable_shift.eq(1) 80 | ) 81 | fsm.act("END", 82 | If(set_clk, 83 | NextState("IDLE") 84 | ), 85 | enable_shift.eq(1), 86 | self.irq.eq(1) 87 | ) 88 | 89 | # miso (captured on clk falling edge) 90 | miso = Signal() 91 | sr_miso = Signal(width) 92 | self.sync += \ 93 | If(enable_shift, 94 | If(set_clk, 95 | miso.eq(pads.miso), 96 | ).Elif(clr_clk, 97 | sr_miso.eq(Cat(miso, sr_miso[:-1])) 98 | ) 99 | ) 100 | self.comb += self._miso.status.eq(sr_miso) 101 | 102 | # mosi (propagated on clk falling edge) 103 | mosi = Signal() 104 | sr_mosi = Signal(width) 105 | self.sync += \ 106 | If(start, 107 | sr_mosi.eq(self._mosi.storage) 108 | ).Elif(set_clk & enable_shift, 109 | sr_mosi.eq(Cat(Signal(), sr_mosi[:-1])) 110 | ).Elif(clr_clk, 111 | pads.mosi.eq(sr_mosi[-1]) 112 | ) 113 | 114 | # cs_n 115 | if hasattr(pads, "cs_n"): 116 | self.comb += pads.cs_n.eq(~enable_cs) 117 | -------------------------------------------------------------------------------- /gateware/wrapper.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 / LambdaConcept / po@lambdaconcept.com 2 | from migen import * 3 | from migen.genlib.misc import WaitTimer 4 | 5 | from litex.soc.interconnect import stream 6 | 7 | from gateware.usb import user_description as usb_description 8 | 9 | 10 | def wrap_description(dw): 11 | payload_layout = [ 12 | ("data", dw), 13 | ] 14 | return stream.EndpointDescription(payload_layout) 15 | 16 | 17 | @ResetInserter() 18 | class WrapCore(Module): 19 | def __init__(self, usb_core, identifier): 20 | self.submodules.sender = sender = WrapSender(identifier) 21 | self.sink = sender.sink 22 | 23 | # # # 24 | 25 | usb_port = usb_core.crossbar.get_port(identifier) 26 | self.comb += [ 27 | sender.source.connect(usb_port.sink), 28 | ] 29 | 30 | 31 | class WrapSender(Module): 32 | def __init__(self, identifier, depth=128): 33 | self.submodules.buf = buf = stream.SyncFIFO(wrap_description(32), depth+1) 34 | self.sink = sink = buf.sink 35 | self.source = source = stream.Endpoint(usb_description(32)) 36 | 37 | # # # 38 | 39 | count = Signal(32) 40 | 41 | self.submodules.timer = WaitTimer(int(1e6)) 42 | 43 | self.comb += [ 44 | source.dst.eq(identifier), 45 | ] 46 | 47 | fsm = FSM() 48 | self.submodules.fsm = fsm 49 | 50 | fsm.act("BUFFER", 51 | 52 | If(buf.level > 0, 53 | self.timer.wait.eq(1), 54 | ), 55 | 56 | # if buffer full or timeout elapsed 57 | If((buf.level >= depth) | self.timer.done, 58 | NextValue(count, buf.level), 59 | NextState("TRANSFER"), 60 | ) 61 | ) 62 | 63 | fsm.act("TRANSFER", 64 | source.length.eq(Cat(C(0, 2), count)), 65 | 66 | source.valid.eq(buf.source.valid), 67 | source.last.eq(count == 1), 68 | source.data.eq(buf.source.data), 69 | buf.source.ready.eq(source.ready), 70 | 71 | If(source.valid & source.ready, 72 | 73 | If(source.last, 74 | # if enough data stay in transfer state 75 | If(buf.level-1 >= depth, 76 | NextValue(count, buf.level-1), 77 | ).Else( 78 | NextState("BUFFER"), 79 | ), 80 | ).Else( 81 | NextValue(count, count - 1), 82 | ) 83 | ), 84 | ) 85 | 86 | 87 | def tb_wrap(dut): 88 | yield dut.source.ready.eq(1) 89 | 90 | val = 0xabcdef01 91 | it = iter([val + i for i in range(18)]) 92 | data = next(it) 93 | 94 | while True: 95 | yield dut.sink.data.eq(data) 96 | yield dut.sink.valid.eq(1) 97 | yield 98 | if (yield dut.sink.ready): 99 | try: 100 | data = next(it) 101 | # simulate large time increment 102 | yield dut.sink.valid.eq(0) 103 | for i in range(10): 104 | yield 105 | except StopIteration: 106 | break 107 | 108 | yield dut.sink.valid.eq(0) 109 | for i in range(200): 110 | yield 111 | 112 | 113 | if __name__ == "__main__": 114 | dut = WrapSender(0) 115 | run_simulation(dut, tb_wrap(dut), vcd_name="test/wrapper.vcd") 116 | -------------------------------------------------------------------------------- /test/test_sdram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import time 5 | 6 | from litex import RemoteClient 7 | 8 | # DDR3 init and test for a7ddrphy design 9 | # use arty_ddr3 design with this script to 10 | # find working bitslip/delay configuration 11 | 12 | dfii_control_sel = 0x01 13 | dfii_control_cke = 0x02 14 | dfii_control_odt = 0x04 15 | dfii_control_reset_n = 0x08 16 | 17 | dfii_command_cs = 0x01 18 | dfii_command_we = 0x02 19 | dfii_command_cas = 0x04 20 | dfii_command_ras = 0x08 21 | dfii_command_wrdata = 0x10 22 | dfii_command_rddata = 0x20 23 | 24 | wb = RemoteClient(debug=False) 25 | wb.open() 26 | 27 | # # # 28 | 29 | wb.regs.sdram_dfii_control.write(0) 30 | 31 | # release reset 32 | wb.regs.sdram_dfii_pi0_address.write(0x0) 33 | wb.regs.sdram_dfii_pi0_baddress.write(0) 34 | wb.regs.sdram_dfii_control.write(dfii_control_odt|dfii_control_reset_n) 35 | time.sleep(0.1) 36 | 37 | # bring cke high 38 | wb.regs.sdram_dfii_pi0_address.write(0x0) 39 | wb.regs.sdram_dfii_pi0_baddress.write(0) 40 | wb.regs.sdram_dfii_control.write(dfii_control_cke|dfii_control_odt|dfii_control_reset_n) 41 | time.sleep(0.1) 42 | 43 | # load mode register 2 44 | wb.regs.sdram_dfii_pi0_address.write(0x408) 45 | wb.regs.sdram_dfii_pi0_baddress.write(2) 46 | wb.regs.sdram_dfii_pi0_command.write(dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs) 47 | wb.regs.sdram_dfii_pi0_command_issue.write(1) 48 | 49 | # load mode register 3 50 | wb.regs.sdram_dfii_pi0_address.write(0x0) 51 | wb.regs.sdram_dfii_pi0_baddress.write(3) 52 | wb.regs.sdram_dfii_pi0_command.write(dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs) 53 | wb.regs.sdram_dfii_pi0_command_issue.write(1) 54 | 55 | # load mode register 1 56 | wb.regs.sdram_dfii_pi0_address.write(0x6); 57 | wb.regs.sdram_dfii_pi0_baddress.write(1); 58 | wb.regs.sdram_dfii_pi0_command.write(dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs) 59 | wb.regs.sdram_dfii_pi0_command_issue.write(1) 60 | 61 | # load mode register 0, cl=7, bl=8 62 | wb.regs.sdram_dfii_pi0_address.write(0x930); 63 | wb.regs.sdram_dfii_pi0_baddress.write(0); 64 | wb.regs.sdram_dfii_pi0_command.write(dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs) 65 | wb.regs.sdram_dfii_pi0_command_issue.write(1) 66 | time.sleep(0.1) 67 | 68 | # zq calibration 69 | wb.regs.sdram_dfii_pi0_address.write(0x400); 70 | wb.regs.sdram_dfii_pi0_baddress.write(0); 71 | wb.regs.sdram_dfii_pi0_command.write(dfii_command_we|dfii_command_cs) 72 | wb.regs.sdram_dfii_pi0_command_issue.write(1) 73 | time.sleep(0.1) 74 | 75 | # hardware control 76 | wb.regs.sdram_dfii_control.write(dfii_control_sel) 77 | 78 | def seed_to_data(seed, random=True): 79 | if random: 80 | return (1664525*seed + 1013904223) & 0xffffffff 81 | else: 82 | return seed 83 | 84 | def write_pattern(length): 85 | for i in range(length): 86 | wb.write(wb.mems.main_ram.base + 4*i, seed_to_data(i)) 87 | 88 | def check_pattern(length, debug=False): 89 | errors = 0 90 | for i in range(length): 91 | error = 0 92 | if wb.read(wb.mems.main_ram.base + 4*i) != seed_to_data(i): 93 | error = 1 94 | if debug: 95 | print("{}: 0x{:08x}, 0x{:08x} KO".format(i, wb.read(wb.mems.main_ram.base + 4*i), seed_to_data(i))) 96 | else: 97 | if debug: 98 | print("{}: 0x{:08x}, 0x{:08x} OK".format(i, wb.read(wb.mems.main_ram.base + 4*i), seed_to_data(i))) 99 | errors += error 100 | return errors 101 | 102 | # find working bitslips and delays 103 | nbitslips = 4 104 | ndelays = 32 105 | nmodules = 2 106 | nwords = 16 107 | 108 | for bitslip in range(nbitslips): 109 | print("bitslip {:d}: |".format(bitslip), end="") 110 | for delay in range(ndelays): 111 | for module in range(nmodules): 112 | wb.regs.ddrphy_dly_sel.write(1< 2 | #include 3 | #include 4 | #include 5 | 6 | struct eb_header_s { 7 | uint16_t magic; /* Magic 0x4E6F*/ 8 | uint8_t pf:1; /* Probe-Flag */ 9 | uint8_t pr:1; /* Probe-Response */ 10 | uint8_t nr:1; /* No-Read */ 11 | uint8_t nc1:1; 12 | uint8_t version:4; /* Version 1 */ 13 | uint8_t port_size:4; /* 8 x val */ 14 | uint8_t addr_size:4; /* 8 x Val */ 15 | uint32_t pad1; 16 | 17 | uint8_t nc2:1; 18 | uint8_t wff:1; /* If should be written to a FIFO register, the WF flag is set */ 19 | uint8_t wca:1; /* If config address space, the WCA flag is set */ 20 | uint8_t cyc:1; /* If last of a cycle, the CYC flag is set */ 21 | uint8_t nc3:1; 22 | uint8_t rff:1; /* Read Fifo */ 23 | uint8_t rca:1; /* Read Config Addr */ 24 | uint8_t bca:1; /* BaseRetAddr */ 25 | uint8_t byte_enable; /* Byte enable 8/16/32 */ 26 | uint8_t w_count; /* write count */ 27 | uint8_t r_count; /* read count */ 28 | }__attribute__((packed)); 29 | 30 | uint32_t wswap(uint32_t const val) 31 | { 32 | uint8_t data[4] = {}; 33 | memcpy(&data, &val, sizeof(data)); 34 | 35 | return ((uint32_t) data[3] << 0) 36 | | ((uint32_t) data[2] << 8) 37 | | ((uint32_t) data[1] << 16) 38 | | ((uint32_t) data[0] << 24); 39 | } 40 | 41 | 42 | void print_header(struct eb_header_s *h) 43 | { 44 | printf("Magic: %04x\n", h->magic); 45 | printf("Version: %d\n", h->version); 46 | printf("NR: %d\n", h->nr); 47 | printf("PR: %d\n", h->pr); 48 | printf("PF: %d\n", h->pf); 49 | printf("Port Size: %d\n", h->port_size); 50 | printf("Adr Size: %d\n", h->addr_size); 51 | printf("CYC: %d\n", h->cyc); 52 | printf("BCA: %d\n", h->bca); 53 | printf("RCA: %d\n", h->rca); 54 | printf("Byte Enable: %01x\n", h->byte_enable); 55 | printf("Read cnt: %d\n", h->r_count); 56 | printf("Write cnt: %d\n", h->w_count); 57 | } 58 | 59 | 60 | void print_eb_packet(char *buf, int len) 61 | { 62 | struct eb_header_s *h; 63 | uint32_t *val; 64 | int i; 65 | 66 | if(len < 12) 67 | return; 68 | h = (struct eb_header_s*)buf; 69 | //print_header(h); 70 | val = (uint32_t*)(buf +sizeof(struct eb_header_s)); 71 | if(h->r_count){ 72 | printf("Base addr: %08x\n", wswap( *(val++))); 73 | for(i = 0; ir_count;i++){ 74 | printf("Addr: %08x\n", wswap(*(val++))); 75 | } 76 | } 77 | if(h->w_count){ 78 | printf("Base addr: %08x\n", wswap(*(val++))); 79 | for(i = 0; iw_count;i++){ 80 | printf("Value: %08x\n", wswap(*(val++))); 81 | } 82 | } 83 | } 84 | 85 | int eb_make_read_pkt( uint32_t addr, uint32_t r_count, char **buf, size_t *len) 86 | { 87 | struct eb_header_s *ebh; 88 | char *tosend; 89 | int *val; 90 | int i; 91 | tosend = malloc(sizeof(struct eb_header_s) + 4 * (r_count + 1)); 92 | memset(tosend, 0, sizeof(struct eb_header_s) + 4 * (r_count + 1)); 93 | ebh=(struct eb_header_s*)tosend; 94 | ebh->magic = 0x6f4e; 95 | ebh->version = 1; 96 | ebh->port_size = 4; 97 | ebh->addr_size = 4; 98 | ebh->r_count = r_count; 99 | ebh->byte_enable = 0xf; 100 | val = (uint32_t*)(tosend +sizeof(struct eb_header_s)+4); 101 | for(i=0; i < r_count; i++) 102 | *(val++) = wswap(addr + (i*4)); 103 | *buf = tosend; 104 | *len = sizeof(struct eb_header_s) + 4 * (r_count + 1); 105 | return 0; 106 | } 107 | 108 | int eb_make_write_pkt( uint32_t addr, uint32_t *data, uint32_t w_count, char **buf, size_t *len) 109 | { 110 | struct eb_header_s *ebh; 111 | char *tosend; 112 | uint32_t *val; 113 | int i; 114 | tosend = malloc(sizeof(struct eb_header_s) + 4 * (w_count + 1)); 115 | memset(tosend, 0, sizeof(struct eb_header_s) + 4 * (w_count + 1)); 116 | ebh=(struct eb_header_s*)tosend; 117 | ebh->magic = 0x6f4e; 118 | ebh->version = 1; 119 | ebh->port_size = 4; 120 | ebh->addr_size = 4; 121 | ebh->w_count = w_count; 122 | ebh->byte_enable = 0xf; 123 | val = (uint32_t*)(tosend +sizeof(struct eb_header_s)); 124 | *(val++) = wswap(addr); 125 | for(i=0; i < w_count; i++) 126 | val[i] = wswap(data[i]); 127 | *buf = tosend; 128 | *len = sizeof(struct eb_header_s) + 4 * (w_count + 1); 129 | return 0; 130 | } 131 | 132 | int eb_decode_rcv_pkt(char *buf, int blen, uint32_t **data, size_t *len) 133 | { 134 | 135 | struct eb_header_s *h; 136 | uint32_t *val; 137 | int i; 138 | uint32_t *tmp; 139 | 140 | if(blen < 12) 141 | return -1; 142 | h = (struct eb_header_s*)buf; 143 | //print_header(h); 144 | val = (uint32_t*)(buf +sizeof(struct eb_header_s) + 4); 145 | tmp = malloc(h->w_count * sizeof(uint32_t)); 146 | for(i=0; i < h->w_count; i++) { 147 | tmp[i] = wswap(val[i]); 148 | } 149 | *data = tmp; 150 | *len = h->w_count; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /software/sniff.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | from sdram_init import * 5 | 6 | from etherbone import Etherbone, USBMux 7 | from gateware.ulpi import ULPIFilter 8 | 9 | def sdram_configure(wb): 10 | # software control 11 | wb.regs.sdram_dfii_control.write(0) 12 | 13 | # sdram initialization 14 | for i, (comment, a, ba, cmd, delay) in enumerate(init_sequence): 15 | print(comment) 16 | wb.regs.sdram_dfii_pi0_address.write(a) 17 | wb.regs.sdram_dfii_pi0_baddress.write(ba) 18 | if i < 2: 19 | wb.regs.sdram_dfii_control.write(cmd) 20 | else: 21 | wb.regs.sdram_dfii_pi0_command.write(cmd) 22 | wb.regs.sdram_dfii_pi0_command_issue.write(1) 23 | 24 | # hardware control 25 | wb.regs.sdram_dfii_control.write(dfii_control_sel) 26 | 27 | # configure bitslip and delay 28 | bitslip = 1 29 | delay = 18 30 | for module in range(2): 31 | wb.regs.ddrphy_dly_sel.write(1< timeout, 170 | oe_n.eq(0), 171 | NextState("RDWAIT") 172 | ).Elif(write_fifo.source.valid, 173 | oe_n.eq(1), 174 | data_w.eq(write_fifo.source.data), 175 | write_fifo.source.ready.eq(1), 176 | NextValue(tempsendval, write_fifo.source.data), 177 | NextValue(temptosend, 0), 178 | wr_n.eq(0), 179 | ).Else( 180 | oe_n.eq(1), 181 | wr_n.eq(1), 182 | NextValue(temptosend, 0), 183 | NextState("IDLE") 184 | ) 185 | ) 186 | 187 | fsm.act("RDWAIT", 188 | rd_n.eq(0), 189 | oe_n.eq(0), 190 | wr_n.eq(1), 191 | NextValue(cnt_read, 0), 192 | NextState("READ") 193 | ) 194 | 195 | fsm.act("READ", 196 | If(wants_write, 197 | NextValue(cnt_read, cnt_read + 1), 198 | ), 199 | 200 | wr_n.eq(1), 201 | If(pads.rxf_n, 202 | oe_n.eq(0), 203 | rd_n.eq(1), 204 | NextState("IDLE"), 205 | ).Elif(cnt_read > timeout, 206 | NextValue(cnt_write, 0), 207 | NextValue(first_write, 1), 208 | NextState("WRITE"), 209 | oe_n.eq(1), 210 | ).Else( 211 | oe_n.eq(0), 212 | read_buffer.sink.valid.eq(1), 213 | read_buffer.sink.data.eq(data_r), 214 | NextValue(tempreadval, data_r), 215 | If(read_buffer.sink.ready, 216 | rd_n.eq(0) 217 | ).Else( 218 | NextValue(temptoread, 1), 219 | NextState("IDLE"), 220 | rd_n.eq(1) 221 | ) 222 | ) 223 | ) 224 | -------------------------------------------------------------------------------- /gateware/usb.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 / EnjoyDigital / florent@enjoy-digital.fr 2 | from collections import OrderedDict 3 | 4 | from migen.genlib.misc import WaitTimer 5 | 6 | from litex.soc.interconnect import stream 7 | from litex.soc.interconnect.stream import EndpointDescription 8 | from litex.soc.interconnect.stream_packet import * 9 | from litex.soc.interconnect.wishbonebridge import WishboneStreamingBridge 10 | 11 | 12 | packet_header_length = 12 13 | packet_header_fields = { 14 | "preamble": HeaderField(0, 0, 32), 15 | "dst": HeaderField(4, 0, 32), 16 | "length": HeaderField(8, 0, 32) 17 | } 18 | packet_header = Header(packet_header_fields, 19 | packet_header_length, 20 | swap_field_bytes=True) 21 | 22 | 23 | def phy_description(dw): 24 | payload_layout = [("data", dw)] 25 | return EndpointDescription(payload_layout) 26 | 27 | 28 | def packet_description(dw): 29 | param_layout = packet_header.get_layout() 30 | payload_layout = [ 31 | ("data", dw), 32 | ("error", dw//8) 33 | ] 34 | return EndpointDescription(payload_layout, param_layout) 35 | 36 | 37 | def user_description(dw): 38 | param_layout = [ 39 | ("dst", 8), 40 | ("length", 32) 41 | ] 42 | payload_layout = [ 43 | ("data", dw), 44 | ("error", dw//8) 45 | ] 46 | return EndpointDescription(payload_layout, param_layout) 47 | 48 | 49 | class USBMasterPort: 50 | def __init__(self, dw): 51 | self.source = stream.Endpoint(user_description(dw)) 52 | self.sink = stream.Endpoint(user_description(dw)) 53 | 54 | 55 | class USBSlavePort: 56 | def __init__(self, dw, tag): 57 | self.sink = stream.Endpoint(user_description(dw)) 58 | self.source = stream.Endpoint(user_description(dw)) 59 | self.tag = tag 60 | 61 | 62 | class USBUserPort(USBSlavePort): 63 | def __init__(self, dw, tag): 64 | USBSlavePort.__init__(self, dw, tag) 65 | 66 | 67 | class USBPacketizer(Module): 68 | def __init__(self): 69 | self.sink = sink = stream.Endpoint(user_description(32)) 70 | self.source = source = stream.Endpoint(phy_description(32)) 71 | 72 | # # # 73 | 74 | # Packet description 75 | # - preamble : 4 bytes 76 | # - unused : 3 bytes 77 | # - dst : 1 byte 78 | # - length : 4 bytes 79 | # - payload 80 | header = [ 81 | # preamble 82 | 0x5aa55aa5, 83 | # dst 84 | sink.dst, 85 | # length 86 | sink.length 87 | ] 88 | 89 | header_unpack = stream.Unpack(len(header), phy_description(32)) 90 | self.submodules += header_unpack 91 | 92 | for i, byte in enumerate(header): 93 | chunk = getattr(header_unpack.sink.payload, "chunk" + str(i)) 94 | self.comb += chunk.data.eq(byte) 95 | 96 | fsm = FSM(reset_state="IDLE") 97 | self.submodules += fsm 98 | 99 | fsm.act("IDLE", 100 | If(sink.valid, 101 | NextState("INSERT_HEADER") 102 | ) 103 | ) 104 | 105 | fsm.act("INSERT_HEADER", 106 | header_unpack.sink.valid.eq(1), 107 | source.valid.eq(1), 108 | source.data.eq(header_unpack.source.data), 109 | header_unpack.source.ready.eq(source.ready), 110 | If(header_unpack.sink.ready, 111 | NextState("COPY") 112 | ) 113 | ) 114 | 115 | fsm.act("COPY", 116 | source.valid.eq(sink.valid), 117 | source.data.eq(sink.data), 118 | sink.ready.eq(source.ready), 119 | If(source.ready & sink.valid & sink.last, 120 | NextState("IDLE") 121 | ) 122 | ) 123 | 124 | 125 | class USBDepacketizer(Module): 126 | def __init__(self, clk_freq, timeout=10): 127 | self.sink = sink = stream.Endpoint(phy_description(32)) 128 | self.source = source = stream.Endpoint(user_description(32)) 129 | 130 | # # # 131 | 132 | # Packet description 133 | # - preamble : 4 bytes 134 | # - unused : 3 bytes 135 | # - dst : 1 byte 136 | # - length : 4 bytes 137 | # - payload 138 | preamble = Signal(32) 139 | 140 | header = [ 141 | # dst 142 | source.dst, 143 | # length 144 | source.length 145 | ] 146 | 147 | header_pack = ResetInserter()(stream.Pack(phy_description(32), len(header))) 148 | self.submodules += header_pack 149 | 150 | for i, byte in enumerate(header): 151 | chunk = getattr(header_pack.source.payload, "chunk" + str(i)) 152 | self.comb += byte.eq(chunk.data) 153 | 154 | fsm = FSM(reset_state="IDLE") 155 | self.submodules += fsm 156 | 157 | self.comb += preamble.eq(sink.data) 158 | fsm.act("IDLE", 159 | sink.ready.eq(1), 160 | If((sink.data == 0x5aa55aa5) & sink.valid, 161 | NextState("RECEIVE_HEADER") 162 | ), 163 | header_pack.source.ready.eq(1) 164 | ) 165 | 166 | self.submodules.timer = WaitTimer(clk_freq*timeout) 167 | self.comb += self.timer.wait.eq(~fsm.ongoing("IDLE")) 168 | 169 | fsm.act("RECEIVE_HEADER", 170 | header_pack.sink.valid.eq(sink.valid), 171 | header_pack.sink.payload.eq(sink.payload), 172 | If(self.timer.done, 173 | NextState("IDLE") 174 | ).Elif(header_pack.source.valid, 175 | NextState("COPY") 176 | ).Else( 177 | sink.ready.eq(1) 178 | ) 179 | ) 180 | 181 | self.comb += header_pack.reset.eq(self.timer.done) 182 | 183 | last = Signal() 184 | cnt = Signal(32) 185 | 186 | fsm.act("COPY", 187 | source.valid.eq(sink.valid), 188 | source.last.eq(last), 189 | source.data.eq(sink.data), 190 | sink.ready.eq(source.ready), 191 | If((source.valid & source.ready & last) | self.timer.done, 192 | NextState("IDLE") 193 | ) 194 | ) 195 | 196 | self.sync += \ 197 | If(fsm.ongoing("IDLE"), 198 | cnt.eq(0) 199 | ).Elif(source.valid & source.ready, 200 | cnt.eq(cnt + 1) 201 | ) 202 | self.comb += last.eq(cnt == source.length[2:] - 1) 203 | 204 | 205 | class USBCrossbar(Module): 206 | def __init__(self): 207 | self.users = OrderedDict() 208 | self.master = USBMasterPort(32) 209 | self.dispatch_param = "dst" 210 | 211 | def get_port(self, dst): 212 | port = USBUserPort(32, dst) 213 | if dst in self.users.keys(): 214 | raise ValueError("Destination {0:#x} already assigned".format(dst)) 215 | self.users[dst] = port 216 | return port 217 | 218 | def do_finalize(self): 219 | # TX arbitrate 220 | sinks = [port.sink for port in self.users.values()] 221 | self.submodules.arbiter = Arbiter(sinks, self.master.source) 222 | 223 | # RX dispatch 224 | sources = [port.source for port in self.users.values()] 225 | self.submodules.dispatcher = Dispatcher(self.master.sink, 226 | sources, 227 | one_hot=True) 228 | cases = {} 229 | cases["default"] = self.dispatcher.sel.eq(0) 230 | for i, (k, v) in enumerate(self.users.items()): 231 | cases[k] = self.dispatcher.sel.eq(2**i) 232 | self.comb += \ 233 | Case(getattr(self.master.sink, self.dispatch_param), cases) 234 | 235 | 236 | class USBCore(Module): 237 | def __init__(self, phy, clk_freq): 238 | rx_pipeline = [phy] 239 | tx_pipeline = [phy] 240 | 241 | # depacketizer / packetizer 242 | self.submodules.depacketizer = USBDepacketizer(clk_freq) 243 | self.submodules.packetizer = USBPacketizer() 244 | rx_pipeline += [self.depacketizer] 245 | tx_pipeline += [self.packetizer] 246 | 247 | # crossbar 248 | self.submodules.crossbar = USBCrossbar() 249 | rx_pipeline += [self.crossbar.master] 250 | tx_pipeline += [self.crossbar.master] 251 | 252 | # graph 253 | self.submodules.rx_pipeline = stream.Pipeline(*rx_pipeline) 254 | self.submodules.tx_pipeline = stream.Pipeline(*reversed(tx_pipeline)) 255 | -------------------------------------------------------------------------------- /usbblink.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from migen import * 4 | from migen.genlib.resetsync import AsyncResetSynchronizer 5 | 6 | from litex.build.generic_platform import * 7 | from litex.build.xilinx import XilinxPlatform 8 | 9 | from litex.soc.interconnect.csr import * 10 | from litex.soc.integration.soc_core import * 11 | from litex.soc.integration.soc_sdram import * 12 | from litex.soc.integration.builder import * 13 | from litex.soc.integration.cpu_interface import get_csr_header 14 | from litex.soc.interconnect import stream 15 | from litex.soc.cores.uart import UARTWishboneBridge, RS232PHY 16 | from litex.soc.cores.gpio import GPIOOut 17 | 18 | from litedram import sdram_init 19 | from litedram.modules import MT41K256M16 20 | from litedram.phy import a7ddrphy 21 | 22 | from gateware.usb import USBCore 23 | from gateware.etherbone import Etherbone 24 | from gateware.ft601 import FT601Sync, phy_description 25 | from gateware.ulpi import ULPIPHY, ULPICore, ULPIFilter 26 | from gateware.packer import LTCore, LTPacker 27 | from gateware.dramfifo import LiteDRAMFIFO 28 | 29 | from litescope import LiteScopeAnalyzer 30 | 31 | 32 | _io = [ 33 | ("clk100", 0, Pins("J19"), IOStandard("LVCMOS33")), 34 | 35 | ("rgb_led", 0, 36 | Subsignal("r", Pins("W2")), 37 | Subsignal("g", Pins("Y1")), 38 | Subsignal("b", Pins("W1")), 39 | IOStandard("LVCMOS33"), 40 | ), 41 | 42 | ("rgb_led", 1, 43 | Subsignal("r", Pins("AA1")), 44 | Subsignal("g", Pins("AB1")), 45 | Subsignal("b", Pins("Y2")), 46 | IOStandard("LVCMOS33"), 47 | ), 48 | 49 | ("serial", 0, 50 | Subsignal("tx", Pins("U21")), # FPGA_GPIO0 51 | Subsignal("rx", Pins("T21")), # FPGA_GPIO1 52 | IOStandard("LVCMOS33"), 53 | ), 54 | 55 | ("ddram", 0, 56 | Subsignal("a", Pins( 57 | "M2 M5 M3 M1 L6 P1 N3 N2", 58 | "M6 R1 L5 N5 N4 P2 P6"), 59 | IOStandard("SSTL15")), 60 | Subsignal("ba", Pins("L3 K6 L4"), IOStandard("SSTL15")), 61 | Subsignal("ras_n", Pins("J4"), IOStandard("SSTL15")), 62 | Subsignal("cas_n", Pins("K3"), IOStandard("SSTL15")), 63 | Subsignal("we_n", Pins("L1"), IOStandard("SSTL15")), 64 | Subsignal("dm", Pins("G3 F1"), IOStandard("SSTL15")), 65 | Subsignal("dq", Pins( 66 | "G2 H4 H5 J1 K1 H3 H2 J5", 67 | "E3 B2 F3 D2 C2 A1 E2 B1"), 68 | IOStandard("SSTL15"), 69 | Misc("IN_TERM=UNTUNED_SPLIT_50")), 70 | Subsignal("dqs_p", Pins("K2 E1"), IOStandard("DIFF_SSTL15")), 71 | Subsignal("dqs_n", Pins("J2 D1"), IOStandard("DIFF_SSTL15")), 72 | Subsignal("clk_p", Pins("P5"), IOStandard("DIFF_SSTL15")), 73 | Subsignal("clk_n", Pins("P4"), IOStandard("DIFF_SSTL15")), 74 | Subsignal("cke", Pins("J6"), IOStandard("SSTL15")), 75 | Subsignal("odt", Pins("K4"), IOStandard("SSTL15")), 76 | Subsignal("reset_n", Pins("G1"), IOStandard("SSTL15")), 77 | Misc("SLEW=FAST"), 78 | ), 79 | 80 | ("usb_fifo_clock", 0, Pins("D17"), IOStandard("LVCMOS33")), 81 | ("usb_fifo", 0, 82 | Subsignal("rst", Pins("K22")), 83 | Subsignal("data", Pins("A16 F14 A15 F13 A14 E14 A13 E13 B13 C15 C13 C14 B16 E17 B15 F16", 84 | "A20 E18 B20 F18 D19 D21 E19 E21 A21 B21 A19 A18 F20 F19 B18 B17")), 85 | Subsignal("be", Pins("K16 L16 G20 H20")), 86 | Subsignal("rxf_n", Pins("M13")), 87 | Subsignal("txe_n", Pins("L13")), 88 | Subsignal("rd_n", Pins("K19")), 89 | Subsignal("wr_n", Pins("M15")), 90 | Subsignal("oe_n", Pins("L21")), 91 | Subsignal("siwua", Pins("M16")), 92 | IOStandard("LVCMOS33"), Misc("SLEW=FAST") 93 | ), 94 | 95 | ("ulpi_sw", 0, 96 | Subsignal("s", Pins("Y8")), 97 | Subsignal("oe_n", Pins("Y9")), 98 | IOStandard("LVCMOS33"), 99 | ), 100 | 101 | ("ulpi_clock", 0, Pins("W19"), IOStandard("LVCMOS33")), 102 | ("ulpi", 0, 103 | Subsignal("data", Pins("AB18 AA18 AA19 AB20 AA20 AB21 AA21 AB22")), 104 | Subsignal("dir", Pins("W21")), 105 | Subsignal("stp", Pins("Y22")), 106 | Subsignal("nxt", Pins("W22")), 107 | Subsignal("rst", Pins("V20")), 108 | IOStandard("LVCMOS33"), Misc("SLEW=FAST") 109 | ), 110 | 111 | ("ulpi_clock", 1, Pins("V4"), IOStandard("LVCMOS33")), 112 | ("ulpi", 1, 113 | Subsignal("data", Pins("AB2 AA3 AB3 Y4 AA4 AB5 AA5 AB6")), 114 | Subsignal("dir", Pins("AB7")), 115 | Subsignal("stp", Pins("AA6")), 116 | Subsignal("nxt", Pins("AB8")), 117 | Subsignal("rst", Pins("AA8")), 118 | IOStandard("LVCMOS33"), Misc("SLEW=FAST") 119 | ), 120 | ] 121 | 122 | 123 | class Platform(XilinxPlatform): 124 | default_clk_name = "clk100" 125 | default_clk_period = 10.0 126 | 127 | def __init__(self, toolchain="vivado", programmer="vivado"): 128 | XilinxPlatform.__init__(self, "xc7a35t-fgg484-1", _io, 129 | toolchain=toolchain) 130 | self.toolchain.bitstream_commands = \ 131 | ["set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]", 132 | "set_property BITSTREAM.CONFIG.CONFIGRATE 40 [current_design]"] 133 | self.toolchain.additional_commands = \ 134 | ["write_cfgmem -force -format bin -interface spix4 -size 16 " 135 | "-loadbit \"up 0x0 {build_name}.bit\" -file {build_name}.bin"] 136 | self.programmer = programmer 137 | self.add_platform_command("set_property INTERNAL_VREF 0.750 [get_iobanks 35]") 138 | 139 | 140 | class _CRG(Module, AutoCSR): 141 | def __init__(self, platform): 142 | self.clock_domains.cd_sys = ClockDomain("sys") 143 | self.clock_domains.cd_ulpi0 = ClockDomain("ulpi0") 144 | self.clock_domains.cd_ulpi1 = ClockDomain("ulpi1") 145 | self.clock_domains.cd_sys4x = ClockDomain(reset_less=True) 146 | self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True) 147 | self.clock_domains.cd_clk200 = ClockDomain() 148 | 149 | self.clock_domains.cd_usb = ClockDomain() 150 | 151 | # usb clock domain (100MHz from usb) 152 | self.comb += self.cd_usb.clk.eq(platform.request("usb_fifo_clock")) 153 | self.comb += self.cd_usb.rst.eq(self.cd_sys.rst) 154 | 155 | # ulpi0 clock domain (60MHz from ulpi0) 156 | self.comb += self.cd_ulpi0.clk.eq(platform.request("ulpi_clock", 0)) 157 | 158 | # ulpi1 clock domain (60MHz from ulpi1) 159 | self.comb += self.cd_ulpi1.clk.eq(platform.request("ulpi_clock", 1)) 160 | 161 | clk100 = platform.request("clk100") 162 | 163 | # sys & ddr clock domains 164 | pll_locked = Signal() 165 | pll_fb = Signal() 166 | pll_sys = Signal() 167 | pll_sys4x = Signal() 168 | pll_sys4x_dqs = Signal() 169 | pll_clk200 = Signal() 170 | self.specials += [ 171 | Instance("PLLE2_BASE", 172 | p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, 173 | 174 | # VCO @ 1600 MHz 175 | p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=10.0, 176 | p_CLKFBOUT_MULT=16, p_DIVCLK_DIVIDE=1, 177 | i_CLKIN1=clk100, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, 178 | 179 | # 100 MHz 180 | p_CLKOUT0_DIVIDE=16, p_CLKOUT0_PHASE=0.0, 181 | o_CLKOUT0=pll_sys, 182 | 183 | # 400 MHz 184 | p_CLKOUT1_DIVIDE=4, p_CLKOUT1_PHASE=0.0, 185 | o_CLKOUT1=pll_sys4x, 186 | 187 | # 400 MHz dqs 188 | p_CLKOUT2_DIVIDE=4, p_CLKOUT2_PHASE=90.0, 189 | o_CLKOUT2=pll_sys4x_dqs, 190 | 191 | # 200 MHz 192 | p_CLKOUT3_DIVIDE=8, p_CLKOUT3_PHASE=0.0, 193 | o_CLKOUT3=pll_clk200, 194 | ), 195 | Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), 196 | Instance("BUFG", i_I=pll_sys4x, o_O=self.cd_sys4x.clk), 197 | Instance("BUFG", i_I=pll_sys4x_dqs, o_O=self.cd_sys4x_dqs.clk), 198 | Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk), 199 | AsyncResetSynchronizer(self.cd_sys, ~pll_locked), 200 | AsyncResetSynchronizer(self.cd_clk200, ~pll_locked) 201 | ] 202 | 203 | reset_counter = Signal(4, reset=15) 204 | ic_reset = Signal(reset=1) 205 | self.sync.clk200 += \ 206 | If(reset_counter != 0, 207 | reset_counter.eq(reset_counter - 1) 208 | ).Else( 209 | ic_reset.eq(0) 210 | ) 211 | self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) 212 | 213 | 214 | class BlinkerRGB(Module): 215 | def __init__(self, r, g, b, divbits=26): 216 | counter = Signal(divbits + 2) 217 | self.comb += [ 218 | r.eq(counter[divbits-3]), 219 | g.eq(counter[divbits-2]), 220 | b.eq(counter[divbits-1]), 221 | ] 222 | self.sync += counter.eq(counter + 1) 223 | 224 | 225 | class USBSnifferSoC(SoCCore): 226 | csr_peripherals = [ 227 | ] 228 | csr_map_update(SoCCore.csr_map, csr_peripherals) 229 | 230 | def __init__(self, platform): 231 | clk_freq = int(100e6) 232 | SoCCore.__init__(self, platform, clk_freq, 233 | cpu_type=None, 234 | integrated_rom_size=0, 235 | integrated_sram_size=0x8000, 236 | with_uart=False, 237 | ident="USB2Sniffer Blinker", 238 | with_timer=False 239 | ) 240 | self.submodules.crg = _CRG(platform) 241 | 242 | # usb phy 243 | usb_pads = platform.request("usb_fifo") 244 | self.submodules.usb_phy = FT601Sync(usb_pads, dw=32, timeout=1024) 245 | 246 | # loopback 247 | self.submodules.usb_loopback_fifo = stream.SyncFIFO(phy_description(32), 2048) 248 | self.comb += [ 249 | self.usb_phy.source.connect(self.usb_loopback_fifo.sink), 250 | self.usb_loopback_fifo.source.connect(self.usb_phy.sink) 251 | ] 252 | 253 | # leds 254 | led0 = platform.request("rgb_led", 0) 255 | self.submodules.blinker0 = BlinkerRGB(led0.r, led0.g, led0.b) 256 | 257 | led1 = platform.request("rgb_led", 1) 258 | self.submodules.blinker1 = BlinkerRGB(led1.r, led1.g, led1.b) 259 | 260 | # timing constraints 261 | self.crg.cd_sys.clk.attr.add("keep") 262 | self.crg.cd_usb.clk.attr.add("keep") 263 | self.platform.add_period_constraint(self.crg.cd_sys.clk, 10.0) 264 | self.platform.add_period_constraint(self.crg.cd_usb.clk, 10.0) 265 | 266 | 267 | def main(): 268 | platform = Platform() 269 | soc = USBSnifferSoC(platform) 270 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 271 | vns = builder.build() 272 | soc.do_exit(vns) 273 | 274 | 275 | if __name__ == "__main__": 276 | main() 277 | -------------------------------------------------------------------------------- /software/windows/ft601.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "FTD3XX.h" 7 | 8 | #ifdef __linux__ 9 | #include 10 | #define my_sleep(x) sleep(x/1000) 11 | #else 12 | #define my_sleep(x) Sleep(x) 13 | #endif 14 | 15 | int fake_printf( const char * format, ... ) 16 | { 17 | return 0; 18 | } 19 | 20 | // #define DEBUG 21 | 22 | #ifdef DEBUG 23 | #define my_printf printf 24 | #else 25 | #define my_printf fake_printf 26 | #endif 27 | 28 | #define FALSE 0 29 | #define TRUE 1 30 | 31 | static FT_60XCONFIGURATION oSaveConfigurationData = { 0 }; 32 | 33 | int show_config(FT_60XCONFIGURATION *a_pConfigurationData, int a_bRead); 34 | 35 | int save_config() 36 | { 37 | FT_STATUS ftStatus = FT_OK; 38 | FT_HANDLE ftHandle; 39 | 40 | ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, &ftHandle); 41 | if (FT_FAILED(ftStatus)) 42 | { 43 | return FALSE; 44 | } 45 | 46 | ftStatus = FT_GetChipConfiguration(ftHandle, &oSaveConfigurationData); 47 | if (FT_FAILED(ftStatus)) 48 | { 49 | FT_Close(ftHandle); 50 | return FALSE; 51 | } 52 | 53 | FT_Close(ftHandle); 54 | ftHandle = NULL; 55 | 56 | return TRUE; 57 | } 58 | 59 | int revert_config() 60 | { 61 | FT_STATUS ftStatus = FT_OK; 62 | FT_HANDLE ftHandle; 63 | 64 | ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, &ftHandle); 65 | if (FT_FAILED(ftStatus)) 66 | { 67 | return FALSE; 68 | } 69 | 70 | ftStatus = FT_SetChipConfiguration(ftHandle, &oSaveConfigurationData); 71 | if (FT_FAILED(ftStatus)) 72 | { 73 | FT_Close(ftHandle); 74 | return FALSE; 75 | } 76 | 77 | FT_Close(ftHandle); 78 | ftHandle = NULL; 79 | 80 | return TRUE; 81 | } 82 | 83 | int modify_config() 84 | { 85 | FT_STATUS ftStatus = FT_OK; 86 | FT_HANDLE ftHandle; 87 | 88 | ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, &ftHandle); 89 | if (FT_FAILED(ftStatus)) 90 | { 91 | my_printf("File to create handle\n"); 92 | return FALSE; 93 | } 94 | 95 | FT_60XCONFIGURATION oldconfig = {0}; 96 | FT_60XCONFIGURATION oConfigurationData = {0}; 97 | 98 | ftStatus = FT_GetChipConfiguration(ftHandle, &oldconfig); 99 | if (FT_FAILED(ftStatus)) 100 | { 101 | my_printf("Get Chip Error\n"); 102 | FT_Close(ftHandle); 103 | return FALSE; 104 | } 105 | 106 | //show_config(&oldconfig,1); 107 | memcpy(&oConfigurationData, &oldconfig, sizeof(oConfigurationData)); 108 | 109 | oConfigurationData.FIFOMode = CONFIGURATION_FIFO_MODE_245; 110 | oConfigurationData.ChannelConfig = CONFIGURATION_CHANNEL_CONFIG_1; 111 | 112 | oConfigurationData.OptionalFeatureSupport = CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL; 113 | 114 | if(memcmp(&oldconfig, &oConfigurationData, sizeof(oConfigurationData))) 115 | { 116 | my_printf("Changing chip configuraiton !\n"); 117 | ftStatus = FT_SetChipConfiguration(ftHandle, &oConfigurationData); 118 | if (FT_FAILED(ftStatus)) 119 | { 120 | my_printf("Set chip error\n"); 121 | FT_Close(ftHandle); 122 | return FALSE; 123 | } 124 | 125 | FT_Close(ftHandle); 126 | my_sleep(5000); 127 | ftHandle = NULL; 128 | ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, &ftHandle); 129 | if (FT_FAILED(ftStatus)) 130 | { 131 | my_printf("Get Chip Error2\n"); 132 | return FALSE; 133 | } 134 | } else { 135 | my_printf("Chip configuration was ok, don't need to change !\n"); 136 | } 137 | 138 | memset(&oConfigurationData, 0, sizeof(oConfigurationData)); 139 | ftStatus = FT_GetChipConfiguration(ftHandle, &oConfigurationData); 140 | if (FT_FAILED(ftStatus)) 141 | { 142 | my_printf("Get Chip Error2\n"); 143 | FT_Close(ftHandle); 144 | return FALSE; 145 | } 146 | my_printf("\n\nNew config\n"); 147 | show_config(&oConfigurationData, 1); 148 | if (oConfigurationData.OptionalFeatureSupport != (CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL)) 149 | { 150 | my_printf("Config is not ok\n"); 151 | FT_Close(ftHandle); 152 | return FALSE; 153 | } 154 | 155 | if (oConfigurationData.FIFOMode != CONFIGURATION_FIFO_MODE_245 || 156 | oConfigurationData.ChannelConfig != CONFIGURATION_CHANNEL_CONFIG_1 ) 157 | { 158 | my_printf("Config is not ok\n"); 159 | 160 | FT_Close(ftHandle); 161 | return FALSE; 162 | } 163 | 164 | FT_Close(ftHandle); 165 | return TRUE; 166 | } 167 | 168 | #define NUM_CPUCLOCK 4 169 | #define NUM_FIFOCLOCK 4 170 | #define NUM_FIFOMODE 2 171 | #define NUM_BCCONFIGURATION 4 172 | #define NUM_CHANNELCONFIG 5 173 | 174 | const char *g_szFIFOClock[NUM_FIFOCLOCK] = 175 | { 176 | "100 MHz", 177 | "66 MHz", 178 | "50 MHz", 179 | "40 MHz", 180 | }; 181 | 182 | const char *g_szFIFOMode[NUM_FIFOMODE] = 183 | { 184 | "245 Mode", 185 | "600 Mode", 186 | }; 187 | 188 | const char *g_szBCDConfiguration[NUM_BCCONFIGURATION] = 189 | { 190 | "00 (GPIO1=0, GPIO2=0)", 191 | "01 (GPIO1=0, GPIO2=1)", 192 | "10 (GPIO1=1, GPIO2=0)", 193 | "11 (GPIO1=1, GPIO2=1)", 194 | }; 195 | 196 | const char *g_szChannelConfig[NUM_CHANNELCONFIG] = 197 | { 198 | "4 Channels", 199 | "2 Channels", 200 | "1 Channel", 201 | "1 OUT Pipe", 202 | "1 IN Pipe", 203 | }; 204 | 205 | int show_config(FT_60XCONFIGURATION *a_pConfigurationData, int a_bRead) 206 | { 207 | my_printf("\n"); 208 | my_printf("\tDevice Descriptor\n"); 209 | my_printf("\tVendorID : 0x%04X\n", a_pConfigurationData->VendorID); 210 | my_printf("\tProductID : 0x%04X\n", a_pConfigurationData->ProductID); 211 | 212 | char ucTemp[128] = {0}; 213 | char *pOffset = NULL; 214 | my_printf("\n"); 215 | my_printf("\tString Descriptor\n"); 216 | pOffset = (char*)&a_pConfigurationData->StringDescriptors[0]; 217 | memset(ucTemp, 0, sizeof(ucTemp)); 218 | if (pOffset[0]-2) 219 | { 220 | memcpy(ucTemp, &pOffset[2], pOffset[0]-2); 221 | } 222 | my_printf("\tManufacturer : %s\n", ucTemp); 223 | pOffset += pOffset[0]; 224 | memset(ucTemp, 0, sizeof(ucTemp)); 225 | if (pOffset[0]-2) 226 | { 227 | memcpy(ucTemp, &pOffset[2], pOffset[0]-2); 228 | } 229 | my_printf("\tProduct Description : %s\n", ucTemp); 230 | pOffset += pOffset[0]; 231 | memset(ucTemp, 0, sizeof(ucTemp)); 232 | if (pOffset[0]-2) 233 | { 234 | memcpy(ucTemp, &pOffset[2], pOffset[0]-2); 235 | } 236 | my_printf("\tSerial Number : %s\n", ucTemp); 237 | 238 | my_printf("\n"); 239 | my_printf("\tConfiguration Descriptor\n"); 240 | my_printf("\tPowerAttributes : %s %s\n", FT_IS_BUS_POWERED(a_pConfigurationData->PowerAttributes) ? "Self-powered " : "Bus-powered ", 241 | FT_IS_REMOTE_WAKEUP_ENABLED(a_pConfigurationData->PowerAttributes) ? "Remote wakeup " : ""); 242 | my_printf("\tPowerConsumption : %d\n", a_pConfigurationData->PowerConsumption); 243 | 244 | my_printf("\n"); 245 | my_printf("\tData Transfer\n"); 246 | my_printf("\tFIFOClock : %s\n", g_szFIFOClock[a_pConfigurationData->FIFOClock]); 247 | my_printf("\tFIFOMode : %s\n", g_szFIFOMode[a_pConfigurationData->FIFOMode]); 248 | my_printf("\tChannelConfig : %s\n", g_szChannelConfig[a_pConfigurationData->ChannelConfig]); 249 | 250 | my_printf("\n"); 251 | my_printf("\tOptional Features\n"); 252 | my_printf("\tDisableCancelOnUnderrun : %d\n", (a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN) >> 1); 253 | my_printf("\tNotificationEnabled : %d (%d %d %d %d)\n", 254 | (a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL) >> 2, 255 | (a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH1) ? 1 : 0, 256 | (a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH2) ? 1 : 0, 257 | (a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH3) ? 1 : 0, 258 | (a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH4) ? 1 : 0 259 | ); 260 | my_printf("\tBatteryChargingEnabled : %d\n", a_pConfigurationData->OptionalFeatureSupport & CONFIGURATION_OPTIONAL_FEATURE_ENABLEBATTERYCHARGING); 261 | my_printf("\tBCGPIOPinConfig : 0x%02X\n", a_pConfigurationData->BatteryChargingGPIOConfig); 262 | 263 | if (a_bRead) 264 | { 265 | my_printf("\tFlashEEPROMDetection : 0x%02X\n\n",a_pConfigurationData->FlashEEPROMDetection); 266 | my_printf("\t\tMemory Type : %s\n", a_pConfigurationData->FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<FlashEEPROMDetection & (1<MSIO_Control); 283 | my_printf("\tGPIOControl : 0x%08X\n", a_pConfigurationData->GPIO_Control); 284 | 285 | my_printf("\n"); 286 | } 287 | 288 | 289 | 290 | 291 | 292 | int FT601_Open(FT_HANDLE *ftHandle) 293 | { 294 | int ret; 295 | FT_STATUS ftStatus = FT_OK; 296 | 297 | // ret = modify_config(); 298 | ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, ftHandle); 299 | if (FT_FAILED(ftStatus)) 300 | { 301 | return FALSE; 302 | } 303 | return TRUE; 304 | } 305 | 306 | int FT601_Close(FT_HANDLE ftHandle) 307 | { 308 | FT_Close(ftHandle); 309 | return TRUE; 310 | 311 | } 312 | 313 | 314 | int FT601_Read(FT_HANDLE ftHandle, void *buf, size_t len) 315 | { 316 | 317 | FT_STATUS ftStatus = FT_OK; 318 | unsigned long ulBytesRead = 0; 319 | 320 | ftStatus = FT_ReadPipe(ftHandle, 0x82, buf, len, &ulBytesRead, NULL); 321 | my_printf("Status rd %d, len: %d\n", ftStatus, ulBytesRead); 322 | return ulBytesRead; 323 | } 324 | 325 | int FT601_Write(FT_HANDLE ftHandle, void *buf, size_t len) 326 | { 327 | FT_STATUS ftStatus = FT_OK; 328 | unsigned long ulBytesWritten = 0; 329 | 330 | ftStatus = FT_WritePipe(ftHandle, 0x02, buf, len, &ulBytesWritten, NULL); 331 | my_printf("Status wr %d, len: %d\n", ftStatus, ulBytesWritten); 332 | return ulBytesWritten; 333 | } 334 | -------------------------------------------------------------------------------- /gateware/dramfifo.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 / LambdaConcept / po@lambdaconcept.com 2 | # Copyright (C) 2018 / EnjoyDigital / florent@enjoy-digital.fr 3 | from litex.gen import * 4 | 5 | from litex.soc.interconnect import stream 6 | 7 | from litedram.frontend import dma 8 | from litedram.common import LiteDRAMNativePort 9 | 10 | 11 | def _inc(signal, modulo): 12 | if modulo == 2**len(signal): 13 | return signal.eq(signal + 1) 14 | else: 15 | return If(signal == (modulo - 1), 16 | signal.eq(0) 17 | ).Else( 18 | signal.eq(signal + 1) 19 | ) 20 | 21 | 22 | class _LiteDRAMFIFOCtrl(Module): 23 | def __init__(self, base, depth, read_threshold, write_threshold): 24 | self.base = base 25 | self.depth = depth 26 | self.level = Signal(max=depth+1) 27 | 28 | # # # 29 | 30 | # to buffer write 31 | self.writable = Signal() 32 | self.write_address = Signal(max=depth) 33 | 34 | # from buffer write 35 | self.write = Signal() 36 | self.pending = Signal() 37 | 38 | # to buffer read 39 | self.readable = Signal() 40 | self.read_address = Signal(max=depth) 41 | 42 | # from buffer read 43 | self.read = Signal() 44 | 45 | # # # 46 | 47 | produce = self.write_address 48 | consume = self.read_address 49 | 50 | self.sync += [ 51 | If(self.write, 52 | _inc(produce, depth) 53 | ), 54 | If(self.read, 55 | _inc(consume, depth) 56 | ), 57 | If(self.write & ~self.read, 58 | self.level.eq(self.level + 1), 59 | ).Elif(self.read & ~self.write, 60 | self.level.eq(self.level - 1) 61 | ) 62 | ] 63 | 64 | self.comb += [ 65 | self.writable.eq(self.level < write_threshold), 66 | self.readable.eq(self.level > read_threshold) 67 | ] 68 | 69 | 70 | class _LiteDRAMFIFOLatch(Module): 71 | """When enabled, source is valid only if at least depth data have been 72 | accumulated into the FIFO. 73 | """ 74 | def __init__(self, dw, depth): 75 | self.sink = sink = stream.Endpoint([("data", dw)]) 76 | self.source = source = stream.Endpoint([("data", dw)]) 77 | 78 | # # # 79 | 80 | # from FIFO router 81 | self.en = Signal() 82 | 83 | # to FIFO router 84 | self.writable = Signal() 85 | 86 | # # # 87 | 88 | opened = Signal() 89 | counter = Signal(max=depth) 90 | 91 | self.submodules.fifo = fifo = stream.SyncFIFO([("data", dw)], 2*depth) 92 | 93 | self.comb += [ 94 | self.writable.eq(fifo.level != fifo.depth), 95 | ] 96 | 97 | self.comb += [ 98 | If(~self.en, 99 | sink.connect(fifo.sink), 100 | fifo.source.connect(source), 101 | ).Else( 102 | sink.connect(fifo.sink), 103 | # output to source only when the latch is opened 104 | If(opened, 105 | fifo.source.connect(source), 106 | ), 107 | ) 108 | ] 109 | 110 | self.sync += [ 111 | If(self.en & opened, 112 | If(fifo.source.valid & fifo.source.ready, 113 | If(counter < depth-1, 114 | counter.eq(counter + 1), 115 | ).Else( 116 | counter.eq(0), 117 | # not enough data, close the latch 118 | If(fifo.level-1 < depth, 119 | opened.eq(0), 120 | ), 121 | ), 122 | ) 123 | ).Else( 124 | # enough data accumulated, open the latch 125 | If(fifo.level >= depth, 126 | opened.eq(1), 127 | ), 128 | ), 129 | ] 130 | 131 | 132 | class _LiteDRAMFIFORouter(Module): 133 | def __init__(self, dw, depth, ctrl): 134 | layout = [("data", dw)] 135 | self.sink0 = sink0 = stream.Endpoint(layout) 136 | self.source0 = source0 = stream.Endpoint(layout) 137 | self.sink1 = sink1 = stream.Endpoint(layout) 138 | self.source1 = source1 = stream.Endpoint(layout) 139 | 140 | # # # 141 | 142 | self.submodules.latch = latch = _LiteDRAMFIFOLatch(dw, depth) 143 | 144 | self.submodules.fsm = fsm = FSM() 145 | fsm.act("BYPASS", 146 | latch.en.eq(0), 147 | sink0.connect(latch.sink), 148 | latch.source.connect(source0), 149 | 150 | If(~latch.writable, 151 | NextState("DRAM"), 152 | ), 153 | ) 154 | 155 | fsm.act("DRAM", 156 | latch.en.eq(1), 157 | sink0.connect(latch.sink), 158 | 159 | source1.data.eq(latch.source.data), 160 | source1.valid.eq(latch.source.valid), 161 | latch.source.ready.eq(source1.ready), 162 | 163 | sink1.connect(source0), 164 | 165 | If(~latch.source.valid & ~ctrl.pending & ~ctrl.readable, 166 | NextState("BYPASS"), 167 | ), 168 | ) 169 | 170 | 171 | class _LiteDRAMFIFOWriter(Module): 172 | def __init__(self, port, ctrl): 173 | assert isinstance(port, LiteDRAMNativePort) 174 | self.sink = sink = stream.Endpoint([("data", port.data_width)]) 175 | 176 | # # # 177 | 178 | (cmd, wdata) = port.cmd, port.wdata 179 | 180 | sendcmd = Signal(reset=1) 181 | senddata = Signal() 182 | 183 | self.comb += [ 184 | ctrl.pending.eq(sink.valid), 185 | ] 186 | 187 | self.comb += [ 188 | cmd.we.eq(1), 189 | cmd.addr.eq(ctrl.base + ctrl.write_address), 190 | cmd.valid.eq(sink.valid & ctrl.writable & sendcmd), 191 | ] 192 | self.sync += [ 193 | If(cmd.valid & cmd.ready, 194 | sendcmd.eq(0), 195 | senddata.eq(1), 196 | ), 197 | ] 198 | 199 | self.comb += [ 200 | wdata.we.eq(2**(port.data_width//8)-1), 201 | wdata.data.eq(sink.data), 202 | wdata.valid.eq(sink.valid & ctrl.writable & senddata), 203 | ] 204 | self.sync += [ 205 | If(wdata.valid & wdata.ready, 206 | sendcmd.eq(1), 207 | senddata.eq(0), 208 | ), 209 | ] 210 | 211 | self.comb += [ 212 | If(wdata.valid & wdata.ready, 213 | ctrl.write.eq(1), 214 | sink.ready.eq(1), 215 | ), 216 | ] 217 | 218 | 219 | class _LiteDRAMFIFOReader(Module): 220 | def __init__(self, port, ctrl): 221 | assert isinstance(port, LiteDRAMNativePort) 222 | self.submodules.fifo = fifo = stream.SyncFIFO([("data", port.data_width)], 2) 223 | self.source = source = fifo.source 224 | 225 | # # # 226 | 227 | (cmd, rdata) = port.cmd, port.rdata 228 | 229 | sendcmd = Signal(reset=1) 230 | 231 | self.comb += [ 232 | cmd.we.eq(0), 233 | cmd.addr.eq(ctrl.base + ctrl.read_address), 234 | cmd.valid.eq(ctrl.readable & sendcmd), 235 | ] 236 | self.sync += [ 237 | If(cmd.valid & cmd.ready, 238 | sendcmd.eq(0), 239 | ), 240 | ] 241 | 242 | self.comb += [ 243 | rdata.connect(fifo.sink, omit={"id", "resp"}), 244 | If(source.valid & source.ready, 245 | ctrl.read.eq(1), 246 | ), 247 | ] 248 | self.sync += [ 249 | If(source.valid & source.ready, 250 | sendcmd.eq(1), 251 | ), 252 | ] 253 | 254 | 255 | class _FLInterface(Record): 256 | def __init__(self, description): 257 | layout = [("payload", description.payload_layout), 258 | ("param", description.param_layout), 259 | ("first", 1), 260 | ("last", 1)] 261 | # padding align to next power of two 262 | length = layout_len(layout) 263 | power = 2**bits_for(length) 264 | padding = power - length 265 | layout += [("padding", padding)] 266 | 267 | Record.__init__(self, layout) 268 | 269 | 270 | class _FLPack(Module): 271 | def __init__(self, layout_from): 272 | self.sink = sink = stream.Endpoint(layout_from) 273 | din = _FLInterface(sink.description) 274 | self.source = source = stream.Endpoint([("data", len(din))]) 275 | 276 | # # # 277 | 278 | self.comb += [ 279 | din.payload.eq(sink.payload), 280 | din.param.eq(sink.param), 281 | din.first.eq(sink.first), 282 | din.last.eq(sink.last), 283 | 284 | source.valid.eq(sink.valid), 285 | source.data.eq(din.raw_bits()), 286 | sink.ready.eq(source.ready), 287 | ] 288 | 289 | 290 | class _FLUnpack(Module): 291 | def __init__(self, layout_to): 292 | self.source = source = stream.Endpoint(layout_to) 293 | dout = _FLInterface(source.description) 294 | self.sink = sink = stream.Endpoint([("data", len(dout))]) 295 | 296 | # # # 297 | 298 | self.comb += [ 299 | source.payload.eq(dout.payload), 300 | source.param.eq(dout.param), 301 | source.first.eq(dout.first), 302 | source.last.eq(dout.last), 303 | 304 | source.valid.eq(sink.valid), 305 | dout.raw_bits().eq(sink.data), 306 | sink.ready.eq(source.ready), 307 | ] 308 | 309 | class LiteDRAMFIFO(Module): 310 | def __init__(self, layout, depth, base, crossbar, 311 | read_threshold=None, write_threshold=None, 312 | preserve_first_last=True): 313 | 314 | self.sink = sink = stream.Endpoint(layout) 315 | self.source = source = stream.Endpoint(layout) 316 | 317 | # # # 318 | 319 | # preserve first and last fields 320 | if preserve_first_last: 321 | self.submodules.pack = _FLPack(layout) 322 | self.submodules.unpack = _FLUnpack(layout) 323 | 324 | self.comb += [ 325 | sink.connect(self.pack.sink), 326 | self.unpack.source.connect(source), 327 | ] 328 | 329 | fifo_in = self.pack.source 330 | fifo_out = self.unpack.sink 331 | else: 332 | fifo_in = sink 333 | fifo_out = source 334 | 335 | native_width = crossbar.controller.data_width 336 | dw = len(fifo_in.payload) 337 | if dw <= native_width: 338 | if native_width % dw: 339 | raise ValueError("Ratio must be an int") 340 | ctrl_ratio = native_width // dw 341 | ctrl_depth = ((depth-1) // ctrl_ratio) + 1 342 | fifo_depth = ctrl_ratio 343 | else: 344 | raise NotImplementedError("Only upconverter support for now") 345 | 346 | # use native controller width 347 | read_port = crossbar.get_port(mode="read") 348 | write_port = crossbar.get_port(mode="write") 349 | 350 | if read_threshold is None: 351 | read_threshold = 0 352 | if write_threshold is None: 353 | write_threshold = ctrl_depth 354 | 355 | # ctrl counts blocks in native width 356 | self.submodules.ctrl = _LiteDRAMFIFOCtrl(base, ctrl_depth, read_threshold, write_threshold) 357 | self.submodules.writer = _LiteDRAMFIFOWriter(write_port, self.ctrl) 358 | self.submodules.reader = _LiteDRAMFIFOReader(read_port, self.ctrl) 359 | 360 | # router chooses bypass or dram 361 | self.submodules.router = _LiteDRAMFIFORouter(dw, fifo_depth, self.ctrl) 362 | self.submodules.conv_w = stream.StrideConverter(fifo_in.description, self.writer.sink.description) 363 | self.submodules.conv_r = stream.StrideConverter(self.reader.source.description, fifo_out.description) 364 | 365 | self.comb += [ 366 | # bypass 367 | fifo_in.connect(self.router.sink0), 368 | self.router.source0.connect(fifo_out), 369 | 370 | # dram 371 | self.router.source1.connect(self.conv_w.sink), 372 | self.conv_w.source.connect(self.writer.sink), 373 | self.reader.source.connect(self.conv_r.sink), 374 | self.conv_r.source.connect(self.router.sink1), 375 | ] 376 | -------------------------------------------------------------------------------- /gateware/iti.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Copyright (C) 2019 / LambdaConcept / po@lambdaconcept.com 4 | # Copyright (C) 2019 / LambdaConcept / ramtin@lambdaconcept.com 5 | 6 | from migen import * 7 | 8 | from litex.soc.interconnect import stream 9 | from litex.soc.interconnect.csr import * 10 | 11 | from gateware.clocker import TuneClocker 12 | 13 | 14 | # this module implements the format description from 15 | # https://github.com/vpelletier/ITI1480A-linux/blob/master/iti1480a/parser.py#L124 16 | 17 | 18 | PAYLOAD_NONE = 0 19 | PAYLOAD_EVENT = 1 20 | PAYLOAD_DATA = 2 21 | PAYLOAD_RXCMD = 3 22 | 23 | EVENT_START = 0xe0 24 | EVENT_STOP = 0xf1 25 | 26 | 27 | class ITITime(Module, AutoCSR): 28 | def __init__(self): 29 | self.enable = CSRStorage() 30 | 31 | self.diff = Signal(28) # output, time increment 32 | self.len = Signal(2) # output, time increment length 33 | self.next = Signal() # input, set 1 to reset time increment 34 | 35 | self.overflow = Signal() # output, 1 indicates increment overflow 36 | self.clear = Signal() # input, set 1 to clear overflow 37 | 38 | # # # 39 | 40 | self.submodules.tune = TuneClocker(int((60/100)*2**32)) # 60 MHz clock 41 | 42 | self.sync += [ 43 | If(~self.enable.storage, 44 | self.diff.eq(0), 45 | self.overflow.eq(0), 46 | ).Else( 47 | If(self.next, 48 | self.diff.eq(0), 49 | self.overflow.eq(0), 50 | ).Else( 51 | If(self.tune.en, 52 | self.diff.eq(self.diff + 1), 53 | ), 54 | If(self.diff == (2**28) - 1, 55 | # max value reached, trigger overflow 56 | self.overflow.eq(1), 57 | ).Elif(self.clear, 58 | # acknowledge overflow, clear it 59 | self.overflow.eq(0), 60 | ), 61 | ), 62 | ), 63 | ] 64 | 65 | self.comb += [ 66 | # how many more bytes are required to send the full time increment 67 | If(self.diff > (2**20 - 1), 68 | self.len.eq(3), 69 | ).Elif(self.diff > (2**12 - 1), 70 | self.len.eq(2), 71 | ).Elif(self.diff > (2**4 - 1), 72 | self.len.eq(1), 73 | ).Else( 74 | self.len.eq(0), 75 | ), 76 | ] 77 | 78 | 79 | class ITIEvent(Module, AutoCSR): 80 | def __init__(self): 81 | self.event = CSR(8) 82 | 83 | self.data = data = Signal.like(self.event.r) 84 | self.new = new = Signal() 85 | self.ack = ack = Signal() 86 | 87 | # # # 88 | 89 | self.sync += [ 90 | If(self.event.re, 91 | data.eq(self.event.r), 92 | new.eq(1), 93 | ).Elif(~self.event.re & ack, 94 | new.eq(0), 95 | ) 96 | ] 97 | 98 | 99 | class ITIPacker(Module, AutoCSR): 100 | def __init__(self): 101 | self.sink = sink = stream.Endpoint([('data', 8), ('cmd', 1)]) 102 | self.source = source = stream.Endpoint([("data", 40), ("len", 2)]) 103 | 104 | # # # 105 | 106 | self.submodules.time = ITITime() 107 | self.submodules.ev = ITIEvent() 108 | 109 | payload_type = Signal(2) 110 | payload = Signal.like(sink.data) 111 | diff = Signal.like(self.time.diff) 112 | length = Signal.like(self.time.len) 113 | 114 | self.comb += [ 115 | If(self.time.overflow, 116 | 117 | # priority to overflow 118 | payload.eq(0), 119 | payload_type.eq(PAYLOAD_NONE), 120 | diff.eq(2**28 - 1), # max value 121 | length.eq(3), # max length 122 | 123 | # stream out 124 | source.valid.eq(1), 125 | If(source.ready, 126 | self.time.clear.eq(1), 127 | ), 128 | 129 | ).Elif(self.ev.new, 130 | 131 | # received event 132 | payload.eq(self.ev.data), 133 | payload_type.eq(PAYLOAD_EVENT), 134 | 135 | # fetch time increment 136 | diff.eq(self.time.diff), 137 | length.eq(self.time.len), 138 | If(source.ready, 139 | self.time.next.eq(1), 140 | ), 141 | 142 | # stream out 143 | source.valid.eq(1), 144 | If(source.ready, 145 | self.ev.ack.eq(1), 146 | ), 147 | 148 | ).Elif(sink.valid, 149 | 150 | # rxcmd or data 151 | payload.eq(sink.data), 152 | If(sink.cmd, 153 | payload_type.eq(PAYLOAD_RXCMD), 154 | ).Else( 155 | payload_type.eq(PAYLOAD_DATA), 156 | ), 157 | 158 | # fetch time increment 159 | diff.eq(self.time.diff), 160 | length.eq(self.time.len), 161 | If(source.ready, 162 | self.time.next.eq(1), 163 | ), 164 | 165 | # stream out 166 | source.valid.eq(1), 167 | If(source.ready, 168 | sink.ready.eq(1), 169 | ), 170 | ) 171 | ] 172 | 173 | self.comb += [ 174 | # header 175 | source.data[0:8].eq(Cat(diff[0:4], length, payload_type)), 176 | 177 | # additional timestamp 178 | If(length == 3, 179 | source.data[24:32].eq(diff[20:28]), 180 | source.data[16:24].eq(diff[12:20]), 181 | source.data[ 8:16].eq(diff[ 4:12]), 182 | ).Elif(length == 2, 183 | source.data[16:24].eq(diff[12:20]), 184 | source.data[ 8:16].eq(diff[ 4:12]), 185 | ).Elif(length == 1, 186 | source.data[ 8:16].eq(diff[ 4:12]), 187 | ).Else( 188 | # no timestamp 189 | ), 190 | 191 | # payload 192 | If(length == 3, 193 | source.data[32:40].eq(payload), 194 | ).Elif(length == 2, 195 | source.data[24:32].eq(payload), 196 | ).Elif(length == 1, 197 | source.data[16:24].eq(payload), 198 | ).Else( 199 | source.data[ 8:16].eq(payload), 200 | ), 201 | 202 | # length (-2 to keep value on 2 bits) 203 | If(payload_type == PAYLOAD_NONE, 204 | # 1 byte header + length bytes timestamp 205 | source.len.eq(1 + length - 2), 206 | ).Else( 207 | # 1 byte header + length bytes timestamp + 1 byte payload 208 | source.len.eq(1 + length + 1 - 2), 209 | ), 210 | ] 211 | 212 | 213 | class ITIPattern(Module): 214 | def __init__(self, pattern, length, repeat=1): 215 | """ This module generates a start pattern. 216 | It is used by the software to realign its decoding in case the FIFO 217 | is flushed or corrupted after capture start / stop. 218 | """ 219 | self.source = source = stream.Endpoint([("data", 40), ("len", 2)]) 220 | 221 | self.start = Signal() 222 | self.done = Signal(reset=1) 223 | 224 | # # # 225 | 226 | assert((length >= 2) and (length <= 5)) 227 | 228 | count = Signal(max=repeat+1) 229 | 230 | fsm = FSM() 231 | self.submodules.fsm = fsm 232 | 233 | fsm.act("IDLE", 234 | self.done.eq(1), 235 | If(self.start, 236 | NextValue(count, repeat), 237 | NextState("PATTERN"), 238 | ), 239 | ) 240 | 241 | fsm.act("PATTERN", 242 | source.data.eq(pattern), 243 | source.len.eq(length - 2), 244 | source.valid.eq(1), 245 | If(source.ready, 246 | NextValue(count, count - 1), 247 | If(count == 1, 248 | NextState("IDLE"), 249 | ), 250 | ), 251 | ) 252 | 253 | 254 | @ResetInserter() 255 | class ITICore(Module, AutoCSR): 256 | def __init__(self): 257 | self.sink = sink = stream.Endpoint([('data', 8), ('cmd', 1)]) 258 | self.source = source = stream.Endpoint([("data", 40), ("len", 2)]) 259 | 260 | self.start_pattern = CSR() 261 | 262 | # # # 263 | 264 | self.submodules.pattern = ITIPattern(0xe00050, 3, 4) 265 | self.submodules.packer = ITIPacker() 266 | 267 | self.comb += [ 268 | self.pattern.start.eq(self.start_pattern.re), 269 | 270 | # Mux sources with priority to pattern generator 271 | If(self.pattern.source.valid, 272 | self.pattern.source.connect(source), 273 | ).Else( 274 | self.sink.connect(self.packer.sink), 275 | self.packer.source.connect(source), 276 | ) 277 | ] 278 | 279 | 280 | @ResetInserter() 281 | class Conv4032(Module): 282 | def __init__(self): 283 | self.sink = sink = stream.Endpoint([('data', 40), ('len', 2)]) 284 | self.source = source = stream.Endpoint([('data', 32)]) 285 | 286 | tmp = Signal(32) 287 | remain = Signal(2) 288 | cases_comb = {} 289 | send_next = Signal() 290 | valid = Signal() 291 | 292 | for i in range(16): 293 | a= (i & 0xc) >> 2 294 | b = i & 3 295 | c = a+b+2 296 | 297 | if(a+b+2 < 4): 298 | d=a*8 299 | e=0 300 | v=0; 301 | else: 302 | d=0 303 | e=(4-a)*8 304 | v=1 305 | f = c*8 306 | if f > 32: 307 | f = f-32 308 | print("[{:02d}:{:02d}] <- [{:02d}:{:02d}]".format(d, f, e, (b+2)*8)) 309 | cases_comb[i] = [ 310 | If(self.sink.valid & self.sink.ready, 311 | NextValue(remain, c&3), 312 | ) 313 | ] 314 | if a != 0: 315 | cases_comb[i] += [ 316 | If(self.sink.valid, 317 | self.source.data.eq(Cat(tmp[0:a*8], self.sink.data[0:(4-a)*8])), 318 | ), 319 | ] 320 | else: 321 | cases_comb[i] += [ 322 | If(self.sink.valid, 323 | self.source.data.eq(self.sink.data[0:(4-a)*8]), 324 | ), 325 | ] 326 | 327 | if e != (b+2)*8: 328 | cases_comb[i] += [ 329 | If(self.sink.valid & self.sink.ready, 330 | NextValue(tmp[d : f], self.sink.data[e:(b+2)*8]) 331 | ), 332 | ] 333 | 334 | if(v==1): 335 | cases_comb[i] += [valid.eq(1)] 336 | # case 15 brings you to sending the last tmp 337 | cases_comb[15] += [send_next.eq(1)] 338 | fsm = FSM() 339 | self.submodules += fsm 340 | 341 | fsm.act("NORMAL", 342 | Case(Cat(self.sink.len, remain), cases_comb), 343 | self.source.valid.eq(valid & self.sink.valid), 344 | If((self.source.valid & self.source.ready) | ~self.source.valid, 345 | self.sink.ready.eq(1), 346 | ), 347 | If(send_next & self.sink.valid & self.source.valid & self.source.ready, 348 | NextState("SEND_EXTRA") 349 | ) 350 | ) 351 | 352 | fsm.act("SEND_EXTRA", 353 | self.source.valid.eq(1), 354 | self.source.data.eq(tmp), 355 | If(self.source.ready, 356 | NextState("NORMAL") 357 | ) 358 | ) 359 | 360 | 361 | def tb_conv(dut): 362 | # yield dut.conv4032.source.ready.eq(1) 363 | 364 | it = iter( 365 | [(0xa5, 0), (0xd2, 0), (0xcf, 1)] * 50 366 | ) 367 | data, cmd = next(it) 368 | 369 | cnt = 0 370 | while True: 371 | yield dut.packer.sink.data.eq(data) 372 | yield dut.packer.sink.cmd.eq(cmd) 373 | yield dut.packer.sink.valid.eq(1) 374 | # simulate not always ready 375 | if ((cnt % 5) == 0): 376 | yield dut.conv4032.source.ready.eq(1) 377 | else: 378 | yield dut.conv4032.source.ready.eq(0) 379 | cnt += 1 380 | yield 381 | if (yield dut.packer.sink.ready): 382 | try: 383 | data, cmd = next(it) 384 | # # simulate large time increment 385 | # yield dut.packer.sink.valid.eq(0) 386 | # for i in range(100): 387 | # yield 388 | # yield dut.time.diff.eq(2**28 - 10) 389 | except StopIteration: 390 | break 391 | 392 | yield dut.packer.sink.valid.eq(0) 393 | for i in range(100): 394 | yield 395 | 396 | cnt = 0 397 | # simulate overflow without data 398 | yield dut.packer.time.diff.eq(2**28 - 10) 399 | for i in range(100): 400 | # simulate not always ready 401 | if ((cnt % 6) == 0): 402 | yield dut.conv4032.source.ready.eq(1) 403 | else: 404 | yield dut.conv4032.source.ready.eq(0) 405 | cnt += 1 406 | yield 407 | 408 | cnt = 0 409 | # simulate start event 410 | yield dut.packer.ev.event.r.eq(0xe0) 411 | yield dut.packer.ev.event.re.eq(1) 412 | yield 413 | yield dut.packer.ev.event.re.eq(0) 414 | for i in range(100): 415 | # simulate not always ready 416 | if ((cnt % 6) == 0): 417 | yield dut.conv4032.source.ready.eq(1) 418 | else: 419 | yield dut.conv4032.source.ready.eq(0) 420 | cnt += 1 421 | yield 422 | 423 | cnt = 0 424 | # simulate stop event 425 | yield dut.packer.ev.event.r.eq(0xf1) 426 | yield dut.packer.ev.event.re.eq(1) 427 | yield 428 | yield dut.packer.ev.event.re.eq(0) 429 | for i in range(100): 430 | # simulate not always ready 431 | if ((cnt % 6) == 0): 432 | yield dut.conv4032.source.ready.eq(1) 433 | else: 434 | yield dut.conv4032.source.ready.eq(0) 435 | cnt += 1 436 | yield 437 | 438 | 439 | def tb_pack(dut): 440 | yield dut.source.ready.eq(1) 441 | 442 | it = iter( 443 | [(0xa5, 0), (0xd2, 0), (0xcf, 1)] * 50 444 | ) 445 | data, cmd = next(it) 446 | 447 | while True: 448 | yield dut.sink.data.eq(data) 449 | yield dut.sink.cmd.eq(cmd) 450 | yield dut.sink.valid.eq(1) 451 | yield 452 | if (yield dut.sink.ready): 453 | try: 454 | data, cmd = next(it) 455 | # # simulate large time increment 456 | # yield dut.sink.valid.eq(0) 457 | # for i in range(100): 458 | # yield 459 | # yield dut.time.diff.eq(2**28 - 10) 460 | except StopIteration: 461 | break 462 | 463 | yield dut.sink.valid.eq(0) 464 | for i in range(100): 465 | yield 466 | 467 | # simulate overflow without data 468 | yield dut.time.diff.eq(2**28 - 10) 469 | for i in range(100): 470 | yield 471 | 472 | # simulate start event 473 | yield dut.ev.event.r.eq(0xe0) 474 | yield dut.ev.event.re.eq(1) 475 | yield 476 | yield dut.ev.event.re.eq(0) 477 | for i in range(100): 478 | yield 479 | 480 | # simulate stop event 481 | yield dut.ev.event.r.eq(0xf1) 482 | yield dut.ev.event.re.eq(1) 483 | yield 484 | yield dut.ev.event.re.eq(0) 485 | for i in range(100): 486 | yield 487 | 488 | 489 | def tb_time(dut): 490 | for i in range(2): 491 | yield 492 | 493 | # test len 0 494 | yield dut.next.eq(1) 495 | yield 496 | yield dut.next.eq(0) 497 | for i in range(9): 498 | yield 499 | 500 | # test len 1 501 | yield dut.next.eq(1) 502 | yield 503 | yield dut.next.eq(0) 504 | for i in range(2**5): 505 | yield 506 | 507 | # test len 2 508 | yield dut.next.eq(1) 509 | yield 510 | yield dut.next.eq(0) 511 | yield dut.diff.eq(2**20 - 10) 512 | for i in range(2**13): 513 | yield 514 | 515 | # test len 3 and overflow 516 | yield dut.next.eq(1) 517 | yield 518 | yield dut.next.eq(0) 519 | yield dut.diff.eq(2**28 - 10) 520 | for i in range(2**16): 521 | if (yield dut.overflow): 522 | yield dut.clear.eq(1) 523 | else: 524 | yield dut.clear.eq(0) 525 | yield 526 | 527 | 528 | class TopTestBench(Module): 529 | def __init__(self): 530 | self.submodules.packer = ITIPacker() 531 | self.submodules.conv4032 = Conv4032() 532 | self.comb += [ 533 | self.packer.source.connect(self.conv4032.sink), 534 | ] 535 | 536 | 537 | if __name__ == "__main__": 538 | # dut = ITITime() 539 | # run_simulation(dut, tb_time(dut), vcd_name="test/iti_time.vcd") 540 | 541 | # dut = ITIPacker() 542 | # run_simulation(dut, tb_pack(dut), vcd_name="test/iti_pack.vcd") 543 | 544 | dut = TopTestBench() 545 | run_simulation(dut, tb_conv(dut), vcd_name="test/conv4032.vcd") 546 | -------------------------------------------------------------------------------- /usbsniffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import math 4 | 5 | from migen import * 6 | from migen.genlib.resetsync import AsyncResetSynchronizer 7 | 8 | from litex.build.generic_platform import * 9 | from litex.build.xilinx import XilinxPlatform 10 | 11 | from litex.soc.interconnect.csr import * 12 | from litex.soc.integration.soc_core import * 13 | from litex.soc.integration.soc_sdram import * 14 | from litex.soc.integration.builder import * 15 | from litex.soc.integration.cpu_interface import get_csr_header 16 | from litex.soc.interconnect import stream 17 | from litex.soc.cores.uart import UARTWishboneBridge, RS232PHY 18 | from litex.soc.cores.gpio import GPIOOut 19 | 20 | from litedram import sdram_init 21 | from litedram.modules import MT41K256M16 22 | from litedram.phy import a7ddrphy 23 | 24 | from gateware.usb import USBCore 25 | from gateware.etherbone import Etherbone 26 | from gateware.ft601 import FT601Sync, phy_description 27 | from gateware.ulpi import ULPIPHY, ULPICore, ulpi_cmd_description 28 | from gateware.iti import ITICore, Conv4032 29 | from gateware.wrapper import WrapCore 30 | from gateware.dramfifo import LiteDRAMFIFO 31 | from gateware.spi import SPIMaster 32 | from gateware.flash import Flash 33 | from gateware.storage import OverflowMeter 34 | 35 | from litescope import LiteScopeAnalyzer 36 | 37 | 38 | _io = [ 39 | ("clk100", 0, Pins("J19"), IOStandard("LVCMOS33")), 40 | 41 | ("rgb_led", 0, 42 | Subsignal("r", Pins("W2")), 43 | Subsignal("g", Pins("Y1")), 44 | Subsignal("b", Pins("W1")), 45 | IOStandard("LVCMOS33"), 46 | ), 47 | 48 | ("rgb_led", 1, 49 | Subsignal("r", Pins("AA1")), 50 | Subsignal("g", Pins("AB1")), 51 | Subsignal("b", Pins("Y2")), 52 | IOStandard("LVCMOS33"), 53 | ), 54 | 55 | ("serial", 0, 56 | Subsignal("tx", Pins("U21")), # FPGA_GPIO0 57 | Subsignal("rx", Pins("T21")), # FPGA_GPIO1 58 | IOStandard("LVCMOS33"), 59 | ), 60 | 61 | ("ddram", 0, 62 | Subsignal("a", Pins( 63 | "M2 M5 M3 M1 L6 P1 N3 N2", 64 | "M6 R1 L5 N5 N4 P2 P6"), 65 | IOStandard("SSTL15")), 66 | Subsignal("ba", Pins("L3 K6 L4"), IOStandard("SSTL15")), 67 | Subsignal("ras_n", Pins("J4"), IOStandard("SSTL15")), 68 | Subsignal("cas_n", Pins("K3"), IOStandard("SSTL15")), 69 | Subsignal("we_n", Pins("L1"), IOStandard("SSTL15")), 70 | Subsignal("dm", Pins("G3 F1"), IOStandard("SSTL15")), 71 | Subsignal("dq", Pins( 72 | "G2 H4 H5 J1 K1 H3 H2 J5", 73 | "E3 B2 F3 D2 C2 A1 E2 B1"), 74 | IOStandard("SSTL15"), 75 | Misc("IN_TERM=UNTUNED_SPLIT_50")), 76 | Subsignal("dqs_p", Pins("K2 E1"), IOStandard("DIFF_SSTL15")), 77 | Subsignal("dqs_n", Pins("J2 D1"), IOStandard("DIFF_SSTL15")), 78 | Subsignal("clk_p", Pins("P5"), IOStandard("DIFF_SSTL15")), 79 | Subsignal("clk_n", Pins("P4"), IOStandard("DIFF_SSTL15")), 80 | Subsignal("cke", Pins("J6"), IOStandard("SSTL15")), 81 | Subsignal("odt", Pins("K4"), IOStandard("SSTL15")), 82 | Subsignal("reset_n", Pins("G1"), IOStandard("SSTL15")), 83 | Misc("SLEW=FAST"), 84 | ), 85 | 86 | ("flash", 0, 87 | Subsignal("cs_n", Pins("T19")), 88 | Subsignal("mosi", Pins("P22")), 89 | Subsignal("miso", Pins("R22")), 90 | Subsignal("vpp", Pins("P21")), 91 | Subsignal("hold", Pins("R21")), 92 | IOStandard("LVCMOS33") 93 | ), 94 | 95 | ("usb_fifo_clock", 0, Pins("D17"), IOStandard("LVCMOS33")), 96 | ("usb_fifo", 0, 97 | Subsignal("rst", Pins("K22")), 98 | Subsignal("data", Pins("A16 F14 A15 F13 A14 E14 A13 E13 B13 C15 C13 C14 B16 E17 B15 F16", 99 | "A20 E18 B20 F18 D19 D21 E19 E21 A21 B21 A19 A18 F20 F19 B18 B17")), 100 | Subsignal("be", Pins("K16 L16 G20 H20")), 101 | Subsignal("rxf_n", Pins("M13")), 102 | Subsignal("txe_n", Pins("L13")), 103 | Subsignal("rd_n", Pins("K19")), 104 | Subsignal("wr_n", Pins("M15")), 105 | Subsignal("oe_n", Pins("L21")), 106 | Subsignal("siwua", Pins("M16")), 107 | IOStandard("LVCMOS33"), Misc("SLEW=FAST") 108 | ), 109 | 110 | ("ulpi_sw", 0, 111 | Subsignal("s", Pins("Y8")), 112 | Subsignal("oe_n", Pins("Y9")), 113 | IOStandard("LVCMOS33"), 114 | ), 115 | 116 | ("ulpi_clock", 0, Pins("W19"), IOStandard("LVCMOS33")), 117 | ("ulpi", 0, 118 | Subsignal("data", Pins("AB18 AA18 AA19 AB20 AA20 AB21 AA21 AB22")), 119 | Subsignal("dir", Pins("W21")), 120 | Subsignal("stp", Pins("Y22")), 121 | Subsignal("nxt", Pins("W22")), 122 | Subsignal("rst", Pins("V20")), 123 | IOStandard("LVCMOS33"), Misc("SLEW=FAST") 124 | ), 125 | 126 | ("ulpi_clock", 1, Pins("V4"), IOStandard("LVCMOS33")), 127 | ("ulpi", 1, 128 | Subsignal("data", Pins("AB2 AA3 AB3 Y4 AA4 AB5 AA5 AB6")), 129 | Subsignal("dir", Pins("AB7")), 130 | Subsignal("stp", Pins("AA6")), 131 | Subsignal("nxt", Pins("AB8")), 132 | Subsignal("rst", Pins("AA8")), 133 | IOStandard("LVCMOS33"), Misc("SLEW=FAST") 134 | ), 135 | ] 136 | 137 | 138 | class Platform(XilinxPlatform): 139 | default_clk_name = "clk100" 140 | default_clk_period = 10.0 141 | 142 | def __init__(self, toolchain="vivado", programmer="vivado"): 143 | XilinxPlatform.__init__(self, "xc7a35t-fgg484-1", _io, 144 | toolchain=toolchain) 145 | self.toolchain.bitstream_commands = \ 146 | ["set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]", 147 | "set_property BITSTREAM.CONFIG.CONFIGRATE 40 [current_design]"] 148 | self.toolchain.additional_commands = \ 149 | ["write_cfgmem -force -format bin -interface spix4 -size 16 " 150 | "-loadbit \"up 0x0 {build_name}.bit\" -file {build_name}.bin"] 151 | self.programmer = programmer 152 | self.add_platform_command("set_property INTERNAL_VREF 0.750 [get_iobanks 35]") 153 | 154 | 155 | class _CRG(Module, AutoCSR): 156 | def __init__(self, platform): 157 | self.clock_domains.cd_sys = ClockDomain("sys") 158 | self.clock_domains.cd_ulpi0 = ClockDomain("ulpi0") 159 | self.clock_domains.cd_ulpi1 = ClockDomain("ulpi1") 160 | self.clock_domains.cd_sys4x = ClockDomain(reset_less=True) 161 | self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True) 162 | self.clock_domains.cd_clk200 = ClockDomain() 163 | 164 | self.clock_domains.cd_usb = ClockDomain() 165 | 166 | # usb clock domain (100MHz from usb) 167 | self.comb += self.cd_usb.clk.eq(platform.request("usb_fifo_clock")) 168 | self.comb += self.cd_usb.rst.eq(self.cd_sys.rst) 169 | 170 | # ulpi0 clock domain (60MHz from ulpi0) 171 | self.comb += self.cd_ulpi0.clk.eq(platform.request("ulpi_clock", 0)) 172 | 173 | # ulpi1 clock domain (60MHz from ulpi1) 174 | self.comb += self.cd_ulpi1.clk.eq(platform.request("ulpi_clock", 1)) 175 | 176 | clk100 = platform.request("clk100") 177 | 178 | # sys & ddr clock domains 179 | pll_locked = Signal() 180 | pll_fb = Signal() 181 | pll_sys = Signal() 182 | pll_sys4x = Signal() 183 | pll_sys4x_dqs = Signal() 184 | pll_clk200 = Signal() 185 | self.specials += [ 186 | Instance("PLLE2_BASE", 187 | p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, 188 | 189 | # VCO @ 1600 MHz 190 | p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=10.0, 191 | p_CLKFBOUT_MULT=16, p_DIVCLK_DIVIDE=1, 192 | i_CLKIN1=clk100, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, 193 | 194 | # 100 MHz 195 | p_CLKOUT0_DIVIDE=16, p_CLKOUT0_PHASE=0.0, 196 | o_CLKOUT0=pll_sys, 197 | 198 | # 400 MHz 199 | p_CLKOUT1_DIVIDE=4, p_CLKOUT1_PHASE=0.0, 200 | o_CLKOUT1=pll_sys4x, 201 | 202 | # 400 MHz dqs 203 | p_CLKOUT2_DIVIDE=4, p_CLKOUT2_PHASE=90.0, 204 | o_CLKOUT2=pll_sys4x_dqs, 205 | 206 | # 200 MHz 207 | p_CLKOUT3_DIVIDE=8, p_CLKOUT3_PHASE=0.0, 208 | o_CLKOUT3=pll_clk200, 209 | ), 210 | Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), 211 | Instance("BUFG", i_I=pll_sys4x, o_O=self.cd_sys4x.clk), 212 | Instance("BUFG", i_I=pll_sys4x_dqs, o_O=self.cd_sys4x_dqs.clk), 213 | Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk), 214 | AsyncResetSynchronizer(self.cd_sys, ~pll_locked), 215 | AsyncResetSynchronizer(self.cd_clk200, ~pll_locked) 216 | ] 217 | 218 | reset_counter = Signal(4, reset=15) 219 | ic_reset = Signal(reset=1) 220 | self.sync.clk200 += \ 221 | If(reset_counter != 0, 222 | reset_counter.eq(reset_counter - 1) 223 | ).Else( 224 | ic_reset.eq(0) 225 | ) 226 | self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) 227 | 228 | 229 | class BlinkerKeep(Module): 230 | def __init__(self, s, timeout=int(1e6)): 231 | self.o = Signal() 232 | 233 | # # # 234 | 235 | counter = Signal(max=timeout+1) 236 | self.sync += [ 237 | If(s, 238 | counter.eq(timeout), 239 | ).Elif(counter > 0, 240 | counter.eq(counter - 1), 241 | ).Else( 242 | counter.eq(0), 243 | ) 244 | ] 245 | 246 | self.comb += self.o.eq(counter > 0) 247 | 248 | 249 | class BlinkerRGB(Module, AutoCSR): 250 | def __init__(self, leds, sr, sg, sb, divbits=27): 251 | self.forceblink = CSRStorage() 252 | 253 | # # # 254 | 255 | self.submodules.keepr = BlinkerKeep(sr) 256 | self.submodules.keepg = BlinkerKeep(sg) 257 | self.submodules.keepb = BlinkerKeep(sb) 258 | 259 | counter = Signal(divbits + 2) 260 | self.sync += counter.eq(counter + 1) 261 | 262 | self.comb += [ 263 | If(self.forceblink.storage, 264 | leds.r.eq(~counter[divbits-3]), 265 | leds.g.eq(~counter[divbits-2]), 266 | leds.b.eq(~counter[divbits-1]), 267 | ).Else( 268 | leds.r.eq(~self.keepr.o), 269 | leds.g.eq(~self.keepg.o), 270 | leds.b.eq(~self.keepb.o), 271 | ) 272 | ] 273 | 274 | 275 | class ResetManager(Module, AutoCSR): 276 | def __init__(self, modules): 277 | self.reset = CSR() 278 | 279 | # # # 280 | 281 | for m in modules: 282 | self.sync += m.reset.eq(self.reset.re) 283 | 284 | 285 | class USBSnifferSoC(SoCSDRAM): 286 | csr_peripherals = [ 287 | "flash", 288 | "ddrphy", 289 | "ulpi_phy0", 290 | "ulpi_phy1", 291 | "ulpi_core0", 292 | "ulpi_core1", 293 | "overflow0", 294 | "overflow1", 295 | "ulpi_sw_oe_n", 296 | "ulpi_sw_s", 297 | "iticore0", 298 | "iticore1", 299 | "blinker0", 300 | "blinker1", 301 | "rst_manager", 302 | "analyzer", 303 | ] 304 | csr_map_update(SoCSDRAM.csr_map, csr_peripherals) 305 | 306 | usb_map = { 307 | "wishbone": 0, 308 | "ulpi0": 1, 309 | "ulpi1": 2, 310 | } 311 | 312 | def __init__(self, platform, with_analyzer=False, with_loopback=False): 313 | clk_freq = int(100e6) 314 | SoCSDRAM.__init__(self, platform, clk_freq, 315 | cpu_type=None, 316 | l2_size=32, 317 | csr_data_width=32, csr_address_width=15, # required for flash spi 318 | integrated_rom_size=0, 319 | integrated_sram_size=0x8000, 320 | with_uart=False, 321 | ident="USB2Sniffer design", 322 | with_timer=False 323 | ) 324 | self.submodules.crg = _CRG(platform) 325 | 326 | # flash spi 327 | self.submodules.flash = Flash(platform.request("flash"), div=math.ceil(clk_freq/25e6)) 328 | 329 | # sdram 330 | self.submodules.ddrphy = a7ddrphy.A7DDRPHY(platform.request("ddram")) 331 | sdram_module = MT41K256M16(self.clk_freq, "1:4") 332 | self.register_sdram(self.ddrphy, 333 | sdram_module.geom_settings, 334 | sdram_module.timing_settings) 335 | 336 | # sdram fifo 337 | depth = 32 * 1024 * 1024 338 | self.submodules.dramfifo = ResetInserter()(LiteDRAMFIFO([("data", 32)], depth, 0, 339 | self.sdram.crossbar, preserve_first_last=False)) 340 | 341 | self.submodules.hugefifo = ResetInserter()(stream.SyncFIFO([("data", 32)], 512)) 342 | 343 | # debug wishbone 344 | self.add_cpu(UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=3e6)) 345 | self.add_wb_master(self.cpu.wishbone) 346 | 347 | # usb phy 348 | usb_pads = platform.request("usb_fifo") 349 | self.submodules.usb_phy = FT601Sync(usb_pads, dw=32, timeout=1024) 350 | 351 | if with_loopback: 352 | self.submodules.usb_loopback_fifo = stream.SyncFIFO(phy_description(32), 2048) 353 | self.comb += [ 354 | self.usb_phy.source.connect(self.usb_loopback_fifo.sink), 355 | self.usb_loopback_fifo.source.connect(self.usb_phy.sink) 356 | ] 357 | else: 358 | # usb core 359 | self.submodules.usb_core = USBCore(self.usb_phy, clk_freq) 360 | 361 | # usb <--> wishbone 362 | self.submodules.etherbone = Etherbone(self.usb_core, self.usb_map["wishbone"]) 363 | self.add_wb_master(self.etherbone.master.bus) 364 | 365 | # ulpi switch 366 | ulpi_sw = platform.request("ulpi_sw") 367 | self.submodules.ulpi_sw_oe_n = GPIOOut(ulpi_sw.oe_n) 368 | self.submodules.ulpi_sw_s = GPIOOut(ulpi_sw.s) 369 | 370 | # ulpi 0 371 | self.submodules.ulpi_phy0 = ULPIPHY(platform.request("ulpi", 0), cd="ulpi0") 372 | self.submodules.ulpi_core0 = ULPICore(self.ulpi_phy0) 373 | 374 | # packer0 375 | self.submodules.overflow0 = OverflowMeter(ulpi_cmd_description(8, 1)) 376 | self.submodules.iticore0 = ITICore() 377 | self.submodules.fifo0 = ResetInserter()(stream.SyncFIFO([("data", 40), ("len", 2)], 16)) 378 | self.submodules.conv40320 = Conv4032() 379 | 380 | # ulpi 1 381 | self.submodules.ulpi_phy1 = ULPIPHY(platform.request("ulpi", 1), cd="ulpi1") 382 | self.submodules.ulpi_core1 = ULPICore(self.ulpi_phy1) 383 | 384 | # usb <--> ulpi0 385 | self.submodules.wrapcore0 = WrapCore(self.usb_core, self.usb_map["ulpi0"]) 386 | self.comb += [ 387 | self.ulpi_core0.source.connect(self.overflow0.sink), 388 | self.overflow0.source.connect(self.iticore0.sink), 389 | self.iticore0.source.connect(self.fifo0.sink), 390 | self.fifo0.source.connect(self.conv40320.sink), 391 | self.conv40320.source.connect(self.hugefifo.sink), 392 | self.hugefifo.source.connect(self.dramfifo.sink), 393 | self.dramfifo.source.connect(self.wrapcore0.sink), 394 | ] 395 | 396 | # reset manager 397 | self.rst_manager = ResetManager([self.iticore0, self.fifo0, 398 | self.conv40320, self.hugefifo, 399 | self.dramfifo, self.wrapcore0]) 400 | 401 | # leds 402 | led0 = platform.request("rgb_led", 0) 403 | self.submodules.blinker0 = BlinkerRGB(led0, 404 | self.etherbone.packet.tx.source.valid, 405 | 0, self.etherbone.packet.rx.sink.valid) 406 | 407 | led1 = platform.request("rgb_led", 1) 408 | self.submodules.blinker1 = BlinkerRGB(led1, 409 | self.ulpi_core0.source.valid, 410 | 0, self.wrapcore0.sender.source.valid) 411 | 412 | # timing constraints 413 | self.crg.cd_sys.clk.attr.add("keep") 414 | self.crg.cd_usb.clk.attr.add("keep") 415 | self.platform.add_period_constraint(self.crg.cd_sys.clk, 10.0) 416 | self.platform.add_period_constraint(self.crg.cd_usb.clk, 10.0) 417 | 418 | if with_analyzer: 419 | analyzer_signals = [ 420 | self.ulpi_core0.source.valid, 421 | self.ulpi_core0.source.ready, 422 | self.ulpi_core0.source.data, 423 | self.iticore0.source.valid, 424 | self.iticore0.source.ready, 425 | self.iticore0.source.data, 426 | self.fifo.source.valid, 427 | self.fifo.source.ready, 428 | self.fifo.source.data, 429 | self.wrapcore0.sender.source.valid, 430 | self.wrapcore0.sender.source.ready, 431 | self.wrapcore0.sender.source.data, 432 | ] 433 | self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals, 1024, clock_domain="sys") 434 | 435 | def do_exit(self, vns): 436 | if hasattr(self, "analyzer"): 437 | self.analyzer.export_csv(vns, "test/analyzer.csv") 438 | 439 | def generate_software_header(self): 440 | csr_header = get_csr_header(self.get_csr_regions(), 441 | self.get_constants(), 442 | with_access_functions=True) 443 | tools.write_to_file(os.path.join("software/generated/csr.h"), csr_header) 444 | 445 | phy_header = sdram_init.get_sdram_phy_c_header( 446 | self.sdram.controller.settings.phy, 447 | self.sdram.controller.settings.timing) 448 | tools.write_to_file(os.path.join("software/generated/sdram_phy.h"), phy_header) 449 | 450 | 451 | def main(): 452 | platform = Platform() 453 | soc = USBSnifferSoC(platform, with_loopback=False, with_analyzer=False) 454 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 455 | vns = builder.build() 456 | soc.do_exit(vns) 457 | soc.generate_software_header() 458 | 459 | 460 | if __name__ == "__main__": 461 | main() 462 | -------------------------------------------------------------------------------- /gateware/etherbone.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 / EnjoyDigital / florent@enjoy-digital.fr 2 | from liteeth.common import * 3 | 4 | from litex.soc.interconnect import wishbone 5 | from litex.soc.interconnect.stream_packet import * 6 | 7 | 8 | from gateware.usb import user_description 9 | 10 | 11 | etherbone_magic = 0x4e6f 12 | etherbone_version = 1 13 | etherbone_packet_header_length = 8 14 | etherbone_packet_header_fields = { 15 | "magic": HeaderField(0, 0, 16), 16 | 17 | "version": HeaderField(2, 4, 4), 18 | "nr": HeaderField(2, 2, 1), 19 | "pr": HeaderField(2, 1, 1), 20 | "pf": HeaderField(2, 0, 1), 21 | 22 | "addr_size": HeaderField(3, 4, 4), 23 | "port_size": HeaderField(3, 0, 4) 24 | } 25 | etherbone_packet_header = Header(etherbone_packet_header_fields, 26 | etherbone_packet_header_length, 27 | swap_field_bytes=True) 28 | 29 | etherbone_record_header_length = 4 30 | etherbone_record_header_fields = { 31 | "bca": HeaderField(0, 0, 1), 32 | "rca": HeaderField(0, 1, 1), 33 | "rff": HeaderField(0, 2, 1), 34 | "cyc": HeaderField(0, 4, 1), 35 | "wca": HeaderField(0, 5, 1), 36 | "wff": HeaderField(0, 6, 1), 37 | 38 | "byte_enable": HeaderField(1, 0, 8), 39 | 40 | "wcount": HeaderField(2, 0, 8), 41 | 42 | "rcount": HeaderField(3, 0, 8) 43 | } 44 | etherbone_record_header = Header(etherbone_record_header_fields, 45 | etherbone_record_header_length, 46 | swap_field_bytes=True) 47 | 48 | def _remove_from_layout(layout, *args): 49 | r = [] 50 | for f in layout: 51 | remove = False 52 | for arg in args: 53 | if f[0] == arg: 54 | remove = True 55 | if not remove: 56 | r.append(f) 57 | return r 58 | 59 | def eth_etherbone_packet_description(dw): 60 | param_layout = etherbone_packet_header.get_layout() 61 | payload_layout = [ 62 | ("data", dw), 63 | ("error", dw//8) 64 | ] 65 | return EndpointDescription(payload_layout, param_layout) 66 | 67 | def eth_etherbone_packet_user_description(dw): 68 | param_layout = etherbone_packet_header.get_layout() 69 | param_layout = _remove_from_layout(param_layout, 70 | "magic", 71 | "portsize", 72 | "addrsize", 73 | "version") 74 | param_layout += user_description(dw).param_layout 75 | payload_layout = [ 76 | ("data", dw), 77 | ("error", dw//8) 78 | ] 79 | return EndpointDescription(payload_layout, param_layout) 80 | 81 | def eth_etherbone_record_description(dw): 82 | param_layout = etherbone_record_header.get_layout() 83 | payload_layout = [ 84 | ("data", dw), 85 | ("error", dw//8) 86 | ] 87 | return EndpointDescription(payload_layout, param_layout) 88 | 89 | def eth_etherbone_mmap_description(dw): 90 | param_layout = [ 91 | ("we", 1), 92 | ("count", 8), 93 | ("base_addr", 32), 94 | ("be", dw//8) 95 | ] 96 | payload_layout = [ 97 | ("addr", 32), 98 | ("data", dw) 99 | ] 100 | return EndpointDescription(payload_layout, param_layout) 101 | 102 | 103 | # etherbone packet 104 | 105 | class EtherbonePacketPacketizer(Packetizer): 106 | def __init__(self): 107 | Packetizer.__init__(self, 108 | eth_etherbone_packet_description(32), 109 | user_description(32), 110 | etherbone_packet_header) 111 | 112 | 113 | class EtherbonePacketTX(Module): 114 | def __init__(self, identifier): 115 | self.sink = sink = stream.Endpoint(eth_etherbone_packet_user_description(32)) 116 | self.source = source = stream.Endpoint(user_description(32)) 117 | 118 | # # # 119 | 120 | self.submodules.packetizer = packetizer = EtherbonePacketPacketizer() 121 | self.comb += [ 122 | packetizer.sink.valid.eq(sink.valid), 123 | packetizer.sink.last.eq(sink.last), 124 | sink.ready.eq(packetizer.sink.ready), 125 | 126 | packetizer.sink.magic.eq(etherbone_magic), 127 | packetizer.sink.port_size.eq(32//8), 128 | packetizer.sink.addr_size.eq(32//8), 129 | packetizer.sink.pf.eq(sink.pf), 130 | packetizer.sink.pr.eq(sink.pr), 131 | packetizer.sink.nr.eq(sink.nr), 132 | packetizer.sink.version.eq(etherbone_version), 133 | 134 | packetizer.sink.data.eq(sink.data) 135 | ] 136 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 137 | fsm.act("IDLE", 138 | packetizer.source.ready.eq(1), 139 | If(packetizer.source.valid, 140 | packetizer.source.ready.eq(0), 141 | NextState("SEND") 142 | ) 143 | ) 144 | fsm.act("SEND", 145 | packetizer.source.connect(source), 146 | source.dst.eq(identifier), 147 | source.length.eq(sink.length + etherbone_packet_header.length), 148 | If(source.valid & source.last & source.ready, 149 | NextState("IDLE") 150 | ) 151 | ) 152 | 153 | 154 | class EtherbonePacketDepacketizer(Depacketizer): 155 | def __init__(self): 156 | Depacketizer.__init__(self, 157 | user_description(32), 158 | eth_etherbone_packet_description(32), 159 | etherbone_packet_header) 160 | 161 | 162 | class EtherbonePacketRX(Module): 163 | def __init__(self): 164 | self.sink = sink = stream.Endpoint(user_description(32)) 165 | self.source = source = stream.Endpoint(eth_etherbone_packet_user_description(32)) 166 | 167 | # # # 168 | 169 | self.submodules.depacketizer = depacketizer = EtherbonePacketDepacketizer() 170 | self.comb += sink.connect(depacketizer.sink) 171 | 172 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 173 | fsm.act("IDLE", 174 | depacketizer.source.ready.eq(1), 175 | If(depacketizer.source.valid, 176 | depacketizer.source.ready.eq(0), 177 | NextState("CHECK") 178 | ) 179 | ) 180 | valid = Signal() 181 | self.sync += valid.eq( 182 | depacketizer.source.valid & 183 | (depacketizer.source.magic == etherbone_magic) 184 | ) 185 | fsm.act("CHECK", 186 | If(valid, 187 | NextState("PRESENT") 188 | ).Else( 189 | NextState("DROP") 190 | ) 191 | ) 192 | self.comb += [ 193 | source.last.eq(depacketizer.source.last), 194 | 195 | source.pf.eq(depacketizer.source.pf), 196 | source.pr.eq(depacketizer.source.pr), 197 | source.nr.eq(depacketizer.source.nr), 198 | 199 | source.data.eq(depacketizer.source.data), 200 | 201 | source.length.eq(sink.length - etherbone_packet_header.length) 202 | ] 203 | fsm.act("PRESENT", 204 | source.valid.eq(depacketizer.source.valid), 205 | depacketizer.source.ready.eq(source.ready), 206 | If(source.valid & source.last & source.ready, 207 | NextState("IDLE") 208 | ) 209 | ) 210 | fsm.act("DROP", 211 | depacketizer.source.ready.eq(1), 212 | If(depacketizer.source.valid & 213 | depacketizer.source.last & 214 | depacketizer.source.ready, 215 | NextState("IDLE") 216 | ) 217 | ) 218 | 219 | 220 | class EtherbonePacket(Module): 221 | def __init__(self, usb_core, identifier): 222 | self.submodules.tx = tx = EtherbonePacketTX(identifier) 223 | self.submodules.rx = rx = EtherbonePacketRX() 224 | usb_port = usb_core.crossbar.get_port(identifier) 225 | self.comb += [ 226 | tx.source.connect(usb_port.sink), 227 | usb_port.source.connect(rx.sink) 228 | ] 229 | self.sink, self.source = self.tx.sink, self.rx.source 230 | 231 | 232 | # etherbone probe 233 | 234 | class EtherboneProbe(Module): 235 | def __init__(self): 236 | self.sink = sink = stream.Endpoint(eth_etherbone_packet_user_description(32)) 237 | self.source = source = stream.Endpoint(eth_etherbone_packet_user_description(32)) 238 | 239 | # # # 240 | 241 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 242 | fsm.act("IDLE", 243 | sink.ready.eq(1), 244 | If(sink.valid, 245 | sink.ready.eq(0), 246 | NextState("PROBE_RESPONSE") 247 | ) 248 | ) 249 | fsm.act("PROBE_RESPONSE", 250 | sink.connect(source), 251 | source.pf.eq(0), 252 | source.pr.eq(1), 253 | If(source.valid & source.last & source.ready, 254 | NextState("IDLE") 255 | ) 256 | ) 257 | 258 | # etherbone record 259 | 260 | class EtherboneRecordPacketizer(Packetizer): 261 | def __init__(self): 262 | Packetizer.__init__(self, 263 | eth_etherbone_record_description(32), 264 | eth_etherbone_packet_user_description(32), 265 | etherbone_record_header) 266 | 267 | 268 | class EtherboneRecordDepacketizer(Depacketizer): 269 | def __init__(self): 270 | Depacketizer.__init__(self, 271 | eth_etherbone_packet_user_description(32), 272 | eth_etherbone_record_description(32), 273 | etherbone_record_header) 274 | 275 | 276 | class EtherboneRecordReceiver(Module): 277 | def __init__(self, buffer_depth=256): 278 | self.sink = sink = stream.Endpoint(eth_etherbone_record_description(32)) 279 | self.source = source = stream.Endpoint(eth_etherbone_mmap_description(32)) 280 | 281 | # # # 282 | 283 | # TODO: optimize ressources (no need to store parameters as datas) 284 | fifo = stream.SyncFIFO(eth_etherbone_record_description(32), buffer_depth, 285 | buffered=True) 286 | self.submodules += fifo 287 | self.comb += sink.connect(fifo.sink) 288 | 289 | base_addr = Signal(32) 290 | base_addr_update = Signal() 291 | self.sync += If(base_addr_update, base_addr.eq(fifo.source.data)) 292 | 293 | counter = Signal(max=512) 294 | counter_reset = Signal() 295 | counter_ce = Signal() 296 | self.sync += \ 297 | If(counter_reset, 298 | counter.eq(0) 299 | ).Elif(counter_ce, 300 | counter.eq(counter + 1) 301 | ) 302 | 303 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 304 | fsm.act("IDLE", 305 | fifo.source.ready.eq(1), 306 | counter_reset.eq(1), 307 | If(fifo.source.valid, 308 | base_addr_update.eq(1), 309 | If(fifo.source.wcount, 310 | NextState("RECEIVE_WRITES") 311 | ).Elif(fifo.source.rcount, 312 | NextState("RECEIVE_READS") 313 | ) 314 | ) 315 | ) 316 | fsm.act("RECEIVE_WRITES", 317 | source.valid.eq(fifo.source.valid), 318 | source.last.eq(counter == fifo.source.wcount-1), 319 | source.count.eq(fifo.source.wcount), 320 | source.be.eq(fifo.source.byte_enable), 321 | source.addr.eq(base_addr[2:] + counter), 322 | source.we.eq(1), 323 | source.data.eq(fifo.source.data), 324 | fifo.source.ready.eq(source.ready), 325 | If(source.valid & source.ready, 326 | counter_ce.eq(1), 327 | If(source.last, 328 | If(fifo.source.rcount, 329 | NextState("RECEIVE_BASE_RET_ADDR") 330 | ).Else( 331 | NextState("IDLE") 332 | ) 333 | ) 334 | ) 335 | ) 336 | fsm.act("RECEIVE_BASE_RET_ADDR", 337 | counter_reset.eq(1), 338 | If(fifo.source.valid, 339 | base_addr_update.eq(1), 340 | NextState("RECEIVE_READS") 341 | ) 342 | ) 343 | fsm.act("RECEIVE_READS", 344 | source.valid.eq(fifo.source.valid), 345 | source.last.eq(counter == fifo.source.rcount-1), 346 | source.count.eq(fifo.source.rcount), 347 | source.base_addr.eq(base_addr), 348 | source.addr.eq(fifo.source.data[2:]), 349 | fifo.source.ready.eq(source.ready), 350 | If(source.valid & source.ready, 351 | counter_ce.eq(1), 352 | If(source.last, 353 | NextState("IDLE") 354 | ) 355 | ) 356 | ) 357 | 358 | 359 | class EtherboneRecordSender(Module): 360 | def __init__(self, buffer_depth=256): 361 | self.sink = sink = stream.Endpoint(eth_etherbone_mmap_description(32)) 362 | self.source = source = stream.Endpoint(eth_etherbone_record_description(32)) 363 | 364 | # # # 365 | 366 | # TODO: optimize ressources (no need to store parameters as datas) 367 | pbuffer = stream.SyncFIFO(eth_etherbone_mmap_description(32), buffer_depth) 368 | self.submodules += pbuffer 369 | self.comb += sink.connect(pbuffer.sink) 370 | 371 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 372 | fsm.act("IDLE", 373 | pbuffer.source.ready.eq(1), 374 | If(pbuffer.source.valid, 375 | pbuffer.source.ready.eq(0), 376 | NextState("SEND_BASE_ADDRESS") 377 | ) 378 | ) 379 | self.comb += [ 380 | source.byte_enable.eq(pbuffer.source.be), 381 | If(pbuffer.source.we, 382 | source.wcount.eq(pbuffer.source.count) 383 | ).Else( 384 | source.rcount.eq(pbuffer.source.count) 385 | ) 386 | ] 387 | 388 | fsm.act("SEND_BASE_ADDRESS", 389 | source.valid.eq(pbuffer.source.valid), 390 | source.last.eq(0), 391 | source.data.eq(pbuffer.source.base_addr), 392 | If(source.ready, 393 | NextState("SEND_DATA") 394 | ) 395 | ) 396 | fsm.act("SEND_DATA", 397 | source.valid.eq(pbuffer.source.valid), 398 | source.last.eq(pbuffer.source.last), 399 | source.data.eq(pbuffer.source.data), 400 | If(source.valid & source.ready, 401 | pbuffer.source.ready.eq(1), 402 | If(source.last, 403 | NextState("IDLE") 404 | ) 405 | ) 406 | ) 407 | 408 | 409 | class EtherboneRecord(Module): 410 | # Limitation: For simplicity we only support 1 record per packet 411 | def __init__(self, endianness="big"): 412 | self.sink = sink = stream.Endpoint(eth_etherbone_packet_user_description(32)) 413 | self.source = source = stream.Endpoint(eth_etherbone_packet_user_description(32)) 414 | 415 | # # # 416 | 417 | # receive record, decode it and generate mmap stream 418 | self.submodules.depacketizer = depacketizer = EtherboneRecordDepacketizer() 419 | self.submodules.receiver = receiver = EtherboneRecordReceiver() 420 | self.comb += [ 421 | sink.connect(depacketizer.sink), 422 | depacketizer.source.connect(receiver.sink) 423 | ] 424 | if endianness is "big": 425 | self.comb += receiver.sink.data.eq(reverse_bytes(depacketizer.source.data)) 426 | 427 | # receive mmap stream, encode it and send records 428 | self.submodules.sender = sender = EtherboneRecordSender() 429 | self.submodules.packetizer = packetizer = EtherboneRecordPacketizer() 430 | self.comb += [ 431 | sender.source.connect(packetizer.sink), 432 | packetizer.source.connect(source), 433 | source.length.eq(etherbone_record_header.length + 434 | (sender.source.wcount != 0)*4 + sender.source.wcount*4 + 435 | (sender.source.rcount != 0)*4 + sender.source.rcount*4) 436 | ] 437 | if endianness is "big": 438 | self.comb += packetizer.sink.data.eq(reverse_bytes(sender.source.data)) 439 | 440 | 441 | 442 | # etherbone wishbone 443 | 444 | class EtherboneWishboneMaster(Module): 445 | def __init__(self): 446 | self.sink = sink = stream.Endpoint(eth_etherbone_mmap_description(32)) 447 | self.source = source = stream.Endpoint(eth_etherbone_mmap_description(32)) 448 | self.bus = bus = wishbone.Interface() 449 | 450 | # # # 451 | 452 | data = Signal(32) 453 | data_update = Signal() 454 | self.sync += If(data_update, data.eq(bus.dat_r)) 455 | 456 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 457 | fsm.act("IDLE", 458 | sink.ready.eq(1), 459 | If(sink.valid, 460 | sink.ready.eq(0), 461 | If(sink.we, 462 | NextState("WRITE_DATA") 463 | ).Else( 464 | NextState("READ_DATA") 465 | ) 466 | ) 467 | ) 468 | fsm.act("WRITE_DATA", 469 | bus.adr.eq(sink.addr), 470 | bus.dat_w.eq(sink.data), 471 | bus.sel.eq(sink.be), 472 | bus.stb.eq(sink.valid), 473 | bus.we.eq(1), 474 | bus.cyc.eq(1), 475 | If(bus.stb & bus.ack, 476 | sink.ready.eq(1), 477 | If(sink.last, 478 | NextState("IDLE") 479 | ) 480 | ) 481 | ) 482 | fsm.act("READ_DATA", 483 | bus.adr.eq(sink.addr), 484 | bus.sel.eq(sink.be), 485 | bus.stb.eq(sink.valid), 486 | bus.cyc.eq(1), 487 | If(bus.stb & bus.ack, 488 | data_update.eq(1), 489 | NextState("SEND_DATA") 490 | ) 491 | ) 492 | fsm.act("SEND_DATA", 493 | source.valid.eq(sink.valid), 494 | source.last.eq(sink.last), 495 | source.base_addr.eq(sink.base_addr), 496 | source.addr.eq(sink.addr), 497 | source.count.eq(sink.count), 498 | source.be.eq(sink.be), 499 | source.we.eq(1), 500 | source.data.eq(data), 501 | If(source.valid & source.ready, 502 | sink.ready.eq(1), 503 | If(source.last, 504 | NextState("IDLE") 505 | ).Else( 506 | NextState("READ_DATA") 507 | ) 508 | ) 509 | ) 510 | 511 | 512 | # etherbone 513 | 514 | class Etherbone(Module): 515 | def __init__(self, usb_core, identifier): 516 | # decode/encode etherbone packets 517 | self.submodules.packet = packet = EtherbonePacket(usb_core, identifier) 518 | 519 | # packets can be probe (etherbone discovering) or records with 520 | # writes and reads 521 | self.submodules.probe = probe = EtherboneProbe() 522 | self.submodules.record = record = EtherboneRecord() 523 | 524 | # arbitrate/dispatch probe/records packets 525 | dispatcher = Dispatcher(packet.source, [probe.sink, record.sink]) 526 | self.comb += dispatcher.sel.eq(~packet.source.pf) 527 | arbiter = Arbiter([probe.source, record.source], packet.sink) 528 | self.submodules += dispatcher, arbiter 529 | 530 | # create mmap wishbone master 531 | self.submodules.master = master = EtherboneWishboneMaster() 532 | self.comb += [ 533 | record.receiver.source.connect(master.sink), 534 | master.source.connect(record.sender.sink) 535 | ] 536 | --------------------------------------------------------------------------------