本帖最后由 geren2014 于 2019-2-21 15:03 编辑
介绍在2018年8月19日凌晨2点到8点之间夜晚开发,darkriscv是开源RISC-V指令集的一个实验性的实现。如今,经过数周令人兴奋的不眠夜工作,darkriscv已经可以稳定运行,可以用riscv-elf-gcc编译“hello world”并正常运行! 设计概念基于作者的其他早期RISC处理器,由简化的两级流水线组成,其中在第一个时钟中从指令存储器取指令,然后在第二个时钟中解码/执行指令。管道在没有互锁的情况下重叠,在某种程度上,darkriscv可以在大多数时间达到每个指令一个时钟的性能(例外是在分支之后,其中一个时钟在流水线flush中丢失)。另外,代码非常紧凑,有大约200行Verilog代码。
尽管与其他RISC-V实现相比,代码规模不大,但darkriscv具有许多令人印象深刻的功能: - 实现了大多数RISC-V RV32I指令集
- 工作频率高达75MHz,大部分时间每条指令维持1个时钟
- 灵活的哈佛架构(易于集成缓存控制器)
- 在真正的spartan-6 lx9中工作得很好
- 使用RISC-V的gcc 9.0.0可正常工作(无需补丁!)
- 仅使用大约1000个LUT(spartan-6 lx9,仅核心)
- 和最好的功能:BSD许可证
- 随意提出建议和良好的黑客攻击(良好的可扩展性)!
实现说明
因为作者使用的开发版是超低功耗的Xilinx Spartan-6系列FPGA,所以该项目目前使用的开发工具为Xilinx ISE 14.4 for Linux。但是,DarkRISCV没有对Xilinx元件管脚进行明确的引用,并且所有逻辑都是直接从Verilog推断出来的,这意味着该项目可以轻松移植到其他FPGA系列芯片板,并且可以轻松移植到其他环境中(作者将在未来添加对其他FPGA和工具的支持) )。
darkriscv的开发目的是为680x0 / coldfire系列的某些项目创建迁移路径。我的第一种方法是寻找可行的软件方案,经过大量的测试,我发现了picorv32和RISC-V周围的所有生态系统。尽管直接替换680x0系列是一个非常好的选择,但picorv32可能不足以取代coldfire processors系列芯片板。
picorv32的主要问题是大多数指令每条指令需要3或4个时钟,在某些方面类似于68020,但运行频率为150MHz。无论如何,每条指令有3个时钟,峰值性能仅为50MIPS左右。因为我对实验RISC内核有一些很好的经验,我就开始编写darkriscv代码来检查复杂程度。令我惊讶的是,在第一个晚上,我映射了RV32I规范的几乎所有指令,而darkriscv开始在75MHz正确执行第一条指令,每条指令一个时钟,类似于快速和漂亮的68040!哇! :)
RV32I规范本身非常令人印象深刻且易于实现(参见[1],第16页)。当然,还有一些缺点,例如有趣的小端总线(与680x0系列中的网络导向大端总线相反),但经过一些实证测试后很容易就能完成工作。
初始设计非常简单,处理器具有2级流水线--指令预取和指令执行。在预取指令流水线,程序计数器总是提前一个时钟工作。在执行方面,我们发现了所有解码,寄存器组读取,算术和逻辑运算,寄存器组写入和IO操作。只要两个阶段重叠,结果就是连续的指令流以每个指令1个时钟的速率和大约75MIPS。
这意味着当与运行在150MHz并且每个指令有3个时钟的picorv32相比时,75MHz的darkriscv指令执行速度快50%。
不幸的是,我的加载指令有一个小问题:1级执行需要更快的外部存储器!这对于早期的RISC处理器来说不是问题,因为早期的RISC处理器使用基于LUT的小型和更快的存储器,但在darkriscv的情况下,该提议是一种更灵活的设计,在某种程度上可以使用基于BlockRAM的缓存和缓慢的外部存储器。块RAM的问题在于需要两个时钟来回读存储器:一个时钟用于寄存地址,另一个时钟用于寄存数据。外部存储器需要大量时钟。
我的第一个解决方案是使用两个不同的时钟沿:一个用于darkriscv,另一个用于内存/总线接口。
在这种情况下,具有2级流水线的处理器就像2 * 0.5 + 1级流水线一样: - 1/2阶段用于指令预取
- 1/2阶段用于静态指令解码
- 指令执行的1个阶段
在加载/存储指令的特殊情况下,最后一个阶段分为两个不同的阶段,作为一个4 * 0.5阶段的管道: - 1/2阶段用于指令预取
- 1/2阶段用于静态指令解码
- 执行和地址生成的1/2阶段
- 数据读/写的1/2阶段
无论如何,从处理器的角度来看,只有两个阶段。
在正常情况下,建议不要这样做,因为性能会降低2倍,但在darkriscv情况下,性能总是受到有关指令执行的组合逻辑的限制。
作为参考,此处为ISE中提供的其他Xilinx器件提供了一些额外的性能结果(仅合成): - Spartan-3e:47MHz
- Spartan-6:75MHz
- Artix-7:133MHz
- Virtex-6:137MHz
- Kintex-7:167MHz
虽然可以使用直接连接至至少两个blockRAM存储器(一个用于指令,另一个用于数据)的darkriscv工作在相反的时钟边沿,并确定性地保持每个指令1个时钟的非常好的性能,大部分时间在75MHz,最有用的配置是使用cache控制器。在这种情况下,可以使用具有大量等待状态的大型多兆字节存储器,同时,当指令和数据已经被高速缓存时,每条指令达到1个时钟的峰值性能。当然,高速缓存控制器会影响性能,将时钟从75MHz减少到50MHz,并在高速缓存填充周期中插入大量等待状态。
那么,为了绕过这种性能限制,最大的逻辑步骤是增加状态数。在这种情况下,darkriscv可以选择使用真正的3阶段管道: - 第一阶段:指令预取(除缓存外没有其他操作)
- 第二阶段:指令解码(这里没有寄存器或存储器读取!)
- 第三阶段:指令执行(寄存器/存储器读/写)
当然,这不是加载/存储问题的解决方案......可能的解决方案是将管道增加到4级,分裂第3级。在读/写阶段的阶段。尽管可能,但是这一步骤增加了一些混淆,因为当第3阶段中的一个指令使用来自第4阶段中的另一个指令的结果时,长时间需要额外的逻辑以便使管道互锁。但是,使用缓存控制器,3阶段管道工作得很好,我想将来可能会添加一些修复程序,以使其更灵活。
实际上,在运行“hello world”代码时,我们得到以下结果: - darkriscv@75MHz -cache -wait-states 2-stage pipeline 2-phase clock: 6.40us
- darkriscv@75MHz +cache +wait-states 3-stage pipeline 1-phase clock: 9.37us
- darkriscv@50MHz +cache +wait-states 2-stage pipeline 2-phase clock: 13.84us
虽然第一种配置达到了最佳性能,但第二种配置可能是目前最真实的配置!
开发工具(gcc)关于gcc编译器,我正在使用RISC-V的实验性gcc 9.0.0。 除了-march = rv32i之外,darkriscv不需要补丁或更新。 虽然没有实现fence *和crg *指令,但gcc似乎没有使用该指令,并且它们在核心中不可用。
虽然可以使用官方RISC-V站点中提供的编译器集,但我们来自lowRISC项目的同事指出了一种更聪明的方法来构建工具链: https://www.lowrisc.org/blog/2017/09/building-upstream-risc-v-gccbinutilsnewlib-the-quick-and-dirty-way/
配置如下: - git clone --depth=1 git://gcc.gnu.org/git/gcc.git gcc
- git clone --depth=1 git://sourceware.org/git/binutils-gdb.git
- git clone --depth=1 git://sourceware.org/git/newlib-cygwin.git
- mkdir combined
- cd combined
- ln -s ../newlib-cygwin/* .
- ln -sf ../binutils-gdb/* .
- ln -sf ../gcc/* .
- mkdir build
- cd build
- ../configure --target=riscv32-unknown-elf --enable-languages=c --disable-shared --disable-threads --disable-multilib --disable-gdb --disable-libssp --with-newlib --with-arch=rv32ima --with-abi=ilp32 --prefix=/usr/local/share/gcc-riscv32-unknown-elf
- make -j4
- make
- make install
- export PATH=$PATH:/usr/local/share/gcc-riscv32-unknown-elf/bin/
- riscv32-unknown-elf-gcc -v
复制代码一切都会神奇地奏效! (:
最后,只要darkriscv尚未完全测试,有时将代码执行与另一个稳定的引用进行比较是一个非常好的主意! 在这种情况下,我正在使用项目picorv32: https://github.com/cliffordwolf/picorv32
当我有一些时间时,我会尝试创建一个更有条理的支持,以便在相同的缓存,内存和IO子系统中轻松测试darkriscv和picorv32,以便根据所需的功能选择核心例如,使用darkriscv获得更多性能,或使用picorv32获得更多功能。
关于软件,最复杂的问题是使内存设计与链接器布局相匹配。当然,这是一个gcc问题,它甚至不是问题,事实上,软件人员在链接代码和数据时的工作方式!
在最简化的版本中,直接连接到blockRAM,darkriscv是纯哈佛架构处理器,需要在指令和数据块之间进行分离!
当高速缓存控制器被激活时,高速缓存控制器为指令和数据提供单独的存储器,但是为更传统的冯·诺伊曼存储器架构提供接口。
在这两种情况下,正确设计的链接描述文件可能解决了这个问题!
目录说明- ise:ISE项目和配置文件(xise,ucf等)
- rtl:核心和测试SoC的来源
- sim:模拟测试核心和SoC
- src:测试固件的源代码(hello.c,boot.c等)
- tmp:为空,但ISE会在这里创建大量文件)
- ise目录包含要在Xilinx ISE 14.x中打开的xise项目文件,项目的汇编方式使所有文件都可以轻松加载。
虽然提供ucf文件是为了生成完整的版本,但是FPGA没有连接任何特定的配置,您必须添加有关FPGA板的引脚!无论如何,虽然没有连线,但构建总能让您对FPGA利用率和时序有一个很好的估计。
另一方面,模拟将显示一些波形,并且可以在运行示例代码时检查darkriscv操作。 hello.c代码打印字符串“hello world!”在控制台中以及位于SoC中的UART寄存器中。在未来,我将提供一个真正的UART逻辑,以便在真实的FPGA中测试darkriscv。
Future Work!
实现了RV32I的核心,并且还实现了带LUTRAM的缓存控制器,但需要一些修复才能正常工作。 接下来的步骤包括在内核中支持RV64I和RV128I,以及提供支持外部SRAM和UART的SoC。 所有其他功能都是针对短期的未来(H2 / 2018),除了NoC(片上网络),它更复杂,我认为只能在H1 / 2019上使用。
The Friends of DarkRISCV!Special thanks to: Paulo Matias (jedi master and verilog guru), Paulo Bernard (co-worker and verilog guru), Evandro Hauenstein (co-worker and git guru), Lucas Mendes (technology guru), Marcelo Toledo (technology guru), Fabiano Silos (technology guru and darkriscv beta tester), Guilherme Barile (technology guru and first guy to post anything about the darkriscv [2],发表了一篇文章), Alasdair Allan (technology guru, posted an article about the darkriscv [3],发表了一篇文章) and Gareth Halfacree (technology guru, posted an article about the darkriscv [3],发表了一篇文章. Special thanks to all people who directly and indirectly contributed to this project, including the company I work for.
References[1] https://www.amazon.com/RISC-V-Reader-Open-Architecture-Atlas/dp/099924910X [2] https://news.ycombinator.com/item?id=17852876 [3] https://blog.hackster.io/the-rise-of-the-dark-risc-v-ddb49764f392 [4] https://abopen.com/news/darkriscv-an-overnight-bsd-licensed-risc-v-implementation/
该项目在GitHub地址:https://github.com/darklife/darkriscv
本文来源 CSDN
|