无需优化即可加速Redd合成处理器的程序:更换时钟

到目前为止,我们一直在讨论如何使用一些密集的方法来提高系统速度的主题。 但是实际上,有很多方法。 现在,我们正在以50 MHz的时钟频率工作,这与使用大学程序集中的组件相关联(并且没有时钟SDRAM,这要求去往微电路的时钟脉冲相对于主时钟发生偏移)。 当我将此组件引入游戏时,我警告说该解决方案是暂时的。 然后,我在阅读器上转储了太多新信息,以至于任何多余的繁琐工作都会引起惊叹:“好吧,这些FPGA,这里的一切都太复杂了!” 现在我们已经可以轻松自然地构建处理器系统,所有可怕的事情都已经过去。 现在是该弄清楚如何制作自己的组件的时候了,它使您可以增加处理器及其连接的外围设备的时钟频率。



该系列中的先前文章:


  1. 为Redd中安装的FPGA开发最简单的“固件”,并以内存测试为例进行调试。
  2. 为Redd中安装的FPGA开发最简单的“固件”。 第2部分。程序代码。
  3. 开发自己的内核以嵌入基于FPGA的处理器系统。
  4. 以访问FPGA为例,为中央处理器Redd开发程序。
  5. 在Redd Complex的FPGA中CPU和处理器的连接示例中,首先使用流协议进行实验。
  6. Merry Quartusel或处理器如何成为这样的生活。
  7. Redd的代码优化方法。 第1部分:缓存效果。
  8. Redd的代码优化方法。 第2部分:非缓存内存和并行总线操作。

一些理论上的推理


让我们估计一下我们可以无痛地为所有铁时钟设定的频率。 综合大楼中使用的SDRAM芯片的极限频率为133 MHz。 有关处理器时钟速度的信息,请参见《 Nios II性能基准》 。 在那里,对于我们的FPGA Cyclone IV E,保证了160 MHz的Nios II / f核心频率。 我不是将所有汁液挤出系统的支持者,因此我们将讨论在100 MHz的频率下工作。

老实说,我仍然没有受到第32.7节中给出的计算时钟频率偏移的方法的启发 嵌入式外围设备IP用户指南》中的 时钟,PLL和时序注意事项 ,但似乎我并不是唯一的人。 至少,长时间的网上搜索并没有使我找到包含以相同方式计算出的任何结果的文章,但并未包含主文档中给出的频率(相同的50 MHz)。

有一篇有趣的文章,我将直接链接到它: www.emb4fun.de/fpga/nutos1/index.html 。 如果不是一个“而是”,则可以只说一句并说“让我们做作者”:本文的作者使用了PLL块(俄语-PLL,在家庭层面上是一个变频器),插入在VHDL中自己的代码。 正如关于乐趣Quartusel文章中已经提到的那样 ,我坚持这样的思想,即处理器系统应该位于项目层次结构的顶层。 不需要任何语言的插入,无论是VHDL还是Verilog。 最近,我的这种方法得到了又一次确认:我们有一个新员工,这位学生还不会说Verilog,但是可以完美地为Redd复合体编写代码,因为选择的方法可以做到这一点。

事实证明,我们只是以一切正常为基础,以负54度的偏移作为基础(本文描述了哪种度,我在上面给出了段落的链接)。

接下来,请关注另一篇有趣的文章asral.unimap.edu.my/wp-content/uploads/2019/07/2019_IJEECS_Chan_Implementation-Camera-System.pdf 。 一切对于作者而言都可以在负65度的范围内进行。

让我们尝试使我们的系统使用此范围内的值。 如果在RAM的日常测试中没有任何故障,那么我们将该值保留为战斗状态。 我们有权利,因为为Redd开发的“固件”不会交付给客户,而是用于内部需求和成批生产。 如果有的话,总是可以毫无问题地修复所有内容(当有必要在销售的成千上万个设备中更新“固件”时出现困难,并且仅在远程客户处出现)。

新的硬件配置


由于某种原因,在我看来,本文的处理器系统从头开始比在旧系统上重做要容易。 只是如何演示“扭曲,扭曲,想要混淆”的过程,不断参考过去的文章,我宁愿从头开始再次展示一切。 同时,我们修复材料。 因此,让我们开始吧。

在开始时,我们显示了一个完全空的系统,仅包含时钟和复位信号源。



通常我在那儿不做任何更改,但今天我会例外。 我不想被复位电路分散注意力,因为我们仍将在调试器下工作。 因此,我将重置条件从级别切换为负差,并且分支本身随后将被取消。



但是这里的时钟信号频率为50 MHz(此频率由焊接到板上的发生器的特性设置)。 在上面提到的第一篇文章中,使用了添加到主项目中的PLL模块。 我们在哪里得到它? 他在这里!



这是相同的块,但是在这里我们不必在Verilog或VHDL中嵌入任何代码。 一切都已经为我们插入! 的确,不同类型的FPGA的设置差异完全不同。 更精确地说,可调参数大致相同,但是它们在配置对话框中位于根本不同的位置。 由于Cyclone IV E FPGA用于Redd大楼,因此我们将考虑该选项的配置。

在第一个选项卡上,将输入频率替换为50 MHz(默认为100),然后转到下一个选项卡(单击“下一步”,对于Cyclone IV E,我们必须执行多次)。



取消选中其他输入和输出。 我们不需要它们:



我们跳过接下来的几个选项卡,直到设置输出C0。 在那里,我们切换单选按钮以设置频率并输入100 MHz的值:



使用C1,事情要复杂一些。 首先,选中表明也应使用的复选框。 其次,我们类似地将频率设置为100 MHz。 好,第三,我们设置频移。 问哪一个? 负58或负65? 当然,我尝试了两种选择。 两者都赢得了我。 但是,关于负58号的争论似乎不太令人信服,因此在这里我建议输入负65度的值(而自动化将告诉我所达到的实际值将为负63度)。



好吧,就是这样。 现在,您可以单步执行“ 下一步”按钮,也可以单击“ 完成” 。 我们连接输入inclk_interfaceinclk_interface_reset 。 输出c0将用作整个系统的时钟。 输出输出c1来sdram芯片提供时钟。 将来,您将需要记住将数据总线连接到pll_slave输入。 对于Cyclone V,这不是必需的。



其他硬件零件,仅用于固定材料


添加处理器核心。 今天,我们的SDRAM将接受测试。 因此,代码不应位于其中。 反过来,这意味着所有代码都将位于FPGA的内部RAM中。 也就是说,我们不需要指令缓存。 将其关闭,以节省FPGA存储器。 我们还连接了一条高度连接的指令和数据总线。 不需要处理器内核的其他有趣设置。



用手通常移动,添加两个内部RAM FPGA模块。 一个是容量为16 KB的双端口,一个是容量为4 KB的单端口。 我希望大家都记得如何命名它们以及如何建立联系。 上次我喜欢用鲜花突出显示轮胎时,也许为了便于阅读,我将在本文中进行介绍。



不要忘记在个人范围内为这些存储块分配特殊地址并锁定它们。 让CodeMemory分配给0x20000000, DataMemory分配给0x20004000。

好吧,让我们向系统中添加一个SDRAM模块,进行设置,以及用于显示消息的JTAG-UART模块和一个单比特GPIO ,我们将在其上测量实际频率以确保其增加。 作为参考,以下是一些非显而易见的设置:







总的来说,我们得到了这样一个系统(我着重介绍了数据总线,因为它扫描了所有外部设备):



我们将向量分配给处理器,自动分配地址,将中断号自动分配给生成系统。

我们将系统连接到项目,进行粗略的装配,分配支路编号,这一次我们不仅使CKE虚拟,而且使reset_n虚拟(这是完成的过程,我在先前的一篇文章中告诉我,在那儿寻找Virtual Pin)。 我们进行最终组装,将设备填充到FPGA中。 仅此而已。 我们已经完成设备的安装,请转到软件部分。

我们为环境设置了BSP


为了进行更改,让我们基于不是Hello World Small而是Memory Test Small的模板创建一个项目:



创建后,转到BSP编辑器。 和往常一样,我们要做的第一件事就是关闭SysID检查并允许使用C ++(尽管这一次我不会更改文件类型,但这已经成为我的习惯):



但是最重​​要的是我们必须在“ 链接脚本”选项卡上进行修复。 自动化公司认识到指令总线仅进入CodeMemory存储器,因此它在CodeMemory存储器中放置了一段代码(称为.text )。 但是照顾好我们,她将其他所有内容都放在了SDRAM中最大的数据区域中。 她怎么知道我们会无情地抹掉这个记忆?



我们将必须手动逐行,用DataMemory替换区域(选择列表将出现在其中,选择应在其中重新排列)。 我们应该得到这张照片:



程序实验


我们退出编辑器,生成BSP,尝试运行程序进行调试。 我们得到以下文本:



如果按Enter键,则不会成功。 我输入了一些内容(甚至是空格),然后按Enter。 然后他们问我:



一个小时一个小时不容易。 以及输入什么地址? 您可以打开Platform Designer并在其中查看值。 但是我通常会查看通用参考文件system.h(我的项目的完整路径是C:\ Work \ CachePlay5 \ software \ MemoryTest_bsp \ system.h)。 在那里,我们对以下两行感兴趣:



相同文字
#define ALT_MODULE_CLASS_new_sdram_controller_0 altera_avalon_new_sdram_controller #define NEW_SDRAM_CONTROLLER_0_BASE 0x0 #define NEW_SDRAM_CONTROLLER_0_CAS_LATENCY 3 #define NEW_SDRAM_CONTROLLER_0_CONTENTS_INFO #define NEW_SDRAM_CONTROLLER_0_INIT_NOP_DELAY 0.0 #define NEW_SDRAM_CONTROLLER_0_INIT_REFRESH_COMMANDS 2 #define NEW_SDRAM_CONTROLLER_0_IRQ -1 #define NEW_SDRAM_CONTROLLER_0_IRQ_INTERRUPT_CONTROLLER_ID -1 #define NEW_SDRAM_CONTROLLER_0_IS_INITIALIZED 1 #define NEW_SDRAM_CONTROLLER_0_NAME "/dev/new_sdram_controller_0" #define NEW_SDRAM_CONTROLLER_0_POWERUP_DELAY 100.0 #define NEW_SDRAM_CONTROLLER_0_REFRESH_PERIOD 15.625 #define NEW_SDRAM_CONTROLLER_0_REGISTER_DATA_IN 1 #define NEW_SDRAM_CONTROLLER_0_SDRAM_ADDR_WIDTH 0x18 #define NEW_SDRAM_CONTROLLER_0_SDRAM_BANK_WIDTH 2 #define NEW_SDRAM_CONTROLLER_0_SDRAM_COL_WIDTH 9 #define NEW_SDRAM_CONTROLLER_0_SDRAM_DATA_WIDTH 16 #define NEW_SDRAM_CONTROLLER_0_SDRAM_NUM_BANKS 4 #define NEW_SDRAM_CONTROLLER_0_SDRAM_NUM_CHIPSELECTS 1 #define NEW_SDRAM_CONTROLLER_0_SDRAM_ROW_WIDTH 13 #define NEW_SDRAM_CONTROLLER_0_SHARED_DATA 0 #define NEW_SDRAM_CONTROLLER_0_SIM_MODEL_BASE 0 #define NEW_SDRAM_CONTROLLER_0_SPAN 33554432 #define NEW_SDRAM_CONTROLLER_0_STARVATION_INDICATOR 0 


其中十进制33554432等于十六进制0x2000000。 因此,我的回答和工作结果应如下所示:



很好,但这对日常测试不利。 我重写了主要功能,如下所示:

 int main(void) { int step = 0; while (1) { if (step++%100 == 0) { alt_printf ("."); } if (MemTestDevice(NEW_SDRAM_CONTROLLER_0_BASE, NEW_SDRAM_CONTROLLER_0_SPAN)!=0) { printf ("*"); } } return (0); } 

点表示程序未“冻结”。 如果有错误,将显示一个星号。 为了提高可靠性,您可以在其输出上放置一个断点,然后就不要使其休眠。

没错,“左”点从某个地方爬升。 原来,它们显示在MemTestDevice()函数内部。 在那里,我消除了他们的结论。 测试成功。 生成的系统至少可以用于内部需求(即,此类开发是在Redd联合体下进行的)。

检查系统性能


但是我已经习惯了这样的事实:在使用设备时,您什么都不信任。 一切都应仔细检查。 确保与以前的文章相比,我们的工作频率提高了一倍。 添加著名的函数MagicFunction1()。

让我提醒您她的样子。
 void MagicFunction1() { IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); } 


我们将通过main()调用它,我们将在示波器上捕获脉冲,但是这次我们不仅要注意它们的美观,还要注意频率(让我提醒您,每个液滴,甚至向上,向下,都是一个命令,因此您可以测量液滴之间的距离)



只有50兆赫。 频率真的没有增加吗? 与撰写上一篇文章时开发的代码的频率进行比较,我们了解一切都井井有条。 仅仅是普通的pio单元每个端口的输出需要2个时钟周期(在一个自制的pio单元中,我有1个时钟,但是对于我们来说,确保系统性能翻倍就足够了)。



结论


我们学习了如何使用定制PLL单元,而不是使用固定频率振荡器。 的确,检测到的常数用于100 MHz的频率,但是每个人都可以使用众所周知的计算或通过反复试验将其调整为任何其他频率。 我们还增强了创建最佳处理器系统的技能,并确保较高频率的内存稳定运行,并且频率确实增加了。

通常,我们已经可以生产任何计算的东西,甚至可以与中央处理器进行交换,但是复杂的中央处理器将更有效地应对琐碎的计算。 FPGA被添加到Redd,以实现任何高速接口或捕获(运行或播放)信息流。 我们已经掌握了设计的基础知识,我们了解了如何提供或多或少的高性能。 现在是时候继续使用接口了,这将在下一篇文章中进行。 更准确地说,是一组文章,牢记“一个文章-一件事”的规则。

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


All Articles