diff --git a/.gitignore b/.gitignore index f5cc6bed..6ec7c7d2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ out/** !out/.gitkeep build/** !build/.gitkeep +.DS_Store diff --git a/src/ext/b/decoder.sv b/src/ext/b/decoder.sv index 8e32e10b..0de6c880 100644 --- a/src/ext/b/decoder.sv +++ b/src/ext/b/decoder.sv @@ -4,25 +4,28 @@ /* verilator lint_off DECLFILENAME */ module ext__b__decoder /* verilator lint_on DECLFILENAME */ - ( input [2:0] funct3 - , input [6:0] funct7 - , input opcode_t opcode + ( input [2:0] funct3 + , input [6:0] funct7 + , input opcode_t opcode /* verilator lint_off UNUSEDSIGNAL */ - , input reg_t rd + , input reg_t rd /* verilator lint_on UNUSEDSIGNAL */ - , input reg_t rs2 + , input reg_t rs2 , output ext__b__types::b_alu_control_t b_alu_control ); import ext__b__types::*; - localparam bit [6:0] FUNCT7_ZBA = 7'b0010000; - localparam bit [6:0] FUNCT7_ZBB__LOGICAL = 7'b0100000; - localparam bit [6:0] FUNCT7_ZBB__MINMAX = 7'b0000101; - localparam bit [6:0] FUNCT7_ZBB__ZEXT = 7'b0000100; - localparam bit [6:0] FUNCT7_ZBB__ROTATE = 7'b0110000; - localparam bit [6:0] FUNCT7_ZBB__ORCB = 7'b0010100; - localparam bit [6:0] FUNCT7_ZBB__REV8 = 7'b0110100; + localparam bit [6:0] FUNCT7_ZBA = 7'b0010000; + localparam bit [6:0] FUNCT7_ZBB_LOGICAL = 7'b0100000; + localparam bit [6:0] FUNCT7_ZBB_MINMAX = 7'b0000101; + localparam bit [6:0] FUNCT7_ZBB_ZEXT = 7'b0000100; + localparam bit [6:0] FUNCT7_ZBB_ROTATE = 7'b0110000; + localparam bit [6:0] FUNCT7_ZBB_ORCB = 7'b0010100; + localparam bit [6:0] FUNCT7_ZBB_REV8 = 7'b0110100; + localparam bit [6:0] FUNCT7_ZBB__BSET = 7'b0010100; + localparam bit [6:0] FUNCT7_ZBB__BCLR = 7'b0100100; + localparam bit [6:0] FUNCT7_ZBB__BINV = 7'b0110100; always_comb case (opcode) @@ -36,14 +39,15 @@ module ext__b__decoder default: b_alu_control = B_ALU_CTRL__NONE; endcase - FUNCT7_ZBB__LOGICAL: + FUNCT7_ZBB_LOGICAL: case (funct3) 3'b111: b_alu_control = B_ALU_CTRL__ANDN; 3'b110: b_alu_control = B_ALU_CTRL__ORN; 3'b100: b_alu_control = B_ALU_CTRL__XNOR; default: b_alu_control = B_ALU_CTRL__NONE; endcase - FUNCT7_ZBB__MINMAX: + + FUNCT7_ZBB_MINMAX: case (funct3) 3'b100: b_alu_control = B_ALU_CTRL__MIN; 3'b101: b_alu_control = B_ALU_CTRL__MINU; @@ -51,63 +55,95 @@ module ext__b__decoder 3'b111: b_alu_control = B_ALU_CTRL__MAXU; default: b_alu_control = B_ALU_CTRL__NONE; endcase - FUNCT7_ZBB__ROTATE: + + FUNCT7_ZBB_ROTATE: case (funct3) 3'b001: b_alu_control = B_ALU_CTRL__ROL; 3'b101: b_alu_control = B_ALU_CTRL__ROR; default: b_alu_control = B_ALU_CTRL__NONE; endcase - FUNCT7_ZBB__ZEXT: + + FUNCT7_ZBB_ZEXT: case (funct3) 3'b100: case (rs2) 5'b00000: b_alu_control = B_ALU_CTRL__ZEXTH; - default: b_alu_control = B_ALU_CTRL__NONE; + default: b_alu_control = B_ALU_CTRL__NONE; endcase default: b_alu_control = B_ALU_CTRL__NONE; endcase - // TODO: Implement zbs into ALU decoder, also confirm what zbb instructions are being implemented. - default: b_alu_control = B_ALU_CTRL__NONE; + FUNCT7_ZBB__BSET: + if (funct3 == 3'b001) + b_alu_control = B_ALU_CTRL__BSET; + else + b_alu_control = B_ALU_CTRL__NONE; + + FUNCT7_ZBB__BCLR: + case (funct3) + 3'b001: b_alu_control = B_ALU_CTRL__BCLR; + 3'b101: b_alu_control = B_ALU_CTRL__BEXT; + default: b_alu_control = B_ALU_CTRL__NONE; + endcase + + FUNCT7_ZBB__BINV: + if (funct3 == 3'b001) + b_alu_control = B_ALU_CTRL__BINV; + else + b_alu_control = B_ALU_CTRL__NONE; + + default: b_alu_control = B_ALU_CTRL__NONE; endcase + 7'b0010011: case (funct7) 7'b0110000: case (funct3) 3'b001: case (rs2) - 5'b00100: b_alu_control = B_ALU_CTRL__SEXTB; - 5'b00101: b_alu_control = B_ALU_CTRL__SEXTH; - 5'b00000: b_alu_control = B_ALU_CTRL__CLZ; - 5'b00001: b_alu_control = B_ALU_CTRL__CTZ; - 5'b00010: b_alu_control = B_ALU_CTRL__CPOP; + 5'b00100: b_alu_control = B_ALU_CTRL__SEXTB; + 5'b00101: b_alu_control = B_ALU_CTRL__SEXTH; + 5'b00000: b_alu_control = B_ALU_CTRL__CLZ; + 5'b00001: b_alu_control = B_ALU_CTRL__CTZ; + 5'b00010: b_alu_control = B_ALU_CTRL__CPOP; default: b_alu_control = B_ALU_CTRL__NONE; endcase 3'b101: b_alu_control = B_ALU_CTRL__RORI; default: b_alu_control = B_ALU_CTRL__NONE; endcase - FUNCT7_ZBB__ORCB: + + FUNCT7_ZBB_ORCB: case (funct3) 3'b101: case (rs2) 5'b00111: b_alu_control = B_ALU_CTRL__ORCB; - default: b_alu_control = B_ALU_CTRL__NONE; + default: b_alu_control = B_ALU_CTRL__NONE; endcase + 3'b001: b_alu_control = B_ALU_CTRL__BSET; default: b_alu_control = B_ALU_CTRL__NONE; endcase - FUNCT7_ZBB__REV8: + FUNCT7_ZBB_REV8: case (funct3) 3'b101: case (rs2) 5'b11000: b_alu_control = B_ALU_CTRL__REV8; - default: b_alu_control = B_ALU_CTRL__NONE; + default: b_alu_control = B_ALU_CTRL__NONE; endcase + 3'b001: b_alu_control = B_ALU_CTRL__BINV; + default: b_alu_control = B_ALU_CTRL__NONE; + endcase + + FUNCT7_ZBB__BCLR: + case (funct3) + 3'b001: b_alu_control = B_ALU_CTRL__BCLR; + 3'b101: b_alu_control = B_ALU_CTRL__BEXT; default: b_alu_control = B_ALU_CTRL__NONE; endcase default: b_alu_control = B_ALU_CTRL__NONE; endcase + default: b_alu_control = B_ALU_CTRL__NONE; endcase diff --git a/src/ext/b/types.svh b/src/ext/b/types.svh index dc54573c..d9a2d50d 100644 --- a/src/ext/b/types.svh +++ b/src/ext/b/types.svh @@ -3,7 +3,6 @@ /* verilator lint_off DECLFILENAME */ package ext__b__types; -/* verilator lint_on DECLFILENAME */ typedef enum logic [4:0] { B_ALU_CTRL__NONE = 5'b00000 , B_ALU_CTRL__SH1ADD = 5'b00001 @@ -27,6 +26,10 @@ package ext__b__types; , B_ALU_CTRL__RORI = 5'b10011 , B_ALU_CTRL__ORCB = 5'b10100 , B_ALU_CTRL__REV8 = 5'b10101 + , B_ALU_CTRL__BCLR = 5'b10110 + , B_ALU_CTRL__BSET = 5'b10111 + , B_ALU_CTRL__BINV = 5'b11000 + , B_ALU_CTRL__BEXT = 5'b11001 } b_alu_control_t; endpackage diff --git a/src/ext/b/zbs.sv b/src/ext/b/zbs.sv new file mode 100644 index 00000000..c14acc68 --- /dev/null +++ b/src/ext/b/zbs.sv @@ -0,0 +1,23 @@ +`include "src/timescale.svh" +`include "src/headers/types.svh" +`include "src/ext/b/types.svh" + +module zbs + ( input data_t reg1 + , input logic [4:0] reg2 + , input ext__b__types::b_alu_control_t b_alu_control + , output data_t out +); + + import ext__b__types::*; + + always_comb + case (b_alu_control) + B_ALU_CTRL__BCLR: out = reg1 & ~(data_t'(32'h1) << reg2); + B_ALU_CTRL__BSET: out = reg1 | (data_t'(32'h1) << reg2); + B_ALU_CTRL__BINV: out = reg1 ^ (data_t'(32'h1) << reg2); + B_ALU_CTRL__BEXT: out = (reg1 >> reg2) & data_t'(32'h1); + default: out = '0; + endcase + +endmodule diff --git a/src/modules/Instruction_Decode/Instruction_Decode.sv b/src/modules/Instruction_Decode/Instruction_Decode.sv index aa89d757..6089cf0b 100644 --- a/src/modules/Instruction_Decode/Instruction_Decode.sv +++ b/src/modules/Instruction_Decode/Instruction_Decode.sv @@ -17,142 +17,100 @@ module Instruction_Decode ); alu_op_t alu_op; - // reg [2:0] funct3; - // reg [6:0] funct7; - - /* verilator lint_off UNUSEDSIGNAL */ - wire [3:0] state; - /* verilator lint_on UNUSEDSIGNAL */ - - + alu_control_t base_alu_control; logic [6:0] funct7; - assign opcode = opcode_t'(instr[6:0]); - - //combinational logic for extracting funct3 and funct7[5] for ALU Decoder input - /* verilator lint_off UNUSEDSIGNAL */ - reg [2:0] default_funct3; - reg [6:0] default_funct7; - /* verilator lint_on UNUSEDSIGNAL */ - always @(*) begin - - funct3 = 3'b000; - funct7 = 7'b0; + assign opcode = opcode_t'(instr[6:0]); + // combinational logic for extracting funct3 and funct7 for ALU decoder input + always_comb case (opcode) - - OPCODE_OP, OPCODE_OP_IMM: begin //R-Type - - funct3 = instr[14:12]; - funct7 = instr[31:25]; - - end - - OPCODE_LOAD, OPCODE_STORE, OPCODE_BRANCH: begin - - funct3 = instr[14:12]; - - end - default:; + OPCODE_OP, OPCODE_OP_IMM: funct3 = instr[14:12]; + OPCODE_LOAD, OPCODE_STORE, OPCODE_BRANCH: funct3 = instr[14:12]; + default: funct3 = 3'b000; endcase - end - // determine ALU op based on the opcode; see Table 7.2 of the digital design and computer - // architecture book - always @(*) begin + always_comb case (opcode) - OPCODE_OP: alu_op = ALU_OP__REGISTER_OPERATION; - OPCODE_LOAD: alu_op = ALU_OP__ADD; - OPCODE_JALR: alu_op = ALU_OP__ADD; // rs1 + imm - OPCODE_STORE: alu_op = ALU_OP__ADD; - OPCODE_BRANCH: alu_op = ALU_OP__BRANCH; - OPCODE_AUIPC: alu_op = ALU_OP__ADD; // used to add 0 to imm ext - OPCODE_LUI: alu_op = ALU_OP__ADD; // used to add 0 to imm ext - OPCODE_MISC_MEM: alu_op = ALU_OP__UNSET; - default: alu_op = ALU_OP__UNSET; + OPCODE_OP, OPCODE_OP_IMM: funct7 = instr[31:25]; + default: funct7 = 7'b0; endcase - end - - //logic for extracting rs1, rs2, and rd registers from 32-bit instruction field - //The logic depends on the instruction type - - always @(*) begin - - rd = 5'b00000; - rs1 = 5'b00000; - rs2 = 5'b00000; + // determine ALU op based on opcode + always_comb case (opcode) + OPCODE_OP: alu_op = ALU_OP__REGISTER_OPERATION; + OPCODE_LOAD: alu_op = ALU_OP__ADD; + OPCODE_JALR: alu_op = ALU_OP__ADD; + OPCODE_STORE: alu_op = ALU_OP__ADD; + OPCODE_BRANCH: alu_op = ALU_OP__BRANCH; + OPCODE_AUIPC: alu_op = ALU_OP__ADD; + OPCODE_LUI: alu_op = ALU_OP__ADD; + OPCODE_MISC_MEM: alu_op = ALU_OP__UNSET; + default: alu_op = ALU_OP__UNSET; + endcase - OPCODE_OP: begin //R-Type - - rd = instr[11:7]; - rs1 = instr[19:15]; - rs2 = instr[24:20]; - - end - - OPCODE_OP_IMM, OPCODE_LOAD, OPCODE_JALR: begin //I-Type (where lw is I type) - - rd = instr[11:7]; - rs1 = instr[19:15]; - rs2 = instr[24:20]; - - end - - OPCODE_STORE, OPCODE_BRANCH: begin //S-type and B-Type - rs1 = instr[19:15]; - rs2 = instr[24:20]; - - end - - OPCODE_AUIPC, OPCODE_LUI, OPCODE_JAL: begin - rd = instr[11:7]; - end + always_comb + case (opcode) + OPCODE_OP + , OPCODE_OP_IMM + , OPCODE_LOAD + , OPCODE_JALR + , OPCODE_AUIPC + , OPCODE_LUI + , OPCODE_JAL: rd = instr[11:7]; + default: rd = 5'b00000; + endcase - default: begin - rd = 5'b0; - rs1 = 5'b0; - rs2 = 5'b0; - end + always_comb + case (opcode) + OPCODE_OP: rs1 = instr[19:15]; + OPCODE_OP_IMM, OPCODE_LOAD, OPCODE_JALR: rs1 = instr[19:15]; + OPCODE_STORE, OPCODE_BRANCH: rs1 = instr[19:15]; + default: rs1 = 5'b00000; endcase - end - // case statement for choosing 32-bit immediate format; based on opcode - // this is essentially the extend module of the processor - always @(*) begin + always_comb case (opcode) - OPCODE_OP_IMM : imm_ext = {{20{instr[31]}}, instr[31:20]}; - OPCODE_LOAD : imm_ext = {{20{instr[31]}}, instr[31:20]}; - OPCODE_JALR : imm_ext = {{20{instr[31]}}, instr[31:20]}; - OPCODE_STORE : imm_ext = {{20{instr[31]}}, instr[31:25], instr[11:7]}; - OPCODE_BRANCH : imm_ext = {{20{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0}; - OPCODE_JAL : imm_ext = {{12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0}; - OPCODE_AUIPC : imm_ext = {instr[31:12], 12'h000}; - OPCODE_LUI : imm_ext = {instr[31:12], 12'h000}; - default: imm_ext = 32'b0; + OPCODE_OP: rs2 = instr[24:20]; + OPCODE_OP_IMM, OPCODE_LOAD, OPCODE_JALR: rs2 = instr[24:20]; + OPCODE_STORE, OPCODE_BRANCH: rs2 = instr[24:20]; + default: rs2 = 5'b00000; endcase - end - //Instantiate ALU Decoder module + + // immediate decode + always_comb + case (opcode) + OPCODE_OP_IMM: imm_ext = {{20{instr[31]}}, instr[31:20]}; + OPCODE_LOAD: imm_ext = {{20{instr[31]}}, instr[31:20]}; + OPCODE_JALR: imm_ext = {{20{instr[31]}}, instr[31:20]}; + OPCODE_STORE: imm_ext = {{20{instr[31]}}, instr[31:25], instr[11:7]}; + OPCODE_BRANCH: imm_ext = {{20{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0}; + OPCODE_JAL: imm_ext = {{12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0}; + OPCODE_AUIPC: imm_ext = {instr[31:12], 12'h000}; + OPCODE_LUI: imm_ext = {instr[31:12], 12'h000}; + default: imm_ext = 32'b0; + endcase ALUdecoder instanceALUDec - ( .funct3(funct3) - , .funct7(funct7) - , .alu_op(alu_op) - , .alu_control(ALUControl) + ( .funct3 ( funct3 ) + , .funct7 ( funct7 ) + , .alu_op ( alu_op ) + , .alu_control ( base_alu_control ) ); - `ifdef UTOSS_RISCV_ENABLE_B_EXT - ext__b__decoder u_ext__b__decoder - ( .funct3 ( funct3 ) - , .funct7 ( funct7 ) - , .opcode ( opcode ) - , .rd ( rd ) - , .rs2 ( rs2 ) - , .b_alu_control ( b_alu_control ) + ext__b__decoder instanceBExtDec + ( .funct3 ( funct3 ) + , .funct7 ( funct7 ) + , .opcode ( opcode ) + , .rd ( rd ) + , .rs2 ( rs2 ) + , .b_alu_control ( b_alu_control ) ); `endif + assign ALUControl = base_alu_control; + endmodule diff --git a/test/zbs_decoder_tb.sv b/test/zbs_decoder_tb.sv new file mode 100644 index 00000000..6dca6435 --- /dev/null +++ b/test/zbs_decoder_tb.sv @@ -0,0 +1,139 @@ +`timescale 1ns/1ns +`include "src/headers/types.svh" +`include "src/ext/b/types.svh" +`include "test/utils.svh" + +module zbs_decoder_tb; + + import ext__b__types::*; + + logic [31:0] instr; + logic [3:0] alu_control; + opcode_t opcode; + imm_t imm_ext; + logic [2:0] funct3; + logic [4:0] rd; + logic [4:0] rs1; + logic [4:0] rs2; +`ifdef UTOSS_RISCV_ENABLE_B_EXT + b_alu_control_t b_alu_control; +`endif + + function automatic logic [31:0] make_r_zbs + ( input logic [6:0] funct7_i + , input logic [2:0] funct3_i + , input logic [4:0] rs2_i + , input logic [4:0] rs1_i + , input logic [4:0] rd_i + ); + return {funct7_i, rs2_i, rs1_i, funct3_i, rd_i, 7'b0110011}; + endfunction + + function automatic logic [31:0] make_i_zbs + ( input logic [6:0] funct7_i + , input logic [4:0] shamt_i + , input logic [4:0] rs1_i + , input logic [4:0] rd_i + , input logic [2:0] funct3_i + ); + return {funct7_i, shamt_i, rs1_i, funct3_i, rd_i, 7'b0010011}; + endfunction + + Instruction_Decode uut + ( .instr ( instr ) + , .ALUControl ( alu_control ) + , .opcode ( opcode ) + , .imm_ext ( imm_ext ) + , .funct3 ( funct3 ) + , .rd ( rd ) + , .rs1 ( rs1 ) + , .rs2 ( rs2 ) +`ifdef UTOSS_RISCV_ENABLE_B_EXT + , .b_alu_control ( b_alu_control ) +`endif + ); + + initial begin + instr = 32'd0; + #1; + + instr = 32'h00000013; // addi x0, x0, 0 + #1; + assert (opcode == OPCODE_OP_IMM) else + $fatal(1, "opcode decode failed for addi: got %0h", opcode); + assert (alu_control == ALU_CONTROL_ADD) else + $fatal(1, "base decode sanity check failed: got %0h", alu_control); + assert (imm_ext == 32'h0) else + $fatal(1, "imm decode failed for addi: got %0h", imm_ext); + assert (funct3 == 3'b000) else + $fatal(1, "funct3 decode failed for addi: got %0h", funct3); + assert (rd == 5'h0) else + $fatal(1, "rd decode failed for addi: got %0h", rd); + assert (rs1 == 5'h0) else + $fatal(1, "rs1 decode failed for addi: got %0h", rs1); + assert (rs2 == 5'h0) else + $fatal(1, "rs2 decode failed for addi: got %0h", rs2); + +`ifdef UTOSS_RISCV_ENABLE_B_EXT + instr = make_r_zbs(7'b0010100, 3'b001, 5'd3, 5'd2, 5'd1); // bset + #1; + assert (opcode == OPCODE_OP) else + $fatal(1, "opcode decode failed for bset: got %0h", opcode); + assert (funct3 == 3'b001) else + $fatal(1, "funct3 decode failed for bset: got %0h", funct3); + assert (rd == 5'd1) else + $fatal(1, "rd decode failed for bset: got %0h", rd); + assert (rs1 == 5'd2) else + $fatal(1, "rs1 decode failed for bset: got %0h", rs1); + assert (rs2 == 5'd3) else + $fatal(1, "rs2 decode failed for bset: got %0h", rs2); + assert (b_alu_control == B_ALU_CTRL__BSET) else + $fatal(1, "bset decode failed: got %0h", b_alu_control); + + instr = make_r_zbs(7'b0100100, 3'b001, 5'd3, 5'd2, 5'd1); // bclr + #1; + assert (b_alu_control == B_ALU_CTRL__BCLR) else + $fatal(1, "bclr decode failed: got %0h", b_alu_control); + + instr = make_r_zbs(7'b0110100, 3'b001, 5'd3, 5'd2, 5'd1); // binv + #1; + assert (b_alu_control == B_ALU_CTRL__BINV) else + $fatal(1, "binv decode failed: got %0h", b_alu_control); + + instr = make_r_zbs(7'b0100100, 3'b101, 5'd3, 5'd2, 5'd1); // bext + #1; + assert (b_alu_control == B_ALU_CTRL__BEXT) else + $fatal(1, "bext decode failed: got %0h", b_alu_control); + + instr = make_i_zbs(7'b0010100, 5'd3, 5'd2, 5'd1, 3'b001); // bseti + #1; + assert (opcode == OPCODE_OP_IMM) else + $fatal(1, "opcode decode failed for bseti: got %0h", opcode); + assert (b_alu_control == B_ALU_CTRL__BSET) else + $fatal(1, "bseti decode failed: got %0h", b_alu_control); + + instr = make_i_zbs(7'b0100100, 5'd3, 5'd2, 5'd1, 3'b001); // bclri + #1; + assert (b_alu_control == B_ALU_CTRL__BCLR) else + $fatal(1, "bclri decode failed: got %0h", b_alu_control); + + instr = make_i_zbs(7'b0110100, 5'd3, 5'd2, 5'd1, 3'b001); // binvi + #1; + assert (b_alu_control == B_ALU_CTRL__BINV) else + $fatal(1, "binvi decode failed: got %0h", b_alu_control); + + instr = make_i_zbs(7'b0100100, 5'd3, 5'd2, 5'd1, 3'b101); // bexti + #1; + assert (b_alu_control == B_ALU_CTRL__BEXT) else + $fatal(1, "bexti decode failed: got %0h", b_alu_control); +`else + $display("B extension is disabled; Zbs integration checks were skipped."); +`endif + + $display("Zbs decode tests passed!"); + $finish; + end + + `SETUP_VCD_DUMP(zbs_decoder_tb) + +endmodule diff --git a/test/zbs_tb.sv b/test/zbs_tb.sv new file mode 100644 index 00000000..ffa80bc8 --- /dev/null +++ b/test/zbs_tb.sv @@ -0,0 +1,116 @@ +`timescale 1ns/1ns +`include "test/utils.svh" +`include "src/ext/b/types.svh" + +module zbs_tb; + import ext__b__types::*; + + logic [31:0] reg1; + logic [31:0] reg2; + b_alu_control_t b_alu_control; + logic [31:0] out; + logic [31:0] expected; + + zbs uut + ( .reg1 ( reg1 ) + , .reg2 ( reg2[4:0] ) + , .b_alu_control ( b_alu_control ) + , .out ( out ) + ); + + initial begin + // -------- bclr test -------- + reg1 = 32'b1010; + reg2 = 32'd1; // clear bit 1 + b_alu_control = B_ALU_CTRL__BCLR; + #10; + + expected = reg1 & ~(32'h1 << reg2[4:0]); + assert (out == expected) else + $fatal("bclr failed: expected %b got %b", expected, out); + + // -------- bset test -------- + reg1 = 32'b1110; + reg2 = 32'd0; // set bit 0 + b_alu_control = B_ALU_CTRL__BSET; + #10; + + expected = reg1 | (32'h1 << reg2[4:0]); + assert (out == expected) else + $fatal("bset failed: expected %b got %b", expected, out); + + // -------- binv test -------- + reg1 = 32'b1010; + reg2 = 32'd1; + b_alu_control = B_ALU_CTRL__BINV; + #10; + + expected = reg1 ^ (32'h1 << reg2[4:0]); + assert (out == expected) else + $fatal("binv failed: expected %b got %b", expected, out); + + // -------- bext test -------- + reg1 = 32'b1010; + reg2 = 32'd3; + b_alu_control = B_ALU_CTRL__BEXT; + #10; + + expected = {31'b0, reg1[reg2[4:0]]}; + assert (out == expected) else + $fatal("bext failed: expected %b got %b", expected, out); + + // -------- Edge Cases -------- + reg1 = 32'hFFFFFFFF; + reg2 = 32'd0; + b_alu_control = B_ALU_CTRL__BCLR; + #10; + + expected = reg1 & ~(32'h1 << reg2[4:0]); + assert (out == expected) else + $fatal("corner case bit0 failed"); + + reg1 = 32'hFFFFFFFF; + reg2 = 32'd31; + b_alu_control = B_ALU_CTRL__BCLR; + #10; + + expected = reg1 & ~(32'h1 << reg2[4:0]); + assert (out == expected) else + $fatal("corner case bit31 failed"); + + // ----------- Randomized Testing (Experimental) ----------- + repeat (1000) begin + reg1 = $urandom; + reg2 = $urandom % 32; + + // pick a Zbs operation without arithmetic on enums to avoid width expansion + case ($urandom % 4) + 0: b_alu_control = B_ALU_CTRL__BCLR; + 1: b_alu_control = B_ALU_CTRL__BSET; + 2: b_alu_control = B_ALU_CTRL__BINV; + 3: b_alu_control = B_ALU_CTRL__BEXT; + default: b_alu_control = B_ALU_CTRL__BCLR; + endcase + + #1; + + case (b_alu_control) + B_ALU_CTRL__BCLR: expected = reg1 & ~(32'h1 << reg2[4:0]); + B_ALU_CTRL__BSET: expected = reg1 | (32'h1 << reg2[4:0]); + B_ALU_CTRL__BINV: expected = reg1 ^ (32'h1 << reg2[4:0]); + B_ALU_CTRL__BEXT: expected = {31'b0, reg1[reg2[4:0]]}; + default: expected = 32'd0; + endcase + + assert (out == expected) else + $fatal("random test failed: b_alu_control=%0d reg1=%h reg2=%d expected=%h got=%h" + , b_alu_control, reg1, reg2, expected, out); + end + + $display("All tests passed!"); + $finish; + end + + `SETUP_VCD_DUMP(zbs_tb) + +endmodule