电商网站设计推荐亿企邦,免费主页空间申请网站,销售类网站开发,wordpress 上下篇FPGA之Usb数据传输
Usb 通信
你也许会有疑问#xff0c;明明有这么多通信方式和数据传输#xff08;SPI、I2C、UART、以太网#xff09;为什么偏偏使用USB呢?
原因有很多#xff0c;如下#xff1a; 1. 高速数据传输能力 高带宽#xff1a;USB接口提供了较高的数据传…FPGA之Usb数据传输
Usb 通信
你也许会有疑问明明有这么多通信方式和数据传输SPI、I2C、UART、以太网为什么偏偏使用USB呢?
原因有很多如下 1. 高速数据传输能力 高带宽USB接口提供了较高的数据传输速率尤其是随着USB版本的升级如USB 3.0及更高版本其理论速度可达5 Gbps甚至更高。这对于需要高速数据传输的应用如视频处理、实时数据采集等尤为重要。低延迟相比一些其他接口如UARTUSB的延迟更低能够满足实时性要求较高的场景。 2. 通用性和兼容性 广泛的硬件支持几乎所有现代计算机和嵌入式系统都配备了USB接口这意味着使用USB进行通信可以轻松实现跨平台支持无需额外的硬件适配。标准化接口USB是一种标准化的接口遵循统一的协议和规范。这不仅简化了开发过程还确保了不同设备之间的互操作性。 3. 开发便利性 丰富的工具支持大多数FPGA开发工具如Xilinx Vivado、Altera Quartus等都提供了对USB接口的支持简化了设计和验证过程。成熟的驱动和库资源大量的现成驱动程序和库资源可以轻松集成到项目中减少了软件开发的工作量。 4. 灵活的通信模式 全双工通信USB支持全双工通信模式允许同时进行数据的上传和下载提高了通信效率。多种数据传输类型USB支持控制传输、批量传输、中断传输和同步传输等多种数据传输类型能够适应不同应用场景的需求。 成本效益 低成本解决方案相比于一些高端接口如PCIeUSB的成本较低适合预算有限的项目。减少外部组件需求由于USB的标准化和广泛支持可以减少对外部组件的需求从而降低整体硬件成本。 也正是因为如此usb广泛应用于数据的采集和处理、视频和音频传输、嵌入式系统开发等。
而我们今天即将要学习的就是FPGA的USB传输以FX2芯片为例
FX2
USB是一种通用的数据传输协议和接口标准定义了设备与主机如电脑之间的通信规则如协议、电气特性、数据传输模式等FX系列芯片FX2, FX3是Cypress(现英飞凌)推出的USB控制芯片用于实现高速USB设备的功能。说的再简单直白一点USB是协议标准FX芯片是实现这一标准的硬件载体
FX芯片可以
自动处理USB复杂协议无需开发者手动实现支持高速传输FX2支持USB2.0高速传输 480Mbps; FX3则为 5Gbps提供灵活的接口GPIFSlave FIFO方便直接连接外设内置微控制器可以通过固件配置USB功能
FX2控制器内部结构图如下
FX2可以通过两种方式到FPGA一个是(通用可编程接口)GPIF模式和从设备FIFO模式
GPIFFX2是总线的主控者用户自定义时序灵活但开发复杂
Slave FIFO FX2是被动的FIFO从设备外部主控直接控制简单但灵活性受限 在实际项目中Slave FIFO模式更常用尤其是FPGA做为主控的场景而GPIF模式需要更精确控制总线的特殊需求
回环测试
介绍
我们此处就以简单的回环测试为例实现FPGA的Usb数据传输。 所谓回环测试就是说由 PC 发送数据到 FX2 芯片的 OUT 端点 2然后再由主机将端点 2 中的数据读出拷贝到IN 端点 6。使用 FPGA 设计 SlaveFIFO 读取和写入接口逻辑将端点 2 中的数据读出然后写入端点 6 中再由电脑上位机从端点 6 中将数据读回从而实现数据的回环。
代码
FIFO module FiFo #( Depth 512,Width 16
)
(input fifo_clk,input rst_n,input write_busy,input read_busy,input fifo_flush,input [Width-1:0]din,output reg fifo_full,output reg fifo_empty,output reg [Width-1:0] dout
);localparam ADDR_Width $clog2(Depth);//计数多加一位防止溢出
reg [ADDR_Width:0] write_occupancy;
reg [ADDR_Width:0] read_occupancy;
wire [ADDR_Width:0] next_write_occupancy;
wire [ADDR_Width:0] next_read_occupancy;//fifo 地址索引
wire [ADDR_Width-1:0] next_write_ptr;
reg [ADDR_Width-1:0] write_ptr;
wire [ADDR_Width-1:0] next_read_ptr;
reg [ADDR_Width-1:0] read_ptr;reg [Width-1:0] data_array[Depth-1:0];wire write_enable;
wire read_enable;// 写使能和读使能逻辑
assign write_enable !write_busy !fifo_full;
assign read_enable !read_busy !fifo_empty;// 下一个指针和计数器计算
assign next_write_ptr (write_enable) ? (write_ptr 1) : write_ptr;
assign next_read_ptr (read_enable) ? (read_ptr 1) : read_ptr;assign next_write_occupancy fifo_flush ? 10d0 : (write_enable ? (write_occupancy 1) : write_occupancy);
assign next_read_occupancy fifo_flush ? 10d0 : (read_enable ? (read_occupancy 1) : read_occupancy);// 满/空状态判断基于下一个计数器值
wire [ADDR_Width:0] next_occupancy_diff next_write_occupancy - next_read_occupancy;
wire next_fifo_full (next_occupancy_diff Depth);
wire next_fifo_empty (next_occupancy_diff 0);//更新指针
always (posedge fifo_clk or negedge rst_n)beginif(!rst_n)beginwrite_ptr0;read_ptr0;end else if(fifo_flush)beginwrite_ptr0;read_ptr0;end else beginwrite_ptrnext_write_ptr;read_ptrnext_read_ptr;end//else
end//always// 更行空/满信号
always (posedge fifo_clk or negedge rst_n)beginif(!rst_n)beginfifo_full0;fifo_empty1;end else if (fifo_flush)beginfifo_full0;fifo_empty1;end else beginfifo_fullnext_fifo_full;fifo_emptynext_fifo_empty;end
end//always// 读/写计数always (posedge fifo_clk or negedge rst_n)beginif(!rst_n)beginwrite_occupancy0;read_occupancy0;end else if(fifo_flush)beginwrite_occupancy0;read_occupancy0;end else beginwrite_occupancynext_write_occupancy;read_occupancynext_read_occupancy;end//else
end//always//输出数据
always (posedge fifo_clk or negedge rst_n)beginif(!rst_n)dout0;else if(fifo_flush) dout0;else doutdata_array[read_ptr];
end//always//数据写入存储阵列
always (posedge fifo_clk)beginif(write_enable)data_array[write_ptr]din;
end// 溢出警告
always (posedge fifo_clk) beginif (fifo_full write_busy) begin$display(ERROR: %m: Fifo overflow at time %t, $time);$finish;end
end // always// 下溢警告
always (posedge fifo_clk) beginif (fifo_empty read_busy) begin$display(ERROR: %m: Fifo underflow at time %t, $time);$finish;end
end // always
// synthesis translate_on
endmodule
FX2_SF
module FX2_SF(input clk,input reset_n,inout [15:0] fx2_fdata, // 双向数据总线output [1:0] fx2_faddr, // FIFO地址选择output fx2_slrd, // 读使能低有效output fx2_slwr, // 写使能低有效output fx2_sloe, // 输出使能低有效input ep6_full_flag, // EP6满标志可写input ep2_empty_flag, // EP2空标志可读input fx2_ifclk, // 接口时钟60MHzoutput fx2_pkt_end, // 包结束脉冲output fx2_clear, // 复位信号output fx2_slcs // 片选常低
);//------------------------ 参数优化 ------------------------//
localparam [1:0] LOOPBACK_IDLE 2d0,LOOPBACK_READ 2d1,LOOPBACK_WAIT_ep6_full 2d2,LOOPBACK_WRITE 2d3;localparam [1:0]FIFO_ADDR_READ 2b00, // EP2FIFO_ADDR_WRITE 2b10; // EP6//------------------------ 信号声明 ------------------------//
reg [1:0] current_state, next_state;// FIFO控制信号
wire fifo_wr_en;
wire fifo_rd_en;
reg [15:0] fifo_din;
wire [15:0] fifo_dout;// FX2接口信号
reg slrd_n;
reg slwr_n;
reg sloe_n;
reg [1:0] faddr_n;
reg pkt_end_n;//------------------------ 接口分配 ------------------------//
assign fx2_slwr slwr_n;
assign fx2_slrd slrd_n;
assign fx2_sloe sloe_n;
assign fx2_faddr faddr_n;
assign fx2_pkt_end pkt_end_n;
assign fx2_slcs 1b0; // 常使能
assign fx2_clear 1b0; // 未使用// 三态总线控制
assign fx2_fdata (slwr_n 1b0) ? fifo_dout : 16hzzzz;//------------------------ 状态机 ------------------------//
always (posedge fx2_ifclk or negedge reset_n) beginif(!reset_n) current_state LOOPBACK_IDLE;else current_state next_state;
endalways (*) beginnext_state current_state;case(current_state)LOOPBACK_IDLE: //ep2为空, 上位机可传输数据if(ep2_empty_flag) next_state LOOPBACK_READ;LOOPBACK_READ: if(!ep2_empty_flag) next_state LOOPBACK_WAIT_ep6_full;LOOPBACK_WAIT_ep6_full: if(ep6_full_flag) next_state LOOPBACK_WRITE;LOOPBACK_WRITE: beginif(!ep6_full_flag || fifo_empty) next_state LOOPBACK_IDLE;enddefault: next_state LOOPBACK_IDLE;endcase
end//------------------------ 控制信号生成 ------------------------//
always (*) begin// 默认值slrd_n 1b1;sloe_n 1b1;slwr_n 1b1;faddr_n FIFO_ADDR_READ;pkt_end_n 1b1;case(current_state)LOOPBACK_READ: beginfaddr_n FIFO_ADDR_READ;slrd_n !ep2_empty_flag; // 有数据时持续读取sloe_n !ep2_empty_flag;endLOOPBACK_WRITE: beginfaddr_n FIFO_ADDR_WRITE;slwr_n !(ep6_full_flag !fifo_empty);// 在最后一次写入后生成包结束脉冲pkt_end_n (slwr_n 1b0) ? 1b0 : 1b1;endendcase
end//------------------------ FIFO接口 ------------------------//
assign fifo_wr_en (current_state LOOPBACK_READ) !slrd_n;
assign fifo_rd_en (current_state LOOPBACK_WRITE) !slwr_n;// 数据输入寄存器
always (posedge fx2_ifclk) beginif(fifo_wr_en) fifo_din fx2_fdata;
endFiFo #(.Depth(512),.Width(16)
) u_fifo (.fifo_clk (fx2_ifclk),.rst_n (reset_n),.write_busy (1b0), // 外部无写阻塞.read_busy (1b0), // 外部无读阻塞.fifo_flush (1b0), // 禁用自动flush.din (fifo_din),.fifo_full (fifo_full),.fifo_empty (fifo_empty),.dout (fifo_dout)
);wire clk_96m;//生成96M时钟用于ILA采样pll pll_inst(.clk_out1(clk_96m),.clk_in1(clk));//------------------------ 调试模块注释 ------------------------//ila_0 ila_0_inst(.clk(clk_96m), // input wire clk.probe0(fx2_fdata), // input wire [15:0] probe0 .probe1(fx2_faddr), // input wire [1:0] probe1 .probe2(ep2_empty_flag), // input wire [0:0] probe2 .probe3(ep6_full_flag), // input wire [0:0] probe3 .probe4(fx2_sloe), // input wire [0:0] probe4 .probe5(fx2_slwr), // input wire [0:0] probe5 .probe6(fx2_slrd), // input wire [0:0] probe6 .probe7(fifo_empty), // input wire [0:0] probe7 .probe8(fifo_full), // input wire [0:0] probe8 .probe9(fifo_flush) // input wire [0:0] probe9);endmodule注实现该项目时需要使用到 Cypress提供的基本开发包名为CySuiteUsb和安装对应的驱动可以到官网上去下载。