`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