Rong晔大佬教程学习(5):ALU单元设计

发布时间 2023-12-14 11:09:35作者: 郭若晨是我的rbq

  之前我们提到,alu一共有两个输入,通过前几章的指令部分,我们可以发现有以下四种情况:

   注意:虽然上面有四种情况,但由于本项目的U型指令只实现了lui,也就是立即数左移12位,所以把这一类归为寄存器和立即数,而不是第四种立即数和pc,这个是auipc指令实现的功能。所以下面代码中一共只有三种情况。

1.mux_alu.v

`include "rvseed_defines.v"
//使用一个多路选择器实现alu输入数据源的选择(一共有三种情况)
module mux_alu ( 
    input      [`ALU_SRC_WIDTH-1:0] alu_src_sel,// reg or imm to alu,由ctrl提供

    input      [`CPU_WIDTH-1:0]     reg1_rdata, // register 1 read data
    input      [`CPU_WIDTH-1:0]     reg2_rdata, // register 2 read data
    input      [`CPU_WIDTH-1:0]     imm,        // immediate
    input      [`CPU_WIDTH-1:0]     curr_pc,    // current pc addr

    output reg [`CPU_WIDTH-1:0]     alu_src1,   // alu source 1
    output reg [`CPU_WIDTH-1:0]     alu_src2    // alu source 2
);

always @(*) begin
    alu_src1 = reg1_rdata;     // defalut select reg1 data
    alu_src2 = reg2_rdata;     // default select reg2 data
    case (alu_src_sel)
        `ALU_SRC_REG: 
            alu_src2 = reg2_rdata; // select reg2 data
        `ALU_SRC_IMM: 
            alu_src2 = imm;        // select immediate 
        `ALU_SRC_FOUR_PC: begin
            alu_src1 = `CPU_WIDTH'h4; // pc + 4 
            alu_src2 = curr_pc;       //
        end
    endcase
end
endmodule

  上面的alu_src_sel的case判断只有三种情况,原因上面橙色部分已经给出

 

2.alu.v(运算模块)

   运算模块的设计非常简单,因为只有加法和减法两个运算:

`include "rvseed_defines.v"

module alu(
    input      [`ALU_OP_WIDTH-1:0] alu_op,   // alu opcode
    input      [`CPU_WIDTH-1:0]    alu_src1, // alu source 1
    input      [`CPU_WIDTH-1:0]    alu_src2, // alu source 2
    output reg                     zero,     // alu result is zero
    output reg [`CPU_WIDTH-1:0]    alu_res   // alu result
);

//运算结果设计标志位zero是否为0
//当前运算中只有一个加法运算和一个减法运算,且加法为半加器,不考虑逸出
//且这里不考虑有符号无符号问题,其他指令会涉及,此处不涉及
always @(*) begin
    zero = 1'b0;
    alu_res = `CPU_WIDTH'b0;            //运算结果为32位
    case (alu_op)
        `ALU_ADD: 
            alu_res = alu_src1 + alu_src2;
        `ALU_SUB:begin 
            alu_res = alu_src1 - alu_src2;
            zero = (alu_res == `CPU_WIDTH'b0) ? 1'b1 : 1'b0;//运算结果为0,标志信号会被拉高
        end
    endcase
end
endmodule

  (仔细想了一下,这么看下来这个riscv实现的指令肯定比第一章说的那那五个要多,但肯定也没多多少呵呵呵,比如R型指令应该中实现了一个add和一个sub,J型指令只实现了一个jal,U型指令只实现了一个lui,但其实细细想一下其实其他指令的实现也不难,只是需要考虑的更多,实现的运算也更多)

 

`include "rvseed_defines.v"

module alu(
    input      [`ALU_OP_WIDTH-1:0] alu_op,   // alu opcode
    input      [`CPU_WIDTH-1:0]    alu_src1, // alu source 1
    input      [`CPU_WIDTH-1:0]    alu_src2, // alu source 2
    output reg                     zero,     // alu result is zero
    output reg [`CPU_WIDTH-1:0]    alu_res   // alu result
);

//运算结果设计标志位zero是否为0
//当前运算中只有一个加法运算和一个减法运算,且加法为半加器,不考虑逸出
//且这里不考虑有符号无符号问题,其他指令会涉及,此处不涉及
always @(*) begin
    zero = 1'b0;
    alu_res = `CPU_WIDTH'b0;            //运算结果为32位
    case (alu_op)
        `ALU_ADD:
            alu_res = alu_src1 + alu_src2;
        `ALU_SUB:begin
            alu_res = alu_src1 - alu_src2;
            zero = (alu_res == `CPU_WIDTH'b0) ? 1'b1 : 1'b0;//运算结果为0,标志信号会被拉高
        end
    endcase
end
endmodule