diff --git a/.gitignore b/.gitignore index 8738c930..08982cae 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ out/** !out/.gitkeep build/** !build/.gitkeep +.DS_Store diff --git a/src/ext/b/zbs.sv b/src/ext/b/zbs.sv new file mode 100644 index 00000000..e0d8c9fc --- /dev/null +++ b/src/ext/b/zbs.sv @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------------- +// Zbs Extension – Single-Bit Instructions (RV32) +// Reference: +// RISC-V Bitmanip Extension Specification v1.0.0 +// Section 5.4 – Zbs (Single-Bit Instructions) +// +// Notes: +// - Purely combinational logic +// - Bit index = reg2[4:0] +// - R/I distinction handled in decode stage +// ----------------------------------------------------------------------------- + +module zbs ( + input data_t reg1 // rs1 operand + , input data_t reg2 // rs2 or immediate (bit index source) + , input logic [1:0] inst // operation selector + , output data_t out //result +); + + logic [4:0] index; + data_t mask; + + always_comb + index = reg2[4:0]; + + always_comb + mask = data_t'(32'h1) << index; + + always_comb + case (inst) + + // 00 : bclr / bclri → clear selected bit + 2'b00: out = reg1 & ~mask; + + // 01 : bset / bseti → set selected bit + 2'b01: out = reg1 | mask; + + // 10 : binv / binvi → invert selected bit + 2'b10: out = reg1 ^ mask; + + // 11 : bext / bexti → extract selected bit (to bit[0]) + 2'b11: out = (reg1 >> index) & data_t'(32'h1); + + // others → safe default + default: out = '0; + + endcase + +endmodule \ No newline at end of file diff --git a/test/zbs_tb.sv b/test/zbs_tb.sv new file mode 100644 index 00000000..cfe9407f --- /dev/null +++ b/test/zbs_tb.sv @@ -0,0 +1,133 @@ +`timescale 1ns/1ns +`include "test/utils.svh" + +module zbs_tb; + +logic [31:0] reg1; +logic [31:0] reg2; +logic [2:0] inst; +logic [31:0] out; + +logic [31:0] expected; + +zbs uut + ( .reg1 ( reg1 ) + , .reg2 ( reg2 ) + , .inst ( inst ) + , .out ( out ) +); + +initial begin + + // -------- bclr test -------- + reg1 = 32'b1010; + reg2 = 32'd1; // clear bit 1 + inst = 3'b000; + #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 + inst = 3'b001; + #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; + inst = 3'b010; + #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; + inst = 3'b011; + #10; + + expected = {31'b0, reg1[reg2[4:0]]}; + + assert (out == expected) + else $fatal("bext failed: expected %b got %b", expected, out); + + // -------- Edge Cases --------- + + // Bit 0 boundary + reg1 = 32'hFFFFFFFF; + reg2 = 32'd0; + inst = 3'b000; // bclr + #10; + + expected = reg1 & ~(32'h1 << reg2[4:0]); + + assert (out == expected) + else $fatal("corner case bit0 failed"); + + + // Bit 31 boundary + reg1 = 32'hFFFFFFFF; + reg2 = 32'd31; + inst = 3'b000; // 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; + inst = $urandom % 4; + + #1; + + case (inst) + + 3'b000: expected = reg1 & ~(32'h1 << reg2[4:0]); + + 3'b001: expected = reg1 | (32'h1 << reg2[4:0]); + + 3'b010: expected = reg1 ^ (32'h1 << reg2[4:0]); + + 3'b011: expected = {31'b0, reg1[reg2[4:0]]}; + + default: expected = 32'd0; + + endcase + + assert (out == expected) else + $fatal("random test failed: inst=%0d reg1=%h reg2=%d expected=%h got=%h" + , inst, reg1, reg2, expected, out); + + end + + $display("All tests passed!"); + + $finish; + +end + +`SETUP_VCD_DUMP(zbs_tb) + +endmodule