FT601Q介绍
FT601Q 是 FTDI 推出的一款超高速 USB3.0 芯片,提供高达 5Gbps 的带宽。该芯片不需要额外的固件开发,共有 4 个写通道和 4 个读通道,每个通道的缓冲大小均为 4KB。FT601Q 具有多种工作模式,本文介绍并实现相对简单的同步 FIFO 模式——245 mode。
FT601 工作模式在上电时检测 GPIO0/GPIO1 来确定,当 {GPIO1,GPIO0}=2‘b00 时,FT601Q 将进入 245 工作模式。
首先对 FT601Q 的引脚功能进行介绍,QFN76 封装如下
- DATA0 - DATA31,数据引脚,inout
- BE0 - BE3,Byte Enable 引脚,inout,高电平有效;DATA被分成了4Byte,每个 Byte 占据一个通道,通道间相互独立,通过 BE 引脚进行控制
- RESET_N,input,控制 FT601Q 的上电
- SIWU_N,input,保留引脚,必须拉高
- TXE_N,input,USB FIFO 可写信号,低电平有效
- RXF_N,input,USB FIFO 可读信号,低电平有效
- WR_N,input,USB FIFO 写使能,低电平有效
- RD_N,input,USB FIFO 读使能,低电平有效
- OE_N,input,USB FIFO DATA OUT Enable,数据输出使能信号,低电平有效
- WAKEUP_N,inout,指示 FT601 是否处于休眠状态,也可以通过拉低该引脚唤醒 FT601Q
- GPIO0、GPIO1,inout,功能较多,本文这里只用到上电时确定工作模式,其余不表
- CLK,output,FT601Q 输出的时钟信号,可配置为 66MHz 或 100MHz,默认 100MHz。有一点需要注意,在 FT601Q 启动后,若没能与上位机建立通信,FT601 将自动休眠,该时钟也会消失!因此必须配合上位机 api 进行使用
245 模式下,同步写时序如下图
在检测到 FIFO 可写信号 TXE_N=L 时,FPGA 拉低 WR_N 以开始写 FT601Q 内部的 FIFO,在 CLK 上升沿,数据将被写入 FIFO 中。我们利用全部的 4 个通道,因此 BE 应置为 0b1111。
同步读时序如下图
在检测到 FIFO 可读信号 RXF_N=L 时,FPGA 需要拉低 OE_N 与 RD_N 引脚以开始数据读取(测试表明 RD_N 信号可以和 OE_N 同时有效,而不必如时序图中检测到 OE_N 有效后才拉低),FT601Q 将在 CLK 的下降沿给出数据,因此用户可在上升沿读到稳定的数据。该模式下,读取会用到全部的四个通道,即 USB 芯片给出 BE=0b1111。
FPGA代码实现
为了可以简单适配各种读写时钟的情况,可以例化两个异步 FIFO,一个用于写通道,将写时钟 clk 下的数据写入 FIFO_w,而一旦检测到 FIFO_w 非空,即可将数据推送给 USB FIFO。另一个用于读通道,一旦检测到 USB FIFO 可读,就获取数据并存入 FIFO_r,用户可在读时钟 clk 域下从 FIFO_r 中获取数据。
/* * file : FT601Q.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-04-03* version : v2.0* description : usb3.0 芯片 FT601Q-QFN76 读写控制* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
// 两种模式:245 Synchronous FIFO mode、Multi-Channel FIFO mode
// 这里实现 245 Synchronous FIFO mode
module FT601Q(
input clk, //用于复位计时及外部FIFO读写时钟
input rst_n,//---------------FT601Q------------------
input USB_CLK, //FT601Q输出的时钟,可配置为66MHz或100MHz
//通过官方提供的上位机软件(FT600ChipConfigurationProgUtility_v1.3.0.2),可以配置PID、VID、Clock Freq等一系列属性inout [31:0] USB_D, //32bit数据总线,I/O
inout [3:0] USB_BE, //Byte Enable,高电平有效 1111 表示使用 4 Channelinput USB_TXE, //FT601Q-FIFO可写信号(即601Q-FIFO非满),低电平有效
input USB_RXF, //FT601Q-FIFO可读信号(即601Q-FIFO非空),低电平有效output reg USB_OE, //FT601Q-输出使能(Data Output Enable),低电平有效
output reg USB_RD, //FT601Q-读使能,低电平有效
output reg USB_WR, //FT601Q-写使能,低电平有效inout USB_Wakeup,
//Suspend/Remote Wakeup pin by default Low when USB is active, high when USB is in suspend.
//Application can drive this pin low in in USB suspend to generate a remote wakeup signal to the USB host.output reg USB_RSTn, //RESET_N,也即USB_EN,控制USB的上电output USB_SIWU, //reserved,should pull-upinout USB_GPIO0, //GPIO0
inout USB_GPIO1, //GPIO1//-------------FPGA Control------------------
input wr_en, //高电平有效
input [31:0] wrdat,
output full,input rd_en, //高电平有效
output [31:0] rddat,
output empty
);//-------------------------state define------------------------------
localparam RESET = 8'h01;
localparam IDLE = 8'h02;
localparam WRITE = 8'h04; //若write fifo非空,且usb fifo可写,将数据推入USB,写优先
localparam READ = 8'h08; //若usb fifo可读,且read fifo非满,将数据读到read fifo//------------------------------------------------------------------
reg [7:0] state = RESET;
reg [7:0] next_state = RESET;wire w_fifo_full;
wire w_fifo_empty;
wire r_fifo_full;
wire r_fifo_empty;reg w_fifo_rden = 1'b0;
reg r_fifo_wren = 1'b0;wire fifo_rst;
assign fifo_rst = ~rst_n;reg [7:0] rst_cnt = 8'd0;//----------------------------iobuf---------------------------------
wire [31:0] USB_D_buf;
reg USB_D_link = 1'b0;assign USB_D = (USB_D_link)? USB_D_buf : 32'dz;wire GPIO0_buf;
wire GPIO1_buf;
reg GPIO_link = 1'b0;assign GPIO0_buf = 1'b0;
assign GPIO1_buf = 1'b0;
assign USB_GPIO0 = (GPIO_link)? GPIO0_buf : 1'bz;
assign USB_GPIO1 = (GPIO_link)? GPIO1_buf : 1'bz;wire [3:0] USB_BE_buf;
reg USB_BE_link = 1'b0;assign USB_BE_buf = 4'b1111;
assign USB_BE = (USB_BE_link)? USB_BE_buf : 4'dz;//-------------------------write fifo--------------------------------
//异步FIFO 采用 First Word Fall Through 模式
fifo_generator_ft60x fifo_w(.rst (fifo_rst),.wr_clk (clk),.rd_clk (USB_CLK),.din (wrdat),.wr_en (wr_en),.rd_en (w_fifo_rden),.dout (USB_D_buf),.full (w_fifo_full),.empty (w_fifo_empty)
);//--------------------------read fifo--------------------------------
//异步FIFO
fifo_generator_ft60x fifo_r(.rst (fifo_rst),.wr_clk (USB_CLK),.rd_clk (clk),.din (USB_D),.wr_en (r_fifo_wren),.rd_en (rd_en),.dout (rddat),.full (r_fifo_full),.empty (r_fifo_empty)
);//-----------------------------FSM----------------------------------
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginstate <= RESET;rst_cnt <= 8'd0;endelse beginstate <= next_state;if(rst_cnt < 8'd200) beginrst_cnt <= rst_cnt + 1'b1;endelse beginrst_cnt <= rst_cnt;endend
endalways @(*) begincase(state)RESET: beginif(rst_cnt >= 8'd200) beginnext_state <= IDLE;endelse beginnext_state <= RESET;endendIDLE: beginif((~USB_TXE) && (~w_fifo_empty)) beginnext_state <= WRITE;endelse if((~USB_RXF) && (~r_fifo_full)) beginnext_state <= READ;endelse beginnext_state <= IDLE;endendWRITE: beginif(USB_TXE || w_fifo_empty) beginnext_state <= IDLE;endelse beginnext_state <= WRITE;endendREAD: beginif(USB_RXF || r_fifo_full) beginnext_state <= IDLE;endelse beginnext_state <= READ;endenddefault: beginnext_state <= IDLE;endendcase
end//----------------------------Control--------------------------------
assign full = w_fifo_full;
assign empty = r_fifo_empty;assign USB_SIWU = 1'b1;always @(posedge clk) begincase(state)RESET: beginGPIO_link <= 1'b1;enddefault: beginGPIO_link <= 1'b0;endendcase
endalways @(*) begincase(state)RESET: beginUSB_RSTn <= 1'b0;enddefault: beginUSB_RSTn <= 1'b1;endendcase
endalways @(*) begincase(state)WRITE: beginUSB_D_link <= 1'b1;enddefault: beginUSB_D_link <= 1'b0;endendcase
end// ------------USB FIFO write--------------
always @(*) begincase(state)WRITE: beginUSB_BE_link <= 1'b1;USB_WR <= ((~USB_TXE) & (~w_fifo_empty))? 1'b0 : 1'b1;w_fifo_rden <= ((~USB_TXE) & (~w_fifo_empty))? 1'b1 : 1'b0;enddefault: beginUSB_BE_link <= 1'b0;USB_WR <= 1'b1;w_fifo_rden <= 1'b0;endendcase
end// ------------USB FIFO read---------------
always @(*) begincase(state)READ: beginUSB_OE <= ((~USB_RXF) & (~r_fifo_full))? 1'b0 : 1'b1;USB_RD <= ((~USB_RXF) & (~r_fifo_full))? 1'b0 : 1'b1;enddefault: beginUSB_OE <= 1'b1;USB_RD <= 1'b1;endendcase
endalways @(posedge USB_CLK) begincase(state)READ: beginr_fifo_wren <= ((~USB_RXF) & (~r_fifo_full))? 1'b1 : 1'b0;enddefault: beginr_fifo_wren <= 1'b0;endendcase
endendmodule
环回测试
使用官方提供的上位机 Loopback 测试程序进行测试,结果如下
由测试结果可以看到,FT601Q是小端传输,首先发送 B0(将接收到的第一个字节放在 B0),然后发送 B1(将接收到的第二个字节放在 B1),依次类推 B2、B3 的收发次序。例如本次 LoopBack 测试,发送的 0x764dfffe,在 FPGA 接收到的是 0xfeff4d76,直接转发回上位机,又得 0x764dfffe,实际使用时请务必注意这个问题!
something
在上位机测试时遇到的一些坑,在此记录:
- 使用 Python 进行调试(D3XXPython_Release)时提示缺失 dll:需要将 FTD3XXLibray 压缩包中的 FTD3XX.dll 添加到路径
- 启动 LoopbackDemo、StreamerDemo 后,检测不到 USB 设备,然而查看设备管理器可以看到该设备:是因为缺少 FTDI 驱动,安装驱动(ftdi_ft60x_1.2.0.5)后即可解决
以上压缩包和驱动均能在 FTDI 官网找到。
FT600ChipConfigurationProgUtility 压缩包中是 FT600/FT601Q 的配置程序,通过该程序可以修改芯片的 VID、PID、Serial Number、Clock Frequency 等。