diff --git a/src/getting_started/onboarding/RiSC-16/pc.v b/src/getting_started/onboarding/RiSC-16/pc.v new file mode 100644 index 0000000..2e57f86 --- /dev/null +++ b/src/getting_started/onboarding/RiSC-16/pc.v @@ -0,0 +1,38 @@ +module pc ( + input clk, + input rst_n, + input [1:0] MUX_output, // Controlled by Control module + input [15:0] pc_plus1, + input [15:0] pc_plus1_imm, + input [15:0] alu_out, // The output from regB that has been passed through the ALU + output [15:0] nxt_instr +); + +reg [15:0] pc_reg; // Internal Register to hold state of pc + +assign nxt_instr = pc_reg; // Assigns output to be linked to pc_reg + +always @(posedge clk or negedge rst_n) +begin + if (!rst_n) + begin + // On reset, the program counter is initialized to the starting address 0. + pc_reg <= 16'h0000; + end + else + begin + // On a clock edge, the next value of the PC is determined by the MUX selector. + case (MUX_output) + 2'b00: + pc_reg <= pc_plus1; + 2'b01: + pc_reg <= pc_plus1_imm; + 2'b10: + pc_reg <= alu_out; + default: + pc_reg <= pc_plus1; // safety + endcase + end +end + +endmodule diff --git a/src/getting_started/onboarding/RiSC-16/pc_tb.v b/src/getting_started/onboarding/RiSC-16/pc_tb.v new file mode 100644 index 0000000..09d08e2 --- /dev/null +++ b/src/getting_started/onboarding/RiSC-16/pc_tb.v @@ -0,0 +1,179 @@ +// Testbench for the Program Counter (pc) module +`timescale 1ns / 1ps + +module pc_tb; + + // Inputs to the DUT (Device Under Test) + reg clk; + reg rst_n; + reg [1:0] MUX_output; + reg [15:0] pc_plus1; + reg [15:0] pc_plus1_imm; + reg [15:0] alu_out; + + // Output from the DUT + wire [15:0] nxt_instr; + + // Instantiate the Program Counter module + pc uut ( + .clk(clk), + .rst_n(rst_n), + .MUX_output(MUX_output), + .pc_plus1(pc_plus1), + .pc_plus1_imm(pc_plus1_imm), + .alu_out(alu_out), + .nxt_instr(nxt_instr) + ); + + // Clock generation: 100MHz clock (10ns period) + always #5 clk = ~clk; + + // Test sequence + initial begin + // 1. Initial state setup + $display("T=%0t: [SETUP] Initializing testbench...", $time); + clk = 0; + rst_n = 1; + MUX_output = 2'b00; + pc_plus1 = 16'h0000; + pc_plus1_imm = 16'h0000; + alu_out = 16'h0000; + #10; // Wait for a moment + + // 2. Test Case: Asynchronous Reset + $display("T=%0t: [TEST] Asserting active-low reset.", $time); + rst_n = 0; + #10; // Hold reset for a short period + if (nxt_instr == 16'h0000) begin + $display("T=%0t: [PASS] PC reset to 0x%h as expected.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC is 0x%h, expected 0x0000.", $time, nxt_instr); + end + #10; + rst_n = 1; // De-assert reset + $display("T=%0t: [SETUP] De-asserting reset.", $time); + + + // 3. Test Case: Sequential Increment (MUX_output = 00) + $display("\n T=%0t: [TEST] Testing sequential increment (MUX_output = 2'b00).", $time); + MUX_output = 2'b00; + pc_plus1 = 16'h0001; + @(posedge clk); + #1; + if (nxt_instr === 16'h0001) begin + $display("T=%0t: [PASS] PC incremented to 0x%h.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC is 0x%h, expected 0x0001.", $time, nxt_instr); + end + + pc_plus1 = 16'h0002; + @(posedge clk); + #1; + if (nxt_instr === 16'h0002) begin + $display("T=%0t: [PASS] PC incremented to 0x%h.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC is 0x%h, expected 0x0002.", $time, nxt_instr); + end + + + // 4. Test Case: Branch with Immediate (MUX_output = 01) + $display("\n T=%0t: [TEST] Testing branch with immediate (MUX_output = 2'b01).", $time); + MUX_output = 2'b01; + pc_plus1_imm = 16'h1234; + // Set other inputs to different values to ensure they aren't selected + pc_plus1 = 16'hFFFF; + alu_out = 16'hEEEE; + @(posedge clk); + #1; + if (nxt_instr === 16'h1234) begin + $display("T=%0t: [PASS] PC branched to immediate address 0x%h.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC is 0x%h, expected 0x1234.", $time, nxt_instr); + end + + + // 5. Test Case: Jump via ALU (MUX_output = 10) + $display("\n T=%0t: [TEST] Testing jump via ALU output (MUX_output = 2'b10).", $time); + MUX_output = 2'b10; + alu_out = 16'hABCD; + // Set other inputs to different values + pc_plus1 = 16'hFFFF; + pc_plus1_imm = 16'hEEEE; + @(posedge clk); + #1; + if (nxt_instr === 16'hABCD) begin + $display("T=%0t: [PASS] PC jumped to ALU address 0x%h.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC is 0x%h, expected 0xABCD.", $time, nxt_instr); + end + + // 6. Test Case: Back-to-back operations + $display("\n T=%0t: [TEST] Testing mixed back-to-back operations.", $time); + + // a. Increment + MUX_output = 2'b00; + pc_plus1 = 16'hABCE; + @(posedge clk); + #1; + $display("T=%0t: [INFO] Current PC: 0x%h (After increment)", $time, nxt_instr); + + // b. Jump + MUX_output = 2'b10; + alu_out = 16'hBEEF; + @(posedge clk); + #1; + $display("T=%0t: [INFO] Current PC: 0x%h (After jump)", $time, nxt_instr); + + // c. Branch + MUX_output = 2'b01; + pc_plus1_imm = 16'hCAFE; + @(posedge clk); + #1; + $display("T=%0t: [INFO] Current PC: 0x%h (After branch)", $time, nxt_instr); + if (nxt_instr !== 16'hCAFE) begin + $display("T=%0t: [FAIL] Back-to-back sequence failed. PC is 0x%h, expected 0xCAFE.", $time, nxt_instr); + end else begin + $display("T=%0t: [PASS] Back-to-back sequence successful.", $time); + end + + // 7. Test Case: Undefined MUX select (2'b11) + // Since the module's case statement does not define 2'b11, it should perform the default + // of incrementing the PC + $display("\n T=%0t: [TEST] Testing undefined MUX select (2'b11).", $time); + MUX_output = 2'b11; + pc_plus1 = 16'h0009; // Change inputs to see if they are ignored + pc_plus1_imm = 16'h000A; + alu_out = 16'h000B; + @(posedge clk); + #1; + if (nxt_instr === 16'h0009) begin + $display("T=%0t: [PASS] PC correctly incremented to PC+1 at 16'h0009.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC changed to 16'h000A or 16'h000B, expected it to go to 16'h0009.", $time, nxt_instr); + end + + // 8. Test Case: Asynchronous Reset again + $display("\n T=%0t: [TEST] Asserting active-low reset.", $time); + rst_n = 0; + #10; // Hold reset for a short period + if (nxt_instr == 16'h0000) begin + $display("T=%0t: [PASS] PC reset to 0x%h as expected.", $time, nxt_instr); + end else begin + $display("T=%0t: [FAIL] PC is 0x%h, expected 0x0000.", $time, nxt_instr); + end + #10; + rst_n = 1; // De-assert reset + $display("T=%0t: [SETUP] De-asserting reset.", $time); + + + $display("\n T=%0t: [INFO] All tests completed.", $time); + $finish; // End simulation + end + + // Optional: Monitor to see signal changes at every time step + // initial begin + // $monitor("T=%0t | clk=%b rst_n=%b | MUX_sel=%b | pc+1=%h pc+1+imm=%h alu_out=%h | nxt_instr_addr=%h", + // $time, clk, rst_n, MUX_output, pc_plus1, pc_plus1_imm, alu_out, nxt_instr); + // end + +endmodule