Skip to content

Commit 0e93086

Browse files
authored
feat: add example pc file and testbench (#391)
* Update nm4207.md * Create pc.v * Add files via upload
1 parent 0bf37f0 commit 0e93086

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module pc (
2+
input clk,
3+
input rst_n,
4+
input [1:0] MUX_output, // Controlled by Control module
5+
input [15:0] pc_plus1,
6+
input [15:0] pc_plus1_imm,
7+
input [15:0] alu_out, // The output from regB that has been passed through the ALU
8+
output [15:0] nxt_instr
9+
);
10+
11+
reg [15:0] pc_reg; // Internal Register to hold state of pc
12+
13+
assign nxt_instr = pc_reg; // Assigns output to be linked to pc_reg
14+
15+
always @(posedge clk or negedge rst_n)
16+
begin
17+
if (!rst_n)
18+
begin
19+
// On reset, the program counter is initialized to the starting address 0.
20+
pc_reg <= 16'h0000;
21+
end
22+
else
23+
begin
24+
// On a clock edge, the next value of the PC is determined by the MUX selector.
25+
case (MUX_output)
26+
2'b00:
27+
pc_reg <= pc_plus1;
28+
2'b01:
29+
pc_reg <= pc_plus1_imm;
30+
2'b10:
31+
pc_reg <= alu_out;
32+
default:
33+
pc_reg <= pc_plus1; // safety
34+
endcase
35+
end
36+
end
37+
38+
endmodule
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Testbench for the Program Counter (pc) module
2+
`timescale 1ns / 1ps
3+
4+
module pc_tb;
5+
6+
// Inputs to the DUT (Device Under Test)
7+
reg clk;
8+
reg rst_n;
9+
reg [1:0] MUX_output;
10+
reg [15:0] pc_plus1;
11+
reg [15:0] pc_plus1_imm;
12+
reg [15:0] alu_out;
13+
14+
// Output from the DUT
15+
wire [15:0] nxt_instr;
16+
17+
// Instantiate the Program Counter module
18+
pc uut (
19+
.clk(clk),
20+
.rst_n(rst_n),
21+
.MUX_output(MUX_output),
22+
.pc_plus1(pc_plus1),
23+
.pc_plus1_imm(pc_plus1_imm),
24+
.alu_out(alu_out),
25+
.nxt_instr(nxt_instr)
26+
);
27+
28+
// Clock generation: 100MHz clock (10ns period)
29+
always #5 clk = ~clk;
30+
31+
// Test sequence
32+
initial begin
33+
// 1. Initial state setup
34+
$display("T=%0t: [SETUP] Initializing testbench...", $time);
35+
clk = 0;
36+
rst_n = 1;
37+
MUX_output = 2'b00;
38+
pc_plus1 = 16'h0000;
39+
pc_plus1_imm = 16'h0000;
40+
alu_out = 16'h0000;
41+
#10; // Wait for a moment
42+
43+
// 2. Test Case: Asynchronous Reset
44+
$display("T=%0t: [TEST] Asserting active-low reset.", $time);
45+
rst_n = 0;
46+
#10; // Hold reset for a short period
47+
if (nxt_instr == 16'h0000) begin
48+
$display("T=%0t: [PASS] PC reset to 0x%h as expected.", $time, nxt_instr);
49+
end else begin
50+
$display("T=%0t: [FAIL] PC is 0x%h, expected 0x0000.", $time, nxt_instr);
51+
end
52+
#10;
53+
rst_n = 1; // De-assert reset
54+
$display("T=%0t: [SETUP] De-asserting reset.", $time);
55+
56+
57+
// 3. Test Case: Sequential Increment (MUX_output = 00)
58+
$display("\n T=%0t: [TEST] Testing sequential increment (MUX_output = 2'b00).", $time);
59+
MUX_output = 2'b00;
60+
pc_plus1 = 16'h0001;
61+
@(posedge clk);
62+
#1;
63+
if (nxt_instr === 16'h0001) begin
64+
$display("T=%0t: [PASS] PC incremented to 0x%h.", $time, nxt_instr);
65+
end else begin
66+
$display("T=%0t: [FAIL] PC is 0x%h, expected 0x0001.", $time, nxt_instr);
67+
end
68+
69+
pc_plus1 = 16'h0002;
70+
@(posedge clk);
71+
#1;
72+
if (nxt_instr === 16'h0002) begin
73+
$display("T=%0t: [PASS] PC incremented to 0x%h.", $time, nxt_instr);
74+
end else begin
75+
$display("T=%0t: [FAIL] PC is 0x%h, expected 0x0002.", $time, nxt_instr);
76+
end
77+
78+
79+
// 4. Test Case: Branch with Immediate (MUX_output = 01)
80+
$display("\n T=%0t: [TEST] Testing branch with immediate (MUX_output = 2'b01).", $time);
81+
MUX_output = 2'b01;
82+
pc_plus1_imm = 16'h1234;
83+
// Set other inputs to different values to ensure they aren't selected
84+
pc_plus1 = 16'hFFFF;
85+
alu_out = 16'hEEEE;
86+
@(posedge clk);
87+
#1;
88+
if (nxt_instr === 16'h1234) begin
89+
$display("T=%0t: [PASS] PC branched to immediate address 0x%h.", $time, nxt_instr);
90+
end else begin
91+
$display("T=%0t: [FAIL] PC is 0x%h, expected 0x1234.", $time, nxt_instr);
92+
end
93+
94+
95+
// 5. Test Case: Jump via ALU (MUX_output = 10)
96+
$display("\n T=%0t: [TEST] Testing jump via ALU output (MUX_output = 2'b10).", $time);
97+
MUX_output = 2'b10;
98+
alu_out = 16'hABCD;
99+
// Set other inputs to different values
100+
pc_plus1 = 16'hFFFF;
101+
pc_plus1_imm = 16'hEEEE;
102+
@(posedge clk);
103+
#1;
104+
if (nxt_instr === 16'hABCD) begin
105+
$display("T=%0t: [PASS] PC jumped to ALU address 0x%h.", $time, nxt_instr);
106+
end else begin
107+
$display("T=%0t: [FAIL] PC is 0x%h, expected 0xABCD.", $time, nxt_instr);
108+
end
109+
110+
// 6. Test Case: Back-to-back operations
111+
$display("\n T=%0t: [TEST] Testing mixed back-to-back operations.", $time);
112+
113+
// a. Increment
114+
MUX_output = 2'b00;
115+
pc_plus1 = 16'hABCE;
116+
@(posedge clk);
117+
#1;
118+
$display("T=%0t: [INFO] Current PC: 0x%h (After increment)", $time, nxt_instr);
119+
120+
// b. Jump
121+
MUX_output = 2'b10;
122+
alu_out = 16'hBEEF;
123+
@(posedge clk);
124+
#1;
125+
$display("T=%0t: [INFO] Current PC: 0x%h (After jump)", $time, nxt_instr);
126+
127+
// c. Branch
128+
MUX_output = 2'b01;
129+
pc_plus1_imm = 16'hCAFE;
130+
@(posedge clk);
131+
#1;
132+
$display("T=%0t: [INFO] Current PC: 0x%h (After branch)", $time, nxt_instr);
133+
if (nxt_instr !== 16'hCAFE) begin
134+
$display("T=%0t: [FAIL] Back-to-back sequence failed. PC is 0x%h, expected 0xCAFE.", $time, nxt_instr);
135+
end else begin
136+
$display("T=%0t: [PASS] Back-to-back sequence successful.", $time);
137+
end
138+
139+
// 7. Test Case: Undefined MUX select (2'b11)
140+
// Since the module's case statement does not define 2'b11, it should perform the default
141+
// of incrementing the PC
142+
$display("\n T=%0t: [TEST] Testing undefined MUX select (2'b11).", $time);
143+
MUX_output = 2'b11;
144+
pc_plus1 = 16'h0009; // Change inputs to see if they are ignored
145+
pc_plus1_imm = 16'h000A;
146+
alu_out = 16'h000B;
147+
@(posedge clk);
148+
#1;
149+
if (nxt_instr === 16'h0009) begin
150+
$display("T=%0t: [PASS] PC correctly incremented to PC+1 at 16'h0009.", $time, nxt_instr);
151+
end else begin
152+
$display("T=%0t: [FAIL] PC changed to 16'h000A or 16'h000B, expected it to go to 16'h0009.", $time, nxt_instr);
153+
end
154+
155+
// 8. Test Case: Asynchronous Reset again
156+
$display("\n T=%0t: [TEST] Asserting active-low reset.", $time);
157+
rst_n = 0;
158+
#10; // Hold reset for a short period
159+
if (nxt_instr == 16'h0000) begin
160+
$display("T=%0t: [PASS] PC reset to 0x%h as expected.", $time, nxt_instr);
161+
end else begin
162+
$display("T=%0t: [FAIL] PC is 0x%h, expected 0x0000.", $time, nxt_instr);
163+
end
164+
#10;
165+
rst_n = 1; // De-assert reset
166+
$display("T=%0t: [SETUP] De-asserting reset.", $time);
167+
168+
169+
$display("\n T=%0t: [INFO] All tests completed.", $time);
170+
$finish; // End simulation
171+
end
172+
173+
// Optional: Monitor to see signal changes at every time step
174+
// initial begin
175+
// $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",
176+
// $time, clk, rst_n, MUX_output, pc_plus1, pc_plus1_imm, alu_out, nxt_instr);
177+
// end
178+
179+
endmodule

0 commit comments

Comments
 (0)