323 lines
15 KiB
Verilog
323 lines
15 KiB
Verilog
`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
|