From 80d07e4115cd15720471b4e89da65209aed8a76b Mon Sep 17 00:00:00 2001 From: MyDariell Date: Sun, 21 Jun 2026 18:55:45 -0700 Subject: Implement ADD and SUB operations in sliced ALU Signed-off-by: Dariell Sugiaman --- rtl/alu.sv | 25 +++++-- tb/alu/alu_add_sub_tb.sv | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 6 deletions(-) create mode 100644 tb/alu/alu_add_sub_tb.sv diff --git a/rtl/alu.sv b/rtl/alu.sv index f5dde19..8805bf9 100644 --- a/rtl/alu.sv +++ b/rtl/alu.sv @@ -24,22 +24,35 @@ module alu /* We used Karnaugh maps here. If the encoding for fu_op_t changes, this has to be updated. */ assign negate_b = alu_op_i[3] || (!alu_op_i[2] && alu_op_i[1]); + /* Carry signals. */ + logic carry, carry_d, carry_q; + /* Adder width is SLICE_WIDTH + 1 to account for carry-out. */ logic [SLICE_WIDTH:0] adder_a, adder_b, adder_result; - assign adder_a = {1'b0, a_i}; - assign adder_b = {1'b0, (b_i ^ {SLICE_WIDTH{negate_b}})}; + /* For the first slice, carry-in must be driven to zero for addition + * and one for subtraction or comparisons. All other slices use the + * carry-out from the previous slice */ - /* Carry signals. */ - logic carry, carry_d, carry_q; + assign carry = (count_i == '0) ? negate_b : carry_q; - assign carry = (count_i == '0) ? negate_b : carry_q; + /* ADD, SUB, SLT, SLTU use the same adder to computer their results*/ + + assign adder_a = {1'b0, a_i}; + assign adder_b = {1'b0, (b_i ^ {SLICE_WIDTH{negate_b}})}; + assign adder_result = adder_a + adder_b + (SLICE_WIDTH + 1)'(carry); + assign carry_o = adder_result[SLICE_WIDTH]; + assign carry_d = carry_o; /* Combinational arithmetic/logic core. */ always_comb begin : alu_core + result_o = '0; + unique casez (alu_op_i[2:0]) /* ADD, SUB, SLT, SLTU. */ - 3'b0??: begin end + 3'b0??: begin + result_o = adder_result[SLICE_WIDTH-1:0]; + end /* XOR. */ 3'b100: begin end /* OR. */ diff --git a/tb/alu/alu_add_sub_tb.sv b/tb/alu/alu_add_sub_tb.sv new file mode 100644 index 0000000..aac976b --- /dev/null +++ b/tb/alu/alu_add_sub_tb.sv @@ -0,0 +1,188 @@ +/* Testbench for ALU ADD and SUB operations. + * + * Functional requirements verified: + * FR-ALU-ADD-010 to FR-ALU-ADD-040 + * FR-ALU-SUB-010 to FR-ALU-SUB-040 + * FR-ALU-CMN-010 to FR-ALU-CMN-020 + */ + +module alu_add_sub_tb; + import montreal_pkg::*; + + logic clk_i; + logic rst_ni; + fu_op_t alu_op_i; + logic [1:0] count_i; + slice_t a_i, b_i; + slice_t result_o; + logic carry_o; + + alu dut ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .alu_op_i (alu_op_i), + .count_i (count_i), + .a_i (a_i), + .b_i (b_i), + .result_o (result_o), + .carry_o (carry_o) + ); + + /* 10ns clock period. */ + initial clk_i = 0; + always #5 clk_i = ~clk_i; + + int pass_count; + int fail_count; + + /* Run one 32-bit ADD or SUB operation across 4 slices. + * Inputs are presented on negedge so they are stable at the following posedge, + * where carry_q updates. Result is sampled before the posedge. */ + task automatic run_op( + input fu_op_t op, + input logic [31:0] a, + input logic [31:0] b, + output logic [31:0] result, + output logic final_carry + ); + for (int i = 0; i < 4; i++) begin + @(negedge clk_i); + alu_op_i = op; + count_i = i[1:0]; + a_i = a[i*8 +: 8]; + b_i = b[i*8 +: 8]; + + #1; /* Wait for combinational outputs to settle. */ + result[i*8 +: 8] = result_o; + if (i == 3) final_carry = carry_o; + + @(posedge clk_i); /* Clock edge registers carry_d into carry_q for next slice. */ + end + endtask + + /* Check result and print pass/fail. */ + task automatic check( + input string name, + input logic [31:0] a, + input logic [31:0] b, + input logic [31:0] got, + input logic [31:0] expected + ); + if (got === expected) begin + $display(" PASS %s: %h op %h = %h", name, a, b, got); + pass_count++; + end else begin + $display(" FAIL %s: %h op %h = %h (expected %h)", name, a, b, got, expected); + fail_count++; + end + endtask + + task automatic check_carry( + input string name, + input logic [31:0] a, + input logic [31:0] b, + input logic got, + input logic expected + ); + if (got === expected) begin + $display(" PASS %s carry: %h op %h -> carry_o = %b", name, a, b, got); + pass_count++; + end else begin + $display(" FAIL %s carry: %h op %h -> carry_o = %b (expected %b)", + name, a, b, got, expected); + fail_count++; + end + endtask + + task automatic test_add(input logic [31:0] a, input logic [31:0] b); + logic [31:0] result; + logic final_carry; + run_op(ADD, a, b, result, final_carry); + check("ADD", a, b, result, a + b); + endtask + + task automatic test_sub(input logic [31:0] a, input logic [31:0] b); + logic [31:0] result; + logic final_carry; + run_op(SUB, a, b, result, final_carry); + check("SUB", a, b, result, a - b); + endtask + + task automatic test_add_carry( + input logic [31:0] a, input logic [31:0] b, input logic expected_carry + ); + logic [31:0] result; + logic final_carry; + run_op(ADD, a, b, result, final_carry); + check("ADD", a, b, result, a + b); + check_carry("ADD", a, b, final_carry, expected_carry); + endtask + + task automatic test_sub_carry( + input logic [31:0] a, input logic [31:0] b, input logic expected_carry + ); + logic [31:0] result; + logic final_carry; + run_op(SUB, a, b, result, final_carry); + check("SUB", a, b, result, a - b); + check_carry("SUB", a, b, final_carry, expected_carry); + endtask + + initial begin + pass_count = 0; + fail_count = 0; + + /* FR-ALU-CMN-020: reset clears carry_q. */ + rst_ni = 0; + alu_op_i = ADD; + count_i = 0; + a_i = 0; + b_i = 0; + @(posedge clk_i); + @(posedge clk_i); + rst_ni = 1; + + $display("=== ADD tests ==="); + + /* FR-ALU-ADD-010/020: basic add, no carry. */ + test_add(32'h0000_0001, 32'h0000_0001); // 1 + 1 = 2 + + /* FR-ALU-ADD-030: carry propagates from slice 0 to slice 1. */ + test_add(32'h0000_00FF, 32'h0000_0001); // 255 + 1 = 256 + + /* FR-ALU-ADD-030: carry propagates across all 4 slices. */ + test_add(32'hFFFF_FFFF, 32'h0000_0001); // -1 + 1 = 0 (wraps) + + /* FR-ALU-ADD-040: carry-out on final slice (overflow cases). */ + test_add_carry(32'hFFFF_FFFF, 32'hFFFF_FFFF, 1'b1); // max + max → overflow + test_add_carry(32'hFFFF_FFFF, 32'h0000_0001, 1'b1); // -1 + 1 = 0 → overflow + test_add_carry(32'h0000_0001, 32'h0000_0001, 1'b0); // no overflow + + /* FR-ALU-CMN-010: multi-byte result assembled correctly. */ + test_add(32'h1234_5678, 32'h8765_4321); // mixed bytes + + test_add(32'h0000_0000, 32'h0000_0000); // 0 + 0 = 0 + + $display("\n=== SUB tests ==="); + + /* FR-ALU-SUB-010/020: basic sub, no borrow. */ + test_sub(32'h0000_0005, 32'h0000_0003); // 5 - 3 = 2 + + /* FR-ALU-SUB-030: borrow propagates from slice 0 to slice 1. */ + test_sub(32'h0000_0100, 32'h0000_0001); // 256 - 1 = 255 + + /* FR-ALU-SUB-030: borrow propagates across all 4 slices. */ + test_sub_carry(32'h0000_0000, 32'h0000_0001, 1'b0); // 0 - 1 wraps, carry_o = 0 (borrow) + test_sub_carry(32'h0000_0005, 32'h0000_0003, 1'b1); // 5 - 3, no borrow → carry_o = 1 + + /* FR-ALU-CMN-010: multi-byte result assembled correctly. */ + test_sub(32'hFFFF_FFFF, 32'h0000_0001); // max - 1 + + test_sub(32'h0000_0000, 32'h0000_0000); // 0 - 0 = 0 + + $display("\n=== Results: %0d passed, %0d failed ===", pass_count, fail_count); + + $finish; + end + +endmodule : alu_add_sub_tb -- cgit v1.2.3 From 4381dbcfccee329aed76158ea62a62a9b221bdd8 Mon Sep 17 00:00:00 2001 From: MyDariell Date: Mon, 22 Jun 2026 19:52:30 -0700 Subject: Implement XOR, OR, AND logical operations in sliced ALU Signed-off-by: MyDariell --- rtl/alu.sv | 15 ++++- tb/alu/alu_logic_tb.sv | 151 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 tb/alu/alu_logic_tb.sv diff --git a/rtl/alu.sv b/rtl/alu.sv index 8805bf9..30645c1 100644 --- a/rtl/alu.sv +++ b/rtl/alu.sv @@ -54,11 +54,20 @@ module alu result_o = adder_result[SLICE_WIDTH-1:0]; end /* XOR. */ - 3'b100: begin end + 3'b100: begin + result_o = a_i ^ b_i; + carry_d = 0; + end /* OR. */ - 3'b110: begin end + 3'b110: begin + result_o = a_i | b_i; + carry_d = 0; + end /* AND. */ - 3'b111: begin end + 3'b111: begin + result_o = a_i & b_i; + carry_d = 0; + end default: begin end endcase end : alu_core diff --git a/tb/alu/alu_logic_tb.sv b/tb/alu/alu_logic_tb.sv new file mode 100644 index 0000000..6533132 --- /dev/null +++ b/tb/alu/alu_logic_tb.sv @@ -0,0 +1,151 @@ +/* Testbench for ALU XOR, OR, AND operations. + * + * Logical operations are purely combinational per slice — no carry propagation. + * carry_o MUST be 0 on every slice for all logical operations. + */ + +module alu_logic_tb; + import montreal_pkg::*; + + logic clk_i; + logic rst_ni; + fu_op_t alu_op_i; + logic [1:0] count_i; + slice_t a_i, b_i; + slice_t result_o; + logic carry_o; + + alu dut ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .alu_op_i (alu_op_i), + .count_i (count_i), + .a_i (a_i), + .b_i (b_i), + .result_o (result_o), + .carry_o (carry_o) + ); + + initial clk_i = 0; + always #5 clk_i = ~clk_i; + + int pass_count; + int fail_count; + + task automatic run_op( + input fu_op_t op, + input logic [31:0] a, + input logic [31:0] b, + output logic [31:0] result, + output logic final_carry + ); + for (int i = 0; i < 4; i++) begin + @(negedge clk_i); + alu_op_i = op; + count_i = i[1:0]; + a_i = a[i*8 +: 8]; + b_i = b[i*8 +: 8]; + + #1; + result[i*8 +: 8] = result_o; + if (i == 3) final_carry = carry_o; + + @(posedge clk_i); + end + endtask + + task automatic check( + input string name, + input logic [31:0] a, + input logic [31:0] b, + input logic [31:0] got, + input logic [31:0] expected + ); + if (got === expected) begin + $display(" PASS %s: %h op %h = %h", name, a, b, got); + pass_count++; + end else begin + $display(" FAIL %s: %h op %h = %h (expected %h)", name, a, b, got, expected); + fail_count++; + end + endtask + + task automatic check_carry( + input string name, + input logic [31:0] a, + input logic [31:0] b, + input logic got + ); + if (got === 1'b0) begin + $display(" PASS %s carry: %h op %h -> carry_o = 0", name, a, b); + pass_count++; + end else begin + $display(" FAIL %s carry: %h op %h -> carry_o = %b (expected 0)", name, a, b, got); + fail_count++; + end + endtask + + task automatic test_xor(input logic [31:0] a, input logic [31:0] b); + logic [31:0] result; + logic final_carry; + run_op(XOR, a, b, result, final_carry); + check("XOR", a, b, result, a ^ b); + check_carry("XOR", a, b, final_carry); + endtask + + task automatic test_or(input logic [31:0] a, input logic [31:0] b); + logic [31:0] result; + logic final_carry; + run_op(OR, a, b, result, final_carry); + check("OR", a, b, result, a | b); + check_carry("OR", a, b, final_carry); + endtask + + task automatic test_and(input logic [31:0] a, input logic [31:0] b); + logic [31:0] result; + logic final_carry; + run_op(AND, a, b, result, final_carry); + check("AND", a, b, result, a & b); + check_carry("AND", a, b, final_carry); + endtask + + initial begin + pass_count = 0; + fail_count = 0; + + rst_ni = 0; + alu_op_i = XOR; + count_i = 0; + a_i = 0; + b_i = 0; + @(posedge clk_i); + @(posedge clk_i); + rst_ni = 1; + + $display("=== XOR tests ==="); + test_xor(32'hFFFF_FFFF, 32'hFFFF_FFFF); // all 1s XOR all 1s = 0 + test_xor(32'hFFFF_FFFF, 32'h0000_0000); // all 1s XOR 0 = all 1s + test_xor(32'hAAAA_AAAA, 32'h5555_5555); // alternating bits + test_xor(32'h1234_5678, 32'h8765_4321); // mixed + test_xor(32'h0000_0000, 32'h0000_0000); // 0 XOR 0 = 0 + + $display("\n=== OR tests ==="); + test_or(32'hFFFF_FFFF, 32'h0000_0000); // all 1s OR 0 = all 1s + test_or(32'h0000_0000, 32'h0000_0000); // 0 OR 0 = 0 + test_or(32'hAAAA_AAAA, 32'h5555_5555); // alternating → all 1s + test_or(32'h1234_5678, 32'h8765_4321); // mixed + test_or(32'hFFFF_FFFF, 32'hFFFF_FFFF); // all 1s OR all 1s = all 1s + + $display("\n=== AND tests ==="); + test_and(32'hFFFF_FFFF, 32'hFFFF_FFFF); // all 1s AND all 1s = all 1s + test_and(32'hFFFF_FFFF, 32'h0000_0000); // all 1s AND 0 = 0 + test_and(32'hAAAA_AAAA, 32'h5555_5555); // alternating → 0 + test_and(32'h1234_5678, 32'h8765_4321); // mixed + test_and(32'h0000_0000, 32'h0000_0000); // 0 AND 0 = 0 + + $display("\n=== Results: %0d passed, %0d failed ===", pass_count, fail_count); + + $finish; + end + +endmodule : alu_logic_tb -- cgit v1.2.3 From 7643e8f7afc960429dac33471dbcd56f1416a85f Mon Sep 17 00:00:00 2001 From: MyDariell Date: Mon, 22 Jun 2026 19:55:02 -0700 Subject: Clean up ALU before merge Co-authored-by: Warrick Lo Signed-off-by: Warrick Lo --- .gitignore | 4 ++++ rtl/alu.sv | 47 ++++++++++++++++++----------------------------- rtl/montreal_pkg.sv | 28 ---------------------------- tb/alu/alu_logic_tb.sv | 21 ++------------------- 4 files changed, 24 insertions(+), 76 deletions(-) delete mode 100644 rtl/montreal_pkg.sv diff --git a/.gitignore b/.gitignore index ad3001c..fe0d8c0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ .vscode .claude/ +# iverilog build artifacts +*.vvp +obj_dir/ + # SymbiYosys output verif/formal/simple_alu/simple_alu/ verif/formal/simple_alu/simple_alu_bmc/ diff --git a/rtl/alu.sv b/rtl/alu.sv index 30645c1..2966135 100644 --- a/rtl/alu.sv +++ b/rtl/alu.sv @@ -1,6 +1,8 @@ -module alu - import montreal_pkg::*; -#( +/* SPDX-License-Identifier: CERN-OHL-P-2.0 */ + +`include "types.svh" + +module alu #( parameter int unsigned XLEN = config_pkg::XLEN, parameter int unsigned SLICE_WIDTH = config_pkg::SLICE_WIDTH, @@ -27,48 +29,35 @@ module alu /* Carry signals. */ logic carry, carry_d, carry_q; - /* Adder width is SLICE_WIDTH + 1 to account for carry-out. */ - logic [SLICE_WIDTH:0] adder_a, adder_b, adder_result; - - /* For the first slice, carry-in must be driven to zero for addition - * and one for subtraction or comparisons. All other slices use the + /* For the first slice, carry-in must be driven to zero for addition + * and one for subtraction or comparisons. All other slices use the * carry-out from the previous slice */ + assign carry = (count_i == '0) ? negate_b : carry_q; - assign carry = (count_i == '0) ? negate_b : carry_q; + /* Adder width is SLICE_WIDTH + 1 to account for carry-out. */ + logic [SLICE_WIDTH:0] adder_a, adder_b, adder_result; - /* ADD, SUB, SLT, SLTU use the same adder to computer their results*/ - assign adder_a = {1'b0, a_i}; assign adder_b = {1'b0, (b_i ^ {SLICE_WIDTH{negate_b}})}; + + /* ADD, SUB, SLT, SLTU use the same adder to computer their results*/ assign adder_result = adder_a + adder_b + (SLICE_WIDTH + 1)'(carry); + assign carry_o = adder_result[SLICE_WIDTH]; assign carry_d = carry_o; /* Combinational arithmetic/logic core. */ always_comb begin : alu_core - result_o = '0; - unique casez (alu_op_i[2:0]) /* ADD, SUB, SLT, SLTU. */ - 3'b0??: begin - result_o = adder_result[SLICE_WIDTH-1:0]; - end + 3'b0??: result_o = adder_result[SLICE_WIDTH-1:0]; /* XOR. */ - 3'b100: begin - result_o = a_i ^ b_i; - carry_d = 0; - end + 3'b100: result_o = a_i ^ b_i; /* OR. */ - 3'b110: begin - result_o = a_i | b_i; - carry_d = 0; - end + 3'b110: result_o = a_i | b_i; /* AND. */ - 3'b111: begin - result_o = a_i & b_i; - carry_d = 0; - end - default: begin end + 3'b111: result_o = a_i & b_i; + default: result_o = '0; endcase end : alu_core diff --git a/rtl/montreal_pkg.sv b/rtl/montreal_pkg.sv deleted file mode 100644 index 741a926..0000000 --- a/rtl/montreal_pkg.sv +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: CERN-OHL-P-2.0 */ - -package montreal_pkg; - - typedef logic unsigned [config_pkg::XLEN-1:0] word_t; - typedef logic unsigned [config_pkg::SLICE_WIDTH-1:0] slice_t; - - typedef enum logic [3:0] { - /* Arithmetic operations. */ - ADD = 4'b0000, - SUB = 4'b1000, - /* Logical operations. */ - XOR = 4'b0100, - OR = 4'b0110, - AND = 4'b0111, - /* Shift operations. */ - SLL = 4'b0001, - SRL = 4'b0101, - SRA = 4'b1101, - /* Conditional set operations. */ - SLT = 4'b0010, - SLTU = 4'b0011, - /* Zicond operations. */ - CZERO_EQZ = 4'b1001, - CZERO_NEZ = 4'b1011 - } fu_op_t; - -endpackage : montreal_pkg diff --git a/tb/alu/alu_logic_tb.sv b/tb/alu/alu_logic_tb.sv index 6533132..7d70bda 100644 --- a/tb/alu/alu_logic_tb.sv +++ b/tb/alu/alu_logic_tb.sv @@ -1,7 +1,8 @@ /* Testbench for ALU XOR, OR, AND operations. * * Logical operations are purely combinational per slice — no carry propagation. - * carry_o MUST be 0 on every slice for all logical operations. + * carry_o is NOT valid for logical operations and MUST NOT be used by the + * caller. It is not checked in this testbench. */ module alu_logic_tb; @@ -70,27 +71,11 @@ module alu_logic_tb; end endtask - task automatic check_carry( - input string name, - input logic [31:0] a, - input logic [31:0] b, - input logic got - ); - if (got === 1'b0) begin - $display(" PASS %s carry: %h op %h -> carry_o = 0", name, a, b); - pass_count++; - end else begin - $display(" FAIL %s carry: %h op %h -> carry_o = %b (expected 0)", name, a, b, got); - fail_count++; - end - endtask - task automatic test_xor(input logic [31:0] a, input logic [31:0] b); logic [31:0] result; logic final_carry; run_op(XOR, a, b, result, final_carry); check("XOR", a, b, result, a ^ b); - check_carry("XOR", a, b, final_carry); endtask task automatic test_or(input logic [31:0] a, input logic [31:0] b); @@ -98,7 +83,6 @@ module alu_logic_tb; logic final_carry; run_op(OR, a, b, result, final_carry); check("OR", a, b, result, a | b); - check_carry("OR", a, b, final_carry); endtask task automatic test_and(input logic [31:0] a, input logic [31:0] b); @@ -106,7 +90,6 @@ module alu_logic_tb; logic final_carry; run_op(AND, a, b, result, final_carry); check("AND", a, b, result, a & b); - check_carry("AND", a, b, final_carry); endtask initial begin -- cgit v1.2.3