Menu Close

RISC-V ALU模块和branch(1)ALU模块

RISC-V 的ALU 模块用来处理指令执行部分。这个模块将之前ITCM读取的指令,译码模块分离出来的相关信息进行处理。处理过程包括对32个通用寄存器的赋值,对memory的访存,对pc 指针的更改等等。

相关参考文章:

RISC-V 教学教案

 

RISC-V cpu 架构:

%title插图%num

 

%title插图%num

ALU 模块代码:

module exu_alu (
input sys_clk, // 系统时钟
input rst_n,


input [ 4: 0 ] i_rd_idx, // 汇编指令中rd 所指定的32个通用寄存器中的一个
input [ 31: 0 ] i_rs1_val, // 汇编指令中rs1 指定的寄存器 值

input [ 31: 0 ] i_PC, // 当前正在执行的这条指令的PC

input i_OP_IMM, // op_imm 指令组
input i_LOAD, // load 指令组
input [ 8: 0 ] i_opimm_instr, // op_imm 指令组 成员:SRAI, SRLI, SLLI, ANDI, ORI, XORI, SLTIU, SLTI, ADDI
input [ 31: 0 ] i_I_imm, // I-type 立即数

input i_OP, // op 指令组
input [ 9: 0 ] i_op_instr, // op 指令组 成员:SRA, SUB, SRL, SLL, XOR, OR, AND, SLTU, SLT, ADD
input [ 31: 0 ] i_rs2_val, // 汇编指令中rs2 指定的寄存器 值

input i_LUI, // LUI 指令
input i_AUIPC, // AUIPC 指令
input [ 31: 0 ] i_U_imm, // U-type 立即数

input i_JAL, // JAL 指令
input i_JALR, // JALR 指令
input [ 31: 0 ] i_J_imm, // J-type 立即数

input i_STORE, // STORE 指令
input [ 31: 0 ] i_S_imm, // s-type 立即数
//===============================================================================
output o_J_vld, // 跳转有效
output [ 31: 0 ] o_J_PC, // 跳转到新的PC
//output addition result to LOAD/STORE unit
output [ 31: 0 ] o_D_PC, // 跳转 memroy 访存 的 PC

output o_rd_wen, // 写回 enable
output [ 4: 0 ] o_wb_rd_idx, // 写回 rd 寄存器
output reg [ 31: 0 ] o_wb_data// 写回 数据值

);

//===============================================================================
//reg [31:0] wb_data_opimm;
//reg [31:0] wb_data_op;
//reg [ 31: 0 ] wb_data_LUI;
//reg [ 31: 0 ] wb_data_AUIPC;

wire [ 31: 0 ] opd1 = ( i_AUIPC | i_JAL ) ? i_PC : i_rs1_val;

wire [ 31: 0 ] opd2 = ( { 32{ i_OP_IMM | i_JALR | i_LOAD} } & i_I_imm ) |
( { 32{ i_OP } } & i_rs2_val ) |
( { 32{ i_JAL } } & i_J_imm ) |
( { 32{ i_AUIPC } } & i_U_imm ) |
( { 32{ i_STORE } } & i_S_imm ) ; //the second operand;

// rv32i_slti rv32i_slt
wire [ 33: 0 ] ext_opd1 = ({i_opimm_instr[1],i_op_instr[1]} != 0) ? {opd1[31],opd1[31],opd1} : {2'b00, opd1};
wire [ 33: 0 ] ext_opd2 = ({i_opimm_instr[1],i_op_instr[1]} != 0) ? {opd2[31],opd2[31],opd2} : {2'b00, opd2};

wire [ 33: 0 ] comp_opd2 = ~ext_opd2 + 32'b1; //2's complement

// sub sltiu, slti sltu, slt
wire [ 33: 0 ] comp_ext_opd2 = ({i_op_instr[8], i_opimm_instr[2:1],i_op_instr[2:1]} != 0) ? comp_opd2 : ext_opd2;

wire [ 33: 0 ] add_res = ext_opd1 + comp_ext_opd2;

wire [ 31: 0 ] xor_res = i_rs1_val ^ opd2;
wire [ 31: 0 ] or_res = i_rs1_val | opd2;
wire [ 31: 0 ] and_res = i_rs1_val & opd2;
wire [ 31: 0 ] sll_res = i_rs1_val << opd2[ 4: 0 ];
wire [ 31: 0 ] srl_res = i_rs1_val >> opd2[ 4: 0 ];
//===============================================================================
// shift right arithmetic immediate x[rd] = ( x[rs1] >>s shamt)
wire [ 31: 0 ] eff_mask = ( ~( 32'b0 ) ) >> opd2[ 4: 0 ];
wire [ 31: 0 ] sra_res = ( srl_res & eff_mask ) | ( { 32{ i_rs1_val[ 31 ] } } & ( ~eff_mask ) );

//wire [ 31: 0 ] sra_res = i_rs1_val >>> opd2[ 4: 0 ];

//===============================================================================
always@( * )
begin
o_wb_data <= 32'b0;
/*
assign o_opimm_instr = { rv32i_srai, rv32i_srli, rv32i_slli,
rv32i_andi, rv32i_ori, rv32i_xori,
rv32i_sltiu, rv32i_slti, rv32i_addi };
*/
if ( i_OP_IMM )
case ( i_opimm_instr ) //one hot decoder and execute
9'h001: //rv32i_addi
o_wb_data <= add_res[31:0];
9'h002: //rv32i_slti
o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
9'h004: //rv32i_sltiu
o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
9'h008: //xori
o_wb_data <= xor_res;
9'h010: //rv32i_ori
o_wb_data <= or_res;
9'h020: //rv32i_andi
o_wb_data <= and_res;
9'h040: //rv32i_slli
o_wb_data <= sll_res;
9'h080: //rv32i_srli
o_wb_data <= srl_res;
9'h100: //rv32i_srai
o_wb_data <= sra_res;
default: ;
endcase

/*
assign o_op_instr = { rv32i_sra, rv32i_sub, rv32i_srl,
rv32i_sll, rv32i_xor, rv32i_or,
rv32i_and, rv32i_sltu, rv32i_slt,
rv32i_add };
*/
if ( i_OP )
case ( i_op_instr ) //one hot decoder and execute
10'h001: //rv32i_add
o_wb_data <= add_res[31:0];
10'h002: //rv32i_slt
o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
10'h004: //rv32i_sltu
o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
10'h008: //rv32i_and
o_wb_data <= and_res;
10'h010: //rv32i_or
o_wb_data <= or_res;
10'h020: //rv32i_xor
o_wb_data <= xor_res;
10'h040: //rv32i_sll
o_wb_data <= sll_res;
10'h080: //rv32i_srl
o_wb_data <= srl_res;
10'h100: //rv32i_sub
o_wb_data <= add_res[31:0];
10'h200: //rv32i_sra
o_wb_data <= sra_res;
default: ;
endcase

if ( i_LUI )
o_wb_data <= i_U_imm;

if ( i_AUIPC )
o_wb_data <= add_res[31:0];


if ( i_JAL | i_JALR )
o_wb_data <= i_PC + 4;

end
//===============================================================================

// wire rv32i_sub =o_op_instr[8];
// wire rv32i_sra =o_op_instr[9];

assign o_wb_rd_idx = i_rd_idx;
assign o_rd_wen = i_OP_IMM | i_OP | i_LUI | i_AUIPC | i_JAL | i_JALR;

assign o_D_PC = add_res[31:0];
assign o_J_PC = add_res[31:0];

assign o_J_vld = i_JAL | i_JALR;
//===============================================================================

endmodule



端口描述:

input sys_clk,                   // 系统时钟
input rst_n,


input [ 4: 0 ] i_rd_idx, // 汇编指令中rd 所指定的32个通用寄存器中的一个
input [ 31: 0 ] i_rs1_val, // 汇编指令中rs1 指定的寄存器 值

input [ 31: 0 ] i_PC, // 当前正在执行的这条指令的PC

input i_OP_IMM, // op_imm 指令组
input i_LOAD, // load 指令组
input [ 8: 0 ] i_opimm_instr, // op_imm 指令组 成员:SRAI, SRLI, SLLI, ANDI, ORI, XORI, SLTIU, SLTI, ADDI
input [ 31: 0 ] i_I_imm, // I-type 立即数

input i_OP, // op 指令组
input [ 9: 0 ] i_op_instr, // op 指令组 成员:SRA, SUB, SRL, SLL, XOR, OR, AND, SLTU, SLT, ADD
input [ 31: 0 ] i_rs2_val, // 汇编指令中rs2 指定的寄存器 值

input i_LUI, // LUI 指令
input i_AUIPC, // AUIPC 指令
input [ 31: 0 ] i_U_imm, // U-type 立即数

input i_JAL, // JAL 指令
input i_JALR, // JALR 指令
input [ 31: 0 ] i_J_imm, // J-type 立即数

input i_STORE, // STORE 指令
input [ 31: 0 ] i_S_imm, // s-type 立即数
//===============================================================================
output o_J_vld, // 跳转有效
output [ 31: 0 ] o_J_PC, // 跳转到新的PC
//output addition result to LOAD/STORE unit
output [ 31: 0 ] o_D_PC, // 跳转 memroy 访存 的 PC

output o_rd_wen, // 写回 enable
output [ 4: 0 ] o_wb_rd_idx, // 写回 rd 寄存器
output reg [ 31: 0 ] o_wb_data // 写回 数据值

 

所有RISC-V 指令的操作(执行),都是小于等于两个操作数的。 (大于两个操作数的指令,一直在riscv 标准中进行讨论,目前没有实施。)

举例:

nop 指令, addi x0, x0, 0 两个操作数

load 指令, lw rd, offset(rs1) 两个操作数

jal 指令, jal rd, offset  x[rd] = pc + 4;  pc = pc + sext(offset), 只有一个操作数,

addi 指令, addi rd, rs1, imm x[rd] = x[rs1] + imm  只有两个操作数。

所以在ALU 操作中:最多只有两个操作数。

wire [ 31: 0 ] opd1 = ( i_AUIPC | i_JAL ) ? i_PC : i_rs1_val;

wire [ 31: 0 ] opd2 = ( { 32{ i_OP_IMM | i_JALR | i_LOAD} } & i_I_imm ) |
                                      ( { 32{ i_OP } } & i_rs2_val ) |
                                      ( { 32{ i_JAL } } & i_J_imm ) |
                                      ( { 32{ i_AUIPC } } & i_U_imm ) |
                                      ( { 32{ i_STORE } } & i_S_imm ) ; //the second operand;

在opd1 (操作数1) 中 , 整理一下RISC-V 指令集,我们会发现 只有 在 (AUIPC , JAL) 时, 会使用PC, 其他情况(其他指令) 只能是rs1 的值。

在opd2 (操作数2)中 :

当指令为 OP_IMM,指令组, JALR,  LOAD 指令组时, 会使用 I_imm;  

当指令为 OP指令组,会使用 rs2_val;  

当指令为 JAL指令,   会使用 J_imm;  

当指令为 AUIPC指令,会使用 U_imm;  

当指令为 STORE指令组,会使用 S_imm;  

这些指令在某个具体时钟下,只会有一种指令被译码,不会出现多个指令同时有效的情况。

 

整理操作数 1,2:

//                                                    rv32i_slti               rv32i_slt
wire [ 33: 0 ] ext_opd1 = ({i_opimm_instr[1],i_op_instr[1]} != 0) ? {opd1[31],opd1[31],opd1} : {2’b00, opd1};
wire [ 33: 0 ] ext_opd2 = ({i_opimm_instr[1],i_op_instr[1]} != 0) ? {opd2[31],opd2[31],opd2} : {2’b00, opd2};

将操作数扩展为34位,即32位的操作数 + 进位位 + 符号位。slti, slt 需要进行符号扩展, 其他指令 都认为是正数。

 

负数操作:

wire [ 33: 0 ] comp_opd2 = ~ext_opd2 + 32’b1; //2’s complement

将操作数2 变为负数, 相当于  comp_opd2 =ext_opd2;

 

//                                                           rv32i_sub            rv32i_sltiu, rv32i_slti      rv32i_sltu, rv32i_slt
wire [ 33: 0 ] comp_ext_opd2 = ({i_op_instr[8],     i_opimm_instr[2:1],        i_op_instr[2:1]} != 0) ? comp_opd2 : ext_opd2;

如果当前指令是 sub, sltiu, slti, sltu, slt 时 ,需要 操作数 变为负数, 其他指令不需要。

 

加法操作:

wire [ 33: 0 ] add_res = ext_opd1 + comp_ext_opd2;    

异或操作:

wire [ 31: 0 ] xor_res = i_rs1_val ^ opd2;

或操作:

wire [ 31: 0 ] or_res = i_rs1_val | opd2;

与操作:

wire [ 31: 0 ] and_res = i_rs1_val & opd2;

逻辑左移操作:

wire [ 31: 0 ] sll_res = i_rs1_val << opd2[ 4: 0 ];

逻辑右移操作:

wire [ 31: 0 ] srl_res = i_rs1_val >> opd2[ 4: 0 ];

算数右移操作:

wire [ 31: 0 ] eff_mask = ( ~( 32’b0 ) ) >> opd2[ 4: 0 ];
wire [ 31: 0 ] sra_res = ( srl_res & eff_mask ) | ( { 32{ i_rs1_val[ 31 ] } } & ( ~eff_mask ) );

相当于 sra_res = i_rs1_val >>> opd2[4:0];

 

assign o_rd_wen = i_OP_IMM | i_OP | i_LUI | i_AUIPC | i_JAL | i_JALR;

这些指令需要写回32个通用寄存器。

 

assign o_J_vld = i_JAL | i_JALR;

当为 JAL , JALR 指令时, J_vld 需要enable

Posted in FPGA, FPGA, IC, RISC-V, RISC-V IPcore设计, RISC-V 教案, 教材与教案, 文章

发表评论

相关链接