RISC-V学习笔记【系统设计】

蜂鸟E200系列处理器简介

特色:

  • 开源、免费
  • 高能效比
  • 针对IoT领域设计,支持RV32I/E/A/M/C/F/D等指令子集和机器模式
  • 2级流水线,功耗和性能均优于主流商用的ARM Cortex-M处理器
  • 提供完整的配套SoC(包括中断控制器、定时器、UART、QSPI、PWM等外设)、FPGA原型平台搭建步骤、软件运行示例
  • 方便开发(提供GCC编译工具链)、调试(提供JTAG接口和成熟的软件调试工具)

型号系列简介:

  • E201(对标Cortex-M0+)

    面积最小,可配置为RV32IC/EC,不支持其他扩展子集

  • E203

    配备面积优化的多周期硬件乘除法单元,可配置为RV32IMAC/EMAC

  • E205(对标Cortex-M3)

    配备单周期硬件乘法单元和多周期硬件除法单元,固定RV32IMAC架构

  • E205f

    在E205基础上增加了单精度FPU

  • E205fd(提供Cortex-M7中才具备的双精度FPU)

    在E205基础上增加单精度、双精度FPU

目前开源的型号为E203

蜂鸟E200处理器设计

架构:指令集架构的简称

微架构:处理器的硬件实现方案

设计哲学:

  1. 模块化和可重用性
  2. 面积最小化
  3. 结构简单化
  4. 性能不追求极端,提倡够用即可

总结:追求弄出来最小的核,性能?又不是不能用!

代码风格

E203使用统一的Verilog RTL编码风格

  • 使用标准DFF模块例化生成寄存器

    一般使用always块语法生成寄存器,蜂鸟E200处理器推荐使用如下原则:

    1. 避免直接使用always块,而是采用模块化的标准DFF模块进行例化;这样有助于全局替换寄存器类型在寄存器中全局插入延迟有明确的使能信号,能方便综合工具自动插入寄存器级的门控时钟以降低动态功耗规避Verilog语法中if-else不能传播不定态的问题

    2. 标准DFF模块为一系列不同的模块,用于实现各种类型寄存器

    3. 模块内部使用always块编写。由于Verilog中的if-else语法不能传播不定态,所以对于处于if条件中的输入信号为不定态的非法情况使用断言进行捕捉,书中给出例程如下

      //标准DFF模块的代码片段
      module sirv_gnrl_dfflr # (
      	parameter W=32
      ) (
      	input 				lden,
          input 	[DW-1:0] 	dnxt,
          output 	[DW-1:0] 	qout,
          
          input 				clk,
          input 				rst_n
      );
      
      reg [DW-1:0] qout_r;
          
      //使用always块编写寄存器逻辑
          always @(posedge clk or negedge rst_n)
          begin : DFLR_PROC
              if(rst_n==1'b0)
                  qout_r<={DW{1'b0}};
              else if(lden==1'b1)
                  qout_r<=dnxt;
          end
          
          assign qout=qout_r;
          
      //使用断言捕捉lden信号的不定态
          'ifndef FPGA_SOURCE//{
          'ifndef SYNTHESIS//{
          sirrv_gnrl_xchecker # {//该模块内部是一个SystemVerilog编写的断言
              .DW(1)
          } u_sirv_gnrl_xchecker(
              .i_dat(lden),
              .clk(clk)
          );
          'endif//}
          'endif//}
      endmodule
      
      //sirv_gnrl_xchecker模块代码片段
      module sirv_gnrl_xchecker # (
      	parameter DW=32
      ) (
          input [DW-1:0] i_dat,
      	input clk
      );
          CHECK_THE_X_VALUE:
          assert property (@(posedge clk))
              ((^(i_dat))!==1'bx)
          else $fatal ("\n Error:Oops,detected a X value!!! This should never happen. \n")
      endmodule
      
  • 推荐使用assign语句替代if-else和case语法进行代码编写

    原因:Verilog中的if-else和case语法不能传播不定态且会产生优先级选择电路而不是并行选择电路,不利于时序和面积

  • 数据通路商可以使用不带reset的寄存器,只在控制通路上使用带reset的寄存器

    原因:带有reset的寄存器会面积较大且时序会差一点

  • 信号名定义避免使用拼音,全部使用英文缩写;信号名不能定义过长,争取做到代码自解释

  • Clock和Reset信号只能用在DFF作为其时钟和复位信号

模块层次与源代码

E203在github上开源,项目名e200_opensource

/doc/目录存放说明文档

RTL文件都存放在/rtl/e203/目录下

测试平台testbench文件存放在/tb/目录下

  • tb_top.v 简单的Verilog Testbench顶层文件

vsim目录存放仿真

  • bin目录:存放脚本的文件夹
  • Makefile:运行的MakeFile
  • run:运行目录

fpga目录存放FPGA项目和脚本的目录

riscv-tools存放所需的riscv-tools

  • riscv-fesvr 用于编译指令模拟器Spike的源码

  • riscv-isa-sim 用于编译指令模拟器Spike的源码

  • riscv-tests 存放一些测试用例

    测试用例是一种能够自我检测运行成功还是失败的测试程序

核心文件目录如下

  • general:存放公用的通用RTL代码
  • fab:存放总线bus fabric的RTL代码
  • subsys:存放完整子系统顶层的RTL代码
  • mems:存放memory模块的RTL代码
  • perips:存放外设(peripherals)模块的RTL代码
  • debug:存放debug相关模块的RTL代码
  • fpga:存放FPGA实现的RTL代码
  • core:存放核心的RTL代码
    • config.v 参数配置文件
    • e203_biu.v BIU模块,总线接口单元
    • e203_reset_ctrl.v 核心复位控制模块,用于将外界的异步reset信号进行同步,使之变成“异步置位同步释放”的复位信号
    • e203_clk_ctrl.v 核心时钟控制模块,用于控制处理器各个主要组件的自动时钟门控
    • e203_cpu_top.v 核心顶层模块,例化了e203_cpu和e203_srams
    • e203_cpu.v 核心中去除SRAM后的逻辑顶层模块(核心的所有逻辑部分)
    • e203_core.v 核心的主体逻辑模块
    • e203_exu.v 核心内部执行单元顶层模块,负责执行指令逻辑
    • e203_ifu.v 核心内部取指令单元顶层模块,负责取指令逻辑
    • e203_lsu.v 核心内部存储器访问单元顶层模块,负责内部存储器访问逻辑
    • e203_srams.v 核心的所有SRAM顶层模块,包括DTCM和ITCM的SRAM模块
    • e203_dtcm_ctrl.v DTCM控制模块
    • e203_itcm_ctrl.v ITCM控制模块
    • e203_dtcm_ram.v DTCM的SRAM模块
    • e203_itcm_ram.v ITCM的SRAM模块

流水线设计

经典MIPS五级流水线

在这个经典流水线设计中,一条指令的生命周期分为以下几个步骤

  1. 取指IF(Instruction Fetch):将指令从存储器读取出来

  2. 译码ID(Instruction Decode):将从存储器取出的指令进行翻译

    经过译码之后得到指令需要的操作数寄存器索引,使用该索引从通用寄存器组中将操作数读出

  3. 执行EX(Instruction Execute):对指令进行真正运算的过程

    指令译码后所需要进行的计算类型都已得知,并且已经从通用寄存器组中读取出了所需的操作数

    在EX阶段最常见的部件是算术逻辑部件运算器(Arithmetic Logical Unit,ALU),它负责实施具体的运算

  4. 访存MEM(Memory Access):使用存储器访问指令将数据从存储器中读出或写入寄存器

  5. 写回WB(Write-Back):将指令执行的结果写回通用寄存器组,如果是普通运算指令,该结果值来自执行阶段计算的结果;如果是存储器读指令,结果来自于访存阶段从存储器读出的数据

流水线和状态机

流水线的本质是以面积换性能,以空间换时间的手段

状态机本质上是以性能换面积,以时间换空间的手段

两者存在“对偶”关系

如果处理器不使用流水线而是采用状态机来完成,则需要多个时钟周期才能完成一条指令的所有操作,即每个时钟周期完成状态机的一个状态,8051内核微架构就使用了类似状态机的实现方式,省掉了流水线多余的寄存器开销且能复用组合逻辑数据通路,总体上减小了面积

流水线深度

流水线深度并不是越深越好

现代流水线往往有几十级、二十几级的流水线深度。流水线的级数越多,每级流水线内容纳的硬件逻辑越少,根据数字同步电路设计的知识,两级寄存器之间的硬件逻辑越少,可以运行到的主频越高,于是现代处理器的流水线就能运行到更高的主频。

但更多的流水线级数会消耗更多的寄存器,增加更大的面积;每级流水线都需要进行握手,流水线最后一级的反压信号可能会一直串扰到最前一级导致严重的时序问题;更深的流水线还会导致可能预取的错误指令流更多,如果在最后需要丢弃则会白白浪费很多功耗和性能,也就是说流水线越深,浪费和损失越严重

目前市场上的处理器朝着越来越深和越来越浅的流水线级数的两个极端发展,需要根据需求决定流水线深度

深流水线

Cortex-A7主打低功耗、能效比高,使用8级流水线

Cortex-A15主打高性能,使用15级流水线

大多数Intel和ARM高性能处理器都在使用十几级的流水线

浅流水线

Cortex-M3主打低功耗与性能之间的平衡,使用3级流水线

Cortex-M0+处理器主打能效比,使用2级流水线

主打低功耗的处理器一般使用5级以下的流水线

蜂鸟E203使用浅流水线设计

流水线乱序

乱序执行:在指令的执行阶段由不同的运算单元同时执行不同的指令(并行处理)

但是乱序流水线的写回一般还需要严格按顺序写回

特别地,有的处理器配备重排序缓存ROB(Re-Order Buffer),运算单元完成执行后将结果先写回ROB,最后由ROB排序后按顺序写回寄存器组Regfile,但是ROB往往会占据很大面积,数据会被写回两次导致动态功耗较大

乱序执行可以提高处理器的效率,但是可能会增大面积、增加功耗,还需要遵守顺序写回寄存器的规则,否则会导致出错

这部分内容会在之后详细讲解

处理流水线中的反压

现代处理器设计中通常使用如下方法解决流水线中的反压问题

  1. 取消握手

    直接从根源消除反压。但是取消握手意味着流水线中的每一级并不会与其下一级进行握手,可能会造成功能错误或者指令丢失。这种方法往往需要配合重执行、预留大缓存等其他机制

    该方法比较激进,一般只有在非常高级的处理器设计中才会用到

  2. 加入乒乓缓存

    用面积换时序,也是解决反压的最简单方法。使用有两个表项的乒乓缓存替换掉普通的一级流水线(只有一个表项),可以使此级流水线向上一级流水线的握手接收信号仅关注乒乓缓存中是否有一个以上空的表项,无需将下一级握手接收信号串扰到上一级

  3. 加入向前旁路缓存

    还是用面积换时序。旁路缓存仅有一个表项,但由于加入了额外的缓存表项,可以将后向的握手信号时序路径砍断,但是对向前路径不受影响,因此可以广泛使用于握手接口。

    蜂鸟E200就是用了这种设计

这些设计在ASIC电路设计中也会经常用到

流水线的冲突
资源冲突

流水线中硬件资源的冲突被称为资源冲突,说人话就是流水线要用的硬件不够用了

一般通过复制硬件资源或者流水线停顿等待硬件资源的方法解决资源冲突

数据冲突

数据冲突指不同指令之间的操作数存在着数据相关性造成的冲突

数据相关性可以直接理解成两个读写寄存器指令的目标寄存器重复了

常见数据相关性如下所示

  1. WAR(Write-After-Read)相关性

    又称先读后写相关性:写入寄存器指令和读取寄存器指令的目标相同时,写入指令比读取指令先执行,会导致读取到错误的值。理论上来说在流水线中后序执行的指令一定不能比和它有WAR相关性的前序指令先执行

  2. WAW(Write-After-Write)相关性

    又称先写后写相关性。两个写入指令的目标寄存器相同时,后面写入寄存器的指令会将之前写入指令执行的结果覆盖

  3. RAW(Read-After-Write)相关性

    又称先写后读相关性,表示后续执行的指令需要读取的源操作数寄存器索引与谦虚执行的指令需要写回的结果寄存器索引相同造成的数据相关性

可以使用寄存器重命名的方法将WAW和WAR相关性去除;无法通过寄存器重命名的方法去除RAW相关性,RAW相关性被称为真数据相关。一旦产生RAW相关性,后续的指令一定要使用和它有RAW数据相关性的秦绪指令执行完成的结果,会造成流水线停顿,为了解决这个问题可以使用动态调度方法

  • 数据旁路传播技术:尽可能让前序指令的计算结果更快的旁路传播给后续相关指令的操作数
  • 尽可能让后续相关指令在等待过程中不阻塞流水线,让其他无关的指令继续顺利运行
  • 使用Tomasulo算法中的保留站可以优化以上过程‘
  • 在每个运算单元前配置乱序发射队列,发射队列仅追踪RAW相关性,不存放操作数。在发射队列中的指令一旦相关性解除后,再从发射单元中发射出来读取物理寄存器组,然后再发送给运算单元进行计算

蜂鸟E200的流水线

蜂鸟E200处理器核的流水线级数比较难界定,严格来讲使用了变长流水线,如下所示:

  1. 取址

    由IFU完成

    这个步骤带流水线第一级完成

  2. 译码、执行、写回

    由EXU进行译码和执行步骤,由WB完成写回步骤

    这三个步骤在流水线第二级的同一个时钟周期内完成

  3. 访存

    由LSU完成

    该步骤处于第三级流水线,但LSU写回的结果还需要通过WB模块再写回到通用寄存器组里

由于流水线按序主体是位于第一级的“取址”和位于第二级的“执行”和“写回”,可以非严谨地定义E200处理器核心的流水线深度为2级

E200的流水线冲突问题会在EXU单元中得到解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值