查看: 1328|回复: 0
收起左侧

从零开始实现一个基于RISC-V的流水线处理器

[复制链接]

  离线 

  • TA的每日心情
    奋斗
    2021-3-3 12:32
  • 签到天数: 10 天

    [LV.3]

    发表于 2020-8-23 20:41:07 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 皋陶 于 2020-8-26 15:10 编辑

    从零开始实现一个基于RISC-V的流水线处理器
    (1) :RISC-V指令集架构详解
    本文目录
    • 基于RISC-V的流水线处理器
      • RISC-V指令集
      • RV32I
        • R-Type
        • I-Type
        • J-Type
        • B-Type
        • Load & Store
      • 总结
      • 后记

    基于RISC-V的流水线处理器

    RISC-V是一个基于精简指令集原则的开源指令集架构。它由加州大学伯克利分校的David Patterson教授领导下的小组完成,至今已成为RISC处理器中一股强劲的新生力量。


    至今github上已有多款优秀的基于RISC-V指令集的微处理器项目被发布,如蜂鸟E203、RocketChip等,但它们所涉及的知识层级较深,初学者很难迅速上手。而CSDN上也没有找到类似的RISC-V处理器的项目,因此笔者决定从零开始,由基础指令集做起,实现一款基于RISC-V指令集的微处理器。


    RISC-V指令集

    实现一款处理器,首先也是最重要的就是要确定处理器的指令集架构。


    RISC-V指令集可以分为以下几个子集:


    • RV32I:基本整数集,包括整数计算指令,LOAD/STORE,以及控制指令。RV32I拥有32位寻址空间,32个32位寄存器。
    • RV32E:指令与RV32I相同,但是寄存器数量变为16个,用于嵌入式环境。
    • RV64I:整数指令,拥有64位寻址空间,32个64位寄存器。
    • RV128I:整数指令,拥有128位寻址空间,32个128位寄存器。


    上述子集是RISC-V的基本指令集。在实际设计中,我们可以根据需要加入如下的扩展指令集:


    • M:标准乘法和除法扩展,增加了乘法和除法的指令,并把结果保存在整数寄存器。
    • A:标准原子指令扩展,增加了原子的读,修改,以及写存储器的指令。
    • F:标准单精度浮点扩展,增加了浮点寄存器,单精度计算指令,以及单精度的LOAD/STORE指令。
    • D:标准双精度浮点扩展,同样增加了浮点寄存器,且增加了双精度计算指令,双精度的LOAD/STORE指令。


    上述均为RISC-V基金会认证的扩展指令集,此外还有V/P/T等处于草稿修改阶段的扩展指令集,这里就不再赘述了。


    RV32I

    在本次设计中,我们要实现的是基于最基本的整数指令集——RV32I的微处理器。就让我们从RV32I的具体指令格式看起。


    下图展示了RV32I的基本指令格式。为了简化译码过程,源寄存器(rs1 和 rs2)和目标寄存器(rd)在RISC-V ISA 的所有指令格式中的位置保持一致。立即数被压缩在指令中最左边的可用位,并已经分配好,从而降低硬件复杂度。特别的,对于所有的立即数,指令的 31 位总是符号位,这样可以加速符号扩展电路。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(1)



    下面我们将对各类型的指令进行具体的解释。


    R-Type

    R-Type的指令为寄存器-寄存器指令,其格式如下图所示。R-Type指令的特点是2个源操作数和目标操作数对象都是寄存器,在实际执行过程中需要经历寄存器的写回。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(2)




    InstructionDescriptionFormatComment
    ADDADD rd, rs1, rs2rd = rs1 + rs2,忽略溢出,保留低32bit
    SUBSUB rd, rs1, rs2rd = rs1 - rs2,忽略溢出,保留低32bit
    ANDAND rd, rs1, rs2rd = rs1 & rs2,按位与
    OROR rd, rs1, rs2rd = rs1 | rs2,按位或
    XORXOR rd, rs1, rs2rd = rs1 ^ rs2, 按位异或
    SLTset less thanSLT rd, rs1, rs2if (rs1 < rs2) rd = 1 else rd = 0
    SLTUset less than unsignedSLTU rd, rs1, rs2if((unsigned)rs1 < (unsigned)rs2) rd = 1 else rd = 0
    SLLshift left logicalSLL rd, rs1, rs2rd = rs1 << rs2 [4 : 0],逻辑左移,低位补零,rs2的lower 5 bits作为偏移量
    SRLshift right logicalSRL rd, rs1, rs2rd = rs1 >> rs2 [4 : 0],逻辑右移,高位补零,rs2的lower 5 bits作为偏移量
    SRAshift rignt arithmetricSRA rd, rs1, rs2rd = rs1 >> rs2 [4 : 0],算数右移,高位补符号位,rs2的lower 5 bits作为偏移量
    NOPno operationNOP不进行任何操作,相当于ADDI x0, x0, 0

    I-Type

    I-Type的指令为寄存器-立即数指令,其指令格式如下图所示。R-Type指令中rs2和funct7的位置在I-Type指令中被imm替代,指令仅靠funct3来确定具体指令类型。


    imm是一个12位的立即数,在与32位的寄存器进行逻辑运算时必须进行符号位扩展。同样地,I-Type指令也要经历寄存器的写回。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(3)



    InstructionDescriptionFormatComment
    ADDIadd immediateADD rd, rs1, immrd = rs1 + (sign-extended) imm,忽略溢出,保留低32bit
    ANDIand immediateAND rd, rs1, immrd = rs1 & (sign-extended) imm,按位与
    ORIor immediateOR rd, rs1, immrd = rs1 | (sign-extended) imm,按位或
    XORIxor immediateXOR rd, rs1, immrd = rs1 ^ (sign-extended) imm, 按位异或
    SLTIset less than immediateSLT rd, rs1, immif (rs1 < (sign-extended) imm) rd = 1 else rd = 0
    SLTIUset less than unsigned immediateSLTU rd, rs1, immif((unsigned)rs1 < (unsigned)imm) rd = 1 else rd = 0

    对于移位指令,使用立即数的低5位作为偏移量,高7位也作为判断具体指令的操作码,如下图所示。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(4)



    InstructionDescriptionFormatComment
    SLLIshift left logicalSLL rd, rs1, immrd = rs1 << shamt,逻辑左移,低位补零
    SRLIshift right logicalSRL rd, rs1, immrd = rs1 >> shamt,逻辑右移,高位补零
    SRAIshift rignt arithmetricSRA rd, rs1, immrd = rs1 >> shamt,算数右移,高位补符号位

    J-Type

    J-Type指令为无条件跳转指令,主要功能是更改PC的指向地址,让处理器在下一个时钟上升沿在指定的地址中取指令。


    JAL的指令格式如下图所示。对于JAL,使用一个20位的有符号立即数作为偏移量,目标地址为pc + offset,并把原pc + 4的地址存入rd。



    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(5)


    JALR的指令格式如下图所示。对于JALR,其使用的指令格式是I-Type的指令格式,使用一个12位的有符号立即数作为偏移量,目标地址为pc + offset后把LSB置为0后的结果,并把原pc + 4的地址存入rd。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(6)



    由此,我们可以写出下表的指令。


    InstructionDescriptionFormatComment
    JALjump and linkJAL rd, offsetpc += (sign-extended) offset, rd = pc + 4
    JALRjump and link registerJALR rd, rs1, offsetpc = [rs + (sign-extended) offset] &~ 1, rd = pc + 4
    JRjump registerJR rs1pc = rs1

    B-Type

    B-Type指令为条件分支指令,仅在满足条件的情况下进行跳转,跳转的偏移量由一个12位的立即数给出。其指令格式如下图所示。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(7)



    InstructionDescriptionFormatComment
    BEQbranch equalBEQ rs1, rs2, offsetif (rs1 == rs2) pc += (sign-extended) offset
    BNEbranch not equalBNE rs1, rs2, offsetif (rs1 != rs2) pc += (sign-extended) offset
    BLTbranch less thanBLT rs1, rs2, offsetif (rs1 < rs2) pc += (sign-extended) offset
    BGEbranch bigger than or equalBGE rs1, rs2, offsetif (rs1 >= rs2) pc += (sign-extended) offset
    BGEUbranch bigger than or equal unsignedBGEU rs1, rs2, offsetif((unsigned) rs1 >= (unsigned) rs2) pc += (sign-extended) offset

    Load & Store

    在RV32I中,只有Load & Store指令拥有访问内存的权限。RV32I提供32-byte的寻址空间。


    下图展示了Load & Store指令的具体格式。可以注意到,Load指令使用的是I-Type指令的格式,而Store指令使用的是S-Type指令的格式。Load和Store指令的偏移量均由一个12位的立即数给出,但是它们在指令中的位置不同,这是由指令的功能决定的:Load指令需要将内存中的数据转移到寄存器组,因此需要提供目标寄存器;而Store指令需要将寄存器组中的数据转移到内存,因此需要rs1提供内存中将要写入数据的地址,rs2提供需要转移的数据。


    Load指令和Store指令的目标地址由rs1 + 符号位扩展的偏移量给出。当指令为LH/LB时,从内存的指定地址中取低16位/低8位,进行符号位扩展后存入rd。而当指令为LW时,直接从内存中取4字节的数据存入rd。


    国内芯片技术交流-从零开始实现一个基于RISC-V的流水线处理器risc-v单片机中文社区(8)


    InstructionDescriptionFormatComment
    LWload wordLW rd, rs1, offsetrd = mem[rs1 + (sign-extended) offset] [31:0]
    LHload halfLH rd, rs1, offsetrd = (sign-extended) mem[rs1 + (sign-extend) offset)] [15:0]
    LBload byteLB rd, rs1, offsetrd = (sign-extended) mem[rs1 + (sign-extend) offset)] [7:0]
    LBUload byte unsignedLBU rd, rs1, offsetrd = (zero-extended) mem[rs1 + (sign-extend) offset)] [7:0]
    SWstore wordSW rs1, rs2, offsetmem[rs1 + (sign-extend) offset)] = rs2

    总结

    至此,RV32I中基本指令的说明告一段落。


    然而,这些说明并不完整——RV32I中还有FENCE指令、Environment Call and Breakpoints指令、HINT指令等尚未提及,这些指令对于本次的设计来说过于复杂,由于本设计完成的处理器仅包括基本的运算及存储功能,因此在这里就将对这些指令的描述略去了。感兴趣的读者可以通过这个链接下载RISC-V的中文手册,或自行下载英文原版手册,对其进行深入阅读。


    后记

    RISC-V作为一个新兴的RISC架构,在具体的实现中博采众长,真正做到了对其他早期RISC架构的“取其精华,去其糟粕”。例如,RISC-V取消了MIPS-32 ISA中的延迟分支,单纯依靠现代已经相当发达的硬件预测器预测分支结果,实现了架构和具体实现的分离,使得指令集和芯片具体实现的分离成为可能。


    芯片商可以采用统一的、免费的开放指令集,但各个厂商可以有各自的内部模块实现,并可以申请专利予以保护。这样既可以构建同一个软件生态系统,又保持了芯片企业之间的独立性。


    对于笔者而言,RISC-V和MIPS的区别不仅在于指令中各元素位置的变化,更在于其精心设计的、称得上优雅的具体指令集架构和强大的可扩展性。RV32I中在rs1和rd之间插入一个3 bit的funct,在I-Type的指令中使用12位而非16位的立即数,想必也是从可扩展性的角度考虑得来的结果。


    笔者同样作为一个RISC-V的初学者,对于指令集的理解难免有纰漏或错误的地方,欢迎大家对笔者的文章进行批评指正,也欢迎大家与笔者进行交流。

    本篇完,感谢关注:RISC-V单片机中文网






    上一篇:RISC-V RV32I 基本6种指令集
    下一篇:RISC-V指令集介绍 - 整数基本指令集
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-4-20 11:09 , Processed in 0.856184 second(s), 48 queries .

    快速回复 返回顶部 返回列表