以访问FPGA的示例为中央处理器Redd开发程序

上一篇文章中,我说过,现在是我们继续使用流协议的时候了。 但是开始准备有关它们的故事后,我意识到我自己正在游泳一个非常重要的话题。 如前所述,我与Linux的关系非常独特。 总的来说,我意识到我自己无法从头开始创建满足Redd编程所有原理的C ++应用程序。 您可以要求某人这样做,然后只使用现成的模板,但是该系列文章旨在教大家如何从零开始在Redd下进行开发。 因此,我问我的老板(一位出色的Linux专家),他向我解释了要单击的内容。 然后,我稍微重新考虑了他的话,现在我认为有必要以书面形式修正所有知识。 这样可以使像我这样的人摆脱痛苦的想法:“所以……他所做的事是可以理解的,但是我该如何重复呢?” 通常,在Linux下工作的任何人都可以对角地运行以下两个部分。 您不太可能在那里找到新的东西(您会找到更多的东西)。 对于其余部分,我提供了两种与Redd声明的工作原理相对应的开发方法的选择:开发和远程调试的劳动力成本低。



该周期的所有文章:

  1. 为Redd中安装的FPGA开发最简单的“固件”,并以内存测试为例进行调试
  2. 为Redd中安装的FPGA开发最简单的“固件”。 第2部分。程序代码
  3. 开发自己的内核以嵌入基于FPGA的处理器系统

使用Visual Studio工具


事实证明,您可以进行远程Linux的开发,而无需在本地计算机上进行开发,也无需安装任何非Microsoft软件工具。 此处显示了它如何放在Visual Studio上(我在2019版中安装了它,但似乎在2015年出现了) docs.microsoft.com/ru-ru/cpp/linux/download-install-and-setup- the-linux-development-workload?视图= vs-2019

这是工作理论

好吧,在那里您可以浏览选项卡,很多理论以及所有俄语内容。

太好了! 让我们尝试使用获得的知识来访问FT2232H芯片,Redd中央处理器通过该芯片连接到FPGA。 将来,此渠道将成为我们流媒体工作的基础。 打开Visual Studio,选择“创建项目”。 在过滤器中,选择“语言-C ++”,“平台-Linux”,“项目类型-控制台”。 我将显示该项目类型已经在哪里。 从经过过滤的内容中,我们选择“控制台应用程序”:



我们称它为SimpleConsole。 我们创建了这样的Spartan源代码:

#include <cstdio> int main() { printf("hello from SimpleConsole!\n"); return 0; } 

让我们尝试收集它。 我们被问到有关建立连接的一个非常有趣的问题。 这是Visual Studio开发的功能。 该环境不包含交叉编译器和任何库。 它只是在远程计算机上创建一个源目录,之后,对于每次编译,它都会将更新的文件复制到该目录,并在此开始编译。 这就是为什么不应该建立与远程机器的连接,而是为了正常组装项目而建立的原因。

我们为Redd用户填写参数,代表该用户将在项目上进行工作。



如果有的话-可以在此秘密位置更改参数(不要尝试更改项目属性,这不会带来任何好处):





实际上,您可以在单个行上放置一个断点,并验证项目是否已开始并且可以工作。 但这是微不足道的。 因此,我们继续进行更有趣的任务-使用FT2232。 问题出现了:从哪里获得必要的库? 对于Linux,所有内容都包含在驱动程序包中。 有驱动程序本身,库,与它们一起使用的应用程序示例,甚至有简短说明。 通常,我们进入搜索引擎:

 FTDI D2XX drivers 

他将显示可以在哪里下载。 是的,确切地说,一切对我都不利。 我的提供商阻止了FTDI网站(以及reprap,7zip甚至osronline),并提到了RosKomNadzor。 ILV在回应声明时发送退订信息,说我们没有阻止任何事情,而是自己与提供者打交道。 他们只有在这些退订的地方才不让我转向警察。 他们非常退订。 尝试抱怨ILV不动作的尝试最终被转发到ILV,下一个取消订阅的人来自ILV。 通常,您的提供商可能也会阻止访问。 不要惊慌,只要寻找其他下载方式,就可以花一些时间。 然后他们感到惊讶的是,导弹的发展被推迟了几十年...我从去年11月开始与ILV通信,现在是6月,除了退订-没有行动...但是我离题了。

在包本身的自述文件中可以找到如何使用驱动程序。 您也可以找到文档《 AN_220 FTDI Linux驱动程序安装指南》 。 最后,您可以在YouTube上找到FTDI Chips的《 Linux d2xx驱动程序安装指南》的视频。 链接也位于驱动程序下载页面上。 总体而言,FTDI并未限制通知用户的选项。 实际上,如果您收到现成的Redd软件包,则驱动程序本身已经安装并配置好了。 我们将对头文件和示例感兴趣。

让我们插入一个示例片段\ release \ examples \ EEPROM \ read主要功能的最开始,即打开设备并获取其类型的位置。 这足以确保一切正常。 我讨厌标签,但是由于我们只是快速拖放了10分钟的代码,因此我将拖动原始示例中的标签以节省时间。 原来是这样的:

 #include <cstdio> #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include "ftd2xx.h" int main(int argc, char* argv[]) { printf("hello from ConsoleApplication1!\n"); FT_STATUS ftStatus; FT_HANDLE ftHandle0; int iport; static FT_PROGRAM_DATA Data; static FT_DEVICE ftDevice; DWORD libraryVersion = 0; int retCode = 0; ftStatus = FT_GetLibraryVersion(&libraryVersion); if (ftStatus == FT_OK) { printf("Library version = 0x%x\n", (unsigned int)libraryVersion); } else { printf("Error reading library version.\n"); return 1; } if (argc > 1) { sscanf(argv[1], "%d", &iport); } else { iport = 0; } printf("Opening port %d\n", iport); ftStatus = FT_Open(iport, &ftHandle0); if (ftStatus != FT_OK) { /* This can fail if the ftdi_sio driver is loaded use lsmod to check this and rmmod ftdi_sio to remove also rmmod usbserial */ printf("FT_Open(%d) failed\n", iport); return 1; } printf("FT_Open succeeded. Handle is %p\n", ftHandle0); ftStatus = FT_GetDeviceInfo(ftHandle0, &ftDevice, NULL, NULL, NULL, NULL); if (ftStatus != FT_OK) { printf("FT_GetDeviceType FAILED!\n"); retCode = 1; goto exit; } printf("FT_GetDeviceInfo succeeded. Device is type %d.\n", (int)ftDevice); exit: return 0; } 

尝试收集-不会去。 头文件不足。

 1>main.cpp 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : ftd2xx.h:      1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : #include "ftd2xx.h" 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : ^ 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : compilation terminated. 

但是我把它们放在旁边! 一切都很简单。 在本地,它们在附近,但是装配是远程完成的。 为了让Studio将它们传输到远程计算机,您需要将它们添加到项目中。 此外,从main.cpp文件中,我仅添加ftd2xx.h ,但仍在传输时拉WinTypes.h 。 您必须同时添加两者。





现在链接器发誓。

 1>  1>D:\Work\SimpleConsole\SimpleConsole\obj\x64\Debug\main.o : error : In function `main': 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(18): error : undefined reference to `FT_GetLibraryVersion' 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(37): error : undefined reference to `FT_Open' 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(55): error : undefined reference to `FT_GetDeviceInfo' 1>collect2 : error : ld returned 1 exit status 

显然没有足够的库。 检查示例的makefile文件后 ,我意识到我需要在链接器设置中添加几个参数:





现在该项目正在顺利进行。 我们在最后一行放置一个断点,尝试运行。 我们看到以下文本:

 hello from ConsoleApplication1! Library version = 0x10408 Opening port 0 FT_Open succeeded. Handle is 0x555555768540 FT_GetDeviceInfo succeeded. Device is type 10. 

总体而言-还不错。 东西打开了,甚至找到了一些设备。 什么是10型? 在FTDI的头文件中,我们发现:

 enum { FT_DEVICE_BM, FT_DEVICE_AM, FT_DEVICE_100AX, FT_DEVICE_UNKNOWN, FT_DEVICE_2232C, FT_DEVICE_232R, FT_DEVICE_2232H, FT_DEVICE_4232H, FT_DEVICE_232H, FT_DEVICE_X_SERIES, FT_DEVICE_4222H_0, FT_DEVICE_4222H_1_2, FT_DEVICE_4222H_3, FT_DEVICE_4222_PROG, }; 

我们从上到下数着手指-在我们的前面是FT4222H。 是的,Redd有很多FTDI设备。 现在,我只简单地说一遍 ,我们需要对传递给FT_Open()函数的设备的编号进行排序,该数字作为参数传递给main()函数。 可以在项目的调试属性中设置它。

放置有典型问题的板很有用。 通常,它在任何地方都说“配置Redd”,没有详细信息。 事实是,这些复合体将按自定义分布,因此所有读者都不需要配置规则。 出现问题时,管理员通常会处理配置。 因此,事实证明您可以描述配置规则,但这会占用很多空间。 仅当有人真的需要时才花精力在上面。 因此,在这里我将仅局限于指出问题,以及如何解决问题,我将描述反馈中是否有应用程序。

病征原因
没有单个设备打开。默认情况下,Linux需要管理员权限才能打开USB设备,但登录进行调试的用户没有这些权限。 您应该配置端口,以便普通用户可以使用它们。
FT4222H只能打开几个设备。默认情况下,在FT2232H控制器上安装了标准的虚拟COM端口驱动程序。 您应该告诉操作系统不要这样做。
找不到库。Redd设备操作系统上未安装D2XX驱动程序。

太好了 我们准备使用专用的Microsoft工具来取得成就。 一般来说,这可能就足够了,但以防万一,我会告诉您老板教我的另一种方法。

使用在虚拟机中运行的Linux


前一种方法的主要缺点是远程组装。 您可以想到一千一百个原因,说明为什么应在本地进行汇编,并且仅将完成的二进制文件传输到远程计算机。 这些都是各种偏执的限制(尽管文件是使用安全协议传输的,并且您可以使用安全策略将它们的存储与其他用户分开),这只是Redd驱动器不会从各种库中溢出的考虑,这也是不愿意注册每个头文件的注意事项,如果有成千上万个……那么,还有更多很多。 通常,局部组装技术可能有用,因此请考虑使用。

首先,我们将来自Oracle的VirtualBox程序放在本地计算机上。 如果有任何许可问题(我个人免费使用,但我不知道法人实体到底发生了什么),请选择单独的物理计算机,然后将Linux放在此处。 哪一个 对我来说,这更容易,我对它们的理解差不多。 我的意思是,我几乎一听不懂。 因此,老板告诉我您需要使用Debian,我安装了Debian。 您可以采用相同的方式(使用“为什么不呢?”原则)。 至少在将来,我将依靠与他合作。

使用Linux时,必须遵守两个大大简化生活的规则:

  1. 如果响应命令,他们告诉我们权限不足,则值得重复此操作,在开头添加sudo魔术。
  2. 如果响应命令告诉我们没有这样的命令,则值得尝试通过使用魔术拼写apt-get install <missing something>来安装它。

这样啊 我们刚刚安装了操作系统。 通过安装g ++编译器以及gdb调试器,立即在此添加C ++支持。 怎么了 因此,使用规则2:

apt-get install g ++
apt-get安装gdb

没有给吗 太好了! 重复使用规则1:

须藤apt-get install g ++
须藤apt-get install gdb

现在我们进入互联网,在搜索引擎中键入:

Eclipse IDE

我们找到eclipse.org的链接,其中第一个选项是Java的,我们找到并下载了C / C ++的一个选项:



比如说在家下载并解压。

实际上,不需要安装。 只需转到所有内容都已解压缩的目录,然后运行eclipse文件:



我们处于开发环境中。 好吧,如果您已经使用过微控制器甚至FPGA的处理器内核,那么您可能已经知道Eclipse是什么。 因此,陌生的事情几乎完成了。 我们开始或多或少地熟悉周围的事物。 我们创建C ++项目。 我必须马上说有两种方法。 一个将导致成功,第二个将导致死胡同。 因此,请仔细遵循我所走的道路:







我们创建了一个进展顺利的项目。 要配置其调试,请转到GDB属性:



创建类型为C / C ++远程应用程序的配置,在“连接”组中,单击“新建”:



选择一个像SSH这样的连接:



我们通过将授权类型的单选按钮切换为密码授权来填写连接属性:



实际上,系统已准备好进行调试。 确保确实显示了欢迎文本后,我们尝试在此处转移上一个示例(在Visual Studio中)的代码。 要注册其他库,请选择项目属性:



而且,由于程序集是在本地进行的,因此这些库也必须位于本地usr / local / lib目录中。 我提醒您,这些库是随驱动程序一起下载的,如何安装它们是自述文件以及AN220和YouTube视频,有关详细信息,请参阅Visual Studio上的部分。

经过所有准备工作,我们得到了熟悉的台词。 就是说,与上一节中所考虑的代码完全相同的代码也同样被执行。

仅此而已。 现在,根据情况,我们可以通过Visual Studio和虚拟机运行代码。 如您所见,仅就设置而言,Visual Studio更简单,因此,我选择了ceteris paribus。 但是最好同时拥有这两种技术,因为通过Studio进行操作存在的缺点是调试和组装都在远程。

通过FT2232H进行FPGA写速度测量


好吧 让我们将获得的技能固定在一个或多或少的实际项目中。 当然,不再可能开始非常严肃的事情;每个人都已经累了。 但是我们或多或少地得到了实际的结果。 例如,我们以最大速度测量可以通过FT2232H芯片将数据传输到FPGA的速度。 那里的协议不是最简单的,因此我们不会在两侧进行传输,而是将自己限制为从我们传输到安装FPGA另一端的通道。 在FT245型同步FIFO模式下使用的文档AN_130 FT2232H将为我们提供帮助,因为在复杂系统中,控制器正是在该模式下(同步FIFO)精确打开的。 本文档还包含对结论的描述(以在该模式下使用结论的形式),时序图甚至代码示例,从中我们将得到启发。

这样啊 我们想使用控制器记录在FIFO中。 我们会怎样? 我试过了,我知道。 它将占用1 KB的数据,此后控制器将挂起。 他将拒绝接受其他数据。 问题是千字节是其内部缓冲区的大小。 可能的话,数据将从USB中获取并存储在该空间中。 但是,要使他们进入同步通道,他必须告知准备好接收它们的信息。 我们来看一下相应的临时任务。



这样啊 当控制器的FIFO中有任何数据时,它将丢弃RXF信号。 响应于此,我们必须首先丢弃OE信号,并至少在一个时钟周期内将其保持为零(这比从描述到图的描述要多,而从图本身出发)。 我们将在总线上获得数据,我们必须以低RD信号电平确认它们的接收。 如此-对于整个框架。 当控制器升高RXF线时,我们必须移除OE和RD。 我们今天不会使用这些数据。 要测量速度,只需模拟从FT2232H在FPGA上接收数据。 那好 对于这样的简单操作,不需要处理器系统。 制作简并的自动机就足够了,其自动开发所需的时间比仅仅为处理器和程序做准备而花费的时间要少得多。 因此,我们创建一个仅包含一个具有以下内容的SystemVerilog文件的项目:

 module JustRead( input logic clk, input logic rxf_n, output logic oe_n, output logic rd_n ); enum {IDLE,TRANSFER} state = IDLE; always @ (posedge clk) begin oe_n <= 1; rd_n <= 1; case (state) IDLE: begin if (rxf_n == 0) begin oe_n <= 0; state <= TRANSFER; end end TRANSFER: begin if (rxf_n == 0) begin oe_n <= 0; rd_n <= 0; end else begin state <= IDLE; end end endcase end endmodule 

机器有两种状态。 在这种情况下,OE信号的持续时间取决于以下事实:它在时钟脉冲之后立即被塞住并保持到下一个脉冲。 可以使用以下模型对此进行验证:

 module JustReadTB( output logic clk, output logic rxf_n, input logic oe_n, input logic rd_n ); JustRead dut ( .clk, .rxf_n, .oe_n, .rd_n ); always begin clk = 1; #16; clk = 0; #16; end initial begin rxf_n = 1; #120; rxf_n = 0; #120; rxf_n = 1; end endmodule 

我所用的时间是第一个可用的时间,与时钟信号相关的切换顺序很重要。 我们得到以下时序图:



在第一近似中,这对应于文档所需要的。 但是我们还是不会处理实际数据。

即使在表格中进行编辑,该项目中FPGA支路的分配也不会造成困难(从* .QSF文件中转移分配将花费更长的时间,我一直强调在Redd下开发一日系统时,节省时间是当务之急)。



我们收集,填充它,在关闭综合系统的电源之前,您可以使用该程序,缓冲区溢出后它将不再挂起。

在程序中,我做了两个功能。 第一次搜索并打开设备。 我从上次测试中取得了一些东西,我从AN130那里借了一些东西:

 FT_HANDLE OpenFT2232H() { FT_HANDLE ftHandle0; static FT_DEVICE ftDevice; //      int nDevice = 0; while (true) { //     if (FT_Open(nDevice, &ftHandle0) != FT_OK) { //  ,      return 0; } //     ? if (FT_GetDeviceInfo(ftHandle0, &ftDevice, NULL, NULL, NULL, NULL) == FT_OK) { // ,    if (ftDevice == FT_DEVICE_2232H) { // ,     AN130 FT_SetBitMode(ftHandle0, 0xff, 0x00); usleep(1000000); //Sync FIFO mode FT_SetBitMode(ftHandle0, 0xff, 0x40); FT_SetLatencyTimer(ftHandle0, 2); FT_SetUSBParameters(ftHandle0, maxBlockSize, maxBlockSize); return ftHandle0; } } //    FT_Close(ftHandle0); //    nDevice += 1; } } 

作为Windows的迷,我不得不编写速度测量功能,不断检查Internet,因为我通常使用WIN32 API中的经典高分辨率计时器。 也许您可以提高编写效率,但这是一天的计划。

 const int maxBlockSize = 0x100000; uint8_t buf[maxBlockSize]; … //   BlockSize ,  1 double TestSpeed(FT_HANDLE ftHandle0,int totalSize, int blockSize) { if (blockSize > maxBlockSize) { return -1; } DWORD dwWrittenTotal = 0; timespec before; clock_gettime(CLOCK_REALTIME, &before); for (int i = 0; i < totalSize; i += blockSize) { DWORD dwWritten; FT_Write(ftHandle0, buf, blockSize, &dwWritten); //     dwWrittenTotal += dwWritten; } timespec after; clock_gettime(CLOCK_REALTIME, &after); if (dwWrittenTotal < (DWORD)totalSize) { return -2; } //     uint64_t nsBefore = before.tv_nsec; uint64_t nsAfter = after.tv_nsec; //      nsAfter += (after.tv_sec - before.tv_sec) * 1000000000; //   nsAfter -= nsBefore; //      double res = ((double)nsAfter)/((double)1000000000); //   -    return ((double)dwWrittenTotal) / res; } 

嗯,执行基本功能的代码如下所示:

 int main(int argc, char* argv[]) { FT_HANDLE ftHandle0 = OpenFT2232H(); if (ftHandle0 == 0) { printf("Cannot open device\n"); return -1; } const int totalSize = 0x100000; static const int blockSizes[] = { 0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000, 0x4000,0x8000,0x10000,0x20000,0x40000,0x80000,0 }; for (int i = 0; blockSizes[i] != 0; i++) { double speed = TestSpeed(ftHandle0, totalSize, blockSizes[i]); printf("%d,%d\n", blockSizes[i], (int)(speed/1000.)); int stop = 0; } // ,    FT_Close(ftHandle0); return 0; } 

我知道在USB中,速度很大程度上取决于要发送的块的大小,因此我检查各种选项的速度。 几乎不需要线性枚举。 我只是列出了典型尺寸的清单。 我以千字节每秒的速度输出数据。 字节使眼睛不舒服,兆字节具有低分辨率和小块大小。 每秒千字节是一个合理的折衷。 我们得到以下结果:
 16,59 32,110 64,237 128,490 256,932 512,1974 1024,3760 2048,5594 4096,10729 8192,16109 16384,20170 32768,24248 65536,26664 131072,28583 262144,29370 524288,29832 

我们将它们保存在* .csv文件中,将其加载到Excel中,构建速度与块大小的关系图。



限制是每秒30 MB。 到理论最大值为52 MB / s为止。 也许您可以以某种方式加速,但以实际工作的形式留给读者。 最主要的是,我们已经掌握了使用通道的所有步骤,并准备将FPGA与中央处理器连接到单个系统中。

编译本文时,发现了文档AN_165 ,该文档表示同步FIFO模式下的最大速度为35 MB / s。 也就是说,增长的空间-达到给定的大小。 但是它仍然在那里。

结论


我们熟悉了两种策略,用于开发和调试在Redd中心的中央处理器上执行的程序代码(使用Microsoft Visual Studio工具以及在具有Linux OS的虚拟机上)。 我们还获得了与通道配合使用的实践技能,该通道可通过综合系统的中央处理器与FPGA进行通信。

显然,现在没有任何东西可以阻止我们将有意义的数据从CPU传输到FPGA,反之亦然(尽管在上一篇文章中我写了这些话)。

可在此处下载包含最简单的FPGA“固件”和测量写入USB速度的程序的示例

Source: https://habr.com/ru/post/zh-CN456008/


All Articles