commit 903a3c7b9b19496a3df690e253dee77ecb660e82 Author: WangXuan95 <629708558@qq.com> Date: Sat Jun 3 18:10:27 2023 +0800 add support for byte-enable diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..099dbfb --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ + +FT232H_UniFPGA +FT600_FPGArduino +vivado + +**/__pycache__ diff --git a/FTD2XX_guide.md b/FTD2XX_guide.md new file mode 100644 index 0000000..2ebd9a1 --- /dev/null +++ b/FTD2XX_guide.md @@ -0,0 +1,108 @@ +[English](#en) | [中文](#cn) + +  + +Install FTD2XX Driver and Python ftd2xx Library +==================================== + +To run FT232H related Python programs on Windows, follow these steps: + +> Note: This document was written in 2019. If the official website is updated later, the general process will definitely remain unchanged, but some operational details (for example, the download file cannot be found on the official website) will need to be worked around. + +### Step1: Install FTD2XX Driver and download FTD2XX.dll + +Go to [D2XX Driver website](https://www.ftdichip.com/Drivers/D2XX.htm), in the table in the D2XX Drivers column, download the driver (.exe file) and install it. As shown below. + +Also, to download the DLL, unzip it and find the FTD2XX.dll file that matches your computer. For a 32-bit computer, find the 32-bit(i386) DLL; for a 64-bit computer, find the 64-bit(amd64) DLL. If the file name is FTD2XX64.DLL, etc., please always rename it to FTD2XX.DLL + +![FT232h驱动下载](./figures/ft232h_driver_download.png) + +### Step2: Verify Installation + +Insert the FT232H USB port of the development board into the computer. If the driver is installed successfully, a **USB Serial Converter** should be identified in the Windows Device Manager. As shown below. + +![FT232H被识别](./figures/ft232h_ready.png) + +### Step3: Install Python3 + +If you don't have Python3 installed, please go to [Anaconda website](https://www.anaconda.com/products/individual) to download and install Python3, the major version number must be Python3, not Python2. + +Note: If it is a 32-bit computer, please install 32-bit Python; if it is a 64-bit computer, please install 64-bit Python. + +### Step4: Install ftd2xx Library for Python + +Open CMD or PowerShell, run command: + +```powershell +python -m pip install ftd2xx +``` + +### Step5: Copy FTD2XX.dll File to Python Environment + +Copy the FTD2XX.DLL file we downloaded in step1 to the Python's root directory (for example, on my computer, the Python root directory is **C:/Anaconda3/** ). Note that 32-bit Python must correspond to a 32-bit DLL; 64-bit Python must correspond to a 64-bit DLL. + +Then, you can run the following statement in python to verify its installation: + +```python +import ftd2xx +``` + + +Now the Python runtime environment required by the FT232H is ready. + + +  + +  + +  + +  + +安装 FTD2XX 驱动和 Python FTD2XX 库 +==================================== + +要在 Windows 上运行 FT232H 相关的 Python 程序,请进行以下步骤: + +> :warning: 该文档写于 2019 年,如果之后官网更新,大致流程肯定不变,但一些操作细节(例如官网上找不到下载文件了)就要变通变通了。 + +### 步骤1:安装 FTD2XX 驱动和 FTD2XX.DLL + +进入 [D2XX Driver 官网页面](https://www.ftdichip.com/Drivers/D2XX.htm) ,在 D2XX Drivers 那一栏的表格里,下载exe形式的驱动并安装。如下图。 + +另外,要下载 DLL 压缩包, 解压后在里面找到符合你计算机的 FTD2XX.DLL 文件。若为32位计算机,请找到 32-bit(i386) DLL;若为64位计算机,请找到 64-bit (amd64) DLL。如果文件名是 FTD2XX64.DLL 等,请一律重命名为 FTD2XX.DLL + +![FT232h驱动下载](./figures/ft232h_driver_download.png) + +### 步骤2:验证驱动安装 + +将开发板的 FT232H USB 口插入电脑,如果驱动安装成功,则 Windows 设备管理器里应该识别出 **USB Serial Converter** 。如下图。 + +![FT232H被识别](./figures/ft232h_ready.png) + +### 步骤3:安装 Python3 + +如果你没有安装 Python3, 请前往 [Anaconda官网](https://www.anaconda.com/products/individual) 下载安装 Python3 ,必须是 Python3 ,不能是 Python2 。 + +注:若为32位计算机,请安装32位的Python;若为64位计算机,请安装64位的Python。 + +### 步骤4:安装 python ftd2xx 库 + +打开 CMD 或 PowerShell ,运行命令: + +```powershell +python -m pip install ftd2xx +``` + +### 步骤5:复制 FTD2XX.DLL 文件到 Python 环境中 + +复制 **步骤1** 中我们找到的 FTD2XX.DLL 文件到 Python 根目录(例如在我的电脑上, Python 根目录是 **C:/Anaconda3/** )。注意32位的Python必须对应32位的DLL;64位的Python必须对应64位的DLL。 + +然后,可以在 python 中运行以下语句来验证安装: + +```python +import ftd2xx +``` + + +至此,FT232H 所需的 Python 运行环境已就绪。 diff --git a/FTD3XX_guide.md b/FTD3XX_guide.md new file mode 100644 index 0000000..ae79697 --- /dev/null +++ b/FTD3XX_guide.md @@ -0,0 +1,113 @@ +[English](#en) | [中文](#cn) + +  + +Install FTD3XX Driver and Python ftd3xx Library +==================================== + +To run FT600 related Python programs on Windows, follow these steps: + +> Note: This document was written in 2019. If the official website is updated later, the general process will definitely remain unchanged, but some operational details (for example, the download file cannot be found on the official website) will need to be worked around. + +### Step1: Install FTD3XX Driver and download FTD3XX.dll + +Go to [D3XX Driver webpage](https://www.ftdichip.com/Drivers/D3XX.htm) , in the table in the D3XX Drivers column, download the driver (.exe file) and install it. As shown below. + +Also, to download the DLL, unzip it and find the FTD3XX.dll file that matches your computer. For a 32-bit computer, find the 32-bit(i386) DLL; for a 64-bit computer, find the 64-bit(amd64) DLL. If the file name is FTD3XX64.DLL, etc., please always rename it to FTD3XX.DLL + +![FT600驱动下载](./figures/ft600_driver_download.png) + + + +### Step2: Verify Installation + +Insert the FT600 USB port of the development board into the computer. If the driver is installed successfully, a **FTDI FT600 USB 3.0 Bridge Device** should be identified in the Windows Device Manager. As shown below. + +![FT600被识别](./figures/ft600_ready.png) + +### Step3: Install Python3 + +If you don't have Python3 installed, please go to [Anaconda official website](https://www.anaconda.com/products/individual) to download and install Python3, the major version number must be Python3, not Python2. + +Note: If it is a 32-bit computer, please install 32-bit Python; if it is a 64-bit computer, please install 64-bit Python. + +### Step4: Install ftd3xx Library for Python + +Open CMD or PowerShell, run command: + +```powershell +python -m pip install ftd3xx +``` + +~~ftd3xx library doesn't seem to be able to be installed with the pip install command. Please open the http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm webpage, there is Python support at the bottom.Download and unzip it, find **setup.py** in it, and run the CMD command pytho n setup.py install to install it.~~ + +### Step5: Copy FTD3XX.dll File to Python Environment + +Copy the FTD2XX.DLL file we downloaded in step1 to the Python's root directory (for example, on my computer, the Python root directory is **C:/Anaconda3/** ). Note that 32-bit Python must correspond to a 32-bit DLL; 64-bit Python must correspond to a 64-bit DLL. + +Then, you can run the following statement in python to verify its installation: + +```python +import ftd3xx +``` + +Now the Python runtime environment required by the FT600 is ready. + +  + +  + +  + +  + +安装 FTD3XX 驱动和 Python FTD3XX 库 +==================================== + +要在 Windows 上运行 FT600 相关的 Python 程序,请进行以下步骤: + +> 注:该文档写于 2019 年,如果之后官网更新,大致流程肯定不变,但一些操作细节(例如官网上找不到下载文件了)就要变通变通了。 + +### 步骤1:准备 D3XX 驱动 和 FTD3XX.DLL + +进入 [D3XX Driver 官网页面](https://www.ftdichip.com/Drivers/D3XX.htm) ,在 D3XX Drivers 那一栏的表格里。下载exe形式的驱动并安装。如下图。 + +另外,要下载 DLL 压缩包, 解压后在里面找到符合你计算机的 FTD3XX.DLL 文件。若为32位计算机,请找到 32-bit(i386) DLL;若为64位计算机,请找到 64-bit (amd64) DLL。如果文件名是 FTD3XX64.DLL 等,请一律重命名为 FTD3XX.DLL + +![FT600驱动下载](./figures/ft600_driver_download.png) + +  + +### 步骤2:验证驱动安装 + +将开发板的 FT600 USB 口插入电脑,如果驱动安装成功,则 Windows 设备管理器里应该识别出 **FTDI FT600 USB 3.0 Bridge Device** 。 + +![FT600被识别](./figures/ft600_ready.png) + +### 步骤3:安装 python3 + +如果你没有安装 Python3, 请前往 [Anaconda官网](https://www.anaconda.com/products/individual) 下载安装 Python3 ,必须是 Python3 ,而不是 Python2。 + +注:若为32位计算机,请安装32位的Python;若为64位计算机,请安装64位的Python。 + +### 步骤4:安装 Python ftd3xx 库 + +打开 CMD 或 Powershell ,运行: + +``` +python -m pip install ftd3xx +``` + +~~ftd3xx似乎无法用pip install命令来安装。请打开http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm网页, 网页最下方有 Python 的支持。下载后解压,在里面找到setup.py,在本目录中运行 CMD 命令python setup.py install来安装。~~ + +### 步骤5:复制 FTD3XX.DLL 文件到 Python 环境中 + +复制步骤1中我们找到的 FTD3XX.DLL 文件到 Python 根目录(例如在我的电脑上, Python 根目录是 **C:/Anaconda3/** )。注意32位的Python必须对应32位的DLL;64位的Python必须对应64位的DLL。 + +然后,可以在 python 中运行以下语句来验证安装: + +```python +import ftd3xx +``` + +至此,FT600 所需的 Python 运行环境已就绪。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..314afa9 --- /dev/null +++ b/README.md @@ -0,0 +1,998 @@ +![语言](https://img.shields.io/badge/语言-verilog_(IEEE1364_2001)-9A90FD.svg) ![仿真](https://img.shields.io/badge/仿真-iverilog-green.svg) ![部署](https://img.shields.io/badge/部署-quartus-blue.svg) ![部署](https://img.shields.io/badge/部署-vivado-FF1010.svg) + +[English](#en) | [中文](#cn) + +  + +FTDI 245fifo controller +=========================== + +An FPGA-based controller for [FT232H](https://ftdichip.com/Products/ICs/FT232H.htm) , [FT2232H](https://ftdichip.com/Products/ICs/FT2232H.htm) , [FT600](https://ftdichip.com/Products/ICs/FT600.html) , [FT601](https://ftdichip.com/Products/ICs/FT600.html) chips in sync-245-fifo mode, which can realize fast USB communication between FPGA and Host-PC. + +> Update on June 2023: Add support for byte enable signals. Now it supports all features of FTDI sync-245-fifo mode, and the length of the sending and receiving bytes no longer needs to be a multiple of FTDI chip's byte-width. + +  + +# Introduction + +**sync-245-fifo mode** is the highest speed transmission mode of FTDI's USB series chips. This repository implement a sync-245-fifo mode controller Verilog IP core (**ftdi_245fifo_top**), providing AXI-stream send and receive interfaces for Verilog developers. + +In addition, this repository provides supplementary: +* Simulation code to verify **ftdi_245fifo_top** +* FPGA sample code as a demo of using **ftdi_245fifo_top** + +For Host-PC, I provide: + +* FTDI driver installation tutorials, +* Python3 FTDI USB software library (ftd2xx and ftd3xx) installation tutorials, +* Several Python3 programs for testing. + +> Note: Although the example code of Host-PC is written in Python (I used to use Python), you can also write program in other languages (C++, C #, etc.) to communicate with the FTDI chip. The design on FPGA is not limited to which programming language you use on Host-PC. + +  + +| ![module_structure](./figures/structure.png) | +| :------------------------------------------: | +| **Figure1** : diagram of ftdi_245fifo_top.v | + +  + +## IP's Technical Features + +* Pure Verilog design, which is easy to transplant to FPGAs of Altera, Xilinx, or other manufacturers. +* Standard AXI-stream interface for sending and receiving. +* **Send/Receive scheduling**: The interface between FTDI USB chip and FPGA is half-duplex. This module schedules the sending and receiving using time-division multiplexing to achieve **independent sending and receiving**. +* **Clock Domain Crossing**: The FTDI USB chip has its own clock. This module uses asynchronous FIFO to realize clock domain crossing, so that the send and receive AXI-stream interface can use **custom clocks** . +* **Bit width conversion**: This module realizes the bit width conversion so that the **bit width of the AXI-stream can be customized** . + +  + +## Performance Test Results + +| | FT232H / FT2232H | FT600 | FT601 | +| :-------------------------: | :--------------: | :-------: | :----------: | +| USB generation | USB2.0 HS | USB3.0 SS | USB3.0 SS | +| Theoretical Throughput | 60MB/s | 200MB/s | 400MBps | +| Tested Throughput (FPGA→PC) | 42MB/s | 140MB/s | not test yet | +| Tested Throughput (PC→FPGA) | 35MB/s | 175MB/s | not test yet | + +  + +  + +# ftdi_245fifo Module Manual + +The top design code is in [ftdi_245fifo_top.v](./RTL/ftdi_245fifo/ftdi_245fifo_top.v) in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) folder, which can be called by FPGA developers to develop their own USB communication applications. Its interface and parameters are shown in **Figure2**. + +| ![module_ports](./figures/ports.png) | +| :----------------------------------------: | +| **Figure2** : interfaces of ftdi_245fifo.v | + +  + +## Module Parameters + +To use this module, you should first determine the value of each Verilog parameter, as shown in the following table: + +| parameter | Introduction | +| ----------- | ------------------------------------------------------------ | +| `TX_EW` | Determines the data width of the AXI-stream sending interface (that is, the width of `tx_data`): 0 corresponds to 8bit width, 1 corresponds to 16bit width, 2 corresponds to 32bit width, and so on. It can be set arbitrarily according to your requirement and is not limited by the FTDI USB chip you used. | +| `TX_EA` | Determines the depth of send buffer, depth=2^TX_EA. The default is 10 (that is, the default depth is 1024). If the FPGA BRAM is larger, it can be set larger to improve burst performance. | +| `RX_EW` | Determines the data width of the AXI-stream receiving interface (that is, the width of `rx_data`): 0 corresponds to 8bit width, 1 corresponds to 16bit width, 2 corresponds to 32bit width, and so on. It can be set arbitrarily according to your requirement and is not limited by the FTDI USB chip you used. | +| `RX_EA` | Determines the depth of receive buffer, depth=2^RX_EA. The default is 10 (that is, the default depth is 1024). If the FPGA BRAM is larger, this item can be set larger to improve burst performance. | +| `CHIP_TYPE` | should be `"FTx232H"` , `"FT600"` , or `"FT601"` according to the FTDI chip you used | + +  + +## Module Interface: reset + +The reset signal of ftdi_245fifo_top.v is: + +```verilog +input wire rstn_async; +``` + +`rstn_async` is the global asynchronous reset of the module, 0 means reset, 1 means release reset. This signal can be directly set to `1'b1`, or connected to the global reset signal. + +  + +## Module Interface: Connect to FTDI Chip + +The FTDI chip's signals of ftdi_245fifo_top.v are: + +```verilog +// Connect to FTDI chip ------------------------------ +input wire ftdi_clk; +input wire ftdi_rxf_n; +input wire ftdi_txe_n; +output wire ftdi_oe_n; +output wire ftdi_rd_n; +output wire ftdi_wr_n; +inout [(8< :warning: FTDI chips can only send and receive pure data streams without the concept of data packets, so `tx_tlast` is only used to control whether to send immediately. After the data is sent to the computer, the software cannot know where is the boundary that `tx_tlast` specified. + +  + +## Module Interface: AXI-stream receiving interface + +The receiving interface is a standard AXI-stream master, which allows users to receive data from FTDI chip (from Host-PC). Corresponding definations: + +```verilog +// AXI-stream master receiving interface ------------------------------ +input wire rx_clk; // AXI-stream clock +input wire rx_tready; +output wire rx_tvalid; +output wire [(8< :warning: FTDI chips can only send and receive pure data streams without the concept of data packets, so `rx_tlast` is only used to indicate whether the current data is the last data taken out of the FTDI chip's buffer. The software on the Host-PC cannot control `rx_tlast` signal. For example, if you call `usb.send()` function twice in the software, the two blocks of data may be merged after being sent to the FPGA, and there may not be a `rx_tlast=1` boundary between them. + +  + +## Further explanation for tvalid&tready handshake + +Take the following waveform as an example, in this waveform, the user interface sends 3 data: D1, D2, D3. + +* In the 1st and 2nd cycles, the user sets `tx_tvalid=0` , so it is temporarily idle and no data is sent. + +* In the 3rd cycle, the user wants to send D1, so it set `tx_tvalid=1`, this cycle `tx_tready=1`, indicating that D1 is sent to buffer successfully. + +* In the 4th, 5th, 6th, and 7th cycles, the user wants to send D2, so it set `tx_tvalid=1` , but the 4th, 5th, 6th cycles `tx_ready=0` cause the transmission to temporarily fail until the 7th cycle `tx_tready=1 ` is sent successfully. + +* In the 8th and 9th cycles, the user sets `tx_tvalid=0`, so it is temporarily idle and no data is sent. + +* In the 10th cycle, the user wants to send D3, so it set `tx_tvalid=1`, this cycle `tx_tready=1`, indicating that D3 is sent successfully. + +``` +cycle 1 2 3 4 5 6 7 8 9 10 11 + _ __ __ __ __ __ __ __ __ __ __ __ + clk \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \ + _____________________________ _____ +tx_tvalid ___________/ \___________/ \________ + _________________ ________________________________ +tx_tready \_________________/ + _____ _______________________ _____ +tx_tdata/tx_tkeep/tx_tlast XXXXXXXXXXXX__D1_X___________D2__________XXXXXXXXXXXXX__D3_XXXXXXXXX +``` + +  + +  + +Simulation +============================= + +Simulation related files are in the [SIM](./SIM) folder, where: + +- tb_ftdi_chip_model.v simulates a simple behavior of FTDI chip. +- tb_ftdi_245fifo.v is the top module for simulation, which connect tb_ftdi_chip_model.v to ftdi_ft245fifo_top (design under test). +- tb_ftdi_245fifo_run_iverilog.bat is the command script to run the iverilog simulation (for Windows). + +The behavior of this simulation is that the FTDI chip continuously sends increasing bytes to the FPGA. The FPGA connects the AXI-stream sending interface and receiving interface loopback. Therefore, the data will be sent back to the FTDI chip, and the FTDI chip will receive the increasing bytes. + +## Use iverilog to simulate + +Before using iverilog for simulation, you need to install iverilog , see: [iverilog_usage](https://github.com/WangXuan95/WangXuan95/blob/main/iverilog_usage/iverilog_usage.md) + +Then double-click tb_ftdi_245fifo_run_iverilog.bat to run simulation (only for Windows), and then you can open the generated dump.vcd file to view the waveform. + +## Use other simulators + +You need to add all .v files in [SIM](./SIM) folder and [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) folder into the simulation project. And set tb_ftdi_245fifo.v as the top. And then run simulation. + +  + +  + +# Getting started with FT232H + +FT232H is a USB2.0 High Speed communication chip with a theoretical rate of 60MBps. In this demo, the FPGA communicates with Host-PC through FT232H. + +> :warning: This demo also applies to FT2232H (you need to work around it yourself), because FT2232H is highly similar to FT232H. Note that FT2232H's **channel-A** can be configured to **sync-245-fifo mode** , but **channel-B** cannot. +> + +## Step1: Install FTD2XX Driver and Library + +Please follow the instructions in [FTD2XX_guide.md](./FTD2XX_guide.md) to install the FTD2XX driver and Python FTD2XX library on your Host-PC (Windows system). + +## Step2: Deploy FPGA Project + +Following is the hierarchical structure of the FPGA project: + +- **fpga_top_ft232h_loopback.v** (in [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) ) , is the top of the whole project + - **clock_beat.v** (in [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) ) + - **ftdi_245fifo_top.v** (in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) ) , is the top of IP + - Other .v files in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) , they will be called by **ftdi_245fifo_top** + +Please add above-mentioned files to FPGA project + +Then, please make pin constraints in the FPGA project. Specifically, you need to check the schematic of your FPGA board (or the supporting materials of the development board) to understand which FT232H pin should connected to which FPGA pin; then, constraint the signals of the top layer of the FPGA project to the corresponding FPGA pin. The correspondence is shown in the following table (this table is simplified from [FT232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf) page 9): + +| FT232H pin | FT232H pin name | FT232H pin name (in 245-fifo mode) | signal name in top module | +| :--------: | :-------------: | :--------------------------------: | :-----------------------: | +| 13 | ADBUS0 | D0 | ftdi_data[0] | +| 14 | ADBUS1 | D1 | ftdi_data[1] | +| 15 | ADBUS2 | D2 | ftdi_data[2] | +| 16 | ADBUS3 | D3 | ftdi_data[3] | +| 17 | ADBUS4 | D4 | ftdi_data[4] | +| 18 | ADBUS5 | D5 | ftdi_data[5] | +| 19 | ADBUS6 | D6 | ftdi_data[6] | +| 20 | ADBUS7 | D7 | ftdi_data[7] | +| 21 | ACBUS0 | RXF\# | ftdi_rxf_n | +| 25 | ACBUS1 | TXE\# | ftdi_txe_n | +| 26 | ACBUS2 | RD\# | ftdi_rd_n | +| 27 | ACBUS3 | WR\# | ftdi_wr_n | +| 28 | ACBUS4 | SIWU\# | ftdi_siwu (=1) :warning: | +| 29 | ACBUS5 | CLKOUT | ftdi_clk | +| 30 | ACBUS6 | OE\# | ftdi_oe_n | +| 31 | ACBUS7 | PWRSAV\# | ftdi_pwrsav (=1) :warning: | +| 34 | RESET\# | RESET\# | ftdi_resetn (=1) :warning: | + +> :warning: SIWU\#, PWRSAV\# and RESET\# in the above table are actually always pulled to high. Some boards directly use resistors to pull-up them without connecting them to the FPGA, in this case, you don't need to constrain these pins and simply comment out the statements related to them in fpga_top_example_ft232h.v. + +  + +If you want to design the FT232H PCB by yourself, please refer to **Figure3**. + +| ![ft232h_example_sch](./figures/ft232h_example_sch.png) | +| :----------------------------------------------------------: | +| **Figure3** : Example schematic of FT232H USB interface circuit. | + +  + +Notations of the clock and timing constraints: + +- There are two clocks in this project: the main clock (clk) and the clock from the FT232H chip (ftdi_clk). +- For FT232H, ftdi_clk frequency is 60MHz. +- There's no special limitations on the frequency of clk. which can be from a few MHz to a few hundred MHz. The simplest way is to directly use the oscillator of FPGA board. +- `constraint_ft232h.sdc` shows an example of timing constraints, which constrain: + - Frequency of ftdi_clk (60MHz) + - Frequency of clk (you need to constrain it to the actual frequency) + - Specify that ftdi_clk and clk are asynchronous. + +- `constraint_ft232h.sdc` is directly applicable to Altera Quartus projects. For Xilinx's xdc, the constraint statements should be similar (there may be some slight differences, please modify them yourself) + +- If you are lazy to make timing constraints, you can also leave them unconstrained, it is probably no problem. + +  + +Other Notations: + +- LED[3:0] connects 4 LEDs (not necessary) , where: + - if ftdi_clk runs, LED[3] will blink. Usually ftdi_clk will start to run when first time of communication rather than when power on. + - LED[2:0] shows the low 3 bits of the last-received data. +- parameter `CHIP_TYPE` of ftdi_245fifo_top is set to "FTx232H", because we are using FT232H / FT2232H chip. + +  + +## Step3: Program FT232H chip + +For each FT232H chip, it needs to be programmed to 245-fifo mode when it is used for the first time. Each FT232H chip only needs to be programmed once, because there is an EEPROM chip on the periphery of the FT232H to permanently save the configuration, and it does not need to be programmed again every time it is used (unless you program it to another mode). + +First go to the [FTDI download page](https://ftdichip.com/utilities/#ft_prog) to download and install **FT\_Prog** software. Then do the following steps: + +* Plug the USB of FT232H to the computer, and unplug all other FTDI USB chip or device (including FPGA downloaders, because some FPGA downloaders, such as Xilinx Digilent downloaders, are implemented using FT2232H chip, if the program inside the downloader is overwritten, your downloader will be broken). +* Open **FT\_Prog** software. +* As shown in **Figure4**, click **Scan and Parse** (the small magnifying glass) to scan all FTDI chips plugged into the computer and find the corresponding chip of FT232H. +* In the property tree below FT232H, expand level by level, find and click "Hardware" . +* Select 245 FIFO mode on the right. +* Click **Program** (the small lightning) in the upper toolbar. +* A confirmation window pops up, click Program. Program to FT232H. +* After programming, you need to re-plug the USB to let this configuration takes effect. + +| ![ft232h_program](./figures/ft232h_program.png) | +| :-------------------------------------------------: | +| **Figure4** : Program FT232H chip to 245-fifo mode. | + +  + +## Step4: Run Programs on Host-PC + +Before running these programs, make sure that FT232H has been programmed into 245-fifo mode according to step3, and the FPGA project created in step2 has been programed to FPGA. + +I provide the following Python programs in [python](./python) folder that will communicate with FPGA through FT232H. + +| File Name | Function | +| ----------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | Defines the USB_FTX232H_FT60X_sync245mode class, implements constructor, close, send, recv methods. This class is universal for FT232H, FT2232H, FT600, and FT601. | +| usb_loopback_simple.py | The Host-PC firstly sends 16 bytes to FPGA and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these 16 bytes. This program is associated with the FPGA top design fpga_top_ft232h_loopback.v | +| usb_loopback_mass.py | The Host-PC firstly sends a large amount of data to FPGA, and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these bytes and the program will compare whether the received data is consistent with the previously sent data to verify the correctness of the loopback. This program is associated with the FPGA top design fpga_top_ft232h_loopback.v | + +  + +  + +# Getting Started with FT600 + +The FT600 is a USB3.0 Super Speed communication chip with a theoretical rate of 200MBps. In this example, the FPGA communicates with the Host-PC through FT600. + +## Step1: Install FTD3XX Driver and Library + +Please follow the instructions in [FTD3XX_guide.md](./FTD3XX_guide.md) to install the FTD3XX driver and Python FTD3XX library on your Host-PC (Windows system). + +## Step2: Deploy FPGA Project + +Following is the hierarchical structure of the FPGA project: + +- **fpga_top_ft600_loopback.v** (in [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) ) , is the top of the whole project + - **clock_beat.v** (in [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) ) + - **ftdi_245fifo_top.v** (in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) ) , is the top of IP + - Other .v files in [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) , they will be called by **ftdi_245fifo_top** + +Please add above-mentioned files to FPGA project. + +Then, please make pin constraints in the FPGA project. Specifically, you need to check the schematic of your FPGA board (or the supporting materials of the development board) to understand which FT600 pin should connected to which FPGA pin; then, constraint the signals of the top layer of the FPGA project (that is, fpga_top_example_ft600.v) to the corresponding FPGA pin. The correspondence is shown in the following table (this table is simplified from [FT600 DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf) page 7-10): + +| FT600 pin number | FT600 pin name | signal name in top module | +| :--------------: | :------------: | :----------------------------------------: | +| 33 | DATA_0 | ftdi_data[0] | +| 34 | DATA_1 | ftdi_data[1] | +| 35 | DATA_2 | ftdi_data[2] | +| 36 | DATA_3 | ftdi_data[3] | +| 39 | DATA_4 | ftdi_data[4] | +| 40 | DATA_5 | ftdi_data[5] | +| 41 | DATA_6 | ftdi_data[6] | +| 42 | DATA_7 | ftdi_data[7] | +| 45 | DATA_8 | ftdi_data[8] | +| 46 | DATA_9 | ftdi_data[9] | +| 47 | DATA_10 | ftdi_data[10] | +| 48 | DATA_11 | ftdi_data[11] | +| 53 | DATA_12 | ftdi_data[12] | +| 54 | DATA_13 | ftdi_data[13] | +| 55 | DATA_14 | ftdi_data[14] | +| 56 | DATA_15 | ftdi_data[15] | +| 2 | BE_0 | ftdi_be[0] | +| 3 | BE_1 | ftdi_be[1] | +| 43 | CLKOUT | ftdi_clk | +| 5 | RXF_N | ftdi_rxf | +| 4 | TXE_N | ftdi_txe | +| 9 | OE_N | ftdi_oe | +| 8 | RD_N | ftdi_rd | +| 7 | WR_N | ftdi_wr | +| 6 | SIWU_N | ftdi_siwu (=1) :warning: | +| 10 | RESET_N | ftdi_resetn (=1) :warning: | +| 11 | WAKEUP_N | ftdi_wakeupn (=0) :triangular_flag_on_post: | +| 12 | GPIO0 | ftdi_gpio0 (=0) :triangular_flag_on_post: | +| 13 | GPIO1 | ftdi_gpio1 (=0) :triangular_flag_on_post: | + +> :warning: SIWU\_N and RESET\_N in the above table are actually always pulled to high. Some boards directly use resistors to pull-up them without connecting them to the FPGA, in this case, you don't need to constrain these pins and simply comment out the statements related to them in fpga_top_example_ft600.v. +> +> :triangular_flag_on_post: WAKEUP\_N, GPIO0 and GPIO1 in the above table are actually always pulled to GND. Some boards directly use resistors to pull-down them without connecting them to the FPGA, in this case, you don't need to constrain these pins and simply comment out the statements related to them in fpga_top_example_ft600.v. + +If you want to draw FT600 PCB by yourself, please refer to **Figure5**. + +| ![ft600_example_sch](./figures/ft600_example_sch.png) | +| :----------------------------------------------------------: | +| **Figure3** : Example schematic of FT600 USB interface circuit. | + +  + +Notations of the clock and timing constraints: + +- There are two clocks in this project: the main clock (clk) and the clock from the FT600 chip (ftdi_clk). +- For FT600, ftdi_clk frequency is 100MHz. +- There's no special limitations on the frequency of clk. which can be from a few MHz to a few hundred MHz. The simplest way is to directly use the oscillator of FPGA board. +- `constraint_ft600.sdc` shows an example of timing constraints, which constrain: + - Frequency of ftdi_clk (100MHz) + - Frequency of clk (you need to constrain it to the actual frequency) + - Specify that ftdi_clk and clk are asynchronous. + +- `constraint_ft600.sdc` is directly applicable to Altera Quartus projects. For Xilinx's xdc, the constraint statements should be similar (there may be some slight differences, please modify them yourself) + +- If you are lazy to make timing constraints, you can also leave them unconstrained, it is probably no problem. + +  + +Other Notations: + +- LED[3:0] connects 4 LEDs (not necessary) , where: + - if ftdi_clk runs, LED[3] will blink. Usually ftdi_clk will start to run when first time of communication rather than when power on. + - LED[2:0] shows the low 3 bits of the last-received data. +- parameter `CHIP_TYPE` of ftdi_245fifo_top is set to "FT600", because we are using FT600 chip. + +  + +## Step3: Run Programs on Host-PC + +Before running these programs, make sure that the FPGA project created in step2 has been programed to FPGA. + +I provide the following Python programs in [python](./python) folder that will communicate with FPGA through FT600. + +| File Name | Function | +| ---------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | Defines the USB_FTX232H_FT60X_sync245mode class, implements constructor, close, send, recv methods. This class is universal for FT232H, FT2232H, FT600, and FT601. | +| usb_loopback_simple.py | The Host-PC firstly sends 16 bytes to FPGA and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these 16 bytes. This program is associated with the FPGA top design fpga_top_ft600_loopback.v | +| usb_loopback_mass.py | The Host-PC firstly sends a large amount of data to FPGA, and then receives data. Because the FPGA project loopbacks data, the Host-PC will receive these bytes and the program will compare whether the received data is consistent with the previously sent data to verify the correctness of the loopback. This program is associated with the FPGA top design fpga_top_ft600_loopback.v | + +  + +# Reference + +* FT232H chip:http://www.ftdichip.cn/Products/ICs/FT232H.htm +* FT232H software example:http://www.ftdichip.cn/Support/SoftwareExamples/CodeExamples.htm +* FT600 chip:http://www.ftdichip.cn/Products/ICs/FT600.html +* FT600/FT601 software example: http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm + + +  + +  + +  + +  + + +FTDI 245fifo controller +=========================== + +[FT232H](https://ftdichip.com/Products/ICs/FT232H.htm)、[FT2232H](https://ftdichip.com/Products/ICs/FT2232H.htm)、[FT600](https://ftdichip.com/Products/ICs/FT600.html) 、 [FT601](https://ftdichip.com/Products/ICs/FT600.html) 等芯片在 sync-245-fifo 模式下的控制器,实现 FPGA 与上位机 (电脑) 的高速通信。 + +> 2023/6 更新内容:加入 byte enable 信号的支持。现在已支持 FTDI sync-245-fifo 模式的所有功能,收发字节长度不再必须是 FTDI 芯片位宽的倍数。 + +  + +# 简介 + +**sync-245-fifo 模式** 是 FTDI 公司的 USB 系列芯片的最高速的传输模式。本库将 sync-245-fifo 控制器封装成 Verilog 模块 (IP顶层模块名为 **ftdi_245fifo_top** ),它具有标准 AXI-stream 收发接口 ,供 Verilog 开发者调用。 + +除了 IP 核本身,本库还提供配套的: + +* 仿真代码 (Verilog) +* IP 核调用示例 (Verilog),实现了一些简单的通信业务逻辑:回环、发送指定长度数据包等。供开发者直接上板测试。 + +对于电脑 (上位机) ,我提供了: +* 驱动安装教程 +* Python3 FTDI USB 软件库 (ftd2xx 和 ftd3xx) 的安装教程 +* 几个 Python3 程序用于进行 USB 通信测试 + + +> 注:虽然上位机的示例代码是用 Python 编写的 (我习惯用 Python) ,但你也可以用其它语言 (C++, C# 等) 编写上位机程序与 FTDI 芯片通信。FPGA 上的设计**并不局限你在上位机上使用哪种编程语言**。 + +  + +**图1**是该系统工作时的结构框图。 + +| ![module_structure](./figures/structure.png) | +| :------------------------------------------: | +| **图1**:系统框图 | + +  + +## 特点 + +* **纯 Verilog 编写**,方便仿真,支持 Altera, Xilinx 等各厂商的 FPGA 。 +* **标准 AXI-stream 接口**:用户通过两路 AXI-stream 收发数据 (一路发,一路收)。 +* **收发调度**:FTDI USB 芯片与 FPGA 之间的接口是半双工的,该模块调度收发分时复用,在用户看来,收发的 AXI-stream 是独立的。 +* **跨时钟域**:FTDI USB 芯片有自己的时钟 (ftdi_clk),该时钟并不一直运行,不能作为 FPGA 的主时钟。该模块实现了时钟域转换。 +* **位宽变换**:本模块实现了位宽变换。使得 AXI-stream 接口的 **位宽可自定义** 。 + +  + +## 性能测试结果 + +| 芯片型号 | FT232H / FT2232H | FT600 | FT601 | +| :--------: | :------------: | :------: | :--------: | +| USB模式 | USB2.0 HS | USB3.0 SS | USB3.0 SS | +| 理论速率 | 60MB/s | 200MB/s | 400MB/s | +| 实测速率 (FPGA→上位机) | 42MB/s | 140MB/s | 未测 | +| 实测速率 (上位机→FPGA) | 35MB/s | 175MB/s | 未测 | + +  + +  + +# ftdi_245fifo_top 模块说明 + +Verilog 设计代码见 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录中的 [ftdi_245fifo_top.v](./RTL/ftdi_245fifo/ftdi_245fifo_top.v) ,它是IP的顶层,可供 FPGA 开发者调用来开发自己的 USB 通信业务。它的接口和参数如**图2**。 + +| ![module_ports](./figures/ports.png) | +| :----------------------------------: | +| **图2**:ftdi_245fifo_top.v 的接口 | + +  + +## 模块参数 + +要调用本模块,首先要根据实际情况指定各个参数 (parameter) 的值,如下表: + +| parameter | 说明 | +| ----------- | ------------------------------------------------------------ | +| `TX_EW` | 决定了用户发送接口的数据宽度 (即 `tx_tdata` 的宽度) : 0对应8bit宽,1对应16bit宽,2对应32bit宽,3对应64bit宽,…… 可以根据需要任意设置,不受 USB 芯片型号限制。 | +| `TX_EA` | 决定了用户发送缓存的深度,深度=2^`TX_EA`。默认为10 (深度为1024) ,如果 FPGA BRAM 较大,该项可以设得更大,来提高突发性能。 | +| `RX_EW` | 决定了用户接收接口的数据宽度 (即 `rx_tdata` 的宽度) : 0对应8bit宽,1对应16bit宽,2对应32bit宽,3对应64bit宽,…… 可以根据需要任意设置,不受 USB 芯片型号限制。 | +| `RX_EA` | 决定了用户接收缓存的深度,深度=2^RX_AEXP。默认为10(即默认深度为1024),如果 FPGA BRAM 较大,该项可以设得更大,来提高突发性能。 | +| `CHIP_TYPE` | 必须是 `"FTx232H"` , `"FT600"` , 或 `"FT601"` | + +  + +## 模块接口:复位 + +复位信号见 ftdi_245fifo_top.v 代码中的: + +```verilog +input wire rstn_async; +``` + +`rstn_async` 是模块的全局异步复位信号,`1'b0` 代表复位,`1'b1` 代表释放复位。在使用中,该信号可以直接设为 `1'b1` ,或者连接到工程的复位信号上。 + +  + +## 模块接口:连接 FTDI 芯片 + +FTDI 芯片信号见 ftdi_245fifo_top.v 代码中的: + +```verilog +// 连接 FTDI 芯片的信号 ------------------------------ +input wire ftdi_clk; +input wire ftdi_rxf_n; +input wire ftdi_txe_n; +output wire ftdi_oe_n; +output wire ftdi_rd_n; +output wire ftdi_wr_n; +inout [(8< :warning: FTDI 芯片只能收发单纯的数据流,而没有数据包的概念,因此 `tx_tlast` 仅仅用于控制是否立即发送。数据发到电脑上后,上位机的软件无法知道哪里是 `tx_last` 指定的包的分界点。 + +  + +## 模块接口:AXI-stream master 接收接口 + +接收接口是标准 AXI-stream master ,用户通过它接收来自 FTDI 芯片 (来自电脑) 的数据。对应代码中的: + +```verilog +// AXI-stream master 接收接口 ------------------------------ +input wire rx_clk; // AXI-stream 时钟 +input wire rx_tready; +output wire rx_tvalid; +output wire [(8< :warning: FTDI 芯片只能收发单纯的数据流,而没有数据包的概念,因此 `rx_tlast` 仅仅用于指示当前数据是不是 FTDI 芯片缓存里拿出的最后一个数据。上位机的软件上无法控制 `rx_tlast` 信号。例如,如果你在软件中调用 usb.send() 函数两次,发送了两次数据,发到 FPGA 上后这两次数据可能连在一起,中间不一定有 `rx_tlast=1` 分界。 + +  + +## 关于 AXI-stream tvalid&tready 的进一步说明 + + `tready` 和 `tvalid` 的握手举例如下波形图,用户接口发送了3个数据:D1, D2, D3 。其中: + +* 第 1, 2 周期,用户令 `tx_tvalid=0` ,因此暂时空闲没发数据。 + +* 第 3 周期,用户要发 D1,因此令 `tx_tvalid=1`,本周期 `tx_tready=1` ,说明 D1 即刻发送成功。 + +* 第 4, 5, 6, 7 周期,用户要发 D2,因此令 `tx_tvalid=1`,但第 4, 5, 6 周期 `tx_tready=0` 导致发送暂时失败,直到第 7 周期 `tx_tready=1` 时,才发送成功。 + +* 第 8, 9 周期,用户令 `tx_tvalid=0` ,因此暂时空闲没发数据。 + +* 第 10 周期,用户要发 D3,因此令 `tx_tvalid=1`,本周期 `tx_tready=1` ,D3 即刻发送成功。 + +``` +cycle 1 2 3 4 5 6 7 8 9 10 11 + _ __ __ __ __ __ __ __ __ __ __ __ + clk \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \ + _____________________________ _____ +tx_tvalid ___________/ \___________/ \________ + _________________ ________________________________ +tx_tready \_________________/ + _____ _______________________ _____ +tx_tdata/tx_tkeep/tx_tlast XXXXXXXXXXXX__D1_X___________D2__________XXXXXXXXXXXXX__D3_XXXXXXXXX +``` + +  + +  + +模块仿真 +============================= + +仿真相关的文件在 SIM 目录中,其中: + +- tb_ftdi_chip_model.v 模拟了一个简单的 FTDI 芯片的行为。 +- tb_ftdi_245fifo.v 是 testbench 的顶层,它把 tb_ftdi_chip_model.v 和待测模块 (也即 [ftdi_245fifo_top.v](./RTL/ftdi_245fifo/ftdi_245fifo_top.v) ) 连起来进行仿真。 +- tb_ftdi_245fifo_run_iverilog.bat 是运行 iverilog 仿真的命令脚本 (仅对Windows)。 + +该仿真的行为是:FTDI 芯片不断发送不断递增的字节给 FPGA ,FPGA 将 AXI-stream 发送接口和接收接口回环 (loopback) 连接,因此数据会被发回 FTDI 芯片,因此 FTDI 芯片会收到不断递增的字节。 + +## 使用 iverilog 仿真 + +使用 iverilog 进行仿真前,需要安装 iverilog ,见:[iverilog_usage](https://github.com/WangXuan95/WangXuan95/blob/main/iverilog_usage/iverilog_usage.md) + +然后双击 tb_ftdi_245fifo_run_iverilog.bat 运行仿真,然后可以打开生成的 dump.vcd 文件查看波形。 + +## 使用其它仿真工具 + +需要把 SIM 目录和 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录中 的所有 .v 文件加入仿真工程,以 tb_ftdi_245fifo.v 为仿真的顶层模块,然后运行仿真。 + +  + +  + +# 开始使用 FT232H + +FT232H 是 USB2.0 High Speed 通信芯片,本例实现 FPGA 通过 FT232H 与上位机进行通信。 + +> :warning: 本例也适用于 FT2232H (具体操作时需要你自己变通变通),因为 FT2232H 与 FT232H 高度相似。注意 FT2232H 的 **channel-A** 可配置成 **sync-245-fifo 模式** ,而 **channel-B** 则不行。 +> + +## 步骤1:安装驱动和库 + +请按照 [FTD2XX_guide.md](./FTD2XX_guide.md) 的指示在上位机 (以Windows系统为例) 上安装 FTD2XX 驱动和 Python FTD2XX 库。 + +## 步骤2:部署 FPGA 工程 + +以下是该 FPGA 工程的模块层次结构: + +- **fpga_top_ft232h_loopback.v** (在 [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) 目录),是整个FPGA工程的顶层 + - **clock_beat.v** (在 [RTL/fpga_ft232h_example](./RTL/fpga_ft232h_example) 目录) + - **ftdi_245fifo_top.v** (在 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录),是 ftdi_245fifo_top IP核的顶层 + - [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 文件夹中的**其它.v文件**,它们会被 ftdi_245fifo_top.v 调用 + +请将以上 .v 文件加入工程。 + +然后,请在 FPGA 工程中进行引脚约束,具体而言,你需要查看电路板的原理图 (或开发板配套资料) ,了解 FT232H 的各引脚被连接到了 FPGA 的哪些引脚号上;然后,把 FPGA 工程的顶层的信号分配到对应的 FPGA 引脚号上,使得这些信号与 FT232H 的引脚对应起来。对应关系见下表 (该表简化自 [FT232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf) 第9页) : + +| FT232H引脚号 | FT232H引脚名 | FT232H引脚名 (245-fifo 模式下) | 应约束到顶层的信号名 | +| :----------: | :----------: | :----------------------------: | :------------------------: | +| 13 | ADBUS0 | D0 | ftdi_data[0] | +| 14 | ADBUS1 | D1 | ftdi_data[1] | +| 15 | ADBUS2 | D2 | ftdi_data[2] | +| 16 | ADBUS3 | D3 | ftdi_data[3] | +| 17 | ADBUS4 | D4 | ftdi_data[4] | +| 18 | ADBUS5 | D5 | ftdi_data[5] | +| 19 | ADBUS6 | D6 | ftdi_data[6] | +| 20 | ADBUS7 | D7 | ftdi_data[7] | +| 21 | ACBUS0 | RXF\# | ftdi_rxf_n | +| 25 | ACBUS1 | TXE\# | ftdi_txe_n | +| 26 | ACBUS2 | RD\# | ftdi_rd_n | +| 27 | ACBUS3 | WR\# | ftdi_wr_n | +| 28 | ACBUS4 | SIWU\# | ftdi_siwu (=1) :warning: | +| 29 | ACBUS5 | CLKOUT | ftdi_clk | +| 30 | ACBUS6 | OE\# | ftdi_oe_n | +| 31 | ACBUS7 | PWRSAV\# | ftdi_pwrsav (=1) :warning: | +| 34 | RESET\# | RESET\# | ftdi_resetn (=1) :warning: | + +> :warning: SIWU\# 、 PWRSAV\# 和 RESET\# 实际上是要永远被拉为高电平的 (你也可以看到在 fpga_top_example_ft232h.v 中它们被 assign 为 1) ,因此有些板子的设计直接用电阻把它们拉到高电平上,而没有连接到 FPGA ,对于这种情况,就不用在 FPGA 约束它们的引脚,并直接在 fpga_top_example_ft232h.v 中注释掉与它们相关的语句即可。 +> + +  + +如果你要自己画 FT232H 的 PCB,可参考**图3** 。 + +| ![ft232h_example_sch](./figures/ft232h_example_sch.png) | +| :-----------------------------------------------------: | +| **图3**:FT232H USB 接口电路的参考原理图设计 | + +  + +关于本工程的时钟和时序约束: + +- FPGA 工程存在两个时钟:主时钟 (clk) 和 FT232H 芯片提供的时钟 (ftdi_clk) 。 +- 对于 FT232H , ftdi_clk 频率为 60MHz 。 +- clk 的频率比较自由,几 MHz 到几百 MHz 都可以。最简单的就是直接用开发板的晶振。 +- `constraint_ft232h.sdc` 展示了时序约束示例,它约束了: + - ftdi_clk 的频率 (60MHz) + - clk 的频率 (你需要根据实际频率去约束) + - 指定 ftdi_clk 和 clk 为异步关系 +- `constraint_ft232h.sdc` 直接适用于 Altera Quartus 工程,对于 Xilinx 的 xdc ,约束语句应该差不多 (可能有点小区别,请自行修改) +- 如果懒得进行时序约束,也可以不约束,大概率不会有问题。 + +  + +在 FPGA 工程中,另外注意以下几点: + +* LED[3:0] 信号连接了4颗 LED 灯 (不是必须的) ,其中: + * 如果 ftdi_clk 启动,LED[3] 会闪烁,否则不闪烁。一般 ftdi_clk 只会在第一次通信时启动,而不是上电就启动。 + * LED[2:0] 展示上次收到的数据的最低 3 bit。 +* ftdi_245fifo_top 模块的参数 `CHIP_TYPE` 被设为 `"FTx232H"` ,这是因为我们用的是 FT232H / FT2232H 芯片。 + +然后你就可以编译工程,步骤略。 + +  + +## 步骤3:烧录 FT232H 芯片 + +针对每颗 FT232H 芯片,需要在初次使用时烧录为 sync-245-fifo 模式。每颗 FT232H 芯片只需要烧录一次,因为 FT232H 外围会有个 EEPROM 芯片用来永久保存配置,之后每次使用都不需要再烧录 (除非你又烧录了其它模式) 。 + +首先进入 [FTDI官网下载页面](https://ftdichip.com/utilities/#ft_prog) 下载并安装 **FT\_Prog** 软件。然后进行以下步骤: + +* 在电脑上插入你需要烧录的 FT232H 的 USB 接口,并拔出所有其它 FTDI USB 设备 (包括拔出所有的 FPGA 下载器,因为很多 FPGA 下载器,例如 Xilinx Digilent 下载器是 FT2232H 芯片实现的,如果万一覆盖了下载器内部的程序,你的下载器就废了) 。 +* 打开 **FT\_Prog** 软件。 +* 如**图4**,点击 **Scan and Parse** (小放大镜) ,扫描出插在该电脑的所有 FTDI 芯片,找到 FT232H 对应的芯片。 +* 在 FT232H 下方的属性树中逐级展开,找到并点击 Hardware 。 +* 在右侧选择 245 FIFO 模式。 +* 点击上方工具栏中的 **Program** (小闪电) 。 +* 弹出确认窗口,点击 Program。烧录到 FT232H 。 +* 烧录后,需要重新拔插 FT232H 的 USB 接口,该配置才能生效。 + +| ![ft232h_program](./figures/ft232h_program.png) | +| :-----------------------------------------------: | +| **图4**:烧录 FT232H 芯片,配置为 245-fifo 模式。 | + +  + +## 步骤4:在电脑上运行通信程序 + +运行这些程序之前,请确保 FT232H 已经按照步骤3被烧录为 sync-245-fifo 模式,且 FPGA 中下载了步骤2中建立的工程。 + +我在 [python](./python) 文件夹中提供了以下几个 Python 程序,它们会通过 FT232H 与 FPGA 进行通信。 + +| 文件名 | 功能 | +| ----------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | 定义了 USB_FTX232H_FT60X_sync245mode 类,用来对 FTDI USB 设备进行打开、关闭、发送数据、接收数据。适用于 FT232H、FT2232H、FT600、FT601 。会被以下程序调用。 | +| usb_loopback_simple.py | 上位机先发送 16 字节给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这 16 字节。该程序和 FPGA 顶层设计 fpga_top_ft232h_loopback.v 配套。 | +| usb_loopback_mass.py | 上位机先发送发送大量数据给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这些字节,程序会判断收到的数据和之前发出去的数据是否一致,来验证回环的正确性。该程序和 FPGA 顶层设计 fpga_top_ft232h_loopback.v 配套。 | + +  + +  + +# 开始使用 FT600 + +FT600 是 USB3.0 Super Speed 通信芯片,本例实现 FPGA 通过 FT600 与上位机进行通信。 + +## 步骤1:安装驱动和库 + +请按照 [FTD3XX_guide.md](./FTD3XX_guide.md) 的指示在 Host-PC (Windows系统) 上安装 FTD3XX 驱动和 Python FTD3XX 库。 + +## 步骤2:部署 FPGA 工程 + +以下是该 FPGA 工程的模块层次结构: + +- **fpga_top_ft600_loopback.v** (在 [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) 目录),是整个FPGA工程的顶层 + - **clock_beat.v** (在 [RTL/fpga_ft600_example](./RTL/fpga_ft600_example) 目录) + - **ftdi_245fifo_top.v** (在 [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 目录),是 ftdi_245fifo_top IP核的顶层 + - [RTL/ftdi_245fifo](./RTL/ftdi_245fifo) 文件夹中的**其它.v文件**,它们会被 ftdi_245fifo_top.v 调用 + +请将以上 .v 文件加入工程。 + +然后,请在 FPGA 工程中进行引脚约束,具体而言,你需要查看电路板的原理图 (或开发板配套资料),了解 FT600 的各引脚被连接到了 FPGA 的哪些引脚号上;然后,把 FPGA 工程的顶层的信号分配到对应的 FPGA 引脚号上,使得这些信号与 FT600 的引脚对应起来。对应关系见下表 (该表简化自 [FT600 DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf) 第 7\~10 页) : + +| FT600引脚号 | FT600引脚名 | 应约束到顶层的信号名 | +| :---------: | :---------: | :-----------------------------------------: | +| 33 | DATA_0 | ftdi_data[0] | +| 34 | DATA_1 | ftdi_data[1] | +| 35 | DATA_2 | ftdi_data[2] | +| 36 | DATA_3 | ftdi_data[3] | +| 39 | DATA_4 | ftdi_data[4] | +| 40 | DATA_5 | ftdi_data[5] | +| 41 | DATA_6 | ftdi_data[6] | +| 42 | DATA_7 | ftdi_data[7] | +| 45 | DATA_8 | ftdi_data[8] | +| 46 | DATA_9 | ftdi_data[9] | +| 47 | DATA_10 | ftdi_data[10] | +| 48 | DATA_11 | ftdi_data[11] | +| 53 | DATA_12 | ftdi_data[12] | +| 54 | DATA_13 | ftdi_data[13] | +| 55 | DATA_14 | ftdi_data[14] | +| 56 | DATA_15 | ftdi_data[15] | +| 2 | BE_0 | ftdi_be[0] | +| 3 | BE_1 | ftdi_be[1] | +| 43 | CLKOUT | ftdi_clk | +| 5 | RXF_N | ftdi_rxf | +| 4 | TXE_N | ftdi_txe | +| 9 | OE_N | ftdi_oe | +| 8 | RD_N | ftdi_rd | +| 7 | WR_N | ftdi_wr | +| 6 | SIWU_N | ftdi_siwu (=1) :warning: | +| 10 | RESET_N | ftdi_resetn (=1) :warning: | +| 11 | WAKEUP_N | ftdi_wakeupn (=0) :triangular_flag_on_post: | +| 12 | GPIO0 | ftdi_gpio0 (=0) :triangular_flag_on_post: | +| 13 | GPIO1 | ftdi_gpio1 (=0) :triangular_flag_on_post: | + +> :warning: SIWU_N 和 RESET_N 实际上是要永远被拉为高电平的,因此有些板子的设计直接用电阻把它们拉到高电平上,而没有连接到 FPGA ,对于这种情况,就不用在 FPGA 约束它们的引脚,并直接在 fpga_top_example_ft600.v 中注释掉与它们相关的语句即可。 +> +> :triangular_flag_on_post: WAKEUP_N 、 GPIO0 和 GPIO1 实际上是要永远被拉为低电平的,因此有些板子的设计直接用电阻把它们拉到GND上,而没有连接到 FPGA ,对于这种情况,就不用在 FPGA 约束它们的引脚,并直接在 fpga_top_example_ft600.v 中注释掉与它们相关的语句即可。 + +  + +如果你要自己画 FT600 的 PCB,可参考**图5** 。 + +| ![ft600_example_sch](./figures/ft600_example_sch.png) | +| :---------------------------------------------------: | +| **图5**:FT600 USB 接口电路的参考原理图设计 | + +  + +关于本工程的时钟和时序约束: + +- FPGA 工程存在两个时钟:主时钟 (clk) 和 FT600 芯片提供的时钟 (ftdi_clk) 。 +- 对于 FT600 / FT601 , ftdi_clk 频率为 100MHz 。 +- clk 的频率比较自由,几 MHz 到几百 MHz 都可以。最简单的就是直接用开发板的晶振。 +- `constraint_ft600.sdc` 展示了时序约束示例,它约束了: + - ftdi_clk 的频率 (100MHz) + - clk 的频率 (你需要根据实际频率去约束) + - 指定 ftdi_clk 和 clk 为异步关系 +- `constraint_ft600.sdc` 直接适用于 Altera Quartus 工程,对于 Xilinx 的 xdc ,约束语句应该差不多 (可能有点小区别,请自行修改) +- 如果懒得进行时序约束,也可以不约束,大概率不会有问题。 + +  + +在 FPGA 工程中,另外注意以下几点: + +* LED[3:0] 信号连接了4颗 LED 灯 (不是必须的) ,其中: + * 如果 ftdi_clk 启动,LED[3] 会闪烁,否则不闪烁。一般 ftdi_clk 只会在第一次通信时启动,而不是上电就启动。 + * LED[2:0] 展示上次收到的数据的最低 3 bit。 +* ftdi_245fifo_top 模块的参数 `CHIP_TYPE` 被设为 `"FT600"` ,这是因为我们用的是 FT600 芯片。 + +然后你就可以编译工程,步骤略。 + +  + +## 步骤3:在电脑上运行通信程序 + +运行这些程序之前,请确保 FPGA 中下载了步骤2中建立的工程。 + +我在 [python](./python) 文件夹中提供了以下几个 Python 程序,它们会通过 FT600 与 FPGA 进行通信。 + +| 文件名 | 功能 | +| ---------------------- | ------------------------------------------------------------ | +| USB_FTX232H_FT60X.py | 定义了 USB_FTX232H_FT60X_sync245mode 类,用来对 FTDI USB 设备进行打开、关闭、发送数据、接收数据。适用于 FT232H、FT2232H、FT600、FT601 。会被以下程序调用。 | +| usb_loopback_simple.py | 上位机先发送 16 字节给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这 16 字节。该程序和 FPGA 顶层设计 fpga_top_ft600_loopback.v 配套。 | +| usb_loopback_mass.py | 上位机先发送发送大量数据给 FPGA ,然后接收数据。因为 FPGA 工程将收发回环连接,所以上位机会接收到这些字节,程序会判断收到的数据和之前发出去的数据是否一致,来验证回环的正确性。该程序和 FPGA 顶层设计 fpga_top_ft600_loopback.v 配套。 | + +  + +# 参考资料 + +* FT232H 芯片资料:http://www.ftdichip.cn/Products/ICs/FT232H.htm +* FT232H 软件示例:http://www.ftdichip.cn/Support/SoftwareExamples/CodeExamples.htm +* FT600 芯片资料:http://www.ftdichip.cn/Products/ICs/FT600.html +* FT600/FT601 软件示例: http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm diff --git a/RTL/fpga_ft232h_example/clock_beat.v b/RTL/fpga_ft232h_example/clock_beat.v new file mode 100644 index 0000000..75d4979 --- /dev/null +++ b/RTL/fpga_ft232h_example/clock_beat.v @@ -0,0 +1,31 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : clock_beat +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: When clk runs, beat signal will blink +//-------------------------------------------------------------------------------------------------------- + +module clock_beat # ( + parameter CLK_FREQ = 50000000, // Unit:Hz + parameter BEAT_FREQ = 5 // Unit:Hz +) ( + input wire clk, + output reg beat +); + +localparam CYCLES = (CLK_FREQ / 2 / BEAT_FREQ); + +reg [31:0] count = 0; + +initial beat = 1'b0; + +always @ (posedge clk) + if ( count < (CYCLES-1) ) begin + count <= count + 1; + end else begin + count <= 0; + beat <= ~beat; + end + +endmodule diff --git a/RTL/fpga_ft232h_example/constraint_ft232h.sdc b/RTL/fpga_ft232h_example/constraint_ft232h.sdc new file mode 100644 index 0000000..e6bdde1 --- /dev/null +++ b/RTL/fpga_ft232h_example/constraint_ft232h.sdc @@ -0,0 +1,17 @@ +## Constraints required for design. +## Please modify it according to your actual situation. +## +## Note: +## The clock of FT600/FT601 chip (ftdi_clk) is 100MHz +## The clock of FT232H/FT232H chip (ftdi_clk) is 60MHz +## It is also necessary to declare that the FPGA's main clock and ftdi_clk clock are asynchronous. Because they only transmit data through asynchronous FIFO +## + +## 50MHz FPGA main clock (usually generated by oscillator) +create_clock -name clk -period 20.000 [get_ports clk] + +## 60MHz FT232H chip clock (generated by FT232H) +create_clock -name ftdi_clk -period 16.667 [get_ports ftdi_clk] + +## Declare that these two clocks are asynchronous +set_clock_groups -asynchronous -group {clk} -group {ftdi_clk} diff --git a/RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v b/RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v new file mode 100644 index 0000000..823335c --- /dev/null +++ b/RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v @@ -0,0 +1,115 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fpga_top_ft232h_loopback +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: an example of ftdi_245fifo_top +// the pins of this module should connect to FT232H chip +// This design only sends all data received from the FTDI chip back to the FTDI chip. +// It is also known as 'loopback' +//-------------------------------------------------------------------------------------------------------- + +module fpga_top_ft232h_loopback ( + input wire clk, // main clock, connect to on-board crystal oscillator + + output wire [ 3:0] LED, + + // USB2.0 HS (FT232H chip) ------------------------------------------------------------ + //output wire ftdi_resetn, // to FT232H's pin34 (RESET#) , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + //output wire ftdi_pwrsav, // to FT232H's pin31 (PWRSAV#), !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + //output wire ftdi_siwu, // to FT232H's pin28 (SIWU#) , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + input wire ftdi_clk, // to FT232H's pin29 (CLKOUT) + input wire ftdi_rxf_n, // to FT232H's pin21 (RXF#) + input wire ftdi_txe_n, // to FT232H's pin25 (TXE#) + output wire ftdi_oe_n, // to FT232H's pin30 (OE#) + output wire ftdi_rd_n, // to FT232H's pin26 (RD#) + output wire ftdi_wr_n, // to FT232H's pin27 (WR#) + inout [ 7:0] ftdi_data // to FT232H's pin20~13 (ADBUS7~ADBUS0) +); + + + +//assign ftdi_resetn = 1'b1; // 1=normal operation , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! +//assign ftdi_pwrsav = 1'b1; // 1=normal operation , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! +//assign ftdi_siwu = 1'b1; // 1=send immidiently , !!!!!! UnComment this line if this signal is connected to FPGA !!!!!! + + + + +//----------------------------------------------------------------------------------------------------------------------------- +// user AXI-stream signals (loopback) +//----------------------------------------------------------------------------------------------------------------------------- +localparam AXIS_EW = 2; +wire tready; +wire tvalid; +wire [(8<> 4); + calculate_crc = TABLE_CRC[calculate_crc[3:0]] ^ (calculate_crc >> 4); +end +endfunction + + +reg state = 1'b0; // 0:inputting stream, 1:outputting CRC +reg [31:0] crc = 'hFFFFFFFF; +reg [31:0] len = 0; + +reg [31:0] crc_tmp; // not real register +reg [31:0] len_tmp; // not real register + +integer i; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + state <= 1'b0; + crc <= 'hFFFFFFFF; + len <= 0; + end else begin + if (state == 1'b0) begin // inputting stream + if (i_tvalid) begin + crc_tmp = crc; + len_tmp = len; + for (i=0; i<(1<= 4) begin + length <= length - 4; + end else begin + length <= 0; + state <= RX_BYTE0; + end + end + endcase + end + + +assign i_tready = (state != TX_DATA); + +assign o_tvalid = (state == TX_DATA); + +assign o_tdata = {length[7:0] - 8'd4, + length[7:0] - 8'd3, + length[7:0] - 8'd2, + length[7:0] - 8'd1 }; + +assign o_tkeep = (length>=4) ? 4'b1111 : + (length==3) ? 4'b0111 : + (length==2) ? 4'b0011 : + (length==1) ? 4'b0001 : + /*length==0*/ 4'b0000; + +assign o_tlast = (length>=4) ? 1'b0 : 1'b1; + + +endmodule diff --git a/RTL/fpga_ft600_example/clock_beat.v b/RTL/fpga_ft600_example/clock_beat.v new file mode 100644 index 0000000..75d4979 --- /dev/null +++ b/RTL/fpga_ft600_example/clock_beat.v @@ -0,0 +1,31 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : clock_beat +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: When clk runs, beat signal will blink +//-------------------------------------------------------------------------------------------------------- + +module clock_beat # ( + parameter CLK_FREQ = 50000000, // Unit:Hz + parameter BEAT_FREQ = 5 // Unit:Hz +) ( + input wire clk, + output reg beat +); + +localparam CYCLES = (CLK_FREQ / 2 / BEAT_FREQ); + +reg [31:0] count = 0; + +initial beat = 1'b0; + +always @ (posedge clk) + if ( count < (CYCLES-1) ) begin + count <= count + 1; + end else begin + count <= 0; + beat <= ~beat; + end + +endmodule diff --git a/RTL/fpga_ft600_example/constraint_ft600.sdc b/RTL/fpga_ft600_example/constraint_ft600.sdc new file mode 100644 index 0000000..8582d0b --- /dev/null +++ b/RTL/fpga_ft600_example/constraint_ft600.sdc @@ -0,0 +1,17 @@ +## Constraints required for design. +## Please modify it according to your actual situation. +## +## Note: +## The clock of FT600/FT601 chip (ftdi_clk) is 100MHz +## The clock of FT232H/FT232H chip (ftdi_clk) is 60MHz +## It is also necessary to declare that the FPGA's main clock and ftdi_clk clock are asynchronous. Because they only transmit data through asynchronous FIFO +## + +## 50MHz FPGA main clock (usually generated by oscillator) +create_clock -name clk -period 20.000 [get_ports clk] + +## 100MHz FT600 chip clock (generated by FT600) +create_clock -name ftdi_clk -period 10.000 [get_ports ftdi_clk] + +## Declare that these two clocks are asynchronous +set_clock_groups -asynchronous -group {clk} -group {ftdi_clk} diff --git a/RTL/fpga_ft600_example/fpga_top_ft600_loopback.v b/RTL/fpga_ft600_example/fpga_top_ft600_loopback.v new file mode 100644 index 0000000..15a8678 --- /dev/null +++ b/RTL/fpga_ft600_example/fpga_top_ft600_loopback.v @@ -0,0 +1,120 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fpga_top_ft600_loopback +// Type : synthesizable, FPGA's top, IP's example design +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: an example of ftdi_245fifo_top +// the pins of this module should connect to FT600 chip +// This design only sends all data received from the FTDI chip back to the FTDI chip. +// It is also known as 'loopback' +//-------------------------------------------------------------------------------------------------------- + +module fpga_top_ft600_loopback ( + input wire clk, // main clock, connect to on-board crystal oscillator + + output wire [ 3:0] LED, + + // USB3.0 (FT600 chip) ------------------------------------------------------------ + //output wire ftdi_resetn, // to FT600's pin10 (RESET_N) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_wakeupn, // to FT600's pin11 (WAKEUP_N), !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_gpio0, // to FT600's pin12 (GPIO0) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_gpio1, // to FT600's pin13 (GPIO1) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + //output wire ftdi_siwu, // to FT600's pin6 (SIWU_N) , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + input wire ftdi_clk, // to FT600's pin43 (CLK) + input wire ftdi_rxf_n, // to FT600's pin5 (RXF_N) + input wire ftdi_txe_n, // to FT600's pin4 (TXE_N) + output wire ftdi_oe_n, // to FT600's pin9 (OE_N) + output wire ftdi_rd_n, // to FT600's pin8 (RD_N) + output wire ftdi_wr_n, // to FT600's pin7 (WR_N) + inout [15:0] ftdi_data, // to FT600's pin56~53 (DATA_15~DATA_12) , pin48~45 (DATA_11~DATA_8) , pin42~39 (DATA_7~DATA4) and pin36~33 (DATA_3~DATA_0) + inout [ 1:0] ftdi_be // to FT600's pin3 (BE_1) and pin2 (BE_0) +); + + + +//assign ftdi_resetn = 1'b1; // 1=normal operation , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_wakeupn= 1'b0; // 0=wake up , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_gpio0 = 1'b0; // GPIO[1:0]=00 = 245fifo mode , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_gpio1 = 1'b0; // , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! +//assign ftdi_siwu = 1'b1; // 1=send immidiently , !!!!!! UnComment this line if this signal is connected to FPGA. !!!!!! + + + + +//----------------------------------------------------------------------------------------------------------------------------- +// user AXI-stream signals (loopback) +//----------------------------------------------------------------------------------------------------------------------------- +localparam AXIS_EW = 1; + +wire tready; +wire tvalid; +wire [(8<> 4); + calculate_crc = TABLE_CRC[calculate_crc[3:0]] ^ (calculate_crc >> 4); +end +endfunction + + +reg state = 1'b0; // 0:inputting stream, 1:outputting CRC +reg [31:0] crc = 'hFFFFFFFF; +reg [31:0] len = 0; + +reg [31:0] crc_tmp; // not real register +reg [31:0] len_tmp; // not real register + +integer i; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + state <= 1'b0; + crc <= 'hFFFFFFFF; + len <= 0; + end else begin + if (state == 1'b0) begin // inputting stream + if (i_tvalid) begin + crc_tmp = crc; + len_tmp = len; + for (i=0; i<(1<= 4) begin + length <= length - 4; + end else begin + length <= 0; + state <= RX_BYTE0; + end + end + endcase + end + + +assign i_tready = (state != TX_DATA); + +assign o_tvalid = (state == TX_DATA); + +assign o_tdata = {length[7:0] - 8'd4, + length[7:0] - 8'd3, + length[7:0] - 8'd2, + length[7:0] - 8'd1 }; + +assign o_tkeep = (length>=4) ? 4'b1111 : + (length==3) ? 4'b0111 : + (length==2) ? 4'b0011 : + (length==1) ? 4'b0001 : + /*length==0*/ 4'b0000; + +assign o_tlast = (length>=4) ? 1'b0 : 1'b1; + + +endmodule diff --git a/RTL/ftdi_245fifo/axi_stream_assert.v b/RTL/ftdi_245fifo/axi_stream_assert.v new file mode 100644 index 0000000..cc7ea9a --- /dev/null +++ b/RTL/ftdi_245fifo/axi_stream_assert.v @@ -0,0 +1,107 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : axi_stream_assert +// Type : simulation only (will be ingnored by synthesizer) +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: assert whether a AXI-stream interface follows the AXI-stream standards +// see AMBA 4 AXI4-stream Protocol Version: 1.0 Specification +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_assert #( + parameter NAME = "", + parameter BW = 4, // data byte-width, e.g., 4 means 4-byte-width (32-bit) + parameter ASSERT_MASTER_NOT_Z_OR_X = 1, + parameter ASSERT_SLAVE_NOT_Z_OR_X = 1, + parameter ASSERT_MASTER_NOT_CHANGE_WHEN_HANDSHAKE_FAILED = 1, + parameter ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED = 0, + parameter ASSERT_PACKED = 0 +) ( + input wire clk, + input wire tready, + input wire tvalid, + input wire [8*BW-1:0] tdata, + input wire [ BW-1:0] tkeep, + input wire tlast +); + + +localparam [8*BW-1:0] TDATA_ALL_ZERO = 0; +localparam [ BW-1:0] TKEEP_ALL_ZERO = 0; +localparam [ BW-1:0] TKEEP_ALL_ONE = ~TKEEP_ALL_ZERO; + + +reg tready_d = 1'b0; +reg tvalid_d = 1'b0; +reg [8*BW-1:0] tdata_d = TDATA_ALL_ZERO; +reg [ BW-1:0] tkeep_d = TKEEP_ALL_ZERO; +reg tlast_d = 1'b0; + + +always @ (posedge clk) begin + tready_d <= tready; + tvalid_d <= tvalid; + tdata_d <= tdata; + tkeep_d <= tkeep; + tlast_d <= tlast; +end + + +generate if (ASSERT_SLAVE_NOT_Z_OR_X) begin + always @ (posedge clk) begin + if (tready ===1'bz || tready ===1'bx) begin $display("*** error : %s AXI-stream tready = X or Z", NAME); $stop; end + end +end endgenerate + + +generate if (ASSERT_MASTER_NOT_Z_OR_X) begin + always @ (posedge clk) begin + if (tvalid ===1'bz || tvalid ===1'bx) begin $display("*** error : %s AXI-stream tvalid = X or Z", NAME); $stop; end + if (tvalid) begin + if ((&tdata)===1'bz || (&tdata)===1'bx) begin $display("*** error : %s AXI-stream tdata = X or Z", NAME); $stop; end + if ((&tkeep)===1'bz || (&tkeep)===1'bx) begin $display("*** error : %s AXI-stream tdata = X or Z", NAME); $stop; end + if ((&tlast)===1'bz || (&tlast)===1'bx) begin $display("*** error : %s AXI-stream tdata = X or Z", NAME); $stop; end + end + end +end endgenerate + + +generate if (ASSERT_MASTER_NOT_CHANGE_WHEN_HANDSHAKE_FAILED) begin + always @ (posedge clk) + if ((~tready_d) & tvalid_d) begin // At last cycle, sender sended a data, but receiver not avaiable, assert that sender is still sending a data at this cycle, and assert that data not change + if ( 1'b1 !== tvalid) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal withdraw tvalid", NAME); $stop; end + if (tdata_d !== tdata ) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal change of tdata", NAME); $stop; end + if (tlast_d !== tlast ) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal change of tlast", NAME); $stop; end + if (tkeep_d !== tkeep ) begin $display("*** error : %s AXI-stream sender behavior abnormal : Illegal change of tkeep", NAME); $stop; end + end +end endgenerate + + +generate if (ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED) begin + always @ (posedge clk) + if (tready_d & (~tvalid_d)) begin // At last cycle, receiver avaiable, but sender not sended a data, assert that receiver is still avaiable at this cycle + if (1'b1 !== tready) begin $display("*** error : %s AXI-stream receiver behavior abnormal : Illegal withdraw tready", NAME); $stop; end + end +end endgenerate + + +generate if (ASSERT_PACKED) begin + integer i; + reg has_zero; + always @ (posedge clk) + if (tvalid) begin + if (~tlast) begin + if (tkeep !== TKEEP_ALL_ONE) begin $display("*** error : %s AXI-stream not packing", NAME); $stop; end + end else begin + has_zero = 1'b0; + for (i=0; i 2.3.3 -> downsizing considerations +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_downsizing #( + parameter IEW = 0, // input width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + parameter OEW = 0 // output width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + // OEW must < IEW +) ( + input wire rstn, + input wire clk, + // AXI-stream slave + output wire i_tready, + input wire i_tvalid, + input wire [(8< 2.3.3 -> packing +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_packing #( + parameter EW = 2, // AXI byte width is 1<> 1; // equal to 1< (i_count, i_bytes) + i_bytes = 0; + i_count = 0; + for (i=0; i<(1< POW_EW) begin $display("***error : axi_stream_packing_imm"); $stop; end + + + + +reg unflushed = 1'b0; +reg r_tlast = 1'b0; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + unflushed <= 1'b0; + r_count <= 0; + r_bytes <= 0; + o_tvalid <= 1'b0; + r_tlast <= 1'b0; + o_tdata <= 0; + o_tkeep <= TKEEP_ALL_ZERO; + end else begin + if (o_tready) + o_tvalid <= 1'b0; + if (o_tready | ~o_tvalid) begin // o_tready=1 or o_tvalid=0, then it is available to put a new data to output + if (unflushed) begin + unflushed <= 1'b0; + r_count <= 0; + r_bytes <= 0; + o_tvalid <= 1'b1; + r_tlast <= 1'b1; + o_tdata <= r_bytes; + o_tkeep <= ~(TKEEP_ALL_ONE << r_count); // r_count of bytes is available, so there will be r_count of '1' on tkeep + end else begin + if (i_tvalid) begin + if (t_count > POW_EW_W) begin // More than (1< 2.3.3 -> downsizing considerations / upsizing considerations +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_resizing #( + parameter IEW = 0, // input width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + parameter OEW = 0 // output width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... +) ( + input wire rstn, + input wire clk, + // AXI-stream slave + output wire i_tready, + input wire i_tvalid, + input wire [(8< OEW) begin + + axi_stream_downsizing #( + .IEW ( IEW ), + .OEW ( OEW ) + ) u_axi_stream_downsizing ( + .rstn ( rstn ), + .clk ( clk ), + .i_tready ( i_tready ), + .i_tvalid ( i_tvalid ), + .i_tdata ( i_tdata ), + .i_tkeep ( i_tkeep ), + .i_tlast ( i_tlast ), + .o_tready ( o_tready ), + .o_tvalid ( o_tvalid ), + .o_tdata ( o_tdata ), + .o_tkeep ( o_tkeep ), + .o_tlast ( o_tlast ) + ); + +end else begin // (IEW==OEW) + + assign i_tready = o_tready; + assign o_tvalid = i_tvalid; + assign o_tdata = i_tdata; + assign o_tkeep = i_tkeep; + assign o_tlast = i_tlast; + +end endgenerate + + +endmodule diff --git a/RTL/ftdi_245fifo/axi_stream_upsizing.v b/RTL/ftdi_245fifo/axi_stream_upsizing.v new file mode 100644 index 0000000..3343b82 --- /dev/null +++ b/RTL/ftdi_245fifo/axi_stream_upsizing.v @@ -0,0 +1,127 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : axi_stream_upsizing +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: AXI-stream upsizing or downsizing, +// see AMBA 4 AXI4-stream Protocol Version: 1.0 Specification -> 2.3.3 -> upsizing considerations +//-------------------------------------------------------------------------------------------------------- + +module axi_stream_upsizing #( + parameter IEW = 0, // input width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + parameter OEW = 0 // output width, 0=1Byte, 1=2Byte, 2=4Byte, 3=8Bytes, 4=16Bytes, ... + // OEW must > IEW +) ( + input wire rstn, + input wire clk, + // AXI-stream slave + output wire i_tready, + input wire i_tvalid, + input wire [(8<IEW) ? (OEW-IEW) : 1; + +localparam [DIFF_EW-1:0] IDX_ONE = 1; +localparam [DIFF_EW-1:0] IDX_FIRST = {DIFF_EW{1'b0}}; +localparam [DIFF_EW-1:0] IDX_LAST = ~IDX_FIRST; + +reg [DIFF_EW-1:0] idx = IDX_FIRST; + +reg tmp_full = 1'b0; +reg [(8< data1 + end + end else if (data1_en) begin // half full (data1 exist, but data2 not exist) + if (o_rdy & i_en) begin // allow both output and input simultaneously + data1 <= i_data; // i_data -> data1 + end else if (o_rdy) begin // only allow output + data1_en <= 1'b0; // set data1 to not exist + end else if (i_en) begin // only allow input + data2_en_n <= 1'b0; // set data1 to exist + data2 <= i_data; // i_data -> data2 + end + end else begin // empty (both data1 & data2 not exist) + if (i_en) begin // allow input + data1_en <= 1'b1; // set data1 to exist + data1 <= i_data; // i_data -> data1 + end + end + end + +endmodule diff --git a/RTL/ftdi_245fifo/fifo4.v b/RTL/ftdi_245fifo/fifo4.v new file mode 100644 index 0000000..29314f0 --- /dev/null +++ b/RTL/ftdi_245fifo/fifo4.v @@ -0,0 +1,98 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fifo4 +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: fifo with capacity=4, +// all outputs are register-out, which can be inserted into the stream chain to improve timing +// it has a 'almost_full' pin to report half-full (already 2 data in the fifo) +//-------------------------------------------------------------------------------------------------------- + +module fifo4 # ( + parameter DW = 8 +) ( + input wire rstn, + input wire clk, + output wire i_almost_full, + output wire i_rdy, + input wire i_en, + input wire [DW-1:0] i_data, + input wire o_rdy, + output wire o_en, + output wire [DW-1:0] o_data +); + + +reg data1_en = 1'b0; // fifo data1 exist +reg data2_en = 1'b0; // fifo data2 exist +reg data3_en = 1'b0; // fifo data3 exist +reg data4_en = 1'b0; // fifo data4 exist +reg [DW-1:0] data1 = 0; // fifo data1 +reg [DW-1:0] data2 = 0; // fifo data2 +reg [DW-1:0] data3 = 0; // fifo data3 +reg [DW-1:0] data4 = 0; // fifo data4 + +assign i_rdy = data4_en; +assign i_almost_full = data2_en; + +assign o_en = data1_en; +assign o_data = data1; + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + data1_en <= 1'b0; + data2_en <= 1'b0; + data3_en <= 1'b0; + data4_en <= 1'b0; + data1 <= 0; + data2 <= 0; + data3 <= 0; + data4 <= 0; + end else begin + if (data4_en) begin + if (o_rdy) begin + data4_en <= 1'b0; + {data1, data2, data3} <= {data2, data3, data4}; + end + + end else if (data3_en) begin + if (o_rdy & i_en) begin + {data1, data2, data3} <= {data2, data3, i_data}; + end else if (o_rdy) begin + data3_en <= 1'b0; + {data1, data2} <= {data2, data3}; + end else if (i_en) begin + data4_en <= 1'b1; + data4 <= i_data; + end + + end else if (data2_en) begin + if (o_rdy & i_en) begin + {data1, data2} <= {data2, i_data}; + end else if (o_rdy) begin + data2_en <= 1'b0; + data1 <= data2; + end else if (i_en) begin + data3_en <= 1'b1; + data3 <= i_data; + end + + end else if (data1_en) begin + if (o_rdy & i_en) begin + data1 <= i_data; + end else if (o_rdy) begin + data1_en <= 1'b0; + end else if (i_en) begin + data2_en <= 1'b1; + data2 <= i_data; + end + + end else begin + if (i_en) begin + data1_en <= 1'b1; + data1 <= i_data; + end + end + end + +endmodule diff --git a/RTL/ftdi_245fifo/fifo_async.v b/RTL/ftdi_245fifo/fifo_async.v new file mode 100644 index 0000000..9afec46 --- /dev/null +++ b/RTL/ftdi_245fifo/fifo_async.v @@ -0,0 +1,97 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fifo_async +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: asynchronous fifo +//-------------------------------------------------------------------------------------------------------- + +module fifo_async #( + parameter DW = 8, + parameter EA = 10 +)( + input wire i_rstn, + input wire i_clk, + output wire i_tready, + input wire i_tvalid, + input wire [DW-1:0] i_tdata, + input wire o_rstn, + input wire o_clk, + input wire o_tready, + output reg o_tvalid, + output reg [DW-1:0] o_tdata +); + + + +reg [DW-1:0] buffer [(1<> 1) ^ wptr; +wire [EA:0] rptr_grey = (rptr >> 1) ^ rptr; +wire [EA:0] rptr_next_grey = (rptr_next >> 1) ^ rptr_next; + +always @ (posedge i_clk or negedge i_rstn) + if(~i_rstn) + wq_wptr_grey <= 0; + else + wq_wptr_grey <= wptr_grey; + +always @ (posedge o_clk or negedge o_rstn) + if(~o_rstn) + {rq2_wptr_grey, rq1_wptr_grey} <= 0; + else + {rq2_wptr_grey, rq1_wptr_grey} <= {rq1_wptr_grey, wq_wptr_grey}; + +always @ (posedge o_clk or negedge o_rstn) + if(~o_rstn) + rq_rptr_grey <= 0; + else + rq_rptr_grey <= rptr_grey; + +always @ (posedge i_clk or negedge i_rstn) + if(~i_rstn) + {wq2_rptr_grey, wq1_rptr_grey} <= 0; + else + {wq2_rptr_grey, wq1_rptr_grey} <= {wq1_rptr_grey, rq_rptr_grey}; + +wire w_full = (wq2_rptr_grey == {~wptr_grey[EA:EA-1], wptr_grey[EA-2:0]} ); +wire r_empty = (rq2_wptr_grey == rptr_next_grey ); + +assign i_tready = ~w_full; + + + +always @ (posedge i_clk or negedge i_rstn) + if(~i_rstn) begin + wptr <= 0; + end else begin + if(i_tvalid & ~w_full) + wptr <= wptr + {{EA{1'b0}}, 1'b1}; + end + +always @ (posedge i_clk) + if(i_tvalid & ~w_full) + buffer[wptr[EA-1:0]] <= i_tdata; + + + +initial o_tvalid = 1'b0; + +always @ (posedge o_clk or negedge o_rstn) + if (~o_rstn) begin + rptr <= 0; + o_tvalid <= 1'b0; + end else begin + rptr <= rptr_next; + o_tvalid <= ~r_empty; + end + +always @ (posedge o_clk) + o_tdata <= buffer[rptr_next[EA-1:0]]; + + +endmodule diff --git a/RTL/ftdi_245fifo/fifo_delay_submit.v b/RTL/ftdi_245fifo/fifo_delay_submit.v new file mode 100644 index 0000000..abc3ccd --- /dev/null +++ b/RTL/ftdi_245fifo/fifo_delay_submit.v @@ -0,0 +1,85 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : fifo_delay_submit +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: +//-------------------------------------------------------------------------------------------------------- + +module fifo_delay_submit #( + parameter DW = 8 // AXI byte width is 1< SUBMIT_CHUNK ) begin + wptr_submit <= wptr; + count <= 11'd0; + end else begin + count <= (i_en & i_rdy) ? 11'd0 : (count+11'd1); + if (count == 11'd2047) + wptr_submit <= wptr; + end + end + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + wptr <= 11'h0; + end else begin + if (i_en & i_rdy) + wptr <= wptr + 11'h1; + end + +always @ (posedge clk) + if (i_en & i_rdy) + buffer[wptr[9:0]] <= i_data; + + + +always @ (posedge clk or negedge rstn) + if (~rstn) begin + rptr <= 11'h0; + o_en <= 1'b0; + end else begin + rptr <= rptr_next; + o_en <= (rptr_next != wptr_submit_d); + end + +always @ (posedge clk) + o_data <= buffer[rptr_next[9:0]]; + + +endmodule diff --git a/RTL/ftdi_245fifo/ftdi_245fifo_fsm.v b/RTL/ftdi_245fifo/ftdi_245fifo_fsm.v new file mode 100644 index 0000000..fea176f --- /dev/null +++ b/RTL/ftdi_245fifo/ftdi_245fifo_fsm.v @@ -0,0 +1,129 @@ + +//-------------------------------------------------------------------------------------------------------- +// Module : ftdi_245fifo_fsm +// Type : synthesizable, IP's sub-module +// Standard: Verilog 2001 (IEEE1364-2001) +// Function: FTDI USB chip's 245fifo mode's main control FSM, called by ftdi_245fifo_fsm +//-------------------------------------------------------------------------------------------------------- + +module ftdi_245fifo_fsm #( + parameter CHIP_EW = 0, // FTDI USB chip data width, 0=8bit, 1=16bit, 2=32bit. for FT232H is 0, for FT600 is 1, for FT601 is 2. + parameter CHIP_DRIVE_AT_NEGEDGE = 0 +) ( + input wire rstn, + input wire clk, + output wire tx_tready, + input wire tx_tvalid, + input wire [(8< USB -> PC), AXI-stream slave. + tx_clk, // User-specified clock for send interface + tx_tready, + tx_tvalid, + tx_tdata, + tx_tkeep, + tx_tlast, + // user recv interface (PC -> USB -> FPGA), AXI-stream master. + rx_clk, // User-specified clock for recv interface + rx_tready, + rx_tvalid, + rx_tdata, + rx_tkeep, + rx_tlast, + // FTDI 245FIFO interface, connect these signals to FTDI USB chip + ftdi_clk, + ftdi_rxf_n, + ftdi_txe_n, + ftdi_oe_n, + ftdi_rd_n, + ftdi_wr_n, + ftdi_data, + ftdi_be // only FT600&FT601 have BE signal, ignore it when the chip don't have this signal +); + + + +localparam CHIP_EW = (CHIP_TYPE=="FT601") ? 2 : (CHIP_TYPE=="FT600") ? 1 : 0; +localparam CHIP_DRIVE_AT_NEGEDGE = (CHIP_TYPE=="FT601") ? 0 : (CHIP_TYPE=="FT600") ? 0 : 0; + + +//----------------------------------------------------------------------------------------------------------------------------- +// in/out signals +//----------------------------------------------------------------------------------------------------------------------------- +input wire rstn_async; +input wire tx_clk; +output wire tx_tready; +input wire tx_tvalid; +input wire [(8< CHIP_EW) ? TX_EW : CHIP_EW; +localparam RX_FIFO_EW = (RX_EW > CHIP_EW) ? RX_EW : CHIP_EW; + +wire tx_a_tready; +wire tx_a_tvalid; +wire [(8<= TX_FIFO_EW ) +) u_tx_packing ( + .rstn ( rstn_tx ), + .clk ( tx_clk ), + .i_tready ( tx_a_tready ), + .i_tvalid ( tx_a_tvalid ), + .i_tdata ( tx_a_tdata ), + .i_tkeep ( tx_a_tkeep ), + .i_tlast ( tx_a_tlast ), + .o_tready ( tx_b_tready ), + .o_tvalid ( tx_b_tvalid ), + .o_tdata ( tx_b_tdata ), + .o_tkeep ( tx_b_tkeep ), + .o_tlast ( tx_b_tlast ) +); + + +axi_stream_resizing #( + .IEW ( TX_EW ), + .OEW ( TX_FIFO_EW ) +) u_tx_upsizing ( + .rstn ( rstn_tx ), + .clk ( tx_clk ), + .i_tready ( tx_b_tready ), + .i_tvalid ( tx_b_tvalid ), + .i_tdata ( tx_b_tdata ), + .i_tkeep ( tx_b_tkeep ), + .i_tlast ( tx_b_tlast ), + .o_tready ( tx_c_tready ), + .o_tvalid ( tx_c_tvalid ), + .o_tdata ( tx_c_tdata ), + .o_tkeep ( tx_c_tkeep ), + .o_tlast ( ) +); + + +fifo_async #( + .DW ( (1< USB-device object + function: + open an FTDI USB device + parameters : + device_type : + 'FTX232H' : FT232H or FT2232H + 'FT60X' : FT600 or FT601 + device_name : the USB device's product name to be opened + for 'FTX232H', the chip's default device_name = 'USB <-> Serial Converter' unless the user has modified it. + for 'FT60X' , the chip's default device_name = 'FTDI SuperSpeed-FIFO Bridge' unless the user has modified it. + return: + a USB-device object + ''' + + b_device_name = bytes(device_name, encoding="ASCII") + + if device_type == 'FT60X' : # search the FT60X device ################################################################################################ + + try: + import ftd3xx # import # + except: + return None, 'Failed to import ftd3xx' + + for device_id in range(16): + + if sys.platform == 'win32': + usb = ftd3xx.create(device_id, ftd3xx._ftd3xx_win32.FT_OPEN_BY_INDEX) + elif sys.platform == 'linux2': + usb = ftd3xx.create(device_id, ftd3xx._ftd3xx_linux.FT_OPEN_BY_INDEX) + + if usb is None: + continue + + if sys.platform == 'win32' and usb.getDriverVersion() < 0x01020006: + usb.close() + return None, 'Old D3XX driver version. Please update driver!' + + if usb.getDeviceInfo()['Description'] != b_device_name: + usb.close() + continue + + numChannel = [4, 2, 1, 0, 0][usb.getChipConfiguration().ChannelConfig] + if numChannel != 1: + usb.close() + return None, 'This %s is not in sync-245-fifo mode because number of channel is not 1 ! (numChannel=%d)' % (device_type, numChannel) + + return usb, 'Successfully opened %s USB device: %s' % (device_type, device_name) + + else: # search the FTX232H device ############################################################################################## + + try: + import ftd2xx # import # + except: + return None, 'Failed to import ftd2xx' + + for i in range(16): + + try: + usb = ftd2xx.open(i) + except: + continue + + if usb.description != b_device_name: + usb.close() + continue + + usb.setBitMode(0xff, 0x40) + + return usb, 'Successfully opened %s USB device: %s' % (device_type, device_name) + + return None, 'Could not open %s USB device: %s' % (device_type, device_name) + + + + + +class USB_FTX232H_FT60X_sync245mode(): + + def __init__(self, device_to_open_list = (('FTX232H', 'USB <-> Serial Converter' ), + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) ): + ''' + __init__(str device_type, str device_name) -> USB_FTX232H_FT60X_sync245mode object + function: + open FTDI USB device and return a USB_FTX232H_FT60X_sync245mode object + ''' + + for device_type, device_name in device_to_open_list: + + usb, message = open_ft_usb_device(device_type, device_name) + print(message) + + if not usb is None: + + self.device_type = device_type + self.device_name = device_name + self._usb = usb + self._recv_timeout = 2000 + self._send_timeout = 2000 + + self.set_recv_timeout(self._recv_timeout) + self.set_send_timeout(self._send_timeout) + + if device_type == 'FT60X' : + if usb.getDeviceDescriptor().bcdUSB < 0x300: + print('Warning: Device is NOT connected using USB3.0 cable or port!') + self._chunk = 65536 * 16 + else: + self._chunk = 65536 + usb.setUSBParameters(self._chunk*4, self._chunk*4) + + break + + else: + raise Exception('Could not open USB device') + + + + + def close(self): + ''' + close() + function: + close the FTDI USB device + ''' + + self._usb.close() + self._usb = None + + + + + def set_recv_timeout(self, timeout): + ''' + set_recv_timeout(int timeout) + function: + set recv timeout + parameter: + int timeout : unit: ms + ''' + + self._recv_timeout = timeout + if self.device_type == 'FT60X' : + self._usb.setPipeTimeout(0x82, self._recv_timeout) + else: + self._usb.setTimeouts(self._recv_timeout, self._send_timeout) + + + + + def set_send_timeout(self, timeout): + ''' + set_send_timeout(int timeout) + function: + set send timeout + parameter: + int timeout : unit: ms + ''' + + self._send_timeout = timeout + if self.device_type == 'FT60X' : + self._usb.setPipeTimeout(0x02, self._send_timeout) + else: + self._usb.setTimeouts(self._recv_timeout, self._send_timeout) + + + + + def send(self, data): + ''' + send(bytes data) -> int actual_send_len + function: + send data + parameter: + bytes data : data to send + return: + int actual_send_len : actual sent byte count + if the device cannot accept so many data until timeout, actual_send_len < len(data) + ''' + + txlen = 0 + + for si in range(0, len(data), self._chunk): + ei = si + self._chunk + ei = min(ei, len(data)) + + chunk = data[si:ei] + + if self.device_type == 'FT60X' : + txlen_once = self._usb.writePipe(0x02, chunk, len(chunk)) + else: + txlen_once = self._usb.write(chunk) + + txlen += txlen_once + + if txlen_once < len(chunk): + break + + return txlen + + + + + def recv(self, recv_len): + ''' + recv(int recv_len) -> bytes data + function: + receive data + parameter: + int recv_len : data length to be received + return: + bytes data : received data bytes + if the device cannot send so many data until timeout, len(data) < recv_len + ''' + + data = b'' + + if self.device_type == 'FT60X' : + chunk = bytes(self._chunk) + + zero_count = 0 + si = 0 + + while si < recv_len : + ei = si + self._chunk + ei = min(ei, recv_len) + + rxlen_once = self._usb.readPipe(0x82, chunk, ei-si) # try to read (ei-si) bytes + + si += rxlen_once + + if rxlen_once > 0 : + zero_count = 0 + data += chunk[:rxlen_once] + else : # no any byte received + zero_count += 1 + if zero_count >= 2 : + break + + else: + for si in range(0, recv_len, self._chunk): + ei = si + self._chunk + ei = min(ei, recv_len) + + chunk_len = ei - si + + chunk = self._usb.read(chunk_len) + + data += chunk + + if len(chunk) < chunk_len: + break + + return data + + + + + + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + print('device opened: device_type=%s, device_name=%s' % (usb.device_type, usb.device_name) ) + + usb.close() + + diff --git a/python/usb_loopback_mass.py b/python/usb_loopback_mass.py new file mode 100644 index 0000000..6043243 --- /dev/null +++ b/python/usb_loopback_mass.py @@ -0,0 +1,57 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends random data blocks to FTDI chip. The FPGA immediately returns these data (loopback). +# The program will receive these data blocks and compare them with the previously sent data blocks (they should be the same) +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_loopback.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_loopback.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + +from random import randint + + + +def rand_data (minlen, maxlen) : + minlen = max(1, minlen) + maxlen = max(minlen, maxlen) + length = randint(minlen, maxlen) + array = [randint(0, 255) for i in range(length)] + return bytes(array) + + + +TEST_COUNT = 2000 + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode( device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + total_len = 0 + + for i in range(TEST_COUNT) : + txdata = rand_data(1, 2000) # randomly generate data (with type of 'bytes') + usb.send(txdata) # send data + rxdata = usb.recv( len(txdata) ) # receive data, it will get the same data since the usb is loopback in FPGA (see fpga_top_ft232h_loopback.v or fpga_top_ft600_loopback.v) + + total_len += len(txdata) + + if i % 100 == 0 : + print('[%d/%d] total_len=%d' % (i+1, TEST_COUNT, total_len) ) + + if txdata != rxdata : # check if send data and receive data are the same + print('*** txdata and rxdata mismatch' ) + print('txdata =', txdata) + print('rxdata =', rxdata) + break + + usb.close() diff --git a/python/usb_loopback_simple.py b/python/usb_loopback_simple.py new file mode 100644 index 0000000..7a35569 --- /dev/null +++ b/python/usb_loopback_simple.py @@ -0,0 +1,33 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends 16 bytes to FTDI chip. The FPGA immediately returns these data (loopback). +# The program will receive these bytes (they should be as same as the sended 16 bytes) +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_loopback.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_loopback.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + txlen = usb.send(b'0123456789abcdef') + + print("%d B sent" % txlen) + + data = usb.recv(txlen*2) + + print("recv %d B : %s" % (len(data), str(data)) ) + + usb.close() diff --git a/python/usb_rx_mass.py b/python/usb_rx_mass.py new file mode 100644 index 0000000..628fdab --- /dev/null +++ b/python/usb_rx_mass.py @@ -0,0 +1,55 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends 4 bytes to FTDI chip, and the FPGA will treat these 4 bytes as a length. +# Then the FPGA sends bytes of length to the computer, and the program should receive these bytes. +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_tx_mass.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_rx_mass.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + +from random import randint +import time + + + +TEST_COUNT = 50 + + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + total_rx_len = 0 + + time_start = time.time() + + for i in range (TEST_COUNT) : + expect_len = randint(1, 10000000) # random a length + txdata = bytes( [ expect_len&0xff, (expect_len>>8)&0xff, (expect_len>>16)&0xff, (expect_len>>24)&0xff ] ) # convert length number to a 4-byte byte array (with type of 'bytes') + + usb.send(txdata) # send the 4 bytes to usb + + data = usb.recv(expect_len) # recv from usb + + rx_len = len(data) + + total_rx_len += rx_len + time_total = time.time() - time_start + data_rate = total_rx_len / (time_total + 0.001) / 1e3 + + print('[%d/%d] rx_len=%d total_rx_len=%d data_rate=%.0f kB/s' % (i+1, TEST_COUNT, rx_len, total_rx_len, data_rate) ) + + if expect_len != rx_len : + print('*** expect_len (%d) and rx_len (%d) mismatch' % (expect_len, rx_len) ) + break + + usb.close() diff --git a/python/usb_tx_crc.py b/python/usb_tx_crc.py new file mode 100644 index 0000000..3205357 --- /dev/null +++ b/python/usb_tx_crc.py @@ -0,0 +1,88 @@ +#-*- coding:utf-8 -*- +# Python3 +# +# This program is a test of FPGA+FTDI USB chips (FT232H, FT600, or FT601) +# It sends random data block to FTDI chip. The blocks all ends with 0xFF. Except for the last byte, all other bytes are not 0xFF +# when the FPGA receives the block, it calculates its CRC. When meeting 0xFF, the FPGA send CRC of the block via FTDI chip +# Finally, the program will verify whether the CRC value is the same as the calculated CRC value. +# +# The corresponding FPGA top-level design can be found in fpga_top_ft232h_rx_crc.v (if you are using FT232H or FT2232H chips) +# Or see fpga_top_ft600_rx_crc.v (if you are using an FT600 chip) +# + + +from USB_FTX232H_FT60X import USB_FTX232H_FT60X_sync245mode # see USB_FTX232H_FT60X.py + +from random import randint +import time + + + +def rand_data (minlen, maxlen) : + minlen = max(1, minlen) + maxlen = max(minlen, maxlen) + length = randint(minlen, maxlen) + array = [randint(0, 0xFE) for i in range(length)] + return bytes(array) + + + +def calc_crc (data) : + TABLE_CRC = (0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c) + crc = 0xFFFFFFFF + for byte in data : + crc ^= byte + crc = TABLE_CRC[crc & 0xF] ^ (crc >> 4) + crc = TABLE_CRC[crc & 0xF] ^ (crc >> 4) + return crc + + + + +data_list = [ # this list will contains several data blocks and their CRC + [ data, calc_crc(data) ] + for data in [ # Note that each block of data end with 0xFF, since FPGA start to send CRC when meeting 0xFF (see rx_calc_crc.v) + b'\xFF' , # block length = 1 + rand_data( 1 , 1 ) + b'\xFF' , # block length = 2 + rand_data( 2 , 2 ) + b'\xFF' , # block length = 3 + rand_data(2000000, 3000000) + b'\xFF' , # block length = 2000001 ~ 3000001 + rand_data(2000000, 3000000) + b'\xFF' , # block length = 2000001 ~ 3000001 + rand_data(2000000, 3000000) + b'\xFF' # block length = 2000001 ~ 3000001 + ] +] + + + +TEST_COUNT = 50 + +if __name__ == '__main__': + + usb = USB_FTX232H_FT60X_sync245mode(device_to_open_list = + (('FTX232H', 'USB <-> Serial Converter' ), # firstly try to open FTX232H (FT232H or FT2232H) device named 'USB <-> Serial Converter'. Note that 'USB <-> Serial Converter' is the default name of FT232H or FT2232H chip unless the user has modified it. If the chip's name has been modified, you can use FT_Prog software to look up it. + ('FT60X' , 'FTDI SuperSpeed-FIFO Bridge')) # secondly try to open FT60X (FT600 or FT601) device named 'FTDI SuperSpeed-FIFO Bridge'. Note that 'FTDI SuperSpeed-FIFO Bridge' is the default name of FT600 or FT601 chip unless the user has modified it. + ) + + + total_tx_len = 0 + + time_start = time.time() + + for i in range (TEST_COUNT) : + txdata, tx_crc = data_list[ randint(0, len(data_list)-1) ] # randomly select one data block, and get its CRC + + usb.send(txdata) + + rxdata = usb.recv(4) # recv 4 bytes + rx_crc = (rxdata[3]<<24) + (rxdata[2]<<16) + (rxdata[1]<<8) + (rxdata[0]<<0) # regard the 4 bytes as CRC + + total_tx_len += len(txdata) + time_total = time.time() - time_start + data_rate = total_tx_len / (time_total + 0.001) / 1e3 + + print('[%d/%d] tx_len=%d crc=%08x total_tx_len=%d data_rate=%.0f kB/s' % (i+1, TEST_COUNT, len(txdata), rx_crc, total_tx_len, data_rate) ) + + if tx_crc != rx_crc : + print('*** tx_crc (%08x) and rx_crc (%08x) mismatch' % (tx_crc, rx_crc) ) + break + + usb.close()