今天又收到高大哥的投稿,是将 FPGA 变成 USB 数据采集板的详细设计,又是一篇“保姆级”的教程~

作者github主页:

> https://github.com/gyn

如何将 FPGA 变成 USB 数据采集板

笔者在不修改硬件的情况下在 icestick 板子上实现了从 FPGA 到 USB Host 最高 2.57MBps 的数据传输。

背景信息

icestick 板载 USB 接口芯片 FT2232H 的端口 A 和端口 B 均与 FPGA ice40hx1k 相连。其中,端口 A 处于 MPSSE 模式,用于读写 SPI Flash 以更新 FPGA 的 bitfile,而 B 口默认处于 ASYNC Serial 模式,当作串口使用。

端口 B 都只有一部分引脚连到 FPGA,无法支持 245 FIFO 或者 245 FIFO SYNC 模式以实现高速数据传输。而在 ASYNC Serial 模式时,其支持最大 12Mbaud 即最高 1.14MBps 的数据传输。

笔者发现 FT2232H 还支持一种称为 Fast Opto-Isolated Serial Interface 的模式。在 FTDI 文档和软件中,这一模式也被称为 Fast Serial Interface 模式或者 OPTO Isolate 模式。因为引入了时钟引脚,这一模式可以在使用较少引脚的情况实现比 ASYNC Serial 模式更高带宽的数据传输。

读者可以通过此文了解 OPTO Isolate 模式如何使用并按照说明可以实现最高 2.57MBps 的数据传输。

准备工作

你需要具备以下条件:

  • 一块 icestick 开发板

  • ice40 FPGA 开发工具,开源工具或者 iceCube2

  • FT Prog 程序及 D2XX 驱动

  • FT2232H Datasheet

同时,笔者使用以下软件实现 Windows 端测试程序:

  • zadig 软件

  • libusb 库及开发环境

准备妥当后,我们先尝试修改 FT2232H 芯片端口 B 的模式。

修改模式

首先,我们需要使用 FTDI 公司的 FT Prog 程序来修改端口 B 的模式。而 FT Prog 程序则需要驱动程序 D2XX 驱动。

打开设备管理器,插入 icestick 后,如果 D2XX 设备驱动程序配置正常,在通用总线控制器下会出现 USB Serial Converter AUSB Serial Converter B 设备,如下图所示。

打开 FT Prog 程序,主菜单上点击 DEVICES 然后点击 Scan and Parse 子菜单。如果一切正常,读者应该可以看到以下界面:

选中设备,找到 Hardware SpecificPort BHardware 项,选择 OPTO Isolate 项,然后在主菜单上点击 DEVICES 然后点击 Program 子菜单。

点击 Program 按钮就可以将修改后的配置选项写入 FT2232H 的 EEROM 中。

烧写完毕后可以重新拔插 icestick,打开 FT Prog 再次查看设备的 Port B 的属性是否已经修改成 OPTO Isolate。

修改成功后,我们看一下 OPTO Isolate 模式下接口的规格。

OPTO Isolate 模式

在 FT2232H Datasheet 章节 3.1.4.6 FT2232H Pins used as a Fast Serial Interface 的 Table 3.10 中我们可以找到:

由于本文侧重于 FPGA 到 FT2232H 的数据传输,我们可以暂时忽略用于 FT2232H 到 FPGA 方向数据传输的 FSDO 引脚。

在章节 4.8.2 Incoming Fast Serial Data 中 Figure 4.15 Fast Opto-Isolated Serial Interface Input Data 我们可以看到:

有此时序图我们可以看出:

  • 空闲状态下 FSCTS 和 FSDI 应为高电平

  • FSCLK 作为数据传输的参考时钟,FT2232H 应在 FSCLK 的上升沿采数据

  • FSCTS 为高电平时,FPGA 可以进行数据传输

  • 一次传输的数据由一个由 0 表示的起始位、LSB 优先的 8 位数据和一个 DEST 位组成

  • 在 DEST 位发送后,FSCTS 仍然会保持一段时间的低电平

然后我们在章节 4.8  Fast Opto-Isolated Serial Interface Mode Description 的 Figure 4.13 Fast Opto-Isolated Serial Interface Signal Waveforms 看到具体时序:

以及在同一章节的 Table 4.6 Fast Opto-Isolated Serial Interface Signal Timings 看到具体时序:

根据这些,我们可以得到:

  • FSCLK 的最小周期是 20ns,即最大频率是 50MHz

  • FSDI 的建立时间最小为 10ns,保持时间最小为 5 ns

这样我们可以根据这些参数进行 FPGA 接口设计了。

接口电路设计

在实现接口电路之前,读者需要检查具体用到的 FPGA 的性能。如果 IO 反转性能不能达到 50MHz,那么 IO 就成为瓶颈;如果 IO 性能满足要求,但是接口电路频率不能超过 100MHz 或者在支持支持双沿输出和输入的情况下超过 50MHz,接口电路就会成为瓶颈。

通过查看 ice40hx1k 的文档,我们可以大致确定 ice40hx1k 的普通 IO 反转可以做到 50MHz,而且,在此 FPGA 上实现一个运行在近 100MHz 的电路并不十分困难。

如果我们使用 100MHz 作为内部时钟,那么数据传输时序图会变成:

需要说明的是

  • 我们使用 100MHz 使用来产生一个 50MHz 的 FSCLK 对应 IO 的反转

  • 我们在 FSCLK 的下降沿变更数据,这样保证 FSDI 的建立时间和保持时间都是 10ns

  • 我们会产生一个持续反转的 FSCLK,而不会在不传输数据的时候暂停 FSCLK

通过观察时序图,计数状态机来实现此电路较为简单。同时对于慢速接口电路,valid-ready 信号也是不能缺少的。加入这些信息后,数据传输时序图会变成:

有了这样的时序图,我们可以开始实现具体的电路了。

接口电路实现

首先,输入信号 FSCTS 需要经过跨时钟域处理。我们可以将 i_fscts 连接到 2 个级联的 DFF 上来进行处理,从而得到同步的信号 w_fscts。

//// CDC signals//localparam  DFF_W = 2;wire                    w_fscts;reg     [DFF_W - 1 : 0] r_fscts_sync;always @ (posedge i_clk)r_fscts_sync <= {r_fscts_sync[DFF_W - 2 : 0], i_fscts};assign w_fscts = r_fscts_sync[DFF_W - 1];

其次,Fast Opto-Isolated Serial Interface 电路本质是将并行的数据进行串行数据,我们还需要一个移位寄存器和对应的控制信号:

//// r_data//// r_data keeps the data to be transmitted//localparam  TX_DATA_W = DATA_W + 3;reg     [TX_DATA_W - 1 : 0] r_data;reg     [TX_DATA_W - 1 : 0] w_data_next;wire                        w_data_tick;wire                        w_data_load;wire                        w_status_tick;always @ (posedge i_clk, posedge i_arst)if (i_arst)r_data <= {TX_DATA_W{1'b1}};elser_data <= w_data_next;always @(*) beginw_data_next = r_data;if (w_data_load)w_data_next = {i_channel, i_data, 2'b01};elseif (w_data_tick)w_data_next = {1'b1, r_data[TX_DATA_W - 1 : 1]};end// w_data_load indicates if r_data could be filled safelyassign w_data_load = o_ready & i_valid;assign w_data_tick = w_status_tick | w_status_start;

因为数据的发送需要两个必备条件,我们需要一个 bit 来确保默认情况 FSDI 的数据为高电平。这样,加上起始位和 DEST 位后,我们共需要 11 位移位寄存器。

同时,数据发送时 LSB 优先的,我们的 FSDI 自然而然的会连接到移位寄存器的最低位。

复位时,移位寄存器全部填充为 1;复位解除后,如果需要加载数据时,即 w_data_load 为有效时,那么将表示 DEST 位的 i_channel、8 位数据 i_data 、表示起始位的 0 和默认电平 1 加载到寄存器中;如果遇到 w_data_tick 有效时,那么就将移位寄存器进行右移,最高位填充 1。

然后,因为数据发送的两个条件并不是同时发生,我们需要一个寄存器来表示 r_data 是否已经填充。

//// r_data_status//// r_data_status decides the valid-ready signals and// if r_status FSM starts//reg     r_data_filled;reg     r_data_filled_next;wire    w_data_done;wire    w_status_done;always @ (posedge i_clk, posedge i_arst)if (i_arst)r_data_filled <= 1'b0;elser_data_filled <= r_data_filled_next;always @(*) beginr_data_filled_next = r_data_filled;// if the tx data has been filledif (r_data_filled) begin// and the data transmission has been doneif (w_data_done)r_data_filled_next = 1'b0;end// or if the tx data is emptyelse begin// and upstream module's data is readyif (i_valid)r_data_filled_next = 1'b1;endendassign w_data_done = w_status_done & i_tick;

默认的情况下,r_data_filled 为 0 表示 r_data 是没有填充的;如果没有填充过且输入数据有效,那么就将 r_data_filled 设置为 1,表示已经填充;如果已经填充,且表示数据已经完全被发送出去,即 w_data_done 为 1 时将 r_data_filled 设置为 0。

接着,我们需要实现一个计数状态机来控制 r_data 的移位和 r_data_filled 的更新。

//// r_status//// r_status controls TX FSM//localparam  STATUS_MAX  = 11;localparam  STATUS_W    = $clog2(STATUS_MAX);reg     [STATUS_W - 1 : 0]  r_status;reg     [STATUS_W - 1 : 0]  w_status_next;wire                        w_status_start;wire                        w_status_idle;assign w_status_idle = (r_status == {STATUS_W{1'b0}});assign w_status_done = (r_status == (STATUS_MAX[STATUS_W - 1 : 0] - 1'b1));assign w_status_tick = i_tick & ~w_status_idle;always @ (posedge i_clk, posedge i_arst)if (i_arst)r_status <= {STATUS_W{1'b0}};elser_status <= w_status_next;always @(*) beginw_status_next = r_status;if (w_status_start)w_status_next = {{STATUS_W-1{1'b0}}, 1'b1};elseif (w_status_tick)if (w_status_done)w_status_next = {STATUS_W{1'b0}};elsew_status_next = r_status + 1'b1;end// FSM begins counting when FSM is idleassign w_status_start = w_status_idle &// r_data is filledr_data_filled &// i_fstcs is ready to receive dataw_fscts &// to sync with o_fsclk signali_tick;

计数状态机的实现并不复杂。默认情况下,计数器为 0 表示接口处于空闲状态;在处于空闲状态下,如果 r_data 已经被填充,FSCTS 信号为高,在保证与时钟同步的情况下,计数器变成 1,然后每个 w_status_tick 有效时加一,知道计到最大状态 10 之后又变为 0,等待 w_status_start 再次变为 1。

最后,有了上述电路,那么输出信号处理就较为简单。

//// output signals//// r_data is ready when r_data_filled is 0assign o_ready = ~r_data_filled;assign o_fsdi = r_data[0];

对于 o_ready 信号,只要数据发送完毕就可以进行填充;而 FSDI 信号直接取 r_data 的最低位。

为了测试这一接口模块,我们还需要设计另外一个控制模块来产生数据并驱动此接口模块。笔者实现了一个控制模块,支持周期发送和最大带宽发送两种模式选择。限于篇幅,此处不再赘述其实现细节。

控制模块的代码、接口模块的代码、cocotb 仿真代码及 icestick 完整的工程可以在 icestick-oifs 库中 gateware 目录 oifs-tx 子目录下找到。

需要注意的是,由于 icestick 板载晶振频率 12MHz,使用 PLL 倍频出最大符合规范的时钟频率为 99MHz,所以,实际的 FSCLK 的反转频率是 49.5MHz,而非设计时的 50MHz。

FPGA 部分实现完毕后,我们还需要实现 USB Host 侧的软件来接收数据。

USB Host 侧软件

为了实现跨平台的代码,笔者使用 libusb 库进行 USB Host 侧的代码。

在 Windows 上,读者需要使用 zadig 软件将端口 B 的 D2XX 驱动替换成 libusb 的驱动。如下图所示:

同时,笔者在 msys2 环境下安装 mingw-w64-x86_64-libftdi 和必要的开发工具,用来构建 USB Host 侧的程序。

笔者编写了两个程序,一个程序用来读取数据并计算性能,名为 oifs-rxperf,另外一个程序用来将数据打印到控制台,名为 oifs-rxdump。

USB Host 侧代码可以在 icestick-oifs 库中 software 目录下找到。这些代码可以不用修改或者稍加修改运行在 Linux 平台上。

测试结果

笔者在 Windows 上测试的结果如下:

我们可以看到最高的传输速率可以达到 2.57MBps。笔者在 Ubuntu 20.04 和  Ubuntu 22.04 中分别进行了测试,测试结果与 Windows 平台上得到的结果一致。

在接口逻辑仿真环境中,我们假设了 FSCTS 信号总高,但是实际情况并非如此:

通过逻辑分析仪抓到信号的波形来看,FSCTS 在 DEST 位传输完毕后保持 140ns 的低电平,那么一次传输需要大概 364ns 到 384ns,则极限带宽则约为 2.61MBps。

总结

按照本文的说明及相应的代码,读者应该可以在 icestick 上实现从 FPGA 到 USB Host最高 2.57MBps 的数据传输,从而将 icestick 变成一个 USB 数据采集板。

工程代码

https://github.com/gyn/icestick-oifs

如何将 FPGA 变成 USB 数据采集板相关推荐

  1. 利用FPGA实现外设通信接口之:利用FPGA实现USB 2.0通信接口

    10.3  利用FPGA实现USB 2.0通信接口 10.3.1  USB 2.0接口的实现方式 利用FPGA来实现USB 2.0接口的方式一般有两种,一是借助外围的USB接口芯片,二是FPGA内部实 ...

  2. 基于FPGA的USB高速数据采集系统(免做上位机)

    本篇分享基于FPGA的USB高速数据采集系统,上位机软件采用赛普拉斯官方提供的上位机软件,实现前端AD的采集,经过FPGA处理之后通过USB传输到上位机,将数据保存下来,然后通过MATLAB可以将AD ...

  3. 从USB数据采集板看技术造诣

    随着USB串行总线的流行,USB设备越来越多,价格也非常便宜.U盘我们用得多了,USB鼠标和键盘,我们也经常在用.所以,很多人认为,做一个USB的数据采集卡,一定没什么技术含量.其实不然,对于我们做应 ...

  4. 基于FPGA的USB接口控制器设计(VHDL)(中)

    今天给大侠带来基于 FPGA 的 USB 接口控制器设计(VHDL),由于篇幅较长,分三篇.今天带来第二篇,中篇,USB通信原理.USB 系统开发以及设计实例.话不多说,上货. 之前有关于 Veril ...

  5. 优秀的IC/FPGA开源项目(一)-FPGA+CMOS+USB/SD架构开源项目

    优秀的IC/FPGA开源项目(一)-FPGA+CMOS+USB/SD架构开源项目 <优秀的IC/FPGA开源项目>是新开的系列,旨在介绍单一项目,会比<优秀的 Verilog/FPG ...

  6. 基于FPGA的USB接口控制器设计(VHDL)(上)

    今天给大侠带来基于 FPGA 的 USB 接口控制器设计(VHDL),由于篇幅较长,分三篇.今天带来第一篇,上篇,USB 接口简介 以及 USB 体系结构.话不多说,上货. 之前有关于 Verilio ...

  7. 通过FPGA实现USB接口传输图片,通过MATLAB对图片进行显示

    1.仿真预览 2.理论分析 标准的硬件系统图像显示流程如下: 但是在仿真阶段,为了验证USB的功能,我们需要对系统的工作模式做下调整,使得符合仿真使用.具体如下所示: 注意,由于摄像机,在仿真阶段,没 ...

  8. 深度学习求深度图_关于图的深度学习成功挑战和下一步

    深度学习求深度图 重点 (Top highlight) 图神经网络的下一步是什么? (What is next in store for graph neural networks?) TL;DR T ...

  9. 【LabVIEW FPGA图形化】IP集成节点:USB通信

    目录 一.前情提要 二.FPGA蔡氏定律 三.USB外围电路 四.LabVIEW FPGA IP集成节点网表文件的编写 五.FPGA图形化程序编写 总结 一.前情提要 上一节内容介绍了图形化FPGA测 ...

最新文章

  1. Android Service与Activity的交互
  2. 解决RHEL sendmail服务启动慢
  3. 硬盘引导安装windows7系统的方法
  4. 大数据量下的sort
  5. Hadoop文件的基本操作
  6. python数据抓取课程_Python爬虫入门教程 21-100 网易云课堂课程数据抓取
  7. 给出TREE_INSERT过程的非递归版本(算法导论第三版12.3-1)
  8. ctl命令 usb_USB入门系列之六 —— USB设备的枚举过程
  9. 20180713 考试记录
  10. 十面阿里Java程序员终拿下阿里P6offer
  11. CListCtrl的使用
  12. qc是什么职位_质量管理部门该干什么?又该怎么干?
  13. 知易游戏开发教程cocos2d-x移植版
  14. JS中对象赋值只传值不传对象(地址)的方法,改变新值不影响旧值的两种方法...
  15. gta5服务器端文件夹,GTA5路径在steam哪个文件夹里面
  16. 新唐(Nuvoton)8051单片机开发指南
  17. 100道积分公式证明(41-50)
  18. 谷歌插件数据爬取:基本信息采集
  19. AndroidStudio开启debug调试模式
  20. 数据库完整性之参照完整性

热门文章

  1. CG100-13景程-9S12HZ256VAL
  2. 2022-2028全球与中国半导体用气体检测器市场现状及未来发展趋势
  3. loadrunner监控局域网内其他服务器系统资源设置,loadrunner监控局域网内其他机器的系统资源消耗(windows操作系统)...
  4. 面霸吐血整理:我是如何面试了10家公司,拿到offer的?
  5. 钉钉OA审批事件回调遇坑梳理-如何注册多个事件回调
  6. M1门禁系统如何升级为CPU卡门禁系统?
  7. Spring-01 概述 IOC理论思想
  8. 华为云会议,云上办公更轻松高效
  9. linux 电池管理软件,Linux电源管理笔记本模式工具1.65来延长电池续航能力
  10. Kotlin语法的学习笔记