add support for byte-enable

This commit is contained in:
WangXuan95 2023-06-03 18:10:27 +08:00
commit 903a3c7b9b
47 changed files with 4975 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
FT232H_UniFPGA
FT600_FPGArduino
vivado
**/__pycache__

108
FTD2XX_guide.md Normal file
View File

@ -0,0 +1,108 @@
[English](#en) | [中文](#cn)
 
<span id="en">Install FTD2XX Driver and Python ftd2xx Library</span>
====================================
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.
 
 
 
 
<span id="cn">安装 FTD2XX 驱动和 Python FTD2XX 库</span>
====================================
要在 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位的DLL64位的Python必须对应64位的DLL。
然后,可以在 python 中运行以下语句来验证安装:
```python
import ftd2xx
```
至此FT232H 所需的 Python 运行环境已就绪。

113
FTD3XX_guide.md Normal file
View File

@ -0,0 +1,113 @@
[English](#en) | [中文](#cn)
 
<span id="en">Install FTD3XX Driver and Python ftd3xx Library</span>
====================================
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.
 
 
 
 
<span id="cn">安装 FTD3XX 驱动和 Python FTD3XX 库</span>
====================================
要在 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位的DLL64位的Python必须对应64位的DLL。
然后,可以在 python 中运行以下语句来验证安装:
```python
import ftd3xx
```
至此FT600 所需的 Python 运行环境已就绪。

998
README.md Normal file
View File

@ -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)
 
<span id="en">FTDI 245fifo controller</span>
===========================
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<<CHIP_EW)-1:0] ftdi_data;
inout [(1<<CHIP_EW)-1:0] ftdi_be;
```
Note:
* When the FTDI USB chip works in sync-245-fifo mode, see the chip's datasheet for its pin definations:
* for FT232H, see [FT232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf) page 9;
* for FT2232H, see [FT2232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT2232H.pdf) page 9;
* for FT600 or FT601, see [FT600/FT601 DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf) page7-10.
* `ftdi_be` is the byte enable signal, only FT600 and FT601 chips have this signal. For FT232H and FT2232H, ignore it.
* The waveform of these pins is maintained by the module. You don't need to pay attention about it. If you are interested, you can run [RTL simulation](#Module Simulation).
 
## Module Interface: AXI-stream sending interface
The sending interface is a standard AXI-stream slave, which allows users to send data to FTDI chip (which will be sent to Host-PC). Corresponding definations:
```verilog
// AXI-stream slave sending interface ------------------------------
input wire tx_clk; // AXI-stream's clock
output wire tx_tready;
input wire tx_tvalid;
input wire [(8<<TX_EW)-1:0] tx_tdata;
input wire [(1<<TX_EW)-1:0] tx_tkeep;
input wire tx_tlast;
```
Firstly, you should provide a clock on `tx_clk` (no special limitations of frequency, usually the main clock of FPGA). The AXI-stream is synchronized with the clock.
The operation sequence of AXI-stream is detailed in [AXI-stream specification](https://documentation-service.arm.com/static/60d5b244677cf7536a55c23e) . Here is a brief explanation:
* When `tx_tready=1`, the module is ready to accept a data. When `tx_tready=0`, the send buffer is temporarily full and no more data can be accepted.
* When `tx_tvalid=1`, it means that the user wants to send a data to the send buffer. Meanwhile, `tx_tdata` , `tx_tkeep` and `tx_tlast` should produce a valid data.
* `tx_tvalid` and `tx_tready` are a pair of handshake signals. When both are 1, `tx_tdata` is successfully sended.
* As mentioned earlier, the parameter `TX_EW` determines the data bit width of the AXI-stream:
* if `TX_EW=0` , then `tx_tdata` is 1-byte wide (8-bits), `tx_tkeep` is 1-bit wide;
* if `TX_EW=1` , then `tx_tdata` is 2-byte wide (16-bits), `tx_tkeep` is 2-bit wide;
* if `TX_EW=2` , then `tx_tdata` is 4-byte wide (32-bits), `tx_tkeep` is 4-bit wide;
* if `TX_EW=3` , then `tx_tdata` is 8-byte wide (64-bits), `tx_tkeep` is 8-bit wide.
* `tx_tdata` is in LITTLE ENDIAN. When Host-PC receiving data, The low byte on `tx_tdata` is earlier, the high byte on `tx_tdata` is later.
* `tx_tkeep` is byte valid signal, which specifies which bytes in `tx_tdata` are valid (need to send) and which bytes are invalid (needn't to send)
* For example, when `tx_tdata` is 4-byte wide, `tx_tkeep` is 4-bit wide:
* `tx_tkeep[0]=1` means `tx_tdata[7:0]` need to be sent. Otherwise it will not be sent
* `tx_tkeep[1]=1` means `tx_tdata[15:8]` need to be sent. Otherwise it will not be sent
* `tx_tkeep[2]=1` means `tx_tdata[23:16]` need to be sent. Otherwise it will not be sent
* `tx_tkeep[3]=1` means `tx_tdata[31:24]` need to be sent. Otherwise it will not be sent
* The bits of `tx_tkeep` can be specified arbitrarily. For example, this strange value `tx_tkeep=4'b1010` is also supported.
* `tx_tlast` is the packet end signal of AXI-stream. It is used here to determine whether the current data should be sent immediately or wait for enough bit width to be saved before sending. For example, when the width of `tx_tdata` is 4 bytes, if the user only wants to send 1 byte (such as setting `tx_tkeep=4'b0001`). At this point, if `tx_tlast=0` , the byte will be temporarily stored in the module and will be sent after the subsequent data arrives to make up for 4 bytes. If user wants to send it immediately, they should set `tx_tlast=1` when sending `tx_tdata`
* If users don't want to make it so complicated, they can set `tx_tkeep` to all 1 and set ` tx_tlast` to 1. In this case, although only data with a multiple length of (`2^TX_EW`) can be sent, it is simpler to use.
> :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<<RX_EW)-1:0] rx_tdata;
output wire [(1<<RX_EW)-1:0] rx_tkeep;
output wire rx_tlast;
```
Firstly, you should provide a clock on `rx_clk` (no special limitations of frequency, usually the main clock of FPGA). The AXI-stream is synchronized with the clock.
`rx_clk` can be as same as or different from `tx_clk` .
Note the following:
* When `rx_tready=1`, it means that the user is ready to fetch out a data.
* When `rx_tvalid=1`, it means that the module wants to send a data to the user. Meanwhile, a valid data appeared on `rx_tdata` ,`rx_tkeep` and `rx_tlast` . When `rx_tvalid=0`, the receive buffer is empty and no more data can be fetched out.
* `rx_tvalid` and `rx_tready` are a pair of handshake signals. When both are 1, `rx_tdata` is successfully fetched out from the receive buffer.
* As mentioned earlier, the parameter `RX_EW` determines the data bit width of the AXI-stream:
* if `RX_EW=0` , then `rx_tdata` is 1-byte wide (8-bits), `rx_tkeep` is 1-bit wide;
* if `RX_EW=1` , then `rx_tdata` is 2-byte wide (16-bits), `rx_tkeep` is 2-bit wide;
* if `RX_EW=2` , then `rx_tdata` is 4-byte wide (32-bits), `rx_tkeep` is 4-bit wide;
* if `RX_EW=3` , then `rx_tdata` is 8-byte wide (64-bits), `rx_tkeep` is 8-bit wide.
* `rx_tdata` is in LITTLE ENDIAN. When Host-PC sending data, The earlier byte is on the low byte of `rx_tdata` , the later byte is on the high byte of `rx_tdata` .
* `rx_tkeep` is the byte valid signal of AXI-stream, usually all 1. Only when Host-PC sends data with non integer multiple length of `(2^RX_EW)` bytes can it be not all 1.
* `rx_tlast` is AXI-stream packet last signal. `rx_tlast=1` indicates that there is no more data in the FTDI chip (all the data sent by the computer has been taken), and the current data is currently the last data.
* When `rx_tlast=1` , `rx_tkeep` may not be all 1. When `rx_tlast=0` , `rx_tkeep` must be all 1.
* For example, when `rx_tdata` is 4-byte wide, when meeting `tx_tlast=1` , then `rx_tkeep` may be:
* `rx_tkeep=4'b0001` indicate that only `rx_tdata[7:0]` (1byte) is valid.
* `rx_tkeep=4'b0011` indicate that only `rx_tdata[15:0]` (2bytes) is valid.
* `rx_tkeep=4'b0111` indicate that only `rx_tdata[23:0]` (3bytes) is valid.
* `rx_tkeep=4'b1111` indicate that all `rx_tdata[31:0]` (4bytes) is valid.
* `rx_tkeep` can only be above-mentioned patterns, and wil not be strange like `rx_tkeep=4'b1010` . In contrast, the `tx_tkeep` signal of the sending interface mentioned above supports these strange values.
> :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 chiphttp://www.ftdichip.cn/Products/ICs/FT232H.htm
* FT232H software examplehttp://www.ftdichip.cn/Support/SoftwareExamples/CodeExamples.htm
* FT600 chiphttp://www.ftdichip.cn/Products/ICs/FT600.html
* FT600/FT601 software example http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm
 
 
 
 
<span id="cn">FTDI 245fifo controller</span>
===========================
[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<<CHIP_EW)-1:0] ftdi_data;
inout [(1<<CHIP_EW)-1:0] ftdi_be;
```
这些信号应连接到 FTDI 芯片的引脚上。
* FTDI 芯片工作在 sync-245-fifo 模式时,引脚名称见芯片 Datasheet
* 对于 FT232H ,见 [FT232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf) 第9页
* 对于 FT2232H ,见 [FT2232H DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT2232H.pdf) 第9页
* 对于 FT600 和 FT601 ,见 [FT600/FT601 DataSheet](https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf) 第 7\~10 页。
* `ftdi_be` 是 byte enable 信号。FT232H / FT2232H 没有这个信号,此时请忽略它。
* 这些引脚的时序由模块维护,不需要你关注。如果你感兴趣,可以运行[模块仿真](#模块仿真) 。
 
## 模块接口AXI-stream slave 发送接口
发送接口是标准 AXI-stream slave ,用户通过它发送数据给 FTDI 芯片 (最终会发送给电脑) 。对应代码中的:
```verilog
// AXI-stream slave 发送接口 ------------------------------
input wire tx_clk; // AXI-stream 时钟
output wire tx_tready;
input wire tx_tvalid;
input wire [(8<<TX_EW)-1:0] tx_tdata;
input wire [(1<<TX_EW)-1:0] tx_tkeep;
input wire tx_tlast;
```
首先,需要提供一个 `tx_clk` 时钟 (频率不限,一般都提供 FPGA 主时钟) 。该 AXI-stream 同步于该时钟。
AXI-stream 的操作时序详见 [《AXI-stream specification》](https://documentation-service.arm.com/static/60d5b244677cf7536a55c23e) 。这里简要说明如下:
* `tx_tready=1` 时,说明模块已经准备好接收发送数据。`tx_ready=0` 时,发送缓存暂时满,不能接收更多数据。
* `tx_tvalid=1` 时,说明用户想发送一个数据到模块内部的发送缓存。同时,`tx_tdata` 应产生有效数据。
* `tx_tvalid``tx_tready` 是一对握手信号。二者同时为 1 时, `tx_tdata` 成功发送。
* 上文已经提到,参数 `TX_EW` 决定了该 AXI-stream 的数据位宽:
* `TX_EW=0` 时,`tx_tdata` 宽度为 1 字节 (8-bit) `tx_tkeep` 宽度为 1-bit
* `TX_EW=1` 时,`tx_tdata` 宽度为 2 字节 (16-bit) `tx_tkeep` 宽度为 2-bit
* `TX_EW=2` 时,`tx_tdata` 宽度为 4 字节 (32-bit) `tx_tkeep` 宽度为 4-bit
* `TX_EW=3` 时,`tx_tdata` 宽度为 8 字节 (64-bit) `tx_tkeep` 宽度为 8-bit 。
* `tx_tdata` 是小端序。也即电脑收到字节流时, `tx_tdata` 的低位字节靠前,高位字节靠后。
* `tx_tkeep` 是 AXI-stream 的有效信号,用来指定 `tx_tdata` 的哪些字节有效 (需要发送) ,哪些字节无效 (不需要发送) 。
* 例如,当 `tx_tdata` 的位宽为 4 字节时,`tx_tkeep` 为 4-bit 宽:
* `tx_tkeep[0]=1` 说明 `tx_tdata[7:0]` 需要发送,否则忽略不发送。
* `tx_tkeep[1]=1` 说明 `tx_tdata[15:8]` 需要发送,否则忽略不发送。
* `tx_tkeep[2]=1` 说明 `tx_tdata[23:16]` 需要发送,否则忽略不发送。
* `tx_tkeep[3]=1` 说明 `tx_tdata[31:24]` 需要发送,否则忽略不发送。
* `tx_tkeep` 的各个 bit 可以任意指定,例如 `tx_tkeep=4'b1010` 这种奇怪的取值也是支持的。
* `tx_tlast` 是 AXI-stream 的包结尾信号。在这里用来决定当前数据是立刻发送,还是等待攒够了位宽再发送。例如,当 `tx_tdata` 的位宽为 4 字节时,如果用户每次只想发送 1 字节 (例如令 `tx_tkeep=4'b0001` )。此时如果 `tx_tlast=0` ,则该字节会暂时存在模块里,等待后续数据到来凑够 4 字节后再发送,而不会立刻发送。如果用户需要立刻发送,应该在数据有效时让 `tx_tlast=1`
* 如果用户不想搞的这么复杂,可以让 `tx_tkeep`=全1 , `tx_last`=1 ,此时每次只能发送 `tx_tdata` 位宽个字节,每次发送的数据都立刻发送,例如若 `tx_tdata` 的位宽为 4 字节,则无法实现 4 的非整数倍字节的数据发送,但用起来也更简单。
> :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<<RX_EW)-1:0] rx_tdata;
output wire [(1<<RX_EW)-1:0] rx_tkeep;
output wire rx_tlast;
```
首先,需要提供一个 `rx_clk` 时钟 (频率不限,一般都提供 FPGA 主时钟) 。该 AXI-stream 同步于该时钟。
`rx_clk` 可以和 `tx_clk` 是同一个时钟或不同时钟。
对 AXI-stream 各信号简要说明如下:
* `rx_tready=1` 时,说明用户已经准备好拿出一个数据。
* `rx_tvalid=1` 时,说明模块想发送一个数据给用户。同时,`rx_tdata` 上出现有效数据。而 `rx_tvalid=0` 时,接收缓存空,拿不出更多数据。
* `rx_tvalid``rx_tready` 是一对握手信号。二者同时为 1 时, `rx_tdata` 才成功从接收缓存中取出。
* 上文已经提到,参数 `RX_EW` 决定了该 AXI-stream 的数据位宽:
* `RX_EW=0` 时,`rx_tdata` 宽度为 1 字节 (8-bit) `rx_tkeep` 宽度为 1-bit
* `RX_EW=1` 时,`rx_tdata` 宽度为 2 字节 (16-bit) `rx_tkeep` 宽度为 2-bit
* `RX_EW=2` 时,`rx_tdata` 宽度为 4 字节 (32-bit) `rx_tkeep` 宽度为 4-bit
* `RX_EW=3` 时,`rx_tdata` 宽度为 8 字节 (64-bit) `rx_tkeep` 宽度为 8-bit 。
* `rx_tdata` 是小端序。也即 `rx_tdata` 的低位字节靠前,高位字节靠后。
* `rx_tkeep` 是 AXI-stream 的字节有效信号一般都是全1。只有当电脑发送了非整数位宽的数据时才可能是非全1 。
* `rx_tlast` 是 AXI-stream 的包结尾信号。在这里, `tlast=1` 说明 FTDI 芯片里已经没有更多的数据了 (电脑发送的所有数据已经暂时被拿完了) ,当前数据暂时是最后一拍数据了。
* 当 `rx_tlast=1` 时,`rx_tkeep` 可能不是全1 。当 `rx_tlast=0` 时,`rx_tkeep` 一定是全1。
* 例如当 `rx_tdata` 的位宽为 4 字节时,若遇到 `rx_tlast=1` ,则 `rx_tkeep` 可能有以下取值:
* `rx_tkeep=4'b0001` 意为只有 `rx_tdata[7:0]` 这1字节有效
* `rx_tkeep=4'b0011` 意为只有 `rx_tdata[15:0]` 这2字节有效
* `rx_tkeep=4'b0111` 意为只有 `rx_tdata[23:0]` 这3字节有效
* `rx_tkeep=4'b1111` (全1) 意为 `rx_tdata[31:0]` 这4字节都有效。
* `rx_tkeep` 只可能是如上这种低位为1高位为0的取值不会出现 `4'b1010` 这种奇怪取值也不可能是全0 。相比之下,上文的发送接口的 `tx_tkeep` 信号则可以很自由的取值。
> :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

View File

@ -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

View File

@ -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}

View File

@ -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<<AXIS_EW)-1:0] tdata;
wire [(1<<AXIS_EW)-1:0] tkeep;
wire tlast;
//-----------------------------------------------------------------------------------------------------------------------------
// FTDI USB chip's 245fifo mode controller
//-----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( AXIS_EW ), // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.TX_EA ( 10 ), // TX FIFO depth = 2^TX_AEXP = 2^10 = 1024
.RX_EW ( AXIS_EW ), // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.RX_EA ( 10 ), // RX FIFO depth = 2^RX_AEXP = 2^10 = 1024
.CHIP_TYPE ( "FTx232H" )
) u_ftdi_245fifo_top (
.rstn_async ( 1'b1 ),
.tx_clk ( clk ),
.tx_tready ( tready ),
.tx_tvalid ( tvalid ),
.tx_tdata ( tdata ),
.tx_tkeep ( tkeep ),
.tx_tlast ( tlast ),
.rx_clk ( clk ),
.rx_tready ( tready ),
.rx_tvalid ( tvalid ),
.rx_tdata ( tdata ),
.rx_tkeep ( tkeep ),
.rx_tlast ( tlast ),
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ) // FT232H do not have BE signals
);
//-----------------------------------------------------------------------------------------------------------------------------
// show the low 4-bit of the last received data on LED
//-----------------------------------------------------------------------------------------------------------------------------
reg [2:0] tdata_d = 3'h0;
always @ (posedge clk)
if (tvalid)
tdata_d <= tdata[2:0];
assign LED[2:0] = tdata_d;
//-----------------------------------------------------------------------------------------------------------------------------
// if ftdi_clk continuous run, then beat will blink. The function of this module is to observe whether ftdi_clk is running
//-----------------------------------------------------------------------------------------------------------------------------
clock_beat # (
.CLK_FREQ ( 60000000 ),
.BEAT_FREQ ( 5 )
) u_ftdi_clk_beat (
.clk ( ftdi_clk ),
.beat ( LED[3] )
);
endmodule

View File

@ -0,0 +1,138 @@
//--------------------------------------------------------------------------------------------------------
// Module : fpga_top_ft232h_rx_crc
// 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 will receive a data block from FTDI chip, and calculate its CRC.
// When meeting 0xFF, it thinks that the data block ends, and then send the block's CRC to FTDI chip.
//--------------------------------------------------------------------------------------------------------
module fpga_top_ft232h_rx_crc (
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 RX_AXIS_EW = 1;
wire rx_tready;
wire rx_tvalid;
wire [(8<<RX_AXIS_EW)-1:0] rx_tdata;
wire [(1<<RX_AXIS_EW)-1:0] rx_tkeep;
wire tx_tready;
wire tx_tvalid;
wire [ 31:0] tx_tdata;
//-----------------------------------------------------------------------------------------------------------------------------
// FTDI USB chip's 245fifo mode controller
//-----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( 2 ), // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.TX_EA ( 8 ), // TX FIFO depth = 2^TX_AEXP = 2^10 = 1024
.RX_EW ( RX_AXIS_EW ), // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.RX_EA ( 10 ), // RX FIFO depth = 2^RX_AEXP = 2^10 = 1024
.CHIP_TYPE ( "FTx232H" )
) u_ftdi_245fifo_top (
.rstn_async ( 1'b1 ),
.tx_clk ( clk ),
.tx_tready ( tx_tready ),
.tx_tvalid ( tx_tvalid ),
.tx_tdata ( tx_tdata ),
.tx_tkeep ( 4'b1111 ),
.tx_tlast ( 1'b1 ),
.rx_clk ( clk ),
.rx_tready ( rx_tready ),
.rx_tvalid ( rx_tvalid ),
.rx_tdata ( rx_tdata ),
.rx_tkeep ( rx_tkeep ),
.rx_tlast ( ),
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ) // FT232H do not have BE signals
);
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
rx_calc_crc #(
.IEW ( RX_AXIS_EW )
) u_rx_calc_crc (
.rstn ( 1'b1 ),
.clk ( clk ),
.i_tready ( rx_tready ),
.i_tvalid ( rx_tvalid ),
.i_tdata ( rx_tdata ),
.i_tkeep ( rx_tkeep ),
.o_tready ( tx_tready ),
.o_tvalid ( tx_tvalid ),
.o_tdata ( tx_tdata )
);
//-----------------------------------------------------------------------------------------------------------------------------
// show the low 3-bit of the last received data on LED
//-----------------------------------------------------------------------------------------------------------------------------
reg [2:0] tdata_d = 3'h0;
always @ (posedge clk)
if (rx_tvalid)
tdata_d <= rx_tdata[2:0];
assign LED[2:0] = tdata_d;
//-----------------------------------------------------------------------------------------------------------------------------
// if ftdi_clk continuous run, then beat will blink. The function of this module is to observe whether ftdi_clk is running
//-----------------------------------------------------------------------------------------------------------------------------
clock_beat # (
.CLK_FREQ ( 60000000 ),
.BEAT_FREQ ( 5 )
) u_ftdi_clk_beat (
.clk ( ftdi_clk ),
.beat ( LED[3] )
);
endmodule

View File

@ -0,0 +1,136 @@
//--------------------------------------------------------------------------------------------------------
// Module : fpga_top_ft232h_tx_mass
// 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 will receive 4 bytes from FTDI chip,
// and then regard the 4 bytes as a length, send length of bytes to FTDI chip
//--------------------------------------------------------------------------------------------------------
module fpga_top_ft232h_tx_mass (
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)
//-----------------------------------------------------------------------------------------------------------------------------
wire rx_tready;
wire rx_tvalid;
wire [ 7:0] rx_tdata;
wire tx_tready;
wire tx_tvalid;
wire [31:0] tx_tdata;
wire [ 3:0] tx_tkeep;
wire tx_tlast;
//-----------------------------------------------------------------------------------------------------------------------------
// FTDI USB chip's 245fifo mode controller
//-----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( 2 ), // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.TX_EA ( 10 ), // TX FIFO depth = 2^TX_AEXP = 2^10 = 1024
.RX_EW ( 0 ), // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.RX_EA ( 8 ), // RX FIFO depth = 2^RX_AEXP = 2^10 = 1024
.CHIP_TYPE ( "FTx232H" )
) u_ftdi_245fifo_top (
.rstn_async ( 1'b1 ),
.tx_clk ( clk ),
.tx_tready ( tx_tready ),
.tx_tvalid ( tx_tvalid ),
.tx_tdata ( tx_tdata ),
.tx_tkeep ( tx_tkeep ),
.tx_tlast ( tx_tlast ),
.rx_clk ( clk ),
.rx_tready ( rx_tready ),
.rx_tvalid ( rx_tvalid ),
.rx_tdata ( rx_tdata ),
.rx_tkeep ( ),
.rx_tlast ( ),
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ) // FT232H do not have BE signals
);
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
tx_specified_len u_tx_specified_len (
.rstn ( 1'b1 ),
.clk ( clk ),
.i_tready ( rx_tready ),
.i_tvalid ( rx_tvalid ),
.i_tdata ( rx_tdata ),
.o_tready ( tx_tready ),
.o_tvalid ( tx_tvalid ),
.o_tdata ( tx_tdata ),
.o_tkeep ( tx_tkeep ),
.o_tlast ( tx_tlast )
);
//-----------------------------------------------------------------------------------------------------------------------------
// show the low 3-bit of the last received data on LED
//-----------------------------------------------------------------------------------------------------------------------------
reg [2:0] tdata_d = 3'h0;
always @ (posedge clk)
if (rx_tvalid)
tdata_d <= rx_tdata[2:0];
assign LED[2:0] = tdata_d;
//-----------------------------------------------------------------------------------------------------------------------------
// if ftdi_clk continuous run, then beat will blink. The function of this module is to observe whether ftdi_clk is running
//-----------------------------------------------------------------------------------------------------------------------------
clock_beat # (
.CLK_FREQ ( 60000000 ),
.BEAT_FREQ ( 5 )
) u_ftdi_clk_beat (
.clk ( ftdi_clk ),
.beat ( LED[3] )
);
endmodule

View File

@ -0,0 +1,91 @@
//--------------------------------------------------------------------------------------------------------
// Module : rx_calc_crc
// Type : synthesizable
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: receive data block from AXI-stream slave, calculate its CRC simultaneously
// when meeting 0xFF, End accepting the current data block,
// and then send the CRC value of that block through AXI-stream master
// this module will called by fpga_top_ft600_rx_crc.v or fpga_top_ft232h_rx_crc.v
//--------------------------------------------------------------------------------------------------------
module rx_calc_crc #(
parameter IEW = 2 // AXI byte width is 1<<BW, bit width is 8<<BW
) (
input wire rstn,
input wire clk,
// AXI-stream slave
output wire i_tready,
input wire i_tvalid,
input wire [(8<<IEW)-1:0] i_tdata,
input wire [(1<<IEW)-1:0] i_tkeep,
// AXI-stream master
input wire o_tready,
output wire o_tvalid,
output wire [31:0] o_tdata
);
function [31:0] calculate_crc;
input [31:0] crc;
input [ 7:0] inbyte;
reg [31:0] TABLE_CRC [15:0];
begin
TABLE_CRC[0] = 'h00000000; TABLE_CRC[1] = 'h1db71064; TABLE_CRC[2] = 'h3b6e20c8; TABLE_CRC[3] = 'h26d930ac;
TABLE_CRC[4] = 'h76dc4190; TABLE_CRC[5] = 'h6b6b51f4; TABLE_CRC[6] = 'h4db26158; TABLE_CRC[7] = 'h5005713c;
TABLE_CRC[8] = 'hedb88320; TABLE_CRC[9] = 'hf00f9344; TABLE_CRC[10]= 'hd6d6a3e8; TABLE_CRC[11]= 'hcb61b38c;
TABLE_CRC[12]= 'h9b64c2b0; TABLE_CRC[13]= 'h86d3d2d4; TABLE_CRC[14]= 'ha00ae278; TABLE_CRC[15]= 'hbdbdf21c;
calculate_crc = crc ^ {24'h0, inbyte};
calculate_crc = TABLE_CRC[calculate_crc[3:0]] ^ (calculate_crc >> 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<<IEW); i=i+1) begin
if (i_tkeep[i]) begin
len_tmp = len_tmp + 1;
crc_tmp = calculate_crc(crc_tmp, i_tdata[8*i +: 8] );
if ( i_tdata[8*i +: 8] == 8'hFF ) // when meet byte=0xFF
state <= 1'b1;
end
end
len <= len_tmp;
crc <= crc_tmp;
end
end else begin // outputting CRC
if (o_tready) begin
state <= 1'b0;
crc <= 'hFFFFFFFF;
len <= 0;
end
end
end
assign i_tready = (state == 1'b0);
assign o_tvalid = (state == 1'b1);
assign o_tdata = crc;
endmodule

View File

@ -0,0 +1,95 @@
//--------------------------------------------------------------------------------------------------------
// Module : tx_specified_len
// Type : synthesizable
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: receive 4 bytes from AXI-stream slave,
// then regard the 4 bytes as a length, send length of bytes on AXI-stream master
// this module will called by fpga_top_ft600_tx_mass.v or fpga_top_ft232h_tx_mass.v
//--------------------------------------------------------------------------------------------------------
module tx_specified_len (
input wire rstn,
input wire clk,
// AXI-stream slave
output wire i_tready,
input wire i_tvalid,
input wire [ 7:0] i_tdata,
// AXI-stream master
input wire o_tready,
output wire o_tvalid,
output wire [31:0] o_tdata,
output wire [ 3:0] o_tkeep,
output wire o_tlast
);
localparam [2:0] RX_BYTE0 = 3'd0,
RX_BYTE1 = 3'd1,
RX_BYTE2 = 3'd2,
RX_BYTE3 = 3'd3,
TX_DATA = 3'd4;
reg [ 2:0] state = RX_BYTE0;
reg [31:0] length = 0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
state <= RX_BYTE0;
length <= 0;
end else begin
case (state)
RX_BYTE0 : if (i_tvalid) begin
length[ 7: 0] <= i_tdata;
state <= RX_BYTE1;
end
RX_BYTE1 : if (i_tvalid) begin
length[15: 8] <= i_tdata;
state <= RX_BYTE2;
end
RX_BYTE2 : if (i_tvalid) begin
length[23:16] <= i_tdata;
state <= RX_BYTE3;
end
RX_BYTE3 : if (i_tvalid) begin
length[31:24] <= i_tdata;
state <= TX_DATA;
end
default : // TX_DATA :
if (o_tready) begin
if (length >= 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

View File

@ -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

View File

@ -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}

View File

@ -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<<AXIS_EW)-1:0] tdata;
wire [(1<<AXIS_EW)-1:0] tkeep;
wire tlast;
//-----------------------------------------------------------------------------------------------------------------------------
// FTDI USB chip's 245fifo mode controller
//-----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( AXIS_EW ), // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.TX_EA ( 10 ), // TX FIFO depth = 2^TX_AEXP = 2^10 = 1024
.RX_EW ( AXIS_EW ), // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.RX_EA ( 10 ), // RX FIFO depth = 2^RX_AEXP = 2^10 = 1024
.CHIP_TYPE ( "FT600" )
) u_ftdi_245fifo_top (
.rstn_async ( 1'b1 ),
.tx_clk ( clk ),
.tx_tready ( tready ),
.tx_tvalid ( tvalid ),
.tx_tdata ( tdata ),
.tx_tkeep ( tkeep ),
.tx_tlast ( tlast ),
.rx_clk ( clk ),
.rx_tready ( tready ),
.rx_tvalid ( tvalid ),
.rx_tdata ( tdata ),
.rx_tkeep ( tkeep ),
.rx_tlast ( tlast ),
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ftdi_be )
);
//-----------------------------------------------------------------------------------------------------------------------------
// show the low 3-bit of the last received data on LED
//-----------------------------------------------------------------------------------------------------------------------------
reg [2:0] tdata_d = 3'h0;
always @ (posedge clk)
if (tvalid)
tdata_d <= tdata[2:0];
assign LED[2:0] = tdata_d;
//-----------------------------------------------------------------------------------------------------------------------------
// if ftdi_clk continuous run, then beat will blink. The function of this module is to observe whether ftdi_clk is running
//-----------------------------------------------------------------------------------------------------------------------------
clock_beat # (
.CLK_FREQ ( 100000000 ),
.BEAT_FREQ ( 5 )
) u_ftdi_clk_beat (
.clk ( ftdi_clk ),
.beat ( LED[3] )
);
endmodule

View File

@ -0,0 +1,143 @@
//--------------------------------------------------------------------------------------------------------
// Module : fpga_top_ft600_rx_crc
// 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 will receive a data block from FTDI chip, and calculate its CRC.
// When meeting 0xFF, it thinks that the data block ends, and then send the block's CRC to FTDI chip.
//--------------------------------------------------------------------------------------------------------
module fpga_top_ft600_rx_crc (
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 RX_AXIS_EW = 2;
wire rx_tready;
wire rx_tvalid;
wire [(8<<RX_AXIS_EW)-1:0] rx_tdata;
wire [(1<<RX_AXIS_EW)-1:0] rx_tkeep;
wire tx_tready;
wire tx_tvalid;
wire [ 31:0] tx_tdata;
//-----------------------------------------------------------------------------------------------------------------------------
// FTDI USB chip's 245fifo mode controller
//-----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( 2 ), // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.TX_EA ( 8 ), // TX FIFO depth = 2^TX_AEXP = 2^10 = 1024
.RX_EW ( RX_AXIS_EW ), // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.RX_EA ( 12 ), // RX FIFO depth = 2^RX_AEXP = 2^10 = 1024
.CHIP_TYPE ( "FT600" )
) u_ftdi_245fifo_top (
.rstn_async ( 1'b1 ),
.tx_clk ( clk ),
.tx_tready ( tx_tready ),
.tx_tvalid ( tx_tvalid ),
.tx_tdata ( tx_tdata ),
.tx_tkeep ( 4'b1111 ),
.tx_tlast ( 1'b1 ),
.rx_clk ( clk ),
.rx_tready ( rx_tready ),
.rx_tvalid ( rx_tvalid ),
.rx_tdata ( rx_tdata ),
.rx_tkeep ( rx_tkeep ),
.rx_tlast ( ),
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ftdi_be )
);
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
rx_calc_crc #(
.IEW ( RX_AXIS_EW )
) u_rx_calc_crc (
.rstn ( 1'b1 ),
.clk ( clk ),
.i_tready ( rx_tready ),
.i_tvalid ( rx_tvalid ),
.i_tdata ( rx_tdata ),
.i_tkeep ( rx_tkeep ),
.o_tready ( tx_tready ),
.o_tvalid ( tx_tvalid ),
.o_tdata ( tx_tdata )
);
//-----------------------------------------------------------------------------------------------------------------------------
// show the low 3-bit of the last received data on LED
//-----------------------------------------------------------------------------------------------------------------------------
reg [2:0] tdata_d = 3'h0;
always @ (posedge clk)
if (rx_tvalid)
tdata_d <= rx_tdata[2:0];
assign LED[2:0] = tdata_d;
//-----------------------------------------------------------------------------------------------------------------------------
// if ftdi_clk continuous run, then beat will blink. The function of this module is to observe whether ftdi_clk is running
//-----------------------------------------------------------------------------------------------------------------------------
clock_beat # (
.CLK_FREQ ( 100000000 ),
.BEAT_FREQ ( 5 )
) u_ftdi_clk_beat (
.clk ( ftdi_clk ),
.beat ( LED[3] )
);
endmodule

View File

@ -0,0 +1,141 @@
//--------------------------------------------------------------------------------------------------------
// Module : fpga_top_ft600_tx_mass
// 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 will receive 4 bytes from FTDI chip,
// and then regard the 4 bytes as a length, send length of bytes to FTDI chip
//--------------------------------------------------------------------------------------------------------
module fpga_top_ft600_tx_mass (
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)
//-----------------------------------------------------------------------------------------------------------------------------
wire rx_tready;
wire rx_tvalid;
wire [ 7:0] rx_tdata;
wire tx_tready;
wire tx_tvalid;
wire [31:0] tx_tdata;
wire [ 3:0] tx_tkeep;
wire tx_tlast;
//-----------------------------------------------------------------------------------------------------------------------------
// FTDI USB chip's 245fifo mode controller
//-----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( 2 ), // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.TX_EA ( 10 ), // TX FIFO depth = 2^TX_AEXP = 2^10 = 1024
.RX_EW ( 0 ), // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
.RX_EA ( 8 ), // RX FIFO depth = 2^RX_AEXP = 2^10 = 1024
.CHIP_TYPE ( "FT600" )
) u_ftdi_245fifo_top (
.rstn_async ( 1'b1 ),
.tx_clk ( clk ),
.tx_tready ( tx_tready ),
.tx_tvalid ( tx_tvalid ),
.tx_tdata ( tx_tdata ),
.tx_tkeep ( tx_tkeep ),
.tx_tlast ( tx_tlast ),
.rx_clk ( clk ),
.rx_tready ( rx_tready ),
.rx_tvalid ( rx_tvalid ),
.rx_tdata ( rx_tdata ),
.rx_tkeep ( ),
.rx_tlast ( ),
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ftdi_be )
);
//-----------------------------------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------------------------------
tx_specified_len u_tx_specified_len (
.rstn ( 1'b1 ),
.clk ( clk ),
.i_tready ( rx_tready ),
.i_tvalid ( rx_tvalid ),
.i_tdata ( rx_tdata ),
.o_tready ( tx_tready ),
.o_tvalid ( tx_tvalid ),
.o_tdata ( tx_tdata ),
.o_tkeep ( tx_tkeep ),
.o_tlast ( tx_tlast )
);
//-----------------------------------------------------------------------------------------------------------------------------
// show the low 3-bit of the last received data on LED
//-----------------------------------------------------------------------------------------------------------------------------
reg [2:0] tdata_d = 3'h0;
always @ (posedge clk)
if (rx_tvalid)
tdata_d <= rx_tdata[2:0];
assign LED[2:0] = tdata_d;
//-----------------------------------------------------------------------------------------------------------------------------
// if ftdi_clk continuous run, then beat will blink. The function of this module is to observe whether ftdi_clk is running
//-----------------------------------------------------------------------------------------------------------------------------
clock_beat # (
.CLK_FREQ ( 100000000 ),
.BEAT_FREQ ( 5 )
) u_ftdi_clk_beat (
.clk ( ftdi_clk ),
.beat ( LED[3] )
);
endmodule

View File

@ -0,0 +1,91 @@
//--------------------------------------------------------------------------------------------------------
// Module : rx_calc_crc
// Type : synthesizable
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: receive data block from AXI-stream slave, calculate its CRC simultaneously
// when meeting 0xFF, End accepting the current data block,
// and then send the CRC value of that block through AXI-stream master
// this module will called by fpga_top_ft600_rx_crc.v or fpga_top_ft232h_rx_crc.v
//--------------------------------------------------------------------------------------------------------
module rx_calc_crc #(
parameter IEW = 2 // AXI byte width is 1<<BW, bit width is 8<<BW
) (
input wire rstn,
input wire clk,
// AXI-stream slave
output wire i_tready,
input wire i_tvalid,
input wire [(8<<IEW)-1:0] i_tdata,
input wire [(1<<IEW)-1:0] i_tkeep,
// AXI-stream master
input wire o_tready,
output wire o_tvalid,
output wire [31:0] o_tdata
);
function [31:0] calculate_crc;
input [31:0] crc;
input [ 7:0] inbyte;
reg [31:0] TABLE_CRC [15:0];
begin
TABLE_CRC[0] = 'h00000000; TABLE_CRC[1] = 'h1db71064; TABLE_CRC[2] = 'h3b6e20c8; TABLE_CRC[3] = 'h26d930ac;
TABLE_CRC[4] = 'h76dc4190; TABLE_CRC[5] = 'h6b6b51f4; TABLE_CRC[6] = 'h4db26158; TABLE_CRC[7] = 'h5005713c;
TABLE_CRC[8] = 'hedb88320; TABLE_CRC[9] = 'hf00f9344; TABLE_CRC[10]= 'hd6d6a3e8; TABLE_CRC[11]= 'hcb61b38c;
TABLE_CRC[12]= 'h9b64c2b0; TABLE_CRC[13]= 'h86d3d2d4; TABLE_CRC[14]= 'ha00ae278; TABLE_CRC[15]= 'hbdbdf21c;
calculate_crc = crc ^ {24'h0, inbyte};
calculate_crc = TABLE_CRC[calculate_crc[3:0]] ^ (calculate_crc >> 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<<IEW); i=i+1) begin
if (i_tkeep[i]) begin
len_tmp = len_tmp + 1;
crc_tmp = calculate_crc(crc_tmp, i_tdata[8*i +: 8] );
if ( i_tdata[8*i +: 8] == 8'hFF ) // when meet byte=0xFF
state <= 1'b1;
end
end
len <= len_tmp;
crc <= crc_tmp;
end
end else begin // outputting CRC
if (o_tready) begin
state <= 1'b0;
crc <= 'hFFFFFFFF;
len <= 0;
end
end
end
assign i_tready = (state == 1'b0);
assign o_tvalid = (state == 1'b1);
assign o_tdata = crc;
endmodule

View File

@ -0,0 +1,95 @@
//--------------------------------------------------------------------------------------------------------
// Module : tx_specified_len
// Type : synthesizable
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: receive 4 bytes from AXI-stream slave,
// then regard the 4 bytes as a length, send length of bytes on AXI-stream master
// this module will called by fpga_top_ft600_tx_mass.v or fpga_top_ft232h_tx_mass.v
//--------------------------------------------------------------------------------------------------------
module tx_specified_len (
input wire rstn,
input wire clk,
// AXI-stream slave
output wire i_tready,
input wire i_tvalid,
input wire [ 7:0] i_tdata,
// AXI-stream master
input wire o_tready,
output wire o_tvalid,
output wire [31:0] o_tdata,
output wire [ 3:0] o_tkeep,
output wire o_tlast
);
localparam [2:0] RX_BYTE0 = 3'd0,
RX_BYTE1 = 3'd1,
RX_BYTE2 = 3'd2,
RX_BYTE3 = 3'd3,
TX_DATA = 3'd4;
reg [ 2:0] state = RX_BYTE0;
reg [31:0] length = 0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
state <= RX_BYTE0;
length <= 0;
end else begin
case (state)
RX_BYTE0 : if (i_tvalid) begin
length[ 7: 0] <= i_tdata;
state <= RX_BYTE1;
end
RX_BYTE1 : if (i_tvalid) begin
length[15: 8] <= i_tdata;
state <= RX_BYTE2;
end
RX_BYTE2 : if (i_tvalid) begin
length[23:16] <= i_tdata;
state <= RX_BYTE3;
end
RX_BYTE3 : if (i_tvalid) begin
length[31:24] <= i_tdata;
state <= TX_DATA;
end
default : // TX_DATA :
if (o_tready) begin
if (length >= 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

View File

@ -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<BW; i=i+1) begin
if (tkeep[i] == 1'b0)
has_zero = 1'b1;
if (has_zero & tkeep[i]) begin $display("*** error : %s AXI-stream not packing, tkeep=%b", NAME, tkeep); $stop; end
end
if (tkeep == 0) begin $display("*** error : %s AXI-stream not packing, tkeep=0", NAME); $stop; end
end
end
end endgenerate
endmodule

View File

@ -0,0 +1,82 @@
//--------------------------------------------------------------------------------------------------------
// Module : axi_stream_downsizing
// 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 -> 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<<IEW)-1:0] i_tdata,
input wire [(1<<IEW)-1:0] i_tkeep,
input wire i_tlast,
// AXI-stream master
input wire o_tready,
output wire o_tvalid,
output wire [(8<<OEW)-1:0] o_tdata,
output wire [(1<<OEW)-1:0] o_tkeep,
output wire o_tlast
);
reg [(8<<IEW)-1:0] tmp_data = {(8<<IEW){1'b0}};
reg [(1<<IEW)-1:0] tmp_keep = {(1<<IEW){1'b0}};
reg tmp_last = 1'b0;
wire [(8<<IEW)-1:0] tmp_data_next;
wire [(1<<IEW)-1:0] tmp_keep_next;
assign {tmp_data_next, o_tdata} = {{(8<<OEW){1'b0}}, tmp_data};
assign {tmp_keep_next, o_tkeep} = {{(1<<OEW){1'b0}}, tmp_keep};
assign o_tlast = ( tmp_keep_next == {(1<<IEW){1'b0}} ) ? tmp_last : 1'b0;
assign o_tvalid = (|o_tkeep);
assign i_tready = ( tmp_keep == {(1<<IEW){1'b0}} ) ? 1'b1 :
( tmp_keep_next == {(1<<IEW){1'b0}} ) ? o_tready :
1'b0 ;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
tmp_data <= {(8<<IEW){1'b0}};
tmp_keep <= {(1<<IEW){1'b0}};
tmp_last <= 1'b0;
end else begin
if ( tmp_keep == {(1<<IEW){1'b0}} ) begin // no data in tmp
// i_tready = 1
if (i_tvalid) begin
tmp_data <= i_tdata;
tmp_keep <= i_tkeep;
tmp_last <= i_tlast;
end
end else if ( tmp_keep_next == {(1<<IEW){1'b0}} ) begin // only one data in tmp
if (o_tready) begin
// i_tready = 1
if (i_tvalid) begin
tmp_data <= i_tdata;
tmp_keep <= i_tkeep;
tmp_last <= i_tlast;
end else begin
tmp_data <= tmp_data_next;
tmp_keep <= tmp_keep_next;
end
end
end else if (o_tready | ~o_tvalid) begin
tmp_data <= tmp_data_next;
tmp_keep <= tmp_keep_next;
end
end
endmodule

View File

@ -0,0 +1,147 @@
//--------------------------------------------------------------------------------------------------------
// Module : axi_stream_packing
// Type : synthesizable, IP's sub-module
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: AXI-stream packing,
// see AMBA 4 AXI4-stream Protocol Version: 1.0 Specification -> 2.3.3 -> packing
//--------------------------------------------------------------------------------------------------------
module axi_stream_packing #(
parameter EW = 2, // AXI byte width is 1<<BW, bit width is 8<<BW
parameter SUBMIT_IMMEDIATE = 0 // if SUBMIT_IMMEDIATE=0, This module may retain data (even if there is enough bytes to submit). It will only completely clear retained data when encountering a tlast. The purpose is to correctly pack tlast into the tail of the packet, even if tkeep=0 when tlast=1.
// if SUBMIT_IMMEDIATE=1, This module will not retain data, once there is enough bytes to submit, it will output. However, o_tlast will be not avaiable (stuck to 1)
) (
input wire rstn,
input wire clk,
// AXI-stream slave
output wire i_tready,
input wire i_tvalid,
input wire [(8<<EW)-1:0] i_tdata,
input wire [(1<<EW)-1:0] i_tkeep,
input wire i_tlast,
// AXI-stream master
input wire o_tready,
output reg o_tvalid,
output reg [(8<<EW)-1:0] o_tdata,
output reg [(1<<EW)-1:0] o_tkeep,
output wire o_tlast
);
localparam [(1<<EW)-1:0] TKEEP_ALL_ZERO = 0;
localparam [(1<<EW)-1:0] TKEEP_ALL_ONE = ~TKEEP_ALL_ZERO;
localparam [EW :0] COUNT_ONE = 1;
localparam [EW+1:0] COUNT_MAX = {1'b1, {(EW+1){1'b0}}}; // equal to 1<<(1+EW)
localparam [EW+1:0] POW_EW_W = COUNT_MAX >> 1; // equal to 1<<EW
localparam [EW :0] POW_EW = POW_EW_W[EW:0]; // equal to 1<<EW
reg [(8<<EW)-1:0] i_bytes; // not real register : input packed bytes
reg [ EW :0] i_count; // not real register : input byte count, range : 0 ~ (1<<EW)
integer i;
always @ (*) begin // (i_tkeep, i_tdata) -> (i_count, i_bytes)
i_bytes = 0;
i_count = 0;
for (i=0; i<(1<<EW); i=i+1)
if (i_tkeep[i]) begin
i_bytes[i_count*8 +: 8] = i_tdata[i*8 +: 8];
i_count = i_count + COUNT_ONE;
end
end
reg [ EW :0] r_count = 0; // range : 0 ~ (1<<EW)
reg [ (8<<EW)-1:0] r_bytes;
wire [ EW+1 :0] t_count = {1'b0,i_count} + {1'b0,r_count}; // range : 0 ~ (2<<EW)
reg [2*(8<<EW)-1:0] t_bytes; // not real register
always @ (*) begin
t_bytes = i_bytes;
t_bytes = t_bytes << (8*r_count);
t_bytes = t_bytes | r_bytes;
end
//always @ (posedge clk) if (r_count > 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<<EW) bytes have been saved, so we must submit
unflushed <= i_tlast; // if meet tlast, the remain bytes must also be submitted, so let unflushed=1, to submit it next cycle
r_count <= t_count[EW:0] - POW_EW; // Equivalent to : r_count <= t_count - (1<<EW)
{r_bytes, o_tdata} <= t_bytes;
o_tvalid <= 1'b1;
r_tlast <= 1'b0;
o_tkeep <= TKEEP_ALL_ONE; // all bytes valid
end else if (i_tlast || // not enough bytes for submit, but meet i_tlast, may also need to submit
(SUBMIT_IMMEDIATE & (t_count == POW_EW_W)) ) begin // if SUBMIT_IMMEDIATE=1 and just have enough bytes, also submit
r_count <= 0;
r_bytes <= 0;
o_tvalid <= (t_count != 0); // for a spectcial case of zero-bytes packet, omit this packet (do not submit), otherwise submit
r_tlast <= (t_count != 0) & i_tlast;
o_tdata <= t_bytes[(8<<EW)-1:0];
o_tkeep <= ~(TKEEP_ALL_ONE << t_count); // t_count bytes valid
end else begin // not enough bytes, and not meet i_tlast, dont submit
r_count <= t_count[EW:0];
r_bytes <= t_bytes[(8<<EW)-1:0];
o_tvalid <= 1'b0;
r_tlast <= 1'b0;
o_tdata <= 0;
o_tkeep <= TKEEP_ALL_ZERO;
end
end
// assign i_tready = 1;
end
end
end
initial o_tvalid = 1'b0;
initial o_tdata = 0;
initial o_tkeep = TKEEP_ALL_ZERO;
assign o_tlast = SUBMIT_IMMEDIATE ? 1'b1 : r_tlast;
assign i_tready = (o_tready | ~o_tvalid) & (~unflushed);
endmodule

View File

@ -0,0 +1,82 @@
//--------------------------------------------------------------------------------------------------------
// Module : axi_stream_resizing
// 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 -> 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<<IEW)-1:0] i_tdata,
input wire [(1<<IEW)-1:0] i_tkeep,
input wire i_tlast,
// AXI-stream master
input wire o_tready,
output wire o_tvalid,
output wire [(8<<OEW)-1:0] o_tdata,
output wire [(1<<OEW)-1:0] o_tkeep,
output wire o_tlast
);
generate if (IEW < OEW) begin
axi_stream_upsizing #(
.IEW ( IEW ),
.OEW ( OEW )
) u_axi_stream_upsizing (
.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 if (IEW > 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

View File

@ -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)-1:0] i_tdata,
input wire [(1<<IEW)-1:0] i_tkeep,
input wire i_tlast,
// AXI-stream master
input wire o_tready,
output reg o_tvalid,
output reg [(8<<OEW)-1:0] o_tdata,
output reg [(1<<OEW)-1:0] o_tkeep,
output reg o_tlast
);
localparam DIFF_EW = (OEW>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<<OEW)-1:0] tmp_data = {(8<<OEW){1'b0}};
reg [(1<<OEW)-1:0] tmp_keep = {(1<<OEW){1'b0}};
reg tmp_last = 1'b0;
initial o_tvalid = 1'b0;
initial o_tdata = {(8<<OEW){1'b0}};
initial o_tkeep = {(1<<OEW){1'b0}};
initial o_tlast = 1'b0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
idx <= IDX_FIRST;
tmp_full <= 1'b0;
tmp_data <= {(8<<OEW){1'b0}};
tmp_keep <= {(1<<OEW){1'b0}};
tmp_last <= 1'b0;
o_tvalid <= 1'b0;
o_tdata <= {(8<<OEW){1'b0}};
o_tkeep <= {(1<<OEW){1'b0}};
o_tlast <= 1'b0;
end else begin
if (tmp_full) begin // enough for commit a output
if (o_tready | ~o_tvalid) begin
idx <= IDX_FIRST;
tmp_full <= 1'b0;
tmp_data <= {(8<<OEW){1'b0}};
tmp_keep <= {(1<<OEW){1'b0}};
tmp_last <= 1'b0;
o_tvalid <= 1'b1;
o_tdata <= tmp_data;
o_tkeep <= tmp_keep;
o_tlast <= tmp_last;
end
end else if (idx == IDX_LAST) begin // enough for commit a output
if (o_tready) // output handshake success, deassert o_tvalid
o_tvalid <= 1'b0;
if (o_tready | ~o_tvalid) begin
if (i_tvalid) begin
idx <= IDX_FIRST;
tmp_full <= 1'b0;
tmp_data <= {(8<<OEW){1'b0}};
tmp_keep <= {(1<<OEW){1'b0}};
tmp_last <= 1'b0;
o_tvalid <= 1'b1;
o_tdata <= tmp_data;
o_tdata[idx*(8<<IEW) +: (8<<IEW)] <= i_tdata;
o_tkeep <= tmp_keep;
o_tkeep[idx*(1<<IEW) +: (1<<IEW)] <= i_tkeep;
o_tlast <= tmp_last | i_tlast;
end
end
end else begin
if (o_tready) // output handshake success, deassert o_tvalid
o_tvalid <= 1'b0;
if (i_tvalid) begin
idx <= idx + IDX_ONE;
tmp_full <= i_tlast;
tmp_data[idx*(8<<IEW) +: (8<<IEW)] <= i_tdata;
tmp_keep[idx*(1<<IEW) +: (1<<IEW)] <= i_tkeep;
tmp_last <= tmp_last | i_tlast;
end
end
end
assign i_tready = tmp_full ? 1'b0 :
(idx == IDX_LAST) ? (o_tready | ~o_tvalid) :
1'b1 ;
endmodule

62
RTL/ftdi_245fifo/fifo2.v Normal file
View File

@ -0,0 +1,62 @@
//--------------------------------------------------------------------------------------------------------
// Module : fifo2
// Type : synthesizable, IP's sub-module
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: fifo with capacity=2,
// all outputs are register-out, which can be inserted into the stream chain to improve timing
//--------------------------------------------------------------------------------------------------------
module fifo2 # (
parameter DW = 8
) (
input wire rstn,
input wire clk,
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_n = 1'b1; // fifo data2 exist (n)
reg [DW-1:0] data1 = 0; // fifo data1
reg [DW-1:0] data2 = 0; // fifo data2
assign i_rdy = data2_en_n; // input ready when not full
assign o_en = data1_en; // output valid when not empty
assign o_data = data1;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
data1_en <= 1'b0;
data2_en_n <= 1'b1;
data1 <= 0;
data2 <= 0;
end else begin
if (~data2_en_n) begin // full (data2 exist)
if (o_rdy) begin // allow output
data2_en_n <= 1'b1; // set data2 to not exist
data1 <= data2; // data2 -> 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

98
RTL/ftdi_245fifo/fifo4.v Normal file
View File

@ -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

View File

@ -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<<EA)-1:0]; // may automatically synthesize to BRAM
reg [EA:0] wptr=0, wq_wptr_grey=0, rq1_wptr_grey=0, rq2_wptr_grey=0;
reg [EA:0] rptr=0, rq_rptr_grey=0, wq1_rptr_grey=0, wq2_rptr_grey=0;
wire [EA:0] rptr_next = (o_tvalid & o_tready) ? (rptr + {{EA{1'b0}}, 1'b1}) : rptr;
wire [EA:0] wptr_grey = (wptr >> 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

View File

@ -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<<BW, bit width is 8<<BW
) (
input wire rstn,
input wire clk,
// AXI-stream slave
output wire i_rdy,
input wire i_en,
input wire [DW-1:0] i_data,
// AXI-stream master
input wire o_rdy,
output reg o_en,
output reg [DW-1:0] o_data
);
localparam [10:0] SUBMIT_CHUNK = 11'd128;
reg [10:0] wptr = 11'd0;
reg [10:0] wptr_submit = 11'd0;
reg [10:0] wptr_submit_d = 11'd0;
reg [10:0] rptr = 11'd0;
wire [10:0] rptr_next = (o_en & o_rdy) ? (rptr+11'd1) : rptr;
reg [DW-1:0] buffer [1023:0];
reg [10:0] count = 11'd0;
assign i_rdy = ( wptr != {~rptr[10], rptr[9:0]} );
always @ (posedge clk or negedge rstn)
if (~rstn) begin
wptr_submit <= 11'h0;
wptr_submit_d <= 11'h0;
count <= 11'd0;
end else begin
wptr_submit_d <= wptr_submit;
if ( (wptr-wptr_submit) > 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

View File

@ -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<<CHIP_EW)-1:0] tx_tdata,
input wire [(1<<CHIP_EW)-1:0] tx_tkeep,
input wire rx_almost_full,
output wire rx_tvalid,
output wire [(8<<CHIP_EW)-1:0] rx_tdata,
output wire [(1<<CHIP_EW)-1:0] rx_tkeep,
output wire rx_tlast,
// FTDI chip interface
input wire ftdi_rxf_n,
input wire ftdi_txe_n,
output reg ftdi_oe_n,
output reg ftdi_rd_n,
output reg ftdi_wr_n,
output reg ftdi_master_oe, // 1:master (FPGA) drives DATA/BE 0:master (FPGA) release DATA/BE to High-Z
output reg [(8<<CHIP_EW)-1:0] ftdi_data_out, // when ftdi_master_oe=1, this signal must be drive to DATA
output reg [(1<<CHIP_EW)-1:0] ftdi_be_out, // when ftdi_master_oe=1, this signal must be drive to BE
input wire [(8<<CHIP_EW)-1:0] ftdi_data_in,
input wire [(1<<CHIP_EW)-1:0] ftdi_be_in
);
localparam [(8<<CHIP_EW)-1:0] DATA_ZERO = {(8<<CHIP_EW){1'b0}};
localparam [(1<<CHIP_EW)-1:0] BE_ZERO = {(1<<CHIP_EW){1'b0}};
localparam [(1<<CHIP_EW)-1:0] BE_ALL_ONE = ~BE_ZERO;
localparam [5:0] S_RX_IDLE = 6'b000011,
S_RX_OE = 6'b000010,
S_RX_EN = 6'b000000,
S_RX_WAIT = 6'b000111,
S_RX_END = 6'b001011,
S_TX_IDLE = 6'b010011,
S_TX_EN = 6'b100011;
reg [ 5:0] state = S_RX_IDLE;
wire state_n_rx_oe_or_rx_en = state[0]; // ~(state == S_RX_OE || state == S_RX_EN)
wire state_n_rx_en = state[1]; // ~(state == S_RX_EN)
wire state_rx_end = state[3]; // state == S_RX_END
wire state_tx_en = state[5]; // state == S_TX_EN
assign rx_tlast = (state_rx_end & ftdi_rxf_n);
assign rx_tvalid = (~state_n_rx_en & ~ftdi_rxf_n) | rx_tlast;
assign rx_tdata = state_n_rx_en ? DATA_ZERO : ftdi_data_in;
generate if (CHIP_EW==0)
assign rx_tkeep = state_n_rx_en ? BE_ZERO : BE_ALL_ONE;
else
assign rx_tkeep = state_n_rx_en ? BE_ZERO : ftdi_be_in;
endgenerate
assign tx_tready = state_tx_en & ~ftdi_txe_n;
generate if (CHIP_DRIVE_AT_NEGEDGE) begin
always @ (negedge clk) begin
ftdi_oe_n <= state_n_rx_oe_or_rx_en;
ftdi_rd_n <= state_n_rx_en;
ftdi_wr_n <= state_tx_en ? ~tx_tvalid : 1'b1;
ftdi_master_oe <= state_tx_en;
ftdi_data_out <= tx_tdata;
ftdi_be_out <= tx_tkeep;
end
end else begin
always @ (*) begin
ftdi_oe_n = state_n_rx_oe_or_rx_en;
ftdi_rd_n = state_n_rx_en;
ftdi_wr_n = state_tx_en ? ~tx_tvalid : 1'b1;
ftdi_master_oe = state_tx_en;
ftdi_data_out = tx_tdata;
ftdi_be_out = tx_tkeep;
end
end endgenerate
always @ (posedge clk or negedge rstn)
if (~rstn) begin
state <= S_RX_IDLE;
end else begin
case (state)
S_RX_IDLE :
if (~ftdi_rxf_n & ~rx_almost_full)
state <= S_RX_OE;
else
state <= S_TX_IDLE;
S_RX_OE :
state <= S_RX_EN;
S_RX_EN :
if (ftdi_rxf_n | rx_almost_full)
state <= S_RX_WAIT;
S_RX_WAIT :
state <= S_RX_END;
S_RX_END : // check whether there's no more RX data (ftdi_rxf_n==1) in this state,
state <= S_TX_IDLE;
S_TX_EN :
if (ftdi_txe_n || ~tx_tvalid || (tx_tkeep != BE_ALL_ONE))
state <= S_RX_IDLE;
default : // S_TX_IDLE :
if (~ftdi_txe_n & tx_tvalid)
state <= S_TX_EN;
else
state <= S_RX_IDLE;
endcase
end
endmodule

View File

@ -0,0 +1,675 @@
//--------------------------------------------------------------------------------------------------------
// Module : ftdi_245fifo_top
// Type : synthesizable, IP's top
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: FTDI USB chip's 245fifo mode controller
// support FT232H, FT2232H, FT600, FT601. (and maybe FT245 (untested))
// to realize USB communication
//--------------------------------------------------------------------------------------------------------
module ftdi_245fifo_top #(
parameter TX_EW = 2 , // TX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
parameter TX_EA = 10, // TX FIFO depth = 2^TX_EA
parameter RX_EW = 2 , // RX data stream width, 0=8bit, 1=16bit, 2=32bit, 3=64bit, 4=128bit ...
parameter RX_EA = 10, // RX FIFO depth = 2^RX_EA
parameter CHIP_TYPE = "FTx232H", // can be "FTx232H", "FT600", "FT601"
parameter SIMULATION = 0 // 0:for normal use 1:enable assert for simulation
) (
// asynchronous reset, active low
rstn_async,
// user send interface (FPGA -> 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<<TX_EW)-1:0] tx_tdata;
input wire [(1<<TX_EW)-1:0] tx_tkeep;
input wire tx_tlast;
input wire rx_clk;
input wire rx_tready;
output wire rx_tvalid;
output wire [(8<<RX_EW)-1:0] rx_tdata;
output wire [(1<<RX_EW)-1:0] rx_tkeep;
output wire rx_tlast;
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<<CHIP_EW)-1:0] ftdi_data;
inout [(1<<CHIP_EW)-1:0] ftdi_be;
//-----------------------------------------------------------------------------------------------------------------------------
// tri-state driver for inout pins
//-----------------------------------------------------------------------------------------------------------------------------
wire ftdi_master_oe;
wire [(8<<CHIP_EW)-1:0] ftdi_data_out;
wire [(1<<CHIP_EW)-1:0] ftdi_be_out;
assign ftdi_data = ftdi_master_oe ? ftdi_data_out : {(8<<CHIP_EW){1'bZ}}; // tri-state driver
assign ftdi_be = ftdi_master_oe ? ftdi_be_out : {(1<<CHIP_EW){1'bZ}}; // tri-state driver
//-----------------------------------------------------------------------------------------------------------------------------
// generate reset
//-----------------------------------------------------------------------------------------------------------------------------
wire rstn_ftdi;
wire rstn_tx;
wire rstn_rx;
resetn_sync u_resetn_sync_usb (rstn_async, ftdi_clk, rstn_ftdi);
resetn_sync u_resetn_sync_tx (rstn_async, tx_clk , rstn_tx );
resetn_sync u_resetn_sync_rx (rstn_async, rx_clk , rstn_rx );
//-----------------------------------------------------------------------------------------------------------------------------
// internal signals
//-----------------------------------------------------------------------------------------------------------------------------
localparam TX_FIFO_EW = (TX_EW > 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_EW)-1:0] tx_a_tdata;
wire [(1<<TX_EW)-1:0] tx_a_tkeep;
wire tx_a_tlast;
wire tx_b_tready;
wire tx_b_tvalid;
wire [(8<<TX_EW)-1:0] tx_b_tdata;
wire [(1<<TX_EW)-1:0] tx_b_tkeep;
wire tx_b_tlast;
wire tx_c_tready;
wire tx_c_tvalid;
wire [(8<<TX_FIFO_EW)-1:0] tx_c_tdata;
wire [(1<<TX_FIFO_EW)-1:0] tx_c_tkeep;
wire tx_d_tready;
wire tx_d_tvalid;
wire [(8<<TX_FIFO_EW)-1:0] tx_d_tdata;
wire [(1<<TX_FIFO_EW)-1:0] tx_d_tkeep;
wire tx_e_tready;
wire tx_e_tvalid;
wire [(8<<TX_FIFO_EW)-1:0] tx_e_tdata;
wire [(1<<TX_FIFO_EW)-1:0] tx_e_tkeep;
wire tx_f_tready;
wire tx_f_tvalid;
wire [(8<<CHIP_EW)-1:0] tx_f_tdata;
wire [(1<<CHIP_EW)-1:0] tx_f_tkeep;
wire tx_g_tready;
wire tx_g_tvalid;
wire [(8<<CHIP_EW)-1:0] tx_g_tdata;
wire [(1<<CHIP_EW)-1:0] tx_g_tkeep;
wire tx_h_tready;
wire tx_h_tvalid;
wire [(8<<CHIP_EW)-1:0] tx_h_tdata;
wire [(1<<CHIP_EW)-1:0] tx_h_tkeep;
wire rx_a_almost_full;
wire rx_a_tvalid;
wire [(8<<CHIP_EW)-1:0] rx_a_tdata;
wire [(1<<CHIP_EW)-1:0] rx_a_tkeep;
wire rx_a_tlast;
wire rx_b_tready;
wire rx_b_tvalid;
wire [(8<<CHIP_EW)-1:0] rx_b_tdata;
wire [(1<<CHIP_EW)-1:0] rx_b_tkeep;
wire rx_b_tlast;
wire rx_c_tready;
wire rx_c_tvalid;
wire [(8<<CHIP_EW)-1:0] rx_c_tdata;
wire [(1<<CHIP_EW)-1:0] rx_c_tkeep;
wire rx_c_tlast;
wire rx_d_tready;
wire rx_d_tvalid;
wire [(8<<RX_FIFO_EW)-1:0] rx_d_tdata;
wire [(1<<RX_FIFO_EW)-1:0] rx_d_tkeep;
wire rx_d_tlast;
wire rx_e_tready;
wire rx_e_tvalid;
wire [(8<<RX_FIFO_EW)-1:0] rx_e_tdata;
wire [(1<<RX_FIFO_EW)-1:0] rx_e_tkeep;
wire rx_e_tlast;
wire rx_f_tready;
wire rx_f_tvalid;
wire [(8<<RX_EW)-1:0] rx_f_tdata;
wire [(1<<RX_EW)-1:0] rx_f_tkeep;
wire rx_f_tlast;
//-----------------------------------------------------------------------------------------------------------------------------
// submodules
//-----------------------------------------------------------------------------------------------------------------------------
fifo2 # (
.DW ( 1 + (1<<TX_EW) + (8<<TX_EW) )
) u_tx_fifo2_1 (
.rstn ( rstn_tx ),
.clk ( tx_clk ),
.i_rdy ( tx_tready ),
.i_en ( tx_tvalid ),
.i_data ( {tx_tlast, tx_tkeep, tx_tdata} ),
.o_rdy ( tx_a_tready ),
.o_en ( tx_a_tvalid ),
.o_data ( {tx_a_tlast, tx_a_tkeep, tx_a_tdata} )
);
axi_stream_packing #(
.EW ( TX_EW ),
.SUBMIT_IMMEDIATE ( TX_EW >= 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<<TX_FIFO_EW) + (8<<TX_FIFO_EW) ),
.EA ( TX_EA )
) u_tx_fifo_async (
.i_rstn ( rstn_tx ),
.i_clk ( tx_clk ),
.i_tready ( tx_c_tready ),
.i_tvalid ( tx_c_tvalid ),
.i_tdata ( {tx_c_tkeep, tx_c_tdata} ),
.o_rstn ( rstn_ftdi ),
.o_clk ( ftdi_clk ),
.o_tready ( tx_d_tready ),
.o_tvalid ( tx_d_tvalid ),
.o_tdata ( {tx_d_tkeep, tx_d_tdata} )
);
fifo2 # (
.DW ( (1<<TX_FIFO_EW) + (8<<TX_FIFO_EW) )
) u_tx_fifo2_2 (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_rdy ( tx_d_tready ),
.i_en ( tx_d_tvalid ),
.i_data ( {tx_d_tkeep, tx_d_tdata} ),
.o_rdy ( tx_e_tready ),
.o_en ( tx_e_tvalid ),
.o_data ( {tx_e_tkeep, tx_e_tdata} )
);
axi_stream_resizing #(
.IEW ( TX_FIFO_EW ),
.OEW ( CHIP_EW )
) u_tx_downsizing (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_tready ( tx_e_tready ),
.i_tvalid ( tx_e_tvalid ),
.i_tdata ( tx_e_tdata ),
.i_tkeep ( tx_e_tkeep ),
.i_tlast ( 1'b0 ),
.o_tready ( tx_f_tready ),
.o_tvalid ( tx_f_tvalid ),
.o_tdata ( tx_f_tdata ),
.o_tkeep ( tx_f_tkeep ),
.o_tlast ( )
);
fifo_delay_submit #(
.DW ( (1<<CHIP_EW) + (8<<CHIP_EW) )
) u_tx_fifo_delay_submit (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_rdy ( tx_f_tready ),
.i_en ( tx_f_tvalid ),
.i_data ( {tx_f_tkeep, tx_f_tdata} ),
.o_rdy ( tx_g_tready ),
.o_en ( tx_g_tvalid ),
.o_data ( {tx_g_tkeep, tx_g_tdata} )
);
fifo2 # (
.DW ( (1<<CHIP_EW) + (8<<CHIP_EW) )
) u_tx_fifo2_3 (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_rdy ( tx_g_tready ),
.i_en ( tx_g_tvalid ),
.i_data ( {tx_g_tkeep, tx_g_tdata} ),
.o_rdy ( tx_h_tready ),
.o_en ( tx_h_tvalid ),
.o_data ( {tx_h_tkeep, tx_h_tdata} )
);
ftdi_245fifo_fsm #(
.CHIP_EW ( CHIP_EW ),
.CHIP_DRIVE_AT_NEGEDGE ( CHIP_DRIVE_AT_NEGEDGE )
) u_ftdi_245fifo_fsm (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.tx_tready ( tx_h_tready ),
.tx_tvalid ( tx_h_tvalid ),
.tx_tdata ( tx_h_tdata ),
.tx_tkeep ( tx_h_tkeep ),
.rx_almost_full ( rx_a_almost_full ),
.rx_tvalid ( rx_a_tvalid ),
.rx_tdata ( rx_a_tdata ),
.rx_tkeep ( rx_a_tkeep ),
.rx_tlast ( rx_a_tlast ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_master_oe ( ftdi_master_oe ),
.ftdi_data_out ( ftdi_data_out ),
.ftdi_be_out ( ftdi_be_out ),
.ftdi_data_in ( ftdi_data ),
.ftdi_be_in ( ftdi_be )
);
fifo4 # (
.DW ( 1 + (1<<CHIP_EW) + (8<<CHIP_EW) )
) u_rx_fifo4 (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_almost_full ( rx_a_almost_full ),
.i_rdy ( ),
.i_en ( rx_a_tvalid ),
.i_data ( {rx_a_tlast, rx_a_tkeep, rx_a_tdata} ),
.o_rdy ( rx_b_tready ),
.o_en ( rx_b_tvalid ),
.o_data ( {rx_b_tlast, rx_b_tkeep, rx_b_tdata} )
);
axi_stream_packing #(
.EW ( CHIP_EW )
) u_rx_packing (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_tready ( rx_b_tready ),
.i_tvalid ( rx_b_tvalid ),
.i_tdata ( rx_b_tdata ),
.i_tkeep ( rx_b_tkeep ),
.i_tlast ( rx_b_tlast ),
.o_tready ( rx_c_tready ),
.o_tvalid ( rx_c_tvalid ),
.o_tdata ( rx_c_tdata ),
.o_tkeep ( rx_c_tkeep ),
.o_tlast ( rx_c_tlast )
);
axi_stream_resizing #(
.IEW ( CHIP_EW ),
.OEW ( RX_FIFO_EW )
) u_rx_upsizing (
.rstn ( rstn_ftdi ),
.clk ( ftdi_clk ),
.i_tready ( rx_c_tready ),
.i_tvalid ( rx_c_tvalid ),
.i_tdata ( rx_c_tdata ),
.i_tkeep ( rx_c_tkeep ),
.i_tlast ( rx_c_tlast ),
.o_tready ( rx_d_tready ),
.o_tvalid ( rx_d_tvalid ),
.o_tdata ( rx_d_tdata ),
.o_tkeep ( rx_d_tkeep ),
.o_tlast ( rx_d_tlast )
);
fifo_async #(
.DW ( 1 + (1<<RX_FIFO_EW) + (8<<RX_FIFO_EW) ),
.EA ( RX_EA )
) u_rx_fifo_async (
.i_rstn ( rstn_ftdi ),
.i_clk ( ftdi_clk ),
.i_tready ( rx_d_tready ),
.i_tvalid ( rx_d_tvalid ),
.i_tdata ( {rx_d_tlast, rx_d_tkeep, rx_d_tdata} ),
.o_rstn ( rstn_rx ),
.o_clk ( rx_clk ),
.o_tready ( rx_e_tready ),
.o_tvalid ( rx_e_tvalid ),
.o_tdata ( {rx_e_tlast, rx_e_tkeep, rx_e_tdata} )
);
axi_stream_resizing #(
.IEW ( RX_FIFO_EW ),
.OEW ( RX_EW )
) u_rx_downsizing (
.rstn ( rstn_rx ),
.clk ( rx_clk ),
.i_tready ( rx_e_tready ),
.i_tvalid ( rx_e_tvalid ),
.i_tdata ( rx_e_tdata ),
.i_tkeep ( rx_e_tkeep ),
.i_tlast ( rx_e_tlast ),
.o_tready ( rx_f_tready ),
.o_tvalid ( rx_f_tvalid ),
.o_tdata ( rx_f_tdata ),
.o_tkeep ( rx_f_tkeep ),
.o_tlast ( rx_f_tlast )
);
fifo2 #(
.DW ( 1 + (1<<RX_EW) + (8<<RX_EW) )
) u_rx_fifo2 (
.rstn ( rstn_rx ),
.clk ( rx_clk ),
.i_rdy ( rx_f_tready ),
.i_en ( rx_f_tvalid ),
.i_data ( {rx_f_tlast, rx_f_tkeep, rx_f_tdata} ),
.o_rdy ( rx_tready ),
.o_en ( rx_tvalid ),
.o_data ( {rx_tlast, rx_tkeep, rx_tdata} )
);
//-----------------------------------------------------------------------------------------------------------------------------
// simulation asserts
//-----------------------------------------------------------------------------------------------------------------------------
generate if (SIMULATION) begin
axi_stream_assert #(
.NAME ( "axis_tx_in" ),
.BW ( 1<<TX_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 0 )
) u_axis_assert_tx_in (
.clk ( tx_clk ),
.tready ( tx_tready ),
.tvalid ( tx_tvalid ),
.tdata ( tx_tdata ),
.tkeep ( tx_tkeep ),
.tlast ( tx_tlast )
);
axi_stream_assert #(
.NAME ( "axis_tx_a" ),
.BW ( 1<<TX_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 0 )
) u_axis_assert_tx_a (
.clk ( tx_clk ),
.tready ( tx_a_tready ),
.tvalid ( tx_a_tvalid ),
.tdata ( tx_a_tdata ),
.tkeep ( tx_a_tkeep ),
.tlast ( tx_a_tlast )
);
axi_stream_assert #(
.NAME ( "axis_tx_b" ),
.BW ( 1<<TX_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_b (
.clk ( tx_clk ),
.tready ( tx_b_tready ),
.tvalid ( tx_b_tvalid ),
.tdata ( tx_b_tdata ),
.tkeep ( tx_b_tkeep ),
.tlast ( tx_b_tlast )
);
axi_stream_assert #(
.NAME ( "axis_tx_c" ),
.BW ( 1<<TX_FIFO_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_c (
.clk ( tx_clk ),
.tready ( tx_c_tready ),
.tvalid ( tx_c_tvalid ),
.tdata ( tx_c_tdata ),
.tkeep ( tx_c_tkeep ),
.tlast ( 1'b1 )
);
axi_stream_assert #(
.NAME ( "axis_tx_d" ),
.BW ( 1<<TX_FIFO_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_d (
.clk ( ftdi_clk ),
.tready ( tx_d_tready ),
.tvalid ( tx_d_tvalid ),
.tdata ( tx_d_tdata ),
.tkeep ( tx_d_tkeep ),
.tlast ( 1'b1 )
);
axi_stream_assert #(
.NAME ( "axis_tx_e" ),
.BW ( 1<<TX_FIFO_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_e (
.clk ( ftdi_clk ),
.tready ( tx_e_tready ),
.tvalid ( tx_e_tvalid ),
.tdata ( tx_e_tdata ),
.tkeep ( tx_e_tkeep ),
.tlast ( 1'b1 )
);
axi_stream_assert #(
.NAME ( "axis_tx_f" ),
.BW ( 1<<CHIP_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_f (
.clk ( ftdi_clk ),
.tready ( tx_f_tready ),
.tvalid ( tx_f_tvalid ),
.tdata ( tx_f_tdata ),
.tkeep ( tx_f_tkeep ),
.tlast ( 1'b1 )
);
axi_stream_assert #(
.NAME ( "axis_tx_g" ),
.BW ( 1<<CHIP_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_g (
.clk ( ftdi_clk ),
.tready ( tx_g_tready ),
.tvalid ( tx_g_tvalid ),
.tdata ( tx_g_tdata ),
.tkeep ( tx_g_tkeep ),
.tlast ( 1'b1 )
);
axi_stream_assert #(
.NAME ( "axis_tx_h" ),
.BW ( 1<<CHIP_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 0 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_tx_h (
.clk ( ftdi_clk ),
.tready ( tx_h_tready ),
.tvalid ( tx_h_tvalid ),
.tdata ( tx_h_tdata ),
.tkeep ( tx_h_tkeep ),
.tlast ( 1'b1 )
);
axi_stream_assert #(
.NAME ( "axis_rx_b" ),
.BW ( 1<<CHIP_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 0 ),
.ASSERT_PACKED ( 0 )
) u_axis_assert_rx_b (
.clk ( ftdi_clk ),
.tready ( rx_b_tready ),
.tvalid ( rx_b_tvalid ),
.tdata ( rx_b_tdata ),
.tkeep ( rx_b_tkeep ),
.tlast ( rx_b_tlast )
);
axi_stream_assert #(
.NAME ( "axis_rx_c" ),
.BW ( 1<<CHIP_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_rx_c (
.clk ( ftdi_clk ),
.tready ( rx_c_tready ),
.tvalid ( rx_c_tvalid ),
.tdata ( rx_c_tdata ),
.tkeep ( rx_c_tkeep ),
.tlast ( rx_c_tlast )
);
axi_stream_assert #(
.NAME ( "axis_rx_d" ),
.BW ( 1<<RX_FIFO_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_rx_d (
.clk ( ftdi_clk ),
.tready ( rx_d_tready ),
.tvalid ( rx_d_tvalid ),
.tdata ( rx_d_tdata ),
.tkeep ( rx_d_tkeep ),
.tlast ( rx_d_tlast )
);
axi_stream_assert #(
.NAME ( "axis_rx_e" ),
.BW ( 1<<RX_FIFO_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_rx_e (
.clk ( rx_clk ),
.tready ( rx_e_tready ),
.tvalid ( rx_e_tvalid ),
.tdata ( rx_e_tdata ),
.tkeep ( rx_e_tkeep ),
.tlast ( rx_e_tlast )
);
axi_stream_assert #(
.NAME ( "axis_rx_f" ),
.BW ( 1<<RX_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 1 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_rx_f (
.clk ( rx_clk ),
.tready ( rx_f_tready ),
.tvalid ( rx_f_tvalid ),
.tdata ( rx_f_tdata ),
.tkeep ( rx_f_tkeep ),
.tlast ( rx_f_tlast )
);
axi_stream_assert #(
.NAME ( "axis_rx_out" ),
.BW ( 1<<RX_EW ),
.ASSERT_SLAVE_NOT_CHANGE_WHEN_HANDSHAKE_FAILED ( 0 ),
.ASSERT_PACKED ( 1 )
) u_axis_assert_rx_out (
.clk ( rx_clk ),
.tready ( rx_tready ),
.tvalid ( rx_tvalid ),
.tdata ( rx_tdata ),
.tkeep ( rx_tkeep ),
.tlast ( rx_tlast )
);
end endgenerate
endmodule

View File

@ -0,0 +1,26 @@
//--------------------------------------------------------------------------------------------------------
// Module : resetn_sync
// Type : synthesizable, IP's sub-module
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: Synchronize the asynchronous reset signal to the local clock domain (asynchronous reset, synchronous release)
// called by ftdi_245fifo.v
//--------------------------------------------------------------------------------------------------------
module resetn_sync (
input wire rstn_async,
input wire clk,
output wire rstn
);
reg [3:0] rstn_shift = 4'd0;
always @ (posedge clk or negedge rstn_async)
if (~rstn_async)
rstn_shift <= 4'd0;
else
rstn_shift <= {1'b1, rstn_shift[3:1]};
assign rstn = rstn_shift[0];
endmodule

122
SIM/tb_ftdi_245fifo.v Normal file
View File

@ -0,0 +1,122 @@
//--------------------------------------------------------------------------------------------------------
// Module : tb_ftdi_245fifo
// Type : simulation, top
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: top-level testbench for ftdi_245fifo_top
//--------------------------------------------------------------------------------------------------------
`timescale 1ps/1ps
module tb_ftdi_245fifo ();
// -----------------------------------------------------------------------------------------------------------------------------
// simulation control
// -----------------------------------------------------------------------------------------------------------------------------
initial $dumpvars(0, tb_ftdi_245fifo);
//initial #(1000 * 1000 * 1000 * 1000) $finish;
initial #( 100 * 1000 * 1000) $finish;
// ms us ns ps
// -----------------------------------------------------------------------------------------------------------------------------
// FTDI chip signals
// -----------------------------------------------------------------------------------------------------------------------------
localparam CHIP_TYPE = "FTx232H"; // can be "FTx232H", "FT600", "FT601"
localparam CHIP_EW = (CHIP_TYPE=="FT601") ? 2 : (CHIP_TYPE=="FT600") ? 1 : 0;
wire ftdi_clk;
wire ftdi_rxf_n;
wire ftdi_txe_n;
wire ftdi_oe_n;
wire ftdi_rd_n;
wire ftdi_wr_n;
tri [(8<<CHIP_EW)-1:0] ftdi_data;
tri [(1<<CHIP_EW)-1:0] ftdi_be;
// -----------------------------------------------------------------------------------------------------------------------------
// user signals(loopback)
// -----------------------------------------------------------------------------------------------------------------------------
localparam USER_EW = 0;
wire tready;
wire tvalid;
wire [(8<<USER_EW)-1:0] tdata;
wire [(1<<USER_EW)-1:0] tkeep;
wire tlast;
// -----------------------------------------------------------------------------------------------------------------------------
// user clock
// -----------------------------------------------------------------------------------------------------------------------------
reg rstn = 1'b0;
reg clk = 1'b0;
always #5000 clk = ~clk; // 100MHz
initial begin repeat(4) @(posedge clk); rstn<=1'b1; end
// -----------------------------------------------------------------------------------------------------------------------------
// generate a simple FT232H behavior
// -----------------------------------------------------------------------------------------------------------------------------
tb_ftdi_chip_model # (
.CHIP_EW ( CHIP_EW )
) u_tb_ftdi_chip_model (
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ftdi_be )
);
// -----------------------------------------------------------------------------------------------------------------------------
// ftdi_245fifo_top module
// -----------------------------------------------------------------------------------------------------------------------------
ftdi_245fifo_top #(
.TX_EW ( USER_EW ),
.TX_EA ( 10 ),
.RX_EW ( USER_EW ),
.RX_EA ( 10 ),
.CHIP_TYPE ( CHIP_TYPE ),
.SIMULATION ( 1 )
) u_ftdi_245fifo_top (
.rstn_async ( rstn ),
// user write interface, loopback connect to user read interface
.tx_clk ( clk ),
.tx_tready ( tready ),
.tx_tvalid ( tvalid ),
.tx_tdata ( tdata ),
.tx_tkeep ( tkeep ),
.tx_tlast ( tlast ),
// user read interface, loopback connect to user write interface
.rx_clk ( clk ),
.rx_tready ( tready ),
.rx_tvalid ( tvalid ),
.rx_tdata ( tdata ),
.rx_tkeep ( tkeep ),
.rx_tlast ( tlast ),
// FTDI USB interface, must connect to FT232H pins
.ftdi_clk ( ftdi_clk ),
.ftdi_rxf_n ( ftdi_rxf_n ),
.ftdi_txe_n ( ftdi_txe_n ),
.ftdi_oe_n ( ftdi_oe_n ),
.ftdi_rd_n ( ftdi_rd_n ),
.ftdi_wr_n ( ftdi_wr_n ),
.ftdi_data ( ftdi_data ),
.ftdi_be ( ftdi_be )
);
endmodule

View File

@ -0,0 +1,5 @@
del sim.out dump.vcd
iverilog -g2001 -o sim.out ./*.v ../RTL/ftdi_245fifo/*.v
vvp -n sim.out
del sim.out
pause

129
SIM/tb_ftdi_chip_model.v Normal file
View File

@ -0,0 +1,129 @@
//--------------------------------------------------------------------------------------------------------
// Module : tb_ftdi_245fifo
// Type : simulation
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: simple FT232H / FT600 / FT601 chip model
//--------------------------------------------------------------------------------------------------------
`timescale 1ps/1ps
module tb_ftdi_chip_model #(
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.
) (
output reg ftdi_clk,
output reg ftdi_rxf_n,
output reg ftdi_txe_n,
input wire ftdi_oe_n,
input wire ftdi_rd_n,
input wire ftdi_wr_n,
inout [(8<<CHIP_EW)-1:0] ftdi_data,
inout [(1<<CHIP_EW)-1:0] ftdi_be
);
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
// function : generate random unsigned integer
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
function [31:0] randuint;
input [31:0] min;
input [31:0] max;
begin
randuint = $random;
if ( min != 0 || max != 'hFFFFFFFF )
randuint = (randuint % (1+max-min)) + min;
end
endfunction
wire [(8<<CHIP_EW)-1:0] DATA_HIGHZ = {(8<<CHIP_EW){1'bz}};
localparam [(8<<CHIP_EW)-1:0] DATA_ZERO = {(8<<CHIP_EW){1'b0}};
wire [(1<<CHIP_EW)-1:0] BE_HIGHZ = {(1<<CHIP_EW){1'bz}};
localparam [(1<<CHIP_EW)-1:0] BE_ZERO = {(1<<CHIP_EW){1'b0}};
localparam [(1<<CHIP_EW)-1:0] BE_ALL_ONE = ~BE_ZERO;
initial ftdi_clk = 1'b0; // generate FTDI chip clock
always #8333 ftdi_clk = ~ftdi_clk; // approximately 60MHz.
reg [(8<<CHIP_EW)-1:0] ftdi_r_data = DATA_ZERO;
reg [(1<<CHIP_EW)-1:0] ftdi_r_be = BE_ZERO;
reg [(8<<CHIP_EW)-1:0] tmp_data = DATA_ZERO;
reg [(8<<CHIP_EW)-1:0] tmp_be = (CHIP_EW==0) ? 1 : BE_ZERO;
reg [7:0] rxbyte = (CHIP_EW==0) ? 8'h01 : 8'h00;
integer i;
always @ (posedge ftdi_clk) // data from FTDI-Chip to FPGA (read from FTDI-Chip)
if (~ftdi_rd_n & ~ftdi_rxf_n) begin
tmp_data = ftdi_r_data;
tmp_be = (CHIP_EW==0) ? BE_ALL_ONE : randuint(0, 'hFFFFFFFF);
for (i=0; i<(1<<CHIP_EW); i=i+1) begin
if (tmp_be[i]) begin
//$write(" %02X", rxbyte);
tmp_data[8*i +: 8] = rxbyte;
rxbyte = rxbyte + 1;
end
end
ftdi_r_data <= tmp_data;
ftdi_r_be <= tmp_be;
end
reg [7:0] txbyte = 8'h00;
always @ (posedge ftdi_clk) // data from FPGA to FTDI-Chip (write to FTDI-Chip)
if (~ftdi_wr_n & ~ftdi_txe_n) begin
for (i=0; i<(1<<CHIP_EW); i=i+1) begin
if (ftdi_be[i]) begin
$write(" %02X", ftdi_data[8*i +: 8] );
if (txbyte !== ftdi_data[8*i +: 8]) begin $display("*** error : data incorrect"); $stop; end
txbyte = txbyte + 1;
end
end
end
assign ftdi_data = ftdi_oe_n ? DATA_HIGHZ : ftdi_r_data;
assign ftdi_be = ftdi_oe_n ? BE_HIGHZ : ftdi_r_be;
initial begin
ftdi_rxf_n <= 1'b1;
while (1) begin
repeat (randuint(1, 100)) @ (posedge ftdi_clk);
ftdi_rxf_n <= 1'b0;
repeat (randuint(1, 100)) @ (posedge ftdi_clk);
ftdi_rxf_n <= 1'b1;
end
end
initial begin
ftdi_txe_n <= 1'b1;
while (1) begin
repeat (randuint(1, 100)) @ (posedge ftdi_clk);
ftdi_txe_n <= 1'b0;
repeat (randuint(1, 100)) @ (posedge ftdi_clk);
ftdi_txe_n <= 1'b1;
end
end
endmodule

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
figures/ft232h_program.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
figures/ft232h_ready.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
figures/ft600_ready.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
figures/ports.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
figures/structure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

283
python/USB_FTX232H_FT60X.py Normal file
View File

@ -0,0 +1,283 @@
#-*- coding:utf-8 -*-
# Python3
import sys
def open_ft_usb_device(device_type, device_name):
'''
open_ft_usb_device(str device_type, str device_name) -> 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()

View File

@ -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()

View File

@ -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()

55
python/usb_rx_mass.py Normal file
View File

@ -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()

88
python/usb_tx_crc.py Normal file
View File

@ -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()