Files
stack/cpu_tb.v
T
2026-03-07 23:45:11 -05:00

238 lines
7.4 KiB
Verilog

`timescale 1ns/1ps
module cpu_tb;
// Clock and reset
reg clk;
reg rst;
// Wishbone signals (unused but needed for module)
reg i_ack;
reg [31:0] i_dat;
wire [31:0] o_dat;
wire [31:0] o_adr;
wire [3:0] o_sel;
wire o_we;
wire o_stb;
wire o_cyc;
// Instantiate CPU
cpu dut (
.i_clk(clk),
.i_rst_n(rst)
/*.i_ack(i_ack),
.i_dat(i_dat),
.o_dat(o_dat),
.o_adr(o_adr),
.o_sel(o_sel),
.o_we(o_we),
.o_stb(o_stb),
.o_cyc(o_cyc)*/
);
// Clock generation - 50MHz (20ns period)
initial begin
clk = 0;
forever #10 clk = ~clk;
end
// Simulation control
initial begin
// Initialize
rst = 0;
//i_ack = 0;
//i_dat = 0;
// Dump waveforms
$dumpfile("cpu_tb.vcd");
$dumpvars(0, cpu_tb);
// Reset pulse
#50;
rst = 1;
// Run for some cycles
#10000;
$display("\n=== SIMULATION COMPLETE ===");
$finish;
end
// Monitor important signals
always @(posedge clk) begin
if (rst) begin
// Print state every cycle
$display("T=%0t | PC=0x%03X | HALT=%b | PCP=0x%08X DSP=0x%08X RSP=0x%08X TMP=0x%08X MDR=0x%08X TOS=0x%08X ACC=0x%08X",
$time,
dut.seq_y, // Microcode PC
dut.halt, // CPU halted?
dut.register_file_inst.registers[0], // PCP
dut.register_file_inst.registers[1], // DSP
dut.register_file_inst.registers[2], // RSP
dut.register_file_inst.registers[3], // TMP
dut.register_file_inst.registers[4], // MDR
dut.register_file_inst.registers[5], // TOS
dut.register_file_inst.registers[6] // ACC
);
// Decode current microinstruction
$display(" | uCode: imm=%03X seqop=%b seqsrc=%b aluop=%04b rs1=%d rs2=%d rd=%d rma=%d rwe=%b",
dut.u_imm,
dut.u_seq_op,
dut.u_seq_src,
dut.u_alu_op,
dut.u_rf_rs1,
dut.u_rf_rs2,
dut.u_rf_rd,
dut.u_rf_rma,
dut.u_rf_we
);
// Show ALU operation if register write is happening
if (dut.u_rf_we && !dut.halt) begin
$display(" | ALU: %08X %s %08X = %08X (zero=%b)",
dut.alu_a,
alu_op_name(dut.alu_op),
dut.alu_b,
dut.alu_y,
dut.alu_zero
);
end
// Show memory operations
if (dut.u_mem_act != 0) begin
$display(" | MEM: %s %s @ 0x%08X data=0x%08X",
(dut.u_mem_dir ? "WRITE" : "READ"),
mem_size_name(dut.u_mem_act),
dut.rf_rma_data,
(dut.u_mem_dir ? dut.rf_mdr_data_r : dut.rf_mdr_data_w)
);
end
$display(""); // Blank line between cycles
end
end
// Helper function to decode ALU operation names
function [63:0] alu_op_name;
input [3:0] op;
begin
case(op)
4'b0000: alu_op_name = "PASS ";
4'b0001: alu_op_name = "NOT ";
4'b0010: alu_op_name = "INC ";
4'b0011: alu_op_name = "DEC ";
4'b0100: alu_op_name = "ADD ";
4'b0101: alu_op_name = "SUB ";
4'b0110: alu_op_name = "GT ";
4'b0111: alu_op_name = "LT ";
4'b1000: alu_op_name = "AND ";
4'b1001: alu_op_name = "OR ";
4'b1010: alu_op_name = "XOR ";
4'b1011: alu_op_name = "LSL ";
4'b1100: alu_op_name = "ASR ";
4'b1101: alu_op_name = "MUL ";
4'b1110: alu_op_name = "INC4 ";
4'b1111: alu_op_name = "DEC4 ";
default: alu_op_name = "UNKNOWN ";
endcase
end
endfunction
// Helper function to decode memory size
function [39:0] mem_size_name;
input [1:0] size;
begin
case(size)
2'b00: mem_size_name = "NOP ";
2'b01: mem_size_name = "WORD ";
2'b10: mem_size_name = "HWORD";
2'b11: mem_size_name = "BYTE ";
default: mem_size_name = "?? ";
endcase
end
endfunction
// Detect interesting events
always @(posedge clk) begin
if (rst) begin
// Detect jumps
if (dut.u_seq_op != 2'b00 && dut.seq_cc) begin
case(dut.u_seq_op)
2'b01: $display(">>> JUMP to 0x%03X", dut.seq_a);
2'b10: $display(">>> RETURN to 0x%03X", dut.sequencer_inst.ra);
2'b11: $display(">>> CALL to 0x%03X (RA=0x%03X)",
dut.seq_a, dut.sequencer_inst.pc + 1);
endcase
end
// Detect execution of fetch loop
if (dut.seq_y == 9'h000) begin
$display(">>> FETCH instruction from PCP=0x%08X",
dut.register_file_inst.registers[0]);
end
// Detect init routine
if (dut.seq_y == 9'h1F0) begin
$display(">>> INIT routine started");
end
end
end
// Periodically dump register file state
integer cycle_count;
initial cycle_count = 0;
always @(posedge clk) begin
if (rst) begin
cycle_count = cycle_count + 1;
// Every 50 cycles, dump register state
if (cycle_count % 50 == 0) begin
$display("\n========== REGISTER DUMP (cycle %0d) ==========", cycle_count);
$display("PCP = 0x%08X (Program Counter)",
dut.register_file_inst.registers[0]);
$display("DSP = 0x%08X (Data Stack Pointer)",
dut.register_file_inst.registers[1]);
$display("RSP = 0x%08X (Return Stack Pointer)",
dut.register_file_inst.registers[2]);
$display("TMP = 0x%08X (Temporary)",
dut.register_file_inst.registers[3]);
$display("MDR = 0x%08X (Memory Data)",
dut.register_file_inst.registers[4]);
$display("TOS = 0x%08X (Top of Stack)",
dut.register_file_inst.registers[5]);
$display("ACC = 0x%08X (Accumulator)",
dut.register_file_inst.registers[6]);
$display("===============================================\n");
end
end
end
// Watchdog - detect infinite loops
reg [8:0] last_pc;
integer same_pc_count;
initial begin
last_pc = 9'h000;
same_pc_count = 0;
end
always @(posedge clk) begin
if (rst) begin
if (dut.seq_y == last_pc && !dut.halt) begin
same_pc_count = same_pc_count + 1;
if (same_pc_count > 100) begin
$display("\n!!! ERROR: CPU stuck at PC=0x%03X for %0d cycles !!!",
dut.seq_y, same_pc_count);
$display("This might be an infinite loop or missing microcode.");
$finish;
end
end else begin
last_pc = dut.seq_y;
same_pc_count = 0;
end
end
end
endmodule