238 lines
7.4 KiB
Verilog
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
|