为Redd中安装的FPGA开发最简单的“固件”。 第2部分。程序代码

因此,在上一篇文章中,我们开发了最简单的处理器系统,借助该系统,我们计划测试连接到Redd Complex的FPGA的RAM芯片。 今天,我们将为此硬件环境制作一个C ++程序,并且我们还将弄清楚如何注入该程序,最重要的是调试它。



该程序是在Eclipse环境中开发的。 要启动它,请选择菜单项工具-> Nios II Eclipse的软件构建工具



我们需要为其创建一个项目和一个BSP。 为此,请在Project Explorer区域中单击鼠标右键,然后从Template菜单项中选择New-> Nios II Application and BSP



在处理器系统的生成过程中,将创建用于创建项目的基础模板。 因此,我们找到了包含它的文件。



我们还将为项目命名(我有SDRAMtest )并选择项目类型。 我选择了Hello World Small 。 我想选择Memory Test ,我们正在做一个内存测试,但是现在我们正在考虑一种创建应用程序的通用方法。 因此,我们选择常规选项。



我们创建了两个项目。 第一个是我们的项目,第二个是BSP(大致来说,板级支持包,用于设备使用的库)。



我通常要做的第一件事是编辑BSP设置。 为此,我选择创建的第二个项目,按鼠标右键,然后选择菜单项Nios II-> BSP Editor



在编辑器中,所有内容都分为几组:



但是为了不沿着它们运行,我将选择树的根, Settings元素,并线性地考虑所有内容。 我将列出您应注意的事项。 初始设置屏幕:



禁用了对退出的支持和对退出时的剥离的支持,这从代码中丢弃了不必要的大块代码,但最初将其禁用。 由于我选择了最小选项Hello,World ,因此关闭了这些寒鸦。 选择其他类型的代码时,最好也删除这些代码。 我无法抗拒并打开C ++支持。 还必须删除SysID检查,否则将无法执行任何操作。 事实是,在制作硬件系统时,我没有添加相应的块。 其余设置是不言自明的,尚未更改。 实际上,我也没有更改其他设置。 稍后,我们将回到这里,但是现在我们按下Generate按钮。 我们将根据所做的设置来创建BSP的新版本。 最后,单击“退出”。

现在,乐趣开始了。 如何组装项目。 按通常的Ctrl + B键 -我们得到一个错误。 解码错误编号:



控制台中也没有合理的选择:



选择SDRAMtest项目的Build Project ,我们得到一个合理的解释:



实际上,非工作模板是Quartus的公司标识。 这是为什么我没有选择Memory Test的另一个原因。 有越来越多的跑步。 这很清楚是怎么回事。

alt_putstr.c文件的此功能失败

/* * Uses the ALT_DRIVER_WRITE() macro to call directly to driver if available. * Otherwise, uses newlib provided fputs() routine. */ int alt_putstr(const char* str) { #ifdef ALT_SEMIHOSTING return write(STDOUT_FILENO,str,strlen(str)); #else #ifdef ALT_USE_DIRECT_DRIVERS ALT_DRIVER_WRITE_EXTERNS(ALT_STDOUT_DEV); return ALT_DRIVER_WRITE(ALT_STDOUT_DEV, str, strlen(str), 0); #else return fputs(str, stdout); #endif #endif } 

稍后我们将以某种方式对其进行修复(为此,我们需要使硬件系统复杂化)。 今天,我们只是不需要它。 将她的身体替换为return 0 。 该项目开始组装。

太好了 我们已经有一个就绪的elf文件 (即使它不执行任何有用的功能),并且我们拥有可以在其中上载hex文件的设备 (还记得我们如何告诉Quartus编译器通过设置Onchip RAM从其中加载RAM数据吗?)。 我们如何将elf转换为十六进制 ? 很简单 我们进入工作项目,按鼠标右键,选择菜单项Make Targets-> Build



在出现的窗口中,首先尝试选择第一个选项( mem_init_install ):



什么都不会发生。 但是从提供给我们的错误消息中,我们学习了如何为Quartus完成项目:

 ../SDRAMtest_bsp//mem_init.mk:230: *** Deprecated Makefile Target: 'mem_init_install'. Use target 'mem_init_generate' and then add mem_init/meminit.qip to your Quartus II Project. Stop. 

我们出于mem_init_generate的目的选择 build ,之后( 紧接在!!!之后),我们将指定的qip文件添加到硬件项目中(我们已经学习了如何在向该项目添加处理器系统时添加文件)。



好了,您也可以用手感觉到十六进制文件本身。 这是:



太好了 我们拥有一切开始使用真实功能填充程序的一切。 我们转到文件hello_world_small.c 。 老实说,我对使用纯C有点生气。 因此,我将其重命名为cpp。 为了确保一切正常,我将为现有文本添加一个魔术:



相同的文字:
 extern "C" { #include "sys/alt_stdio.h" } int main() { alt_putstr("Hello from Nios II!\n"); /* Event loop never exits. */ while (1); return 0; } 


替换文件类型后执行Clean Project操作很重要,否则编译器将基于一些缓存信息显示错误消息,指出* .c文件未找到。

我们将简化内存测试。 我没有教导读者正确测试RAM芯片的任务。 我们只是从表面上确保地址和数据总线不发粘并且没有间断。 也就是说,我们在每个单元格中写入所有零和该单元格的地址。 我们的任务是编写有效的代码,其他所有内容(其他填充和延迟常量,以验证是否正在重新生成数据)都是使文本复杂但不改变其本质的实现细节。

让我们从一个简单自然的问题开始:“ SDRAM在地址空间中的什么位置?” 我记得我们曾调用过自动地址分配功能,但是甚至都没有查看实际分配了哪些地址。 实际上,即使现在我们也不会去那里。 所有必需的信息都在文件中:
... \ SDRAMtest_bsp \ system.h

结果,我们得到以下代码:
 extern "C" { #include "sys/alt_stdio.h" #include <stdint.h> #include "../SDRAMtest_bsp/system.h" #include <altera_avalon_pio_regs.h> } int main() { bool bRes = true; volatile static uint32_t* const pSDRAM = (uint32_t*)NEW_SDRAM_CONTROLLER_0_BASE; static const int sizeInDwords = NEW_SDRAM_CONTROLLER_0_SPAN / sizeof (uint32_t); //  for (int i=0;i<sizeInDwords;i++) { pSDRAM [i] = 0; } //   for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != 0) { bRes = false; } } //   for (int i=0;i<sizeInDwords;i++) { pSDRAM [i] = i; } //    for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != i) { bRes = false; } } if (bRes) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x01); } else { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x02); } /* Event loop never exits. */ while (1); return 0; } 


我们收集十六进制文件 (我通过此对话框提醒您):



然后,我们在Quartus中编译设备,并进入编程器以将生成的“固件”和生成的初始化程序代码加载到芯片中。



倒入“固件”,并得到一条消息,指出系统仅在连接到JTAG(自由开发环境中的SDRAM内核许可证的功能)后才能正常使用。 实际上,对于Redd而言,这并不重要。 这个JTAG一直存在。



可以在连接器,触点C21(成功)或B21(错误)上看到RAM测试结果。 我们用示波器连接它们。



两个输出均为零。 这里出了点问题。 仍然需要了解确切的内容。 实际上,一个不起作用的程序非常棒,因为现在我们将开始学习JTAG调试。 我们针对该项目,选择Debug As-> Nios II Hardware



第一次系统找不到硬件。 看起来像这样(注意突出显示的选项卡标题中的红叉):



切换到“ 目标连接”选项卡,然后选择“ 忽略不匹配的系统ID”复选框。 在随后的实验中,有时我还必须设置“ 忽略不匹配的系统时间戳” 。 为了以防万一,我将在图片中用红色突出显示它,而不用红色突出显示它,以强调现在不需要安装它,但是如果未激活Debug按钮,则可能是时候安装它了。



应用应用 ),然后单击刷新连接 (此按钮处于隐藏状态,您需要对其进行滚动):



调试器出现在列表中,您可以单击Debug ...。



我们在main()函数处停止了。 如果需要,可以在算法的末尾放置一个断点,并检查程序是否到达断点:



运行程序:



断点不起作用。 让我们停止该程序,看看它在哪里运行。



一切都不好。 该程序显然崩溃了。 依次设置断点,然后停止(使用红色的“停止”按钮)并重新启动程序(使用带有错误的按钮),我们找到了问题区域。

对于第一个元素执行此行时,所有操作都会死亡:


相同的文字:
  for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != 0) { bRes = false; } } 


从C ++的角度来看,这里的一切都很干净。 但是,如果打开反汇编的代码,则可以看到那里有太多统一的命令。 似乎该代码已擦除自己。 如果链接器将其放入SDRAM(此代码覆盖其内容),则他可以执行此操作。



我们停止调试,关闭调试透视图。



我们转到本文开头的BSP编辑器,但转到“ 链接器脚本”选项卡。 如果我从一开始就纠正了此选项卡,那么我将无法展示进入JTAG调试的技术(并且与简单的调试输出相比,要强调它的全部功能,因为JTAG调试期间卡住的代码的事实引起了我的注意)。 就是这样 一个好的链接器会将所有内容都放在内存中,而内存的大小会更大。



我们将所有内容重定向到onchip_memory ...现在我们有了SDRAM-它只是一块内存,我们尚未保证其性能。 您不能将其交给任何自动编译器操作。



我们重建bsp ,重建项目。 我是否需要重新创建存储器映像并使FPGA过载? 然后,对于半自治工作,将是必需的,但是在调试过程中-否。 当新的调试会话开始时,该程序的新版本将立即加载到RAM中。 但是这样一来,在下次启动FPGA时就不必启动调试器,在调试结束时制作一个新的HEX文件 ,并且希望将FPGA的“固件”与其一起组装。

对于新代码,已达到断点,测试结果为true



示波器很有趣:黄色的射线单位散发出来。



测试通过。 让我们花一点时间,同时检查系统性能。 让我们像这样进行决赛:

  //    GPIO if (bRes) { while (true) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x01); IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x00); } } else { while (true) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x02); IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x00); } } 

结果,我们在示波器上有这样的曲折(所有东西都通过长回路连接,因此第二行上出现了电容式拾音器,无需注意):



结果是在时钟频率为50 MHz时大约为8.3 MHz,而没有对处理器内核进行任何微调和优化程序代码。 也就是说,访问端口的频率略高于16 MHz,这相当于系统频率的三分之一。 并不是说它是完全不同的,但是对于Cyclone V SoC而言,它的时钟频率为925 MHz时要优于4 MHz ...不,NIOS II处理器本身比第五核Cyclone ARM慢几倍,但正如我所说,该处理器,我们的系统中有x64,这里我们需要更多的内核,它提供了铁的逻辑。 通过与端口一起精确地提供此逻辑。 如果使用端口的速度较慢,则其他所有端口将定期闲置,以等待总线完成工作。 所揭示的特征是处理器对端口的访问限制,而不是整个硬件。 但是,如何在整体上实现工作,我们将在下一篇文章中考虑。

结论


本文介绍了如何针对最复杂的Redd开发的处理器环境,使用C ++创建和配置项目。 显示了访问设备的方法以及JTAG调试技术。 在此处下载撰写本文时获得的硬件/软件套件。

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


All Articles