- cpu21_riscv_alu.v:新增 ALU_PASSB(4'd15),直接把 B 端送到 result;
- cpu21_riscv_decoder.v:新增 OP_LUI(IR[6:2]=0x0d,即标准 RISC-V opcode 0x37),
imm = {IR[31:12], 12'b0},用 ALU_SRC 选择立即数、ALU 直通输出写回 rd,
不读源寄存器(uses_rs1/rs2 保持 0);
- 说明文档补充 lui 的功能说明与编码表行。
验证(xsim + tb_no_intr,自编的小汇编器生成测试程序):
依次输出 12345000 / fffff000 / 80000000,以及 lui+addi 组合得到 12345678,
随后 a7=10 停机(共 21 周期,halted=1),无编译错误与警告。
63 lines
1.9 KiB
Verilog
63 lines
1.9 KiB
Verilog
`timescale 1ns / 1ps
|
|
`default_nettype none
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// ALU:纯组合运算单元。
|
|
//
|
|
// result 为主结果;result2 为第二结果,用于 MUL 的高 32 位与 DIVU 的余数。
|
|
// 除法做了除零保护:b==0 时商为 32'hffff_ffff、余数为被除数。
|
|
// -----------------------------------------------------------------------------
|
|
module cpu21_riscv_alu (
|
|
input wire [ 3:0] op,
|
|
input wire [31:0] a,
|
|
input wire [31:0] b,
|
|
output reg [31:0] result,
|
|
output reg [31:0] result2
|
|
);
|
|
localparam ALU_SLL = 4'd0;
|
|
localparam ALU_SRA = 4'd1;
|
|
localparam ALU_SRL = 4'd2;
|
|
localparam ALU_MUL = 4'd3;
|
|
localparam ALU_DIVU = 4'd4;
|
|
localparam ALU_ADD = 4'd5;
|
|
localparam ALU_SUB = 4'd6;
|
|
localparam ALU_AND = 4'd7;
|
|
localparam ALU_OR = 4'd8;
|
|
localparam ALU_XOR = 4'd9;
|
|
localparam ALU_NOR = 4'd10;
|
|
localparam ALU_SLT = 4'd11;
|
|
localparam ALU_SLTU = 4'd12;
|
|
localparam ALU_PASSB = 4'd15; // 直接输出 B 端(供 LUI 使用)
|
|
reg [63:0] mult_result;
|
|
|
|
always @* begin
|
|
mult_result = a * b;
|
|
result2 = 32'b0;
|
|
case (op)
|
|
ALU_SLL: result = a << b[4:0];
|
|
ALU_SRA: result = $signed(a) >>> b[4:0];
|
|
ALU_SRL: result = a >> b[4:0];
|
|
ALU_MUL: begin
|
|
result = mult_result[31:0];
|
|
result2 = mult_result[63:32];
|
|
end
|
|
ALU_DIVU: begin
|
|
result = (b == 32'b0) ? 32'hffff_ffff : a / b;
|
|
result2 = (b == 32'b0) ? a : a % b;
|
|
end
|
|
ALU_ADD: result = a + b;
|
|
ALU_SUB: result = a - b;
|
|
ALU_AND: result = a & b;
|
|
ALU_OR: result = a | b;
|
|
ALU_XOR: result = a ^ b;
|
|
ALU_NOR: result = ~(a | b);
|
|
ALU_SLT: result = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0;
|
|
ALU_SLTU: result = (a < b) ? 32'd1 : 32'd0;
|
|
ALU_PASSB: result = b;
|
|
default: result = 32'b0;
|
|
endcase
|
|
end
|
|
endmodule
|
|
|
|
`default_nettype wire
|