diff --git a/src/ext/m/div.sv b/src/ext/m/div.sv new file mode 100644 index 00000000..ba9890f2 --- /dev/null +++ b/src/ext/m/div.sv @@ -0,0 +1 @@ +// tbd \ No newline at end of file diff --git a/src/ext/m/mul.sv b/src/ext/m/mul.sv new file mode 100644 index 00000000..18fce962 --- /dev/null +++ b/src/ext/m/mul.sv @@ -0,0 +1,35 @@ +module mul( + /* verilator lint_off UNUSEDSIGNAL */ + input clk + , input rst_n + /* verilator lint_on UNUSEDSIGNAL */ + , input start_i + , input [31:0] rs1_i + , input [31:0] rs2_i + , input [1:0] op_i + , output logic [31:0] result_o + , output logic ready_o +); + +assign ready_o = start_i; + +logic rs1_signed; +logic rs2_signed; + +assign rs1_signed = (op_i == 2'b01 || op_i == 2'b10) ? 1'b1 : 1'b0; +assign rs2_signed = (op_i == 2'b01) ? 1'b1 : 1'b0; + +logic signed [32:0] rs1_signed_ext; +logic signed [32:0] rs2_signed_ext; + + +assign rs1_signed_ext = (rs1_signed) ? {rs1_i[31], rs1_i} : {1'b0, rs1_i}; +assign rs2_signed_ext = (rs2_signed) ? {rs2_i[31], rs2_i} : {1'b0, rs2_i}; + +logic signed [63:0] product; +assign product = $signed(rs1_signed_ext) * $signed(rs2_signed_ext); + +assign result_o = (op_i == 2'b00) ? product[31:0] : product[63:32]; + + +endmodule diff --git a/test/mul_tb.sv b/test/mul_tb.sv new file mode 100644 index 00000000..d1a12970 --- /dev/null +++ b/test/mul_tb.sv @@ -0,0 +1,75 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" + +module mul_tb; + + logic clk; + logic rst_n; + logic start_i; + logic [31:0] rs1_i; + logic [31:0] rs2_i; + logic [1:0] op_i; + logic [31:0] result_o; + logic ready_o; + + localparam logic [1:0] MUL = 2'b00; + localparam logic [1:0] MULH = 2'b01; + localparam logic [1:0] MULHSU = 2'b10; + localparam logic [1:0] MULHU = 2'b11; + + mul uut + ( .clk ( clk ) + , .rst_n ( rst_n ) + , .start_i ( start_i ) + , .rs1_i ( rs1_i ) + , .rs2_i ( rs2_i ) + , .op_i ( op_i ) + , .result_o ( result_o ) + , .ready_o ( ready_o ) + ); + + initial begin + clk = 0; rst_n = 1; start_i = 1; + + // ready_o is combinational, asserted same cycle as start_i + #1; + `assert_equal(ready_o, 1'b1) + + // --- MUL: low 32 bits (sign-agnostic) --- + // 7 * 6 = 42 + op_i = MUL; rs1_i = 32'd7; rs2_i = 32'd6; #1; + `assert_equal(result_o, 32'h0000_002A) + + // 0x10000 * 0x10000 = 0x1_0000_0000 -> low 32 = 0 + op_i = MUL; rs1_i = 32'h0001_0000; rs2_i = 32'h0001_0000; #1; + `assert_equal(result_o, 32'h0000_0000) + + // --- MULH: high 32, signed x signed --- + // 2^30 * 2^30 = 2^60 = 0x1000_0000_0000_0000 -> high = 0x1000_0000 + op_i = MULH; rs1_i = 32'h4000_0000; rs2_i = 32'h4000_0000; #1; + `assert_equal(result_o, 32'h1000_0000) + + // (-1) * 2 = -2 = 0xFFFF_FFFF_FFFF_FFFE -> high = 0xFFFF_FFFF + op_i = MULH; rs1_i = 32'hFFFF_FFFF; rs2_i = 32'd2; #1; + `assert_equal(result_o, 32'hFFFF_FFFF) + + // --- The three-way sign test: same bits, three ops, three results --- + // MULH: (-1) * (-1) = 1 -> high = 0x0000_0000 + op_i = MULH; rs1_i = 32'hFFFF_FFFF; rs2_i = 32'hFFFF_FFFF; #1; + `assert_equal(result_o, 32'h0000_0000) + + // MULHU: 0xFFFFFFFF * 0xFFFFFFFF = 0xFFFF_FFFE_0000_0001 -> high = 0xFFFF_FFFE + op_i = MULHU; rs1_i = 32'hFFFF_FFFF; rs2_i = 32'hFFFF_FFFF; #1; + `assert_equal(result_o, 32'hFFFF_FFFE) + + // MULHSU: (-1) * 4294967295 = -4294967295 = 0xFFFF_FFFF_0000_0001 -> high = 0xFFFF_FFFF + op_i = MULHSU; rs1_i = 32'hFFFF_FFFF; rs2_i = 32'hFFFF_FFFF; #1; + `assert_equal(result_o, 32'hFFFF_FFFF) + + $display("All mul_tb tests passed"); + end + + `SETUP_VCD_DUMP(mul_tb) + +endmodule