USB英文全称 Universal Serial Bus,即通用串行总线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。
FTDI 公司的USB2.0 芯片FT232H 进行USB 通信
FH232H芯片在内部不仅完成了USB 硬件接口差分电平转换,还封装了USB 的相关协议,留出数据交互接口
FH232H可以默认配置为UART模式,还可配置为JTAG,FIFO,IIC,SPI,本次将其配置为FIFO进行实验。
作为FPGA开发者只需要关注数据交互接口的时序即可实现USB功能。
MPSSE/Multi_purpose UART/FIFO controler:该模块为多功能UART/FIFO 控制器,它集成了面向用户的IO,直接负责与用户端(FPGA)进行数据交互。当把FT232H 配置成不同的模式时,管脚会有不同的含义
(I/O是对于FT232H来说)
ADBUS[7:0]: (I/O) 数据端口,双向IO接口(读写不能同时发生)
RD: (IN) FT232H中的数据输出使能,由FPGA发送给FT232H,低电平有效
WR: (IN) FT232H中的数据输入使能,由FPGA发送给FT232H,低电平有效
RXF: (OUT) FT232H中的FIFO数据可读标志位,低电平有效
TXE: (OUT) FT232H中的FIFO数据可写标志位,低电平有效
OE: (IN) FT232H中的数据输出,低电平有效
CLKOUT: (OUT) 60MHz时钟信号,由FT232H产生并传递给FPGA作为本次实验主时钟
SIWU: (OUT) FT232H中的数据立即发送使能,低电平有效
读时序:232拉低RXF表示有数据可读 > FPGA拉低OE使能输出 > 等待数据有效 > FPGA拉低RD > 有效数据 > 232拉高RXF > 读空 > FPGA需要同时拉高RD和OE
写时序:232中FIFO未满 > 232拉低TXE > FPGA拉低WR > 向232写入数据 > 232拉高TXE > 写满
硬件设计:参考https://github.com/WangXuan95/FPGA-ftdi245fifo,正点原子达芬奇Pro
连接好硬件后进行FT232H模式修改:
安装后运行,去掉其他FT器件,按如图所示操作
编写代码实现回环(参考正点原子源码)
配置管脚,按硬件连接配置管脚
xilinx vivado FIFO IP核配置:
顶层模块
module usb_loopback (
input usb_clk_60m, //FT232输出的60M时钟
input sys_rst_n, //系统复位 ,低电平
input usb_rxf_n, //FT232H中FIFO数据的可读标
input usb_txe_n, //FT232H中FIFO数据的可写标
output usb_oe_n, //FT232H数据输出使能
output usb_rd_n, //FT232H读使能信号
output usb_wr_n, //FT232H写使能信号
output usb_siwu_n, //send immediate/wake up
output c7, //send immediate/wake up
inout [7:0] usb_data //FT232H双向数据总线
);
//wire define
wire [7:0] fifo_data_in; //从FT232进到FPGA的数据
wire [7:0] fifo_data_out; //从FPGA输出到FT232的数据
wire wr_en; //FPGA FIFO写使能
wire rd_en; //FPGA FIFO读使能
wire full; //FPGA FIFO写满信号
wire empty; //FPGA FIFO读空信号
//*****************************************************
//** main code
//*****************************************************
assign usb_siwu_n = 1'b1; //立即发送,唤醒
assign c7 = 1'b1; //立即发送,唤醒
//USB 同步FIFO读写
usb_rw u_usb_rw (
.usb_clk_60m(usb_clk_60m),
.rst_n (sys_rst_n),
.usb_rxf_n (usb_rxf_n),
.usb_txe_n (usb_txe_n),
.usb_oe_n (usb_oe_n),
.usb_rd_n (usb_rd_n),
.usb_wr_n (usb_wr_n),
.fifo_wr_en (wr_en),
.fifo_rd_en (rd_en),
.empty (empty),
.usb_data (usb_data),
.fifo_data_in (fifo_data_in),
.fifo_data_out(fifo_data_out)
);
//FPGA FIFO调用
fifo_generator_0 u_fifo_generator_0 (
.clk (usb_clk_60m), // input wire clk
.srst (1'b0), // input wire srst
.din (fifo_data_in), // input wire [7 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout (fifo_data_out), // output wire [7 : 0] dout
.full (full), // output wire full
.empty(empty) // output wire empty
);
// ila_0 u_ila_0 (
// .clk(usb_clk_60m), // input wire clk
// .probe0(fifo_data_out), // input wire [7:0] probe0
// .probe1(fifo_data_in), // input wire [7:0] probe1
// .probe2({usb_rxf_n,usb_txe_n,usb_oe_n,usb_rd_n,usb_wr_n}), // input wire [7:0] probe2
// .probe3(0) // input wire [7:0] probe3
// );
endmodule
USB模块:
module usb_rw (
input usb_clk_60m, //FT232 输出的60M 时钟
input rst_n, //系统复位 ,低电平
//FT232H
input usb_rxf_n, //FT232H 中FIFO 数据的可读标志
input usb_txe_n, //FT232H 中FIFO 数据的可写标志
output reg usb_oe_n, //FT232H 数据输出使能
output reg usb_rd_n, //FT232H 读使能信号
output reg usb_wr_n, //FT232H 写使能信号
inout [7:0] usb_data, //FT232H 双向数据总线
//FPGA FIFO
output reg fifo_wr_en, //FPGA FIFO写使能
output reg fifo_rd_en, //FPGA FIFO读使能
input empty, //FPGA FIFO读空信号
input [7:0] fifo_data_out, //FPGA FIFO中读出的数据
output reg [7:0] fifo_data_in //写入FPGA FIFO的数据
);
// localparam define
localparam IDLE = 4'b001; //FT232H 空闲
localparam READ = 4'b010; //FT232H 读状态,此时数据从FT232H发送到FPGA
localparam WRITE = 4'b100; //FT232H 写状态,此时数据从FPGA发送到FT232H
//reg define
reg [2:0] cur_state; //读写现状态
reg [2:0] next_state; //读写次状态
reg usb_oe_n_d1; //usb_oe_n下一拍
//*****************************************************
//** main code
//*****************************************************
//在FT232H写状态,将FIFO的数据输出赋值给将USB数据总线,其他时候为高阻态
assign usb_data = (next_state == WRITE) ? fifo_data_out : 8'hzz;
//产生FT232H数据输出使能usb_oe_n
always @(posedge usb_clk_60m or negedge rst_n) begin
if (!rst_n) usb_oe_n <= 1'b1;
else if (!usb_rxf_n) usb_oe_n <= 1'b0;
else usb_oe_n <= 1'b1;
end
//FT232H数据输出使能usb_oe_n打一拍
always @(posedge usb_clk_60m or negedge rst_n) begin
if (!rst_n) usb_oe_n_d1 <= 1'b1;
else usb_oe_n_d1 <= usb_oe_n;
end
//状态跳转
always @(posedge usb_clk_60m or negedge rst_n) begin
if (!rst_n) cur_state <= IDLE;
else cur_state <= next_state;
end
//读写状态跳转条件
always @(*) begin
case (cur_state)
IDLE: begin
if (usb_rxf_n == 1'b0) //usb_rxf_n拉低,,ft232中数据可读,下一时钟进入去读FT232H数据
next_state <= READ; //usb_txe_n拉低且FPGA FIFO不空进入FT232H写
else if ((usb_txe_n == 1'b0) && (empty == 1'b0)) next_state <= WRITE;//ft232可写且本地fifo不为空
else next_state <= IDLE;
end
READ: begin //usb_rxf_n拉高,ft232数据读空,从FT232H读回到空闲状态
if ((usb_oe_n_d1 == 1'b1) && (usb_rxf_n == 1'b1)) next_state <= IDLE;
else next_state <= READ;
end
WRITE: begin //usb_txe_n拉高或者FPGA FIFO被读空,回到空闲状态
if ((usb_txe_n == 1'b1) || (empty == 1'b1)) next_state <= IDLE;
else next_state <= WRITE;
end
default: next_state <= IDLE;
endcase
end
//状态赋值
always @(*) begin
case (next_state)
IDLE: begin
fifo_data_in <= 8'hzz;
usb_rd_n <= 1'b1;
usb_wr_n <= 1'b1;
fifo_wr_en <= 1'b0;
fifo_rd_en <= 1'b0;
end
//读状态时,将usb数据赋值给fifo_data_in
READ: begin
fifo_data_in <= usb_data;
usb_wr_n <= 1'b1;
fifo_rd_en <= 1'b0;
//在usb_oe_n为低且在usb_oe_n下一拍也为低时拉低usb_rd_n,其他时候为高
if ((usb_oe_n_d1 == 0) && (usb_oe_n == 0)) usb_rd_n <= 1'b0;//拉低读取标志位,开始读取ft232数据
else usb_rd_n <= 1'b1;
//在usb_oe_n下一拍为低,且usb_rxf_n也为低时使能FIFO写
if ((usb_oe_n_d1 == 0) && (usb_rxf_n == 0)) fifo_wr_en <= 1'b1;//使能本地fifo写入数据
else fifo_wr_en <= 1'b0;
end
//写状态时,使能fifo_rd_en和usb_wr_n
WRITE: begin
fifo_data_in <= 8'hzz;
usb_rd_n <= 1'b1;
fifo_wr_en <= 1'b0;
usb_wr_n <= 1'b0;
fifo_rd_en <= 1'b1;
end
default: begin
fifo_data_in <= 8'hzz;
usb_rd_n <= 1'b1;
usb_wr_n <= 1'b1;
fifo_wr_en <= 1'b0;
fifo_rd_en <= 1'b0;
end
endcase
end
endmodule
下载完成后断开下载线,只保留USB连接线,使用串口助手 ATK-FUSB测试:
成功的话会在接收窗口看到同样的数据返回。