`timescale 1ns / 1ps module cpu_tb_enhanced; // Clock and reset reg clk; reg rst_n; reg uart_rx; wire uart_tx; wire [5:0] led; // Instantiate the CPU cpu dut ( .i_clk(clk), .i_rst_n(rst_n), .i_uart_rx(uart_rx), .o_uart_tx(uart_tx), .o_led(led) ); // Clock generation - 10ns period (100MHz) initial begin clk = 0; forever #5 clk = ~clk; end // ============================================================================ // HELPER FUNCTIONS FOR DISPLAY // ============================================================================ function [63:0] reg_name; input [2:0] addr; begin case (addr) 3'd0: reg_name = "PCP"; 3'd1: reg_name = "DSP"; 3'd2: reg_name = "RSP"; 3'd3: reg_name = "TMP"; 3'd4: reg_name = "MDR"; 3'd5: reg_name = "TOS"; 3'd6: reg_name = "ACC"; 3'd7: reg_name = "IMM"; default: reg_name = "???"; endcase end endfunction function [63:0] rma_name; input [1:0] addr; begin case (addr) 2'd0: rma_name = "PCP"; 2'd1: rma_name = "DSP"; 2'd2: rma_name = "RSP"; 2'd3: rma_name = "TMP"; default: rma_name = "???"; endcase end endfunction function [63:0] alu_op_name; input [3:0] op; begin case (op) 4'h0: alu_op_name = "A "; 4'h1: alu_op_name = "NOT "; 4'h2: alu_op_name = "ADD "; 4'h3: alu_op_name = "SUB "; 4'h4: alu_op_name = "SGT "; 4'h5: alu_op_name = "SLT "; 4'h6: alu_op_name = "GT "; 4'h7: alu_op_name = "LT "; 4'h8: alu_op_name = "AND "; 4'h9: alu_op_name = "OR "; 4'ha: alu_op_name = "XOR "; 4'hb: alu_op_name = "LSL "; 4'hc: alu_op_name = "ASR "; 4'hd: alu_op_name = "MUL "; 4'he: alu_op_name = "SEXTB "; 4'hf: alu_op_name = "SEXTH "; default: alu_op_name = "??? "; endcase end endfunction function [63:0] seq_op_name; input [1:0] op; begin case (op) 2'b00: seq_op_name = "STEP"; 2'b01: seq_op_name = "JUMP"; 2'b10: seq_op_name = "RTRN"; 2'b11: seq_op_name = "CALL"; default: seq_op_name = "????"; endcase end endfunction function [63:0] mem_act_name; input [1:0] act; begin case (act) 2'b00: mem_act_name = "NOP "; 2'b01: mem_act_name = "WORD"; 2'b10: mem_act_name = "HWRD"; 2'b11: mem_act_name = "BYTE"; default: mem_act_name = "????"; endcase end endfunction // ============================================================================ // MONITORING CONFIGURATION // ============================================================================ parameter SHOW_UNCHANGED_REGS = 1; // Set to 1 to always show all registers parameter SHOW_MEMORY_CONTENTS = 1; // Set to 1 to show memory around stack pointers parameter MEM_CONTEXT_LINES = 4; // How many memory locations to show around SP parameter COMPACT_MODE = 1; // Set to 1 for more compact output // Previous register values for change detection reg [31:0] prev_regs [0:6]; integer cycle; // ============================================================================ // MAIN MONITOR // ============================================================================ initial begin cycle = 0; for (integer i = 0; i < 7; i = i + 1) prev_regs[i] = 0; $display("\n"); $display("╔════════════════════════════════════════════════════════════════════════════════╗"); $display("║ CPU EXECUTION MONITOR ║"); $display("╚════════════════════════════════════════════════════════════════════════════════╝"); $display("\n"); end always @(posedge clk) begin if (rst_n) begin cycle = cycle + 1; if (!COMPACT_MODE) begin $display("\n┌────────────────────────────────────────────────────────────────────────────────┐"); $display("│ CYCLE %-5d │", cycle); $display("└────────────────────────────────────────────────────────────────────────────────┘"); end else begin $display("\n═══ CYCLE %-5d ═══", cycle); end // ============ SEQUENCER ============ if (!COMPACT_MODE) $display("\n┌─ SEQUENCER ───────────────────────────────────────────────────────────────────┐"); else $display("\n[SEQ]"); $display(" PC=0x%03h → 0x%03h RA=0x%03h %s %s%s%s", dut.sequencer_inst.pc, dut.seq_y, dut.sequencer_inst.ra, seq_op_name(dut.u_seq_op), dut.u_seq_src ? "MAP" : "IMM", dut.u_seq_cce ? $sformatf(" CC=%b", dut.seq_cc) : "", dut.halt ? " [HALT]" : ""); if (!COMPACT_MODE) $display("└───────────────────────────────────────────────────────────────────────────────┘"); // ============ MICROCODE ============ if (!COMPACT_MODE) $display("\n┌─ MICROCODE @ 0x%03h ──────────────────────────────────────────────────────────┐", dut.sequencer_inst.pc); else $display("\n[UCODE @ 0x%03h]", dut.sequencer_inst.pc); $display(" Raw: 0x%09h IMM=0x%03h(%0d)", dut.uinst, dut.u_imm, dut.u_imm); // Decode and display operation $write(" Op: "); // Register write operation if (dut.u_rf_we && !dut.halt) begin $write("%s ← ", reg_name(dut.u_rf_rd)); if (dut.u_alu_src) $write("%s(%s,%s)[RMA[3:0]=0x%h]", alu_op_name(dut.rf_rma_data[3:0]), reg_name(dut.u_rf_rs1), reg_name(dut.u_rf_rs2), dut.rf_rma_data[3:0]); else $write("%s(%s,%s)", alu_op_name(dut.u_alu_op), reg_name(dut.u_rf_rs1), reg_name(dut.u_rf_rs2)); end // Memory operation if (dut.u_mem_act != 2'b00) begin if (dut.u_rf_we && !dut.halt) $write("; "); if (dut.u_mem_dir) $write("MEM[%s]=MDR %s", rma_name(dut.u_rf_rma), mem_act_name(dut.u_mem_act)); else $write("MDR←MEM[%s] %s", rma_name(dut.u_rf_rma), mem_act_name(dut.u_mem_act)); end if (!dut.u_rf_we && dut.u_mem_act == 2'b00) $write("NOP"); $display(""); if (!COMPACT_MODE) $display("└───────────────────────────────────────────────────────────────────────────────┘"); // ============ REGISTERS ============ if (!COMPACT_MODE) $display("\n┌─ REGISTERS ───────────────────────────────────────────────────────────────────┐"); else $display("\n[REGS]"); // Show registers with change indicators for (integer i = 0; i < 7; i = i + 1) begin if (SHOW_UNCHANGED_REGS || (i > 3 ? dut.register_file_inst.data_registers[i % 4] : dut.register_file_inst.addr_registers[i % 4]) != prev_regs[i] || (dut.u_rf_rd == i && dut.rf_we && !dut.halt)) begin $write(" %s: 0x%08h", reg_name(i), (i > 3 ? dut.register_file_inst.data_registers[i % 4] : dut.register_file_inst.addr_registers[i % 4])); if ((i > 3 ? dut.register_file_inst.data_registers[i % 4] : dut.register_file_inst.addr_registers[i % 4]) != prev_regs[i]) $write(" ← 0x%08h", prev_regs[i]); $display(""); prev_regs[i] = (i > 3 ? dut.register_file_inst.data_registers[i % 4] : dut.register_file_inst.addr_registers[i % 4]); end end if (!COMPACT_MODE) $display("└───────────────────────────────────────────────────────────────────────────────┘"); // ============ ALU ============ if (!COMPACT_MODE) $display("\n┌─ ALU ─────────────────────────────────────────────────────────────────────────┐"); else $display("\n[ALU]"); $display(" %s: A=0x%08h B=0x%08h Y=0x%08h Z=%b", alu_op_name(dut.alu_op), dut.alu_a, dut.alu_b, dut.alu_y, dut.alu_zero); if (!COMPACT_MODE) $display("└───────────────────────────────────────────────────────────────────────────────┘"); // ============ MEMORY CONTENTS ============ if (SHOW_MEMORY_CONTENTS && dut.u_mem_act != 2'b00) begin if (!COMPACT_MODE) $display("\n┌─ MEMORY ACCESS ──────────────────────────────────────────────────────────────┐"); else $display("\n[MEM]"); $display(" Addr: 0x%08h %s %s", dut.rf_rma_data, dut.u_mem_dir ? "WRITE" : "READ ", mem_act_name(dut.u_mem_act)); if (dut.u_mem_dir) $display(" Data: 0x%08h (from MDR)", dut.rf_mdr_data_r); else $display(" Data: 0x%08h (to MDR)", dut.srr_data); if (!COMPACT_MODE) $display("└───────────────────────────────────────────────────────────────────────────────┘"); end // ============ STACK CONTEXT ============ if (SHOW_MEMORY_CONTENTS && !COMPACT_MODE) begin $display("\n┌─ STACK CONTEXT ──────────────────────────────────────────────────────────────┐"); $display(" Data Stack (DSP=0x%08h):", dut.register_file_inst.registers[1]); $display(" Return Stack (RSP=0x%08h):", dut.register_file_inst.registers[2]); $display("└───────────────────────────────────────────────────────────────────────────────┘"); end // ============ OUTPUT ============ if (led != 0 || cycle < 10) begin if (!COMPACT_MODE) $display("\n┌─ OUTPUT ──────────────────────────────────────────────────────────────────────┐"); else $display("\n[OUT]"); $display(" LED: 0x%02h (0b%06b)", led, led); if (!COMPACT_MODE) $display("└───────────────────────────────────────────────────────────────────────────────┘"); end end end // ============================================================================ // TEST STIMULUS // ============================================================================ initial begin // Initialize signals rst_n = 0; uart_rx = 1; // Wait for a few cycles repeat(5) @(posedge clk); // Release reset rst_n = 1; $display("\n*** RESET RELEASED ***\n"); // Run for specified number of cycles repeat(500) @(posedge clk); // End simulation $display("\n"); $display("╔════════════════════════════════════════════════════════════════════════════════╗"); $display("║ SIMULATION COMPLETE ║"); $display("║ Total Cycles: %-5d ║", cycle); $display("╚════════════════════════════════════════════════════════════════════════════════╝"); $display("\n"); $finish; end // Waveform dump initial begin $dumpfile("cpu_tb_enhanced.vcd"); $dumpvars(0, cpu_tb_enhanced); end // Timeout watchdog initial begin #200000; // 200us timeout $display("\n*** SIMULATION TIMEOUT ***\n"); $finish; end endmodule