add support for byte-enable
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
FT232H_UniFPGA
|
||||
FT600_FPGArduino
|
||||
vivado
|
||||
|
||||
**/__pycache__
|
108
FTD2XX_guide.md
Normal 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位的DLL;64位的Python必须对应64位的DLL。
|
||||
|
||||
然后,可以在 python 中运行以下语句来验证安装:
|
||||
|
||||
```python
|
||||
import ftd2xx
|
||||
```
|
||||
|
||||
|
||||
至此,FT232H 所需的 Python 运行环境已就绪。
|
113
FTD3XX_guide.md
Normal 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位的DLL;64位的Python必须对应64位的DLL。
|
||||
|
||||
然后,可以在 python 中运行以下语句来验证安装:
|
||||
|
||||
```python
|
||||
import ftd3xx
|
||||
```
|
||||
|
||||
至此,FT600 所需的 Python 运行环境已就绪。
|
998
README.md
Normal 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 chip:http://www.ftdichip.cn/Products/ICs/FT232H.htm
|
||||
* FT232H software example:http://www.ftdichip.cn/Support/SoftwareExamples/CodeExamples.htm
|
||||
* FT600 chip:http://www.ftdichip.cn/Products/ICs/FT600.html
|
||||
* FT600/FT601 software example: http://www.ftdichip.cn/Support/SoftwareExamples/FT60X.htm
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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
|
31
RTL/fpga_ft232h_example/clock_beat.v
Normal 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
|
17
RTL/fpga_ft232h_example/constraint_ft232h.sdc
Normal 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}
|
115
RTL/fpga_ft232h_example/fpga_top_ft232h_loopback.v
Normal 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
|
138
RTL/fpga_ft232h_example/fpga_top_ft232h_rx_crc.v
Normal 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
|
136
RTL/fpga_ft232h_example/fpga_top_ft232h_tx_mass.v
Normal 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
|
91
RTL/fpga_ft232h_example/rx_calc_crc.v
Normal 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
|
95
RTL/fpga_ft232h_example/tx_specified_len.v
Normal 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
|
31
RTL/fpga_ft600_example/clock_beat.v
Normal 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
|
17
RTL/fpga_ft600_example/constraint_ft600.sdc
Normal 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}
|
120
RTL/fpga_ft600_example/fpga_top_ft600_loopback.v
Normal 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
|
143
RTL/fpga_ft600_example/fpga_top_ft600_rx_crc.v
Normal 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
|
141
RTL/fpga_ft600_example/fpga_top_ft600_tx_mass.v
Normal 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
|
91
RTL/fpga_ft600_example/rx_calc_crc.v
Normal 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
|
95
RTL/fpga_ft600_example/tx_specified_len.v
Normal 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
|
107
RTL/ftdi_245fifo/axi_stream_assert.v
Normal 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
|
82
RTL/ftdi_245fifo/axi_stream_downsizing.v
Normal 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
|
147
RTL/ftdi_245fifo/axi_stream_packing.v
Normal 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
|
82
RTL/ftdi_245fifo/axi_stream_resizing.v
Normal 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
|
127
RTL/ftdi_245fifo/axi_stream_upsizing.v
Normal 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
@ -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
@ -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
|
97
RTL/ftdi_245fifo/fifo_async.v
Normal 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
|
85
RTL/ftdi_245fifo/fifo_delay_submit.v
Normal 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
|
129
RTL/ftdi_245fifo/ftdi_245fifo_fsm.v
Normal 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
|
675
RTL/ftdi_245fifo/ftdi_245fifo_top.v
Normal 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
|
26
RTL/ftdi_245fifo/resetn_sync.v
Normal 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
@ -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
|
5
SIM/tb_ftdi_245fifo_run_iverilog.bat
Normal 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
@ -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
|
||||
|
BIN
figures/ft232h_driver_download.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
figures/ft232h_example_sch.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
figures/ft232h_program.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
figures/ft232h_ready.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
figures/ft600_driver_download.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
figures/ft600_example_sch.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
figures/ft600_ready.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
figures/ports.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
figures/structure.png
Normal file
After Width: | Height: | Size: 31 KiB |
283
python/USB_FTX232H_FT60X.py
Normal 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()
|
||||
|
||||
|
57
python/usb_loopback_mass.py
Normal 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()
|
33
python/usb_loopback_simple.py
Normal 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
@ -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
@ -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()
|