USB3.0 PHY方案(FT601Q)在 FPGA上的速率验证
一、背景
高通量在体神经信号采集系统,随着通道数增加、增加实时刺激需求等,采用以太网传输面临带宽极限,亟需一种更快的传输介质。
目前以太网的带宽极限:实测800Mbit/s左右,[移植并使用Iperf3测试ARM Linux网口带宽]https://www.cnblogs.com/dy-stairmed/p/18796116
在8port 30K采集速率下:16*64*2*8*30k = 491.5Mbit/s接近传输极限。
因此采用USB3.0 传输,理论传输带宽:5Gb/s。通过前期调研,目前USB3.0 PHY方案一般通过 FPGA实现。
二、硬件方案
FT601Q FIFO-USB3.0 Bridge,理论:400MB/s,连接至ZYNQ_PL端。

三、逻辑架构设计
-
系统框图
采用两个异步fifo实现多比特跨时钟域处理,将硬件接口转换为 FIFO的读写接口,后续可以根据应用调整为
AXI-STREAM接口。

2. 引脚定义:

3. 拟实现的读写时序


四、代码
-
驱动module
点击查看代码
/*
* file : FT601Q.v
* author : D.y
* date : 2025-03-19
* version : v0.1
* description : usb3.0 FT601Q-QFN76 write and read control rtl.use 245 Synchronous FIFO mode.first release
use async fifo to deal with multi bit cross clock domain
actual test result: 380MB/s for read,340MB/s for write
*
*/
module FT601Q(
input clk, //sysclk,50Mhz at least
input rst_n, //active low
//---------------FT601Q------------------
input USB_CLK, //FT601Q phy output clk, configure to 100MHz using FT600ChipConfigurationProgUtility_v1.3.0.2)
inout [31:0] USB_D, //32bit data inout,I/O
inout [3:0] USB_BE, //Byte Enable,active high,4'b1111 means using 4 Channel
input USB_TXE, //FT601Q-FIFO write enable?601Q-FIFO not full),active low
input USB_RXF, //FT601Q-FIFO read enable ?601Q-FIFO not empty),active low
output reg USB_OE, //FT601Q Data Output Enable active low
output reg USB_RD, //FT601Q-read enable,active low
output reg USB_WR, //FT601Q-write enable,active low
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,enable use
output USB_SIWU, //reserved,should pull-up
inout USB_GPIO0, //GPIO0
inout USB_GPIO1, //GPIO1
//-------------FPGA Control------------------
input wr_en, //active high
input [31:0] wrdat,
output full,
input rd_en, //active high
output [31:0] rddat,
output empty
);
//-------------------------state define------------------------------
localparam RESET = 8'h01;
localparam IDLE = 8'h02;
localparam WRITE = 8'h04; //write fifo not empty,and USB_TXE keep low ,push data into USB
localparam READ = 8'h08; //USB_RXF keep low,and read fifo not full,read data to read fifo
//-------------------------signal define-----------------------------
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;
reg [7:0] rst_cnt = 8'd0;
assign fifo_rst = ~rst_n;
//----------------------------iobuf---------------------------------
wire [31:0] USB_D_buf;
(* mark_debug = "true" *) 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;
reg [31:0] USB_DATA;
reg r_fifo_wren_r1;
assign USB_BE_buf = 4'b1111;
assign USB_BE = (USB_BE_link)? USB_BE_buf : 4'dz;
(* mark_debug = "true" *) wire [31:0] debug_data ;
//----------------------------logic---------------------------------
// for debud only
assign debug_data = USB_D;
//always @(posedge USB_CLK) begin
// if(USB_BE_link)
// debug_data <= USB_D;
// else
// debug_data <= 'd0;
//end
//-------------------------write fifo--------------------------------
//async FIFO First Word Fall Through mode
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--------------------------------
//always @(posedge USB_CLK or negedge rst_n) begin
// if(~rst_n) begin
// USB_DATA <= 'b0;
// r_fifo_wren_r1 <= 1'b0;
// end
// else begin
// USB_DATA <= USB_D;
// r_fifo_wren_r1 <= r_fifo_wren;
// end
//end
//异步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) begin
if(~rst_n) begin
state <= RESET;
rst_cnt <= 8'd0;
end
else begin
state <= next_state;
if(rst_cnt < 8'd200) begin
rst_cnt <= rst_cnt + 1'b1;
end
else begin
rst_cnt <= rst_cnt;
end
end
end
always @(*) begin
case(state)
RESET: begin
if(rst_cnt >= 8'd200) begin
next_state <= IDLE;
end
else begin
next_state <= RESET;
end
end
IDLE: begin
if((~USB_TXE) && (~w_fifo_empty)) begin
next_state <= WRITE;
end
else if((~USB_RXF) && (~r_fifo_full)) begin
next_state <= READ;
end
else begin
next_state <= IDLE;
end
end
WRITE: begin
if(USB_TXE || w_fifo_empty) begin
next_state <= IDLE;
end
else begin
next_state <= WRITE;
end
end
READ: begin
if(USB_RXF || r_fifo_full) begin
next_state <= IDLE;
end
else begin
next_state <= READ;
end
end
default: begin
next_state <= IDLE;
end
endcase
end
//----------------------------Control--------------------------------
assign full = w_fifo_full;
assign empty = r_fifo_empty;
assign USB_SIWU = 1'b1;
always @(posedge clk) begin
case(state)
RESET: begin
GPIO_link <= 1'b1;
end
default: begin
GPIO_link <= 1'b0;
end
endcase
end
always @(*) begin
case(state)
RESET: begin
USB_RSTn <= 1'b0;
end
default: begin
USB_RSTn <= 1'b1;
end
endcase
end
always @(*) begin
case(state)
WRITE: begin
USB_D_link <= 1'b1;
end
default: begin
USB_D_link <= 1'b0;
end
endcase
end
// ------------USB FIFO write--------------
always @(*) begin
case(state)
WRITE: begin
USB_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;
end
default: begin
USB_BE_link <= 1'b0;
USB_WR <= 1'b1;
w_fifo_rden <= 1'b0;
end
endcase
end
// ------------USB FIFO read---------------
always @(*) begin
case(state)
READ: begin
USB_OE <= ((~USB_RXF) & (~r_fifo_full))? 1'b0 : 1'b1;
USB_RD <= ((~USB_RXF) & (~r_fifo_full))? 1'b0 : 1'b1;
end
default: begin
USB_OE <= 1'b1;
USB_RD <= 1'b1;
end
endcase
end
always @(posedge USB_CLK) begin
case(state)
READ: begin
r_fifo_wren <= ((~USB_RXF) & (~r_fifo_full))? 1'b1 : 1'b0;
end
default: begin
r_fifo_wren <= 1'b0;
end
endcase
end
endmodule
点击查看代码
/*
* file : top.v
* author : D.y
* date : 2025-03-19
* version : v0.1
* description : top module for FT601Q USB3.0 PHY
install FT601 MODULE,and generate sys_rst_n and system clk soure 100Mhz
*
*/
module top(
input sys_clk , // using outside 200M OSC
// inout GPIO_0_0_tri_io ,
// inout IIC_0_0_scl_io ,
// inout IIC_0_0_sda_io ,
// output MDIO_ETHERNET_0_0_mdc,
// inout MDIO_ETHERNET_0_0_mdio_io,
//FT601
input USB_CLK ,
inout [31:0] USB_D ,
inout [3:0] USB_BE ,
input USB_TXE ,
input USB_RXF ,
output USB_OE ,
output USB_RD ,
output USB_WR ,
inout USB_Wake ,
output USB_RSTn ,
output USB_SIWU ,
inout USB_GPIO_0 ,
inout USB_GPIO_1
);
//wire define
wire clk_50M ;
wire clk_200M_nRst ;
wire pll_locked ;
wire sys_rst_n ;
wire empty ;
wire fifo_full ;
reg fifo_wr_en ;
reg fifo_rd_en ;
reg [15:0] test_data ='d0 ;
wire [31:0] loop_back_data ;
wire [31:0] rd_data ;
// delay reset, active low
delayed_nReset U_delayed_nReset
(
.CLK(sys_clk),
.nRst(clk_200M_nRst)
);
// mmcm for output 100Mhz sysclk
clk_wiz_0 my_clk_wiz_0
(
// Clock out ports
.clk_out1(clk_50M), // output clk_out1
// Status and control signals
.reset(~clk_200M_nRst), // input reset
.locked(pll_locked), // output locked
// Clock in ports
.clk_in1(sys_clk)
); // input clk_in1
assign sys_rst_n = pll_locked;
//my_block_design_wrapper my_block_design_wrapper_ins
// (
// .MDIO_ETHERNET_0_0_mdc (MDIO_ETHERNET_0_0_mdc) ,
// .MDIO_ETHERNET_0_0_mdio_io (MDIO_ETHERNET_0_0_mdio_io) ,
// .GPIO_0_0_tri_io (GPIO_0_0_tri_io),
// .IIC_0_0_scl_io (IIC_0_0_scl_io),
// .IIC_0_0_sda_io (IIC_0_0_sda_io),
// .clk_out1_0 (clk_50M),
// .locked_0 (pll_locked)
// );
//-------------------------write data test-------------------------------
// USB_TXE is low , fifo write enable,and toggle data
always @(posedge clk_50M or negedge sys_rst_n) begin
if(~sys_rst_n) begin
fifo_wr_en <= 1'b0;
test_data <= 'd0;
end
else begin
if(~USB_TXE) begin
fifo_wr_en <= 1'b1;
test_data <= test_data + 1'b1;
end
else begin
fifo_wr_en <= 1'b0;
test_data <= 'd0;
end
end
end
//-------------------------read data test-------------------------------
//USB_RD is low ,fifo read enable,read data from fifo
always @(posedge USB_CLK or negedge sys_rst_n) begin
if(~sys_rst_n) begin
fifo_rd_en <= 1'b0;
end
else begin
if(~USB_RXF) begin
fifo_rd_en <= 1'b1;
end
else begin
fifo_rd_en <= 1'b0;
end
end
end
FT601Q FT601Q_ins
(
.clk (clk_50M) ,
.rst_n (sys_rst_n) ,
.USB_CLK (USB_CLK) ,
.USB_D (USB_D) ,
.USB_BE (USB_BE) ,
.USB_TXE (USB_TXE) ,
.USB_RXF (USB_RXF) ,
.USB_OE (USB_OE) ,
.USB_RD (USB_RD) ,
.USB_WR (USB_WR) ,
.USB_Wakeup(USB_Wake) ,
.USB_RSTn (USB_RSTn) ,
.USB_SIWU (USB_SIWU) ,
.USB_GPIO0 (USB_GPIO_0) ,
.USB_GPIO1 (USB_GPIO_1) ,
.wr_en (fifo_wr_en) ,
.wrdat ({test_data,test_data}) ,
.full (fifo_full) ,
.rd_en (fifo_rd_en) ,
.rddat (rd_data) ,
.empty (empty )
);
(* mark_debug = "true" *) wire [31:0] top_debug_data = rd_data;
//fifo_generator_ft60x my_fifo_generator_ft60x (
// .rst(~sys_rst_n), // input wire rst
// .wr_clk(USB_CLK), // input wire wr_clk
// .rd_clk(USB_CLK), // input wire rd_clk
// .din(rd_data), // input wire [31 : 0] din
// .wr_en(fifo_rd_en), // input wire wr_en
// .rd_en(fifo_wr_en), // input wire rd_en
// .dout(loop_back_data), // output wire [31 : 0] dout
// .full(), // output wire full
// .empty() // output wire empty
//);
endmodule
五、测试过程
-
驱动安装(需要使用支持 USB3.0 的USB-TYPE B线缆,)

同时电脑的USB接口也需要支持USB3.0,带SS(Super Speed)标志

插入后,windows会自行安装设备驱动,通过设备管理器可以识别如下:

通过Vivado创建项目,并进行引脚约束,生成bitstream,烧录。
-
测试工具
使用官方提供的流测试工具:FT600DataStreamerDemoApp_v1.3.0.10
下载链接如下:
https://ftdichip.com/software-examples/ft600-601-software-examples-2/
六、测试结果
-
写速率(上位机---> FPGA****),382MB/s.

- 读速率(FPGA ---->上位机),344MB/s

需要使用支持 USB3.0 的USB-TYPE B线缆,

浙公网安备 33010602011771号