FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解

FPGA之Usb数据传输

Usb 通信

你也许会有疑问,明明有这么多通信方式和数据传输(SPI、I2C、UART、以太网)为什么偏偏使用USB呢?

原因有很多,如下:

1. 高速数据传输能力

  • 高带宽:USB接口提供了较高的数据传输速率,尤其是随着USB版本的升级(如USB 3.0及更高版本),其理论速度可达5 Gbps甚至更高。这对于需要高速数据传输的应用(如视频处理、实时数据采集等)尤为重要。
  • 低延迟:相比一些其他接口(如UART),USB的延迟更低,能够满足实时性要求较高的场景。

2. 通用性和兼容性

  • 广泛的硬件支持:几乎所有现代计算机和嵌入式系统都配备了USB接口,这意味着使用USB进行通信可以轻松实现跨平台支持,无需额外的硬件适配。
  • 标准化接口:USB是一种标准化的接口,遵循统一的协议和规范。这不仅简化了开发过程,还确保了不同设备之间的互操作性。

3. 开发便利性

  • 丰富的工具支持:大多数FPGA开发工具(如Xilinx Vivado、Altera Quartus等)都提供了对USB接口的支持,简化了设计和验证过程。
  • 成熟的驱动和库资源:大量的现成驱动程序和库资源可以轻松集成到项目中,减少了软件开发的工作量。

4. 灵活的通信模式

  • 全双工通信:USB支持全双工通信模式,允许同时进行数据的上传和下载,提高了通信效率。
  • 多种数据传输类型:USB支持控制传输、批量传输、中断传输和同步传输等多种数据传输类型,能够适应不同应用场景的需求。

成本效益

  • 低成本解决方案:相比于一些高端接口(如PCIe),USB的成本较低,适合预算有限的项目。
  • 减少外部组件需求:由于USB的标准化和广泛支持,可以减少对外部组件的需求,从而降低整体硬件成本。

也正是因为如此,usb广泛应用于数据的采集和处理、视频和音频传输、嵌入式系统开发等。

而我们今天即将要学习的,就是FPGA的USB传输,以FX2芯片为例

FX2

USB是一种通用的数据传输协议和接口标准,定义了设备与主机(如电脑)之间的通信规则(如协议、电气特性、数据传输模式等);FX系列芯片(FX2, FX3)是Cypress(现英飞凌)推出的USB控制芯片,用于实现高速USB设备的功能。说的再简单,直白一点:USB是协议标准,FX芯片是实现这一标准的硬件载体

FX芯片可以

  1. 自动处理USB复杂协议,无需开发者手动实现,
  2. 支持高速传输(FX2:支持USB2.0高速传输, 480Mbps; FX3则为 5Gbps)
  3. 提供灵活的接口(GPIF,Slave FIFO)方便直接连接外设,
  4. 内置微控制器,可以通过固件配置USB功能

FX2控制器内部结构图如下
在这里插入图片描述

FX2可以通过两种方式到FPGA,一个是(通用可编程接口)GPIF模式和从设备FIFO模式

GPIF:FX2是总线的主控者,用户自定义时序,灵活但开发复杂

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 ? 10'd0 : (write_enable ? (write_occupancy + 1) : write_occupancy);
assign next_read_occupancy  = fifo_flush ? 10'd0 : (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)begin
    if(!rst_n)begin
        write_ptr<=0;
        read_ptr<=0;
    end else if(fifo_flush)begin
        write_ptr<=0;
        read_ptr<=0;
    end else begin
        write_ptr<=next_write_ptr;
        read_ptr<=next_read_ptr;
    end//else
end//always


// 更行空/满信号
always @(posedge fifo_clk or negedge rst_n)begin
    if(!rst_n)begin
        fifo_full<=0;
        fifo_empty<=1;
    end else if (fifo_flush)begin
        fifo_full<=0;
        fifo_empty<=1;
    end else begin
        fifo_full<=next_fifo_full;
        fifo_empty<=next_fifo_empty;
    end
end//always

// 读/写计数

always @(posedge fifo_clk or negedge rst_n)begin
    if(!rst_n)begin
        write_occupancy<=0;
        read_occupancy<=0;
    end else if(fifo_flush)begin
        write_occupancy<=0;
        read_occupancy<=0;
    end else begin
        write_occupancy<=next_write_occupancy;
        read_occupancy<=next_read_occupancy;
    end//else
end//always

//输出数据
always @(posedge fifo_clk or negedge rst_n)begin
    if(!rst_n)dout<=0;
    else if(fifo_flush) dout<=0;
    else dout<=data_array[read_ptr];
end//always

//数据写入存储阵列
always @(posedge fifo_clk)begin
    if(write_enable)
        data_array[write_ptr]<=din; 
end

// 溢出警告
always @(posedge fifo_clk) begin
	if (fifo_full && write_busy) begin
		$display("ERROR: %m: Fifo overflow at time %t", $time);
		$finish;
	end
end // always

// 下溢警告
always @(posedge fifo_clk) begin
	if (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,     // 接口时钟(60MHz)
    output       fx2_pkt_end,   // 包结束脉冲
    output       fx2_clear,     // 复位信号
    output       fx2_slcs       // 片选(常低)
);

//------------------------ 参数优化 ------------------------//
localparam [1:0] 
    LOOPBACK_IDLE       = 2'd0,
    LOOPBACK_READ       = 2'd1,
    LOOPBACK_WAIT_ep6_full = 2'd2,
    LOOPBACK_WRITE      = 2'd3;

localparam [1:0]
    FIFO_ADDR_READ  = 2'b00,  // EP2
    FIFO_ADDR_WRITE = 2'b10;  // 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   = 1'b0;     // 常使能
assign fx2_clear  = 1'b0;     // 未使用

// 三态总线控制
assign fx2_fdata = (slwr_n == 1'b0) ? fifo_dout : 16'hzzzz;

//------------------------ 状态机 ------------------------//
always @(posedge fx2_ifclk or negedge reset_n) begin
    if(!reset_n) current_state <= LOOPBACK_IDLE;
    else current_state <= next_state;
end

always @(*) begin
    next_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: begin
            if(!ep6_full_flag || fifo_empty) 
                next_state = LOOPBACK_IDLE;
        end
        
        default: next_state = LOOPBACK_IDLE;
    endcase
end

//------------------------ 控制信号生成 ------------------------//
always @(*) begin
    // 默认值
    slrd_n  = 1'b1;
    sloe_n  = 1'b1;
    slwr_n  = 1'b1;
    faddr_n = FIFO_ADDR_READ;
    pkt_end_n = 1'b1;

    case(current_state)
        LOOPBACK_READ: begin
            faddr_n = FIFO_ADDR_READ;
            slrd_n  = !ep2_empty_flag;  // 有数据时持续读取
            sloe_n  = !ep2_empty_flag;
        end
        
        LOOPBACK_WRITE: begin
            faddr_n = FIFO_ADDR_WRITE;
            slwr_n  = !(ep6_full_flag && !fifo_empty);
            // 在最后一次写入后生成包结束脉冲
            pkt_end_n = (slwr_n == 1'b0) ? 1'b0 : 1'b1;
        end
    endcase
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) begin
    if(fifo_wr_en) 
        fifo_din <= fx2_fdata;
end

FiFo #(
    .Depth(512),
    .Width(16)
) u_fifo (
    .fifo_clk     (fx2_ifclk),
    .rst_n        (reset_n),
    .write_busy   (1'b0),       // 外部无写阻塞
    .read_busy    (1'b0),       // 外部无读阻塞
    .fifo_flush   (1'b0),       // 禁用自动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)和安装对应的驱动,可以到官网上去下载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/983804.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql中in和exists的区别?

大家好&#xff0c;我是锋哥。今天分享关于【mysql中in和exists的区别?】面试题。希望对大家有帮助&#xff1b; mysql中in和exists的区别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 MySQL 中&#xff0c;IN 和 EXISTS 都用于进行子查询&#xff0c;但它…

Unity摄像机跟随物体

功能描述 实现摄像机跟随物体&#xff0c;并使物体始终保持在画面中心位置。 实现步骤 创建脚本&#xff1a;在Unity中创建一个新的C#脚本&#xff0c;命名为CameraFollow。 代码如下&#xff1a; using UnityEngine;public class CameraFollow : MonoBehaviour {public Tran…

springcloud sentinel教程

‌QPS&#xff08;Queries Per Second&#xff09;即每秒查询率 TPS&#xff0c;每秒处理的事务数目 PV&#xff08;page view&#xff09;即页面浏览量 UV 访问数&#xff08;Unique Visitor&#xff09;指独立访客访问数 一、初识Sentinel 什么是雪崩问题? 微服务之间相…

【Tools】Windows下Git 2.48安装教程详解

00. 目录 文章目录 00. 目录01. Git简介02. Git参考资料03. Git安装04. Git测试05. 附录 01. Git简介 Git(读音为/gɪt/。)是一个开源的分布式版本控制系统&#xff0c;可以有效、高速的处理从很小到非常大的项目版本管理。 [1] Git 是 Linus Torvalds 为了帮助管理 Linux 内核…

【Linux系统编程】初识系统编程

目录 一、什么是系统编程1. 系统编程的定义2. 系统编程的特点3. 系统编程的应用领域4. 系统编程的核心概念5. 系统编程的工具和技术 二、操作系统四大基本功能1. 进程管理&#xff08;Process Management&#xff09;2. 内存管理&#xff08;Memory Management&#xff09;3. 文…

神经网络|(十四)|霍普菲尔德神经网络-Hebbian训练

【1】引言 前序学习进程中&#xff0c;除了对基本的神经网络知识进行了学习&#xff0c;还掌握了SOM神经网络原理&#xff0c;文章链接包括且不限于&#xff1a; 神经网络|(十一)|神经元和神经网络-CSDN博客 神经网络|(十二)|常见激活函数-CSDN博客 神经网络|(十三)|SOM神经…

Hive八股

Hive八股 说一下GC模型遇到过gc调优吗yarn有哪些了解讲讲hqI转化为MR源码hbase读写流程hive数据倾斜page cache和buffer的区别和相同近来你关注了大数据生态哪些领域的发展&#xff0c;比如新的feature&#xff0c;新的领域等 Hive1Hive1hive简介2hive架构3hive与Hadoop的关系4…

Docker 部署 Graylog 日志管理系统

Docker 部署 Graylog 日志管理系统 前言一、准备工作二、Docker Compose 配置三、启动 Graylog 服务四、访问 Graylog Web 界面总结 前言 Graylog 是一个开源的日志管理平台&#xff0c;专为实时日志收集、分析和可视化设计。它支持强大的搜索功能&#xff0c;并且与 Elastics…

im即时聊天客服系统SaaS还是私有化部署:成本、安全与定制化的权衡策略

随着即时通讯技术的不断发展&#xff0c;IM即时聊天客服系统已经成为企业与客户沟通、解决问题、提升用户体验的重要工具。在选择IM即时聊天客服系统时&#xff0c;企业面临一个重要决策&#xff1a;选择SaaS&#xff08;软件即服务&#xff09;解决方案&#xff0c;还是进行私…

MySQL(单表)知识点

文章目录 1.数据库的概念2.下载并配置MySQL2.1初始化MySQL的数据2.2注册MYSQL服务2.3启动MYSQL服务2.4修改账户默认密码2.5登录MYSQL2.6卸载MYSQL 3.MYSQL数据模型3.1连接数据库 4.SQL简介4.1SQL的通用语法4.2SQL语句的分类4.3DDL语句4.3.1数据库4.3.2表(创建,查询,修改,删除)4…

同为科技智能PDU在数据中心场景的应用与解决方案

数据中心当前处于一个快速发展和技术变革的特殊时期&#xff0c;全新的人工智能应用正在重塑整个世界&#xff0c;为社会带来便捷的同时&#xff0c;也为数据中心的发展带来了新的机遇和挑战。智能算例的爆发式增长&#xff0c;对数据中心提出了大算力、高性能的新需求&#xf…

基于Asp.net的零食购物商城网站

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

Redis|Springboot集成Redis

文章目录 总体概述本地Java连接Redis常见问题集成Jedis集成lettuce集成RedisTemplate——推荐使用连接单机连接集群 总体概述 jedis-lettuce-RedisTemplate三者的联系 jedis第一代lettuce承上启下redistemplate着重使用 本地Java连接Redis常见问题 bind配置请注释掉保护模式…

【编译器】VSCODE编译C语言

【编译器】VSCODE编译C语言 文章目录 [TOC](文章目录) 前言一、下载配置二、代码1.main.c2.lanuch3.task 三、编译运行——方法一&#xff1a;编译器运行1.编译&#xff1a;终端-运行生成任务&#xff08;ctrlshiftB&#xff09;2.运行&#xff1a;运行-启动调试&#xff08;F5…

信息安全访问控制、抗攻击技术、安全体系和评估(高软42)

系列文章目录 信息安全访问控制、抗攻击技术、安全体系和评估 文章目录 系列文章目录前言一、信息安全技术1.访问控制2.抗攻击技术 二、欺骗技术1.ARP欺骗2.DNS欺骗3.IP欺骗 三、抗攻击技术1.端口扫描2.强化TCP/IP堆栈 四、保证体系和评估1.保证体系2.安全风险管理 五、真题在…

1 、六气概念-六气内涵

复泰中医体系里&#xff0c;木火土金水&#xff0c;属于传统中医理论的5分法。 有六气&#xff1a;厥阴风木、少阴君火、少阳相火、太阴湿土、阳明燥金、太阳寒水&#xff0c;属于6分法。 五行的体系用在补虚的体系里。 六气的体系用于泻实的体系里。 一、六气概念 天有六气&a…

svn删除所有隐藏.svn文件,文件夹脱离svn控制

新建一个文件&#xff0c;取名remove-svn-folders.reg&#xff0c;输入如下内容&#xff1a; Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN] "Delete SVN Folders" [HKEY_LOCAL_MACHINE\SOFTWARE\Class…

【Python 数据结构 10.二叉树】

目录 一、二叉树的基本概念 1.二叉树的定义 2.二叉树的特点 3.特殊的二叉树 Ⅰ、斜树 Ⅱ、满二叉树 Ⅲ、完全二叉树 Ⅳ、完全二叉树和满二叉树的区别 4.二叉树的性质 5.二叉树的顺序存储 Ⅰ、完全二叉树 Ⅱ、非完全二叉树 Ⅲ、稀疏二叉树 6.二叉树的链式存储 7.二叉树的遍历概念…

Windsuf 连接失败问题:[unavailable] unavailable: dial tcp...

问题描述 3月6日&#xff0c;在使用Windsuf 时&#xff0c;遇到以下网络连接错误&#xff1a; [unavailable] unavailable: dial tcp 35.223.238.178:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of…

Hadoop八股

Hadoop八股 HadoopMapReduce考点MR on Yarn 分布式工作原理shuffle&#xff1a;MapTask 和 ReduceTask的处理流程MR中的MapTask 和 ReduceTask 的数量决定MR和Spark两者Shuffle的区别简单讲一下map- reduce 原理**MapReduce 的核心概念****MapReduce 的工作流程****MapReduce 的…