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

news2025/3/5 20:04:37

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)和安装对应的驱动,可以到官网上去下载。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Office-Word】如何自动生成中英文目录

1.目录介绍 Word这个自动生成目录非常强大&#xff0c;涉及的功能很琐碎&#xff0c;想要完美的生成目录不仅仅是只会目录这么简单&#xff0c;前后涉及到的大纲级别、目标样式和域代码等操作是比较头疼的。 下面就一步一步开始介绍 2.多级标题级别编号设置 目录想要设置好…

CentOS 7 安装Nginx-1.26.3

无论安装啥工具、首先认准了就是官网。Nginx Nginx官网下载安装包 Windows下载&#xff1a; http://nginx.org/download/nginx-1.26.3.zipLinxu下载 wget http://nginx.org/download/nginx-1.26.3.tar.gzLinux安装Nginx-1.26.3 安装之前先安装Nginx依赖包、自行选择 yum -y i…

家政预约小程序用例图分析

在和客户进行需求沟通的时候&#xff0c;除了使用常规的问答的形式&#xff0c;我还使用图形化工具更深入的沟通。比如借助UML的用例图来开展系统分析&#xff0c;并且按照角色详细拆解了家政预约小程序的各个用例。在分析阶段思考的越多&#xff0c;沟通的越多&#xff0c;在系…

112页精品PPT | DeepSeek行业应用实践报告

这份文件是一份关于DeepSeek行业应用实践的报告&#xff0c;以PPT形式呈现&#xff0c;共112页&#xff0c;详细介绍了DeepSeek及其核心产品DeepSeek-R1的技术特点、市场表现、应用路径以及在多领域的实践案例。报告展示了DeepSeek在市场上的快速崛起&#xff0c;包括其日活用户…

计算机毕业设计SpringBoot+Vue.js航空机票预定系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

C语言学习笔记-初阶(27)操作符详解1:位操作

1. 操作符的分类 上述的操作符&#xff0c;我们已经学过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作符&#xff0c;今天继续介绍⼀部分&#xff0c;操作符中有一些操作符和二进制有关系&#xff0c;我们先铺垫一下二进制的和进制转换的知识。 2. 二进制、…

网络安全需要学多久才能入门?

网络安全是一个复杂且不断发展的领域&#xff0c;想要入行该领域&#xff0c;我们需要付出足够多的时间和精力好好学习相关知识&#xff0c;才可以获得一份不错的工作&#xff0c;那么网络安全需要学多久才能入门?我们通过这篇文章来了解一下。 学习网络安全的入门时间因个人的…

20250304学习记录

第一部分&#xff0c;先来了解一下各种论文期刊吧&#xff0c;毕竟也是这把岁数了&#xff0c;还什么都不懂呢 国际期刊&#xff1a; EI收集的主要有两种&#xff0c; JA&#xff1a;EI源刊 CA&#xff1a;EI会议 CPCI也叫 ISTP 常说的SCI分区是指&#xff0c;JCR的一区、…

【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架

【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架 1. 引言 本教程旨在帮助嵌入式开发小白从零开始&#xff0c;学习如何在STM32F407微控制器上实现一个基于串口的数据接收程序。该程序能够通过判断数据头来接收一串数据&#xff0c;并将其存储到缓冲区中…

文件上传复现

文件上传漏洞的概念 在现代互联网的web应用程序中&#xff0c;上传文件是一种常见的功能&#xff0c;因为它有助于提高业务效率&#xff0c;比如社交 网站中&#xff0c;允许用户上传图片、视频、头像和许多其他类型的文件。然而向用户提供的功能越多&#xff0c; web应 用受到…

Redis——缓存穿透、击穿、雪崩

缓存穿透 什么是缓存穿透 缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中&#xff0c;导致请求直接到了数据库上&#xff0c;根本没有经过缓存这一层。举个例子&#xff1a;某个黑客故意制造我们缓存中不存在的 key 发起大量请求&#xff0c;导致大量请求落到数据库…

HMC7043和HMC7044芯片配置使用

一,HMC7043芯片 MC7043独特的特性是对14个通道分别进行独立灵活的相位管理。所有14个通道均支持频率和相位调整。这些输出还可针对50 Ω或100 Ω内部和外部端接选项进行编程。HMC7043器件具有RF SYNC功能,支持确定性同步多个HMC7043器件,即确保所有时钟输出从同一时钟沿开始…

VSCode知名主题带毒 安装量900万次

目前微软已经从 Visual Studio Marketplace 中删除非常流行的主题扩展 Material Theme Free 和 Material Theme Icons&#xff0c;微软称这些主题扩展包含恶意代码。 统计显示这些扩展程序的安装总次数近 900 万次&#xff0c;在微软实施删除后现在已安装这些扩展的开发者也会…

mybatis映射文件相关的知识点总结

mybatis映射文件相关的知识点总结 mybatis官网地址 英文版&#xff1a;https://mybatis.org/mybatis-3/index.html 中文版&#xff1a;https://mybatis.p2hp.com/ 搭建环境 /* SQLyog Ultimate v10.00 Beta1 MySQL - 8.0.30 : Database - mybatis-label *****************…

【UCB CS 61B SP24】Lecture 21: Data Structures 5: Priority Queues and Heaps 学习笔记

本文介绍了优先队列与堆&#xff0c;分析了最小堆的插入与删除过程&#xff0c;并用 Java 实现了一个通用类型的最小堆。 1. 优先队列 1.1 介绍 优先队列是一种抽象数据类型&#xff0c;其元素按照优先级顺序被处理。不同于普通队列的先进先出&#xff08;FIFO&#xff09;&…

爬虫系列之发送请求与响应《一》

一、请求组成 1.1 请求方式&#xff1a;GET和POST请求 GET:从服务器获取&#xff0c;请求参数直接附在URL之后&#xff0c;便于查看和分享&#xff0c;常用于获取数据和查询操作 POST&#xff1a;用于向服务器提交数据&#xff0c;其参数不会显示在URL中&#xff0c;而是包含在…

小米手机如何录制屏幕?手机、电脑屏幕录制方法分享

大家最近有没有遇到想记录手机屏幕操作的情况&#xff1f; 比如精彩的游戏瞬间、有趣的视频教程&#xff0c;或者需要录制屏幕来制作演示材料。小米手机在这方面可是个好帮手&#xff0c;今天就来给你好好唠唠&#xff0c;小米手机如何录制屏幕&#xff0c;以及后续如何处理这…

3D Web轻量化引擎HOOPS Communicator的核心优势解析:高性能可视化与灵活部署!

在当今数字化时代&#xff0c;工业领域的工程应用不断向基于Web的方向发展&#xff0c;而HOOPS Web平台作为一款专为构建此类工程应用程序打造的软件开发套件集&#xff0c;正发挥着日益重要的作用&#xff0c;成为构建强大工程应用的基石。 一、HOOPS Web平台概述 HOOPS Web…

从 JVM 源码(HotSpot)看 synchronized 原理

大家好&#xff0c;我是此林。 不知道大家有没有这样一种感觉&#xff0c;网上对于一些 Java 框架和类的原理实现众说纷纭&#xff0c;看了总是不明白、不透彻。常常会想&#xff1a;真的是这样吗&#xff1f; 今天我们就从 HotSpot 源码级别去看 synchronized 的实现原理。全…

深入探索Python机器学习算法:模型调优

深入探索Python机器学习算法&#xff1a;模型调优 文章目录 深入探索Python机器学习算法&#xff1a;模型调优模型调优1. 超参数搜索方法1.1 网格搜索&#xff08;Grid Search&#xff09;1.2 随机搜索&#xff08;Random Search&#xff09;1.3 贝叶斯优化&#xff08;Bayesia…