SOC(片上系统)封装服务端RISC-V软处理器

2020-08-05 23:38:22

基于服务器RISC-V软CPU,此增强添加了XIP(就地执行),允许更大代码和RAM空间,释放宝贵的DPRAM区域,并在用C语言开发时加快编辑-编译-运行周期。

用Verilog编写的服务器CPU是一个出色的位串行软CPU,由Olof Kindgren设计。它是世界上最小的RISC-V CPU。Olof的GitHub页面和附带的视频很好地描述了位串行体系结构如何允许设计一个微小的CPU内核。

我想要一个小内核,但是我需要比基本服务器实现所允许的更多的Flash和RAM空间。受Claire Wolf的picosoc RISC-V的启发,我考虑在服务器中添加基于picosoc的XIP。

信息:设备利用率:信息:IceStorm_LC:1462/5280 27%信息:IceStorm_RAM:1/30 3%信息:SB_IO:21/96 21%信息:SB_GB:8/8 100%信息:IceStorm_PLL:1/1 100%信息:SB_WARMBOOT:0/1%Info:IceStorm_DSP:0/8 0%信息:IceStorm_HFOSC。:0/2 0%信息:Io_I3C:0/2 0%信息:SB_LEDA_IP:0/1%Info:SB_RGBA_DRV:0/1%Info:IceStorm_SPRAM:4/4 100%。

奥洛夫生产的CPU要小得多,使用了大约500个LUT块。仅我的64位计时器和中断处理程序就增加了大约700个。部分是我的代码,部分是通过在CPU中启用IRQ逻辑。但奥洛夫的服务是一个高度受限的SoC。Claire Wolf的picosoc使用了近4000个LC块。我看到了同样的效果,当我启用IRQ时,它增加了500个LUT。它使用的picosoc正在耗尽FPGA空间,导致我。

CPU有两条独立的总线:iBus(指令总线,用于获取指令)和dBus(用于存储器读/写)。在OLOF SERVER SOC中,这两条总线连接到同一个DPRAM。使用在合成阶段添加的.hex文件将程序存储器预加载到DPRAM中,并编译成位流。

我把两辆公共汽车分开了。DBUS连接到SPRAM而不是DPRAM,在使用ICE40UP5K的晶格iCE40部件上,4个块中有128K字节的SPRAM,但在30个块中只有120K比特的DPRAM。

破冰器FPGA板上使用的闪存部件是Winond W25Q128JV,这允许使用四路SPI,但我目前只使用标准SPI。我希望以后再添加QSPI,因为这将显著提高吞吐量。

下图显示了SoC是如何组成的。Ibus显示为红色,dbus显示为蓝色。

指令提取被传递到IBUS_FETCH单元的总线仲裁。这将为所请求的地址发出SPI读取指令,并读回32位数据。此数据将呈现给IBUS,并断言ACK,以表明数据已准备好。

上面的轨迹是用脉冲视图捕获的。它显示了散布着一系列IBUS指令获取的单个IBUS_READ获取。程序计数器正在改变;1000A8、IBUS_READ 1000C0、指令获取1000AC、1000B0...顶部跟踪显示来自IBUS_READ器件的请求(CYC信号)。中间跟踪是SPI芯片选择、活动LO,底部是SPI时钟。

对ibus进行解码和显示的能力也是一个有用的调试辅助工具。我发现,在调试中断处理程序时,查看从ibus提取的内容会很有帮助。

如果您需要向同一ibus添加更多设备,则可以级联bus_arb设备。如果您有多个CPU共享同一闪存设备,则可以这样做,尽管性能会很差。

上面的屏幕截图显示了用于帮助设计bus_arb设备的gtkwave。在开发过程中,我尽可能多地使用TDD(测试驱动开发)。我从软件开发中了解到,TDD是一种更好的工作方式。我现在尝试为每个模块构建一个测试台,并在开发阶段对其进行验证。您可以使用以下命令运行所有测试台:

需要IBUS_READ设备以允许从数据总线读取闪存数据。在启动过程中,初始化的数据段从闪存复制到RAM中。这是在启动时使用以下汇编程序完成的:

#将初始化数据从闪存复制到RAM##ibus_read设备映射到0x70000000#将地址写入闪存桥Devli a0,0x70000000 la a1,_sidata#ROMsw a1中.data段的开始,0(A0)la a2,_sdata#Ramla a3中.data段的开始,_edata#RAMbge a2,a3,end_init_dataloop_init_data中.data段的结束。地址在h/w.lw a1,0(A0)#save in RAMsw a1,0(A2)addi a2,a2,4blt a2,a3,loop_init_dataend_init_data中递增:

Ibus_read器件在每次读取时将总线地址递增4,因此您只需设置起始地址,在本例中为闪存中.data段的开始,即_sidata。该地址将被复制到RAM中的_sdata,直到复制完所有.data段。

地址位置在链接器文件iceBreaker_sections.lds中定义,我在很大程度上基于Claire Wolf的picosoc链接器文件。

我从克莱尔·沃尔夫的Makefile中更改了GCC的设置,首先也是最重要的是机器架构。Picosoc是rv32ic,但服务器是rv32i。Picosoc运行压缩(小于32位)的指令。服务器不会。它要求每条处理器指令都是32位长。

我删除了-nostdlib和添加了-nostartfiles。这允许stdlib函数(例如,数学函数(如除法和乘法),以及内存管理函数malloc()和free()。Sbrk()代码可以按如下方式添加:

/**_sbrk()由malloc()用于分配堆内存。*/链接器config.extern";C";uint32_t_sheap,_eheap;extern";C";void*_sbrk(intptr_t增量){static void*heap=(void*)&;_sheap;void*base=heap;void*next=&;((char*)base)[增量];if(Next&>t;=(void*)。}heap=next;返回base;}。

它向您展示了如何引用链接器定义的变量。我在使用g++而不是gcc编译时添加了extern";C";提供sbrk()允许使用stdlib版本的malloc()和free()。

我将优化级别设置为-O1。这在代码效率方面带来了一些令人印象深刻的改进,但是我确实遇到了一些问题,优化器删除了对内存映射设备的访问。如果您得到了奇怪的结果,那么查看汇编器输出总是值得的。

_/_||_|_\//|_\(_)_\\//\//\__\|_)\\/||_)|/__|/__|。//_)||_|_<;\v/|_<;|\__\(_|_\V/|_/|_|\_\_/|\_\_/|\__/\__|\_/世界上最小的RISC-V CPU。使用位串行架构。https://github.com/olofk/servRAM 131072字节程序:地址0x100000大小0x0006acData:地址0x000000大小0x00062cHeap:地址0x000630大小0x01f000堆栈:地址0x01f000大小0x001000

我在2020年开始这个项目。我想学习Verilog,我以前看过它,但从来没有机会学习或正确使用它。我买了一块破冰板,下载了优秀的开源开发工具。我开始为一个我考虑了很长时间的项目开发DSP音频处理器(我希望很快就能了解更多)。在这个过程中,我学习了Verilog,我非常喜欢它,对RISC有了更好的理解--我很喜欢Verilog,并下载了优秀的开源开发工具。我开始研究DSP音频处理器(我希望很快就会有更多关于这个项目的内容)。在这个过程中,我学习了Verilog,我非常喜欢它,对RISC有了更好的理解。

我要感谢奥洛夫·金德格伦和克莱尔·沃尔夫令人惊叹的工作。开放源码CPU内核的出现为任何进行FPGA开发的人打开了一片广阔的天地。阅读他们的代码并使用他们的工作既有教育意义,又有启发意义。