Rong晔大佬教程学习(5):ALU单元设计
之前我们提到,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