18 Riscv单周期CPU,一文就够 (第一弹)

B线单周期CPU总结

一个最简单的CPU由取指令、译码、执行、写回四个步骤构成。

取指令

module fetch (
    //指令地址
    input wire [`XLEN-1:0] inst_addr,
    //指令内容
    output wire [`INST_LEN-1:0] inst_data
);

  wire [`XLEN-1:0] _mem_data;
  import "DPI-C" function void pmem_read(
    input  longint raddr,
    output longint rdata
  );

  import "DPI-C" function void get_pc(
    input  longint pc,
  );
/*  仿真使用,传递当前 pc 给仿真环境,根据pc 取指令 */
  always @(*) begin
    pmem_read(inst_addr, _mem_data);
    get_pc(inst_addr);
  end

  assign inst_data = _mem_data[31:0];

endmodule

解析上述代码的含义:

输入输出
inst_addrinst_data

inst_addr 表示取指令的地址,inst_data表示取到的指令
DPI-C机制,DPI-C机制采用system verilog的变量形式。

system verilog中的基本变量类型

推荐资料:systemverilog中的基本变量类型
System verilog 存在两种变量类型为2值变量类型与4值变量类型,其中四值变量类型有:
4值变量有4种,wire,reg,logic,integer。
2值变量:

类型bit数
bit1 bit
byte8 bit
shortint16 bit
int32 bit
longint64 bit

decode 译码模块

译码模块的作用就是通过上述取值指令提取到的inst进行解析,从而得到运算指令。


module dcode (
    /* 输入信号 */
    input  [         `INST_LEN-1:0] inst_data,
    /*输出信号: */
    output [    `REG_ADDRWIDTH-1:0] rs1_idx,
    output [    `REG_ADDRWIDTH-1:0] rs2_idx,
    output [    `REG_ADDRWIDTH-1:0] rd_idx,
    output [          `IMM_LEN-1:0] imm_data,
    /* CSR 译码结果 */
    output [          `IMM_LEN-1:0] immCSR,
    output                          isNeedimmCSR,
    output [`CSR_REG_ADDRWIDTH-1:0] csr_idx,

    output [`ALUOP_LEN-1:0] alu_op,  // alu 操作码
    output [`MEMOP_LEN-1:0] mem_op,  // mem 操作码
    output [`EXCOP_LEN-1:0] exc_op,  // exc 操作码
    output [`PCOP_LEN-1:0] pc_op,  // pc 操作码
    output [`CSROP_LEN-1:0] csr_op,  // csr 操作码

);
输入位宽输出位宽
inst_data32rs1_idx(rs1 的通用寄存器号)5
rs2_idx(rs2 的通用寄存器号)5
rd_idx(rd 的通用寄存器号)5
imm_data(经过拓展后的立即数)64
immCSR(CSR的立即数)64
isNeedimmCSR(是否需要CSR立即数)1
csr_idx(csr 的寄存器号)12
alu_op(ALU 的操作类型)6
mem_op(内存 的操作类型)4
exc_op(执行指令 的操作类型)5
pc_op(PC指令 的操作类型)4
csr_op(csr指令 的操作类型)3

ALU类指令

加减以及逻辑类

alu_add 类指令

_alu_add:_inst_add |_inst_addw |_inst_addi |_inst_addiw| _type_load | _type_store | _inst_jal |_inst_jalr |_inst_auipc | _inst_lui|_isNeed_csr;

add = src1 + src2; addw = (src1 + src2)[32位后进行符号拓展] addi = src1 + imm addiw = (src1 + imm)[32位后进行符号拓展]
load = read(src1 + imm) store = write(src1 + imm, src2) jal = pc + 4/ pc + imm jalr = pc + 4 / (src1 + imm) & (~U64(1))

type_store 包括:sb sh sw sd 分别表示存入1、2、4、8位数字
type_load 包括:lb lh lw ld lbu lhu lwu 分别表示载入1、2、4、8位(进行符号拓展) 1、2、4位(不进行符号拓展)
_isNeed_csr 包括:csrrc csrrci csrrs csrrsi csrrw csrrwi

alu_sub 类指令

_alu_sub = _inst_sub | _inst_subw;

sub = src1 - src2 subw = (src1 - src2)[32位相减后符号拓展到64位]

alu_xor 类指令

_inst_xor | _inst_xori;

xor = src1 ^ src2 xori = src1 ^ imm

_alu_and 类指令

_alu_and = _inst_and | _inst_andi;

and = src1 & src2 andi = src1 & imm

_alu_or 类指令

_alu_or = _inst_or | _inst_ori;

or = src1 | src2 ori = src1 | imm

移位类 指令

_alu_sll 类指令

_alu_sll = _inst_sll | _inst_slli;

sll = src1 << (src2)[低6位] slli = src1 << imm 进行无符号拓展

_alu_srl

_alu_srl = _inst_srl | _inst_srli;

srl = src1 >> src2[低6位] srli = src1 >> imm [低6位] 进行无符号拓展

_alu_sra

_alu_sra = _inst_sra | _inst_srai;

sra = src1 >> src2[低六位] srai = src1 >> imm [低6位] 进行符号拓展

_alu_sllw

_alu_sllw = _inst_slliw | _inst_sllw;

slliw = src1 << imm[低五位] sllw = src1 << src2[低五位] 不进行符号拓展的移位后进行符号拓展

_alu_srlw

_alu_srlw = _inst_srliw | _inst_srlw;

srliw = src1 >> imm[低五位] srlw = src1 >> src2[低五位] 不进行符号拓展的移位后进行符号拓展

_alu_sraw

_alu_sraw = _inst_sraiw | _inst_sraw;

sraiw = src1 >> imm[低五位] sraw = src1 >> src2[低五位]进行符号拓展的移位后进行符号拓展

比较类指令

_alu_slt

_alu_slt = _inst_slt | _inst_slti;

slt = src1 < src2 ?1 : 0 slti = src1 < imm ? 1 : 0 有符号比较

_alu_sltu

_alu_sltu = _inst_sltu | _inst_sltiu;

sltu = src1 < src2 ? 1 : 0 sltiu = src1 < imm ? 1 : 0 无符号比较

_alu_beq

_alu_beq = _inst_beq;

beq : src1 == src2 ? dnpc = pc + imm : none

_alu_bne

_alu_bne = _inst_bne;

bne : src1 != src2 ? dnpc = pc + imm : none

_alu_blt

_alu_blt = _inst_blt;

blt : src1 < src2 ? dnpc = pc + imm : none 进行有符号比较

_alu_bge

_alu_bge = _inst_bge;

bge : src1 >= src2 ? dnpc = pc + imm : 0 进行有符号比较

_alu_bltu

_alu_bltu = _inst_bltu;

bltu : src1 < src2 ? dnpc = pc + imm : none 进行无符号比较

_alu_bgeu

_alu_bgeu = _inst_bgeu;

bgeu : src1 >= src2 ? dnpc = pc + imm : 0 进行无符号比较

_alu_mul

_alu_mul = _inst_mul;

mul = src1 * src2

_alu_mulh

_alu_mulh = _inst_mulh;

mulh = (src1 * src2)[128位] >> 64 有符号*有符号

_alu_mulhsu

_alu_mulhsu = _inst_mulhsu;

mulhsu = (src1 * src2)[128位] >> 64 有符号*无符号

_alu_mulhu

_alu_mulhu = _inst_mulhu;

mulhu = (src1 * src2)[128位] >> 64 无符号 * 无符号

_alu_mulw

_alu_mulw = _inst_mulw;

mulw = src1[32位int] * src2[32位int] 随后进行符号拓展

_alu_div

_alu_div = _inst_div;

div = S64(src1) / S64(src2)

_alu_divu

_alu_divu = _inst_divu;

divu = U64(src1) / U64(src2)

_alu_divw

_alu_divw = _inst_divw;

divw = S32(src1) / S32(src2) 随后进行符号拓展

_alu_divuw

_alu_divuw = _inst_divuw;

_nst_divuw = SEXT(U32(src1) / U32(src2), 32)

_alu_remw

_alu_remw = _inst_remw;

inst_remw = SEXT(S32(src1) % S32(src2), 32)

_alu_remuw

alu_remuw = _inst_remuw;

remuw = SEXT(U32(src1) % U32(src2), 32)

_alu_rem

_alu_rem = _inst_rem;

rem = S64(src1) % S64(src2)

_alu_remu

_alu_remu = _inst_remu;

remu = U64(src1) % U64(src2)

 wire [`ALUOP_LEN-1:0] _alu_op = ({`ALUOP_LEN{_alu_add}} & `ALUOP_ADD)|
                                  ({`ALUOP_LEN{_alu_sub}} & `ALUOP_SUB)|
                                  ({`ALUOP_LEN{_alu_xor}} & `ALUOP_XOR)|
                                  ({`ALUOP_LEN{_alu_or}} & `ALUOP_OR)|
                                  ({`ALUOP_LEN{_alu_and}} & `ALUOP_AND)|
                                  ({`ALUOP_LEN{_alu_sll}} & `ALUOP_SLL)|
                                  ({`ALUOP_LEN{_alu_srl}} & `ALUOP_SRL)|
                                  ({`ALUOP_LEN{_alu_sra}} & `ALUOP_SRA)|
                                  ({`ALUOP_LEN{_alu_sllw}} & `ALUOP_SLLW)|
                                  ({`ALUOP_LEN{_alu_srlw}} & `ALUOP_SRLW)|
                                  ({`ALUOP_LEN{_alu_sraw}} & `ALUOP_SRAW)|
                                  ({`ALUOP_LEN{_alu_slt}} & `ALUOP_SLT)|
                                  ({`ALUOP_LEN{_alu_sltu}} & `ALUOP_SLTU)|
                                  ({`ALUOP_LEN{_alu_beq}} & `ALUOP_BEQ)|
                                  ({`ALUOP_LEN{_alu_bne}} & `ALUOP_BNE)|
                                  ({`ALUOP_LEN{_alu_blt}} & `ALUOP_BLT)|
                                  ({`ALUOP_LEN{_alu_bge}} & `ALUOP_BGE)|
                                  ({`ALUOP_LEN{_alu_bltu}} & `ALUOP_BLTU)|
                                  ({`ALUOP_LEN{_alu_bgeu}} & `ALUOP_BGEU)|
                                  ({`ALUOP_LEN{_alu_mul}} & `ALUOP_MUL)|
                                  ({`ALUOP_LEN{_alu_mulh}} & `ALUOP_MULH)|
                                  ({`ALUOP_LEN{_alu_mulhsu}} & `ALUOP_MULHSU)|
                                  ({`ALUOP_LEN{_alu_mulhu}} & `ALUOP_MULHU)|
                                  ({`ALUOP_LEN{_alu_mulw}} & `ALUOP_MULW)|
                                  ({`ALUOP_LEN{_alu_div}} & `ALUOP_DIV)|
                                  ({`ALUOP_LEN{_alu_divu}} & `ALUOP_DIVU)|
                                  ({`ALUOP_LEN{_alu_rem}} & `ALUOP_REM)|
                                  ({`ALUOP_LEN{_alu_remu}} & `ALUOP_REMU)|
                                  ({`ALUOP_LEN{_alu_divw}} & `ALUOP_DIVW)|
                                  ({`ALUOP_LEN{_alu_divuw}} & `ALUOP_DIVUW)|
                                  ({`ALUOP_LEN{_alu_remw}} & `ALUOP_REMW)|
                                  ({`ALUOP_LEN{_alu_remuw}} & `ALUOP_REMUW);

  assign alu_op = _alu_op;

上述代码对输出alu_op操作数进行提取

对存储指令进行分类 mem_op

  /* MEM_OP */
  wire [`MEMOP_LEN-1:0] _mem_op =  ({`MEMOP_LEN{_inst_lb}}&`MEMOP_LB)|
                                   ({`MEMOP_LEN{_inst_lbu}}&`MEMOP_LBU)|
                                   ({`MEMOP_LEN{_inst_lh}}&`MEMOP_LH)|
                                   ({`MEMOP_LEN{_inst_lw}}&`MEMOP_LW)|
                                   ({`MEMOP_LEN{_inst_lhu}}&`MEMOP_LHU)|
                                   ({`MEMOP_LEN{_inst_sb}}&`MEMOP_SB)|
                                   ({`MEMOP_LEN{_inst_sh}}&`MEMOP_SH)|
                                   ({`MEMOP_LEN{_inst_sw}}&`MEMOP_SW)|
                                   ({`MEMOP_LEN{_inst_lwu}}&`MEMOP_LWU)|
                                   ({`MEMOP_LEN{_inst_ld}}&`MEMOP_LD)|
                                   ({`MEMOP_LEN{_inst_sd}}&`MEMOP_SD);
  assign mem_op = _mem_op;

对进行PC改变的指令进行分类

  wire [`PCOP_LEN-1:0] _pc_op = (_B_type)?`PCOP_BRANCH:
                                (_inst_jal)?`PCOP_JAL:
                                (_inst_jalr)?`PCOP_JALR:
                                (_inst_mret|_inst_ecall)?`PCOP_TRAP:
                                `PCOP_INC4;
  assign pc_op = _pc_op;

在上述代码中 _B_type 具体如下所示:

beq bne blt bge bltu bgeu

对不同的执行类型进行分类

  wire [`EXCOP_LEN-1:0] _exc_op = ({`EXCOP_LEN{_type_auipc}}&`EXCOP_AUIPC) |
                                  ({`EXCOP_LEN{_type_lui}}&`EXCOP_LUI) |
                                  ({`EXCOP_LEN{_type_jal}}&`EXCOP_JAL) |
                                  ({`EXCOP_LEN{_type_jalr}}&`EXCOP_JALR) |
                                  ({`EXCOP_LEN{_type_load}}&`EXCOP_LOAD) |
                                  ({`EXCOP_LEN{_type_store}}&`EXCOP_STORE) |
                                  ({`EXCOP_LEN{_type_branch}}&`EXCOP_BRANCH) |
                                  ({`EXCOP_LEN{_type_op_imm}}&`EXCOP_OPIMM) |
                                  ({`EXCOP_LEN{_type_op_imm_32}}&`EXCOP_OPIMM32) |
                                  ({`EXCOP_LEN{_type_op}}&`EXCOP_OP) |
                                  ({`EXCOP_LEN{_type_op_32}}&`EXCOP_OP32) |
                                  ({`EXCOP_LEN{_isNeed_csr}}&`EXCOP_CSR) |
                                  ({`EXCOP_LEN{_inst_ebreak}}&`EXCOP_EBREAK) |
                                  ({`EXCOP_LEN{_NONE_type}} & `EXCOP_NONE);

  assign exc_op = _exc_op;

上述代码中:
type_store 包括:sb sh sw sd 分别表示存入1、2、4、8位数字
type_load 包括:lb lh lw ld lbu lhu lwu 分别表示载入1、2、4、8位(进行符号拓展) 1、2、4位(不进行符号拓展)
_isNeed_csr 包括:csrrc csrrci csrrs csrrsi csrrw csrrwi

_type_branch : 包括B类型的指令 beq bne blt bge bltu bgeu

_type_op_imm 包括如下指令 : _inst_addi _inst_slti _inst_sltiu _inst_xori _inst_ori _inst_andi

_type_op 包括如下指令 : _inst_add _inst_sub _inst_sll _inst_slt _inst_sltu _inst_xor _inst_srl _inst_sra _inst_or _inst_and _inst_mul _inst_mulh _inst_mulhsu _inst_mulhu _inst_div _inst_divu _inst_rem _inst_remu

_type_op_32 包括:_inst_addw _inst_subw _inst_sllw _inst_srlw _inst_sraw _inst_mulw _inst_divw _inst_divuw _inst_remw _inst_remuw

_type_op_imm_32 包括:_inst_addiw _inst_slliw _inst_srliw _inst_sraiw

_type_system 包括: _inst_ecall _inst_ebreak

CSR指令包括:_inst_csrrw _inst_csrrs _inst_csrrc _inst_csrrwi _inst_csrrsi _inst_csrrci _inst_mret

随后有一些神奇的分类

我们将指令分为 R I S B U J

随后有如下代码:

  wire _R_type = _type_op | _type_op_32;
  wire _I_type = _type_load | _type_op_imm | _type_op_imm_32 | _type_jalr | _type_system;
  wire _S_type = _type_store;
  wire _B_type = _type_branch;
  wire _U_type = _type_auipc | _type_lui;
  wire _J_type = _type_jal;

R型指令正好是所有操作类型 包括:_type_op _type_op_32

S型指令 全都是存储指令
B型指令 全都是分支指令
I型指令
J型指令:只有jal
U型指令:

输出信号逐个分析

rs1_idx rs2idx rdidx csridx imm_data immCSR isNeedimmCSR

rs1_idx
  wire _isNeed_rs1 = (_R_type | _I_type | _S_type | _B_type) & (~_isNeed_immCSR);
  
  wire [4:0] _rs1_idx = (_isNeed_rs1) ? _rs1 : 5'b0;
  
  assign rs1_idx = _rs1_idx;
  

**解析:**通过指令的类型我们知道指令是否需要rs1,如果不需要rs1赋值0

rs2_idx
  wire _isNeed_rs2 = (_R_type | _S_type | _B_type);
   wire [4:0] _rs2_idx = (_isNeed_rs2) ? _rs2 : 5'b0;
  assign rs2_idx = _rs2_idx;

与上述类似

rd_idx
  wire _isNeed_rd = (_R_type | _I_type | _U_type | _J_type);
  wire [4:0] _rd_idx = (_isNeed_rd) ? _rd : 5'b0;
  assign rd_idx = _rd_idx;
csr_idx
wire _isNeed_immCSR = (_inst_csrrci | _inst_csrrsi | _inst_csrrwi);
wire _isNeed_csr = (_inst_csrrc|_inst_csrrci|_inst_csrrs|_inst_csrrsi|_inst_csrrw|_inst_csrrwi);
wire [`CSR_REG_ADDRWIDTH-1:0] _csr_idx = (_isNeed_csr) ? _csr : `CSR_REG_ADDRWIDTH'b0;

imm_data

首先对立即数进行预处理,处理如下:

  wire [`IMM_LEN-1:0] _immI = {{21 + 32{_inst[31]}}, _inst[30:20]};
  wire [`IMM_LEN-1:0] _immS = {{21 + 32{_inst[31]}}, _inst[30:25], _inst[11:8], _inst[7]};
  wire [`IMM_LEN-1:0] _immB = {{20 + 32{_inst[31]}}, _inst[7], _inst[30:25], _inst[11:8], 1'b0};
  wire [`IMM_LEN-1:0] _immU = {{1 + 32{_inst[31]}}, _inst[30:20], _inst[19:12], 12'b0};
  wire [`IMM_LEN-1:0] _immJ = {
    {12 + 32{_inst[31]}}, _inst[19:12], _inst[20], _inst[30:25], _inst[24:21], 1'b0
  };
wire _isNeed_imm = (_I_type | _S_type | _B_type | _U_type | _J_type);
wire [`IMM_LEN-1:0] _imm_data = ({`IMM_LEN{_I_type}}&_immI) |
                                  ({`IMM_LEN{_S_type}}&_immS) |
                                  ({`IMM_LEN{_B_type}}&_immB) |
                                  ({`IMM_LEN{_U_type}}&_immU) |
                                  ({`IMM_LEN{_J_type}}&_immJ);

上述通过选择器对不同的立即数进行选择,最后得到的立即数就是我们解析出来的立即数。

imm_CSR
  wire [`IMM_LEN-1:0] _immCSR = {59'b0, _inst[19:15]};
    assign immCSR = _immCSR;
  
isNeedimmCSR
  wire _isNeed_immCSR = (_inst_csrrci | _inst_csrrsi | _inst_csrrwi);
  assign isNeedimmCSR = _isNeed_immCSR;

上述就是通过每个指令的特点进行解析,可以有如下思路总结:
1、根据opcode发现指令存放的规律,用来位ex_op操作进行分类
2、输出立即数时,通过多路选择器将提前解析出来的立即数进行选择
3、通过中间线,进行处理信号得到信号的最终输出

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值