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_addr | inst_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数 |
---|---|
bit | 1 bit |
byte | 8 bit |
shortint | 16 bit |
int | 32 bit |
longint | 64 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_data | 32 | rs1_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、通过中间线,进行处理信号得到信号的最终输出