// // Copyright (c) 2014 by 1801BM1@gmail.com // // Testbench for the 1801BM2 replica, native QBUS version //______________________________________________________________________________ // `include "../../tbe/config.v" //______________________________________________________________________________ // module memory ( input [15:0] a, input [15:0] din, output reg [15:0] dout, input wtbt, input read, input write ); reg [15:0] mem [0:8191]; integer i; always @ (posedge read) begin dout = mem[a[13:1]]; end always @ (negedge write) begin if (wtbt) begin if (a[0]) mem[a[13:1]][15:8] = din[15:8]; else mem[a[13:1]][7:0] = din[7:0]; end else mem[a[13:1]] = din; end initial begin for (i=0; i<8191; i = i + 1) begin mem[i] = 16'h0000; end $readmemh("../../../../tst/out/test.mem", mem); end endmodule //______________________________________________________________________________ // // Primary testbench top module // module tb2(); integer i, i0; wire clko; // internal clock reg clk; // processor clock reg clk_f1; // reg clk_f2; // // reg sp; // peripheral timer input reg dclo; // processor reset reg aclo; // power fail notificaton reg evnt; // timer interrupt requests reg halt; // radial interrupt requests reg virq; // vectored interrupt request // reg ar; // reg waki; // wire wrq; // tri1 init; // peripheral reset // wire [15:0] ad_mux; // tri1 [15:0] ad; // inverted address/data bus reg [15:0] ad_reg; // reg ad_oe; // // tri1 din; // data input strobe tri1 dout; // data output strobe tri1 wtbt; // write/byte status tri1 sync; // address strobe // reg rply; // transaction reply reg dmr; // bus request shared line reg sack; // bus acknowlegement wire dmgo; // bus grant output wire iako; // interrupt vector strobe wire sel; // reg din_iako; // reg din_sel; // reg din_sync; // // reg [15:0] addr; // reg wflg; // reg sel_all; // reg sel_ram; // // reg tty_tx_rdy; // terminal transmitter ready reg tty_tx_ie; // terminal transmitter interrupt enable reg tty_rx_ie; // terminal receiver interrupt enable // wire ram_read, ram_write; //_____________________________________________________________________________ // assign ad = ad_oe ? (sel_ram ? ~ad_mux : ~ad_reg) : 16'hZZZZ; assign ram_read = sel_ram & ~din & ~sync; assign ram_write = sel_ram & ~dout & ~sync; memory ram( .a(addr), .din(~ad), .dout(ad_mux), .wtbt(~wtbt), .read(ram_read), .write(ram_write)); always @(negedge sync) begin rply = 1'b1; addr <= ~ad; wflg <= ~wtbt; if (~ad < 16'o040000) begin sel_all = 1'b1; sel_ram = 1'b1; end if (~ad == 16'o177714) // 7-segment outhex sel_all = 1'b1; if (~ad == 16'o177715) sel_all = 1'b1; if (~ad == 16'o177560) sel_all = 1'b1; if (~ad == 16'o177564) sel_all = 1'b1; if (~ad == 16'o177566) sel_all = 1'b1; if (~sel & (~ad == 16'o000172)) begin $display("Access to halt vector"); $finish; end end always @(posedge sync) begin sel_all = 1'b0; sel_ram = 1'b0; end always @(negedge din) begin din_iako = iako; din_sel = sel; din_sync = sync; `ifdef SIM_CONFIG_DEBUG_IO @(posedge din) if (~din_sync) $display("Read @ %06O (%06O)", addr, ~ad); else if (~din_sel) $display("Read @ Unadressed"); else if (~din_iako) $display("Read @ Vector"); else $display("Read @ Invalid"); `endif end always @(posedge dout) begin `ifdef SIM_CONFIG_DEBUG_IO $display("Write @ %06O (%06O)", addr, ~ad); `endif end always @(posedge din or posedge dout) begin @ (negedge clk); @ (posedge clk); #2 ad_oe = 1'b0; rply = 1'b1; end always @(negedge din) begin if (~sync) begin ar = 1; if (addr == 16'o177560) begin ad_oe = 1'b1; ad_reg = 16'o000200 | (tty_rx_ie << 6); end if (addr == 16'o177564) begin ad_oe = 1'b1; ad_reg = (tty_tx_rdy << 7) | (tty_tx_ie << 6); end if (addr == 16'o177566) begin ad_oe = 1'b1; ad_reg = 16'o000000; end if (sel_all) begin for (i0=0; i0<`SIM_CONFIG_PREFETCH_BUG; i0 = i0 + 1) begin @ (negedge clk); @ (posedge clk); end #2 rply = 1'b0; ad_oe = 1'b1; end end end always @(negedge iako) begin if (~din) begin if (~virq) begin ad_oe = 1'b1; ad_reg = 16'o000064; @ (negedge clk); //@ (posedge clk); #2 virq = 1; rply = 1'b0; end end end always @(negedge dout) begin ar = 1; if (sel_all) begin //@ (negedge clk); //@ (posedge clk); #2 rply = 1'b0; end if (addr == 16'o177560) begin tty_rx_ie = ~ad[6]; end if (addr == 16'o177564) begin tty_tx_ie = ~ad[6]; if (tty_tx_rdy & tty_tx_ie) virq = 0; else virq = 1; end if (addr == 16'o177566) begin tty_tx_rdy = 0; virq = 1; `ifdef SIM_CONFIG_DEBUG_TTY $display("tty: %06O (%c)", ~ad, (~ad > 16'o000037) ? (~ad & 8'o377) : 8'o52); `endif end if (addr == 16'o177676) begin $display("Access to shadow register"); $finish; end end always @(negedge sync) ar = 0; always @(posedge sync) ar = 1; //_____________________________________________________________________________ // // Terminal registers simulation // always @(negedge init) begin tty_tx_ie = 0; tty_rx_ie = 0; tty_tx_rdy = 1; virq = 1; end always @(negedge tty_tx_rdy) begin for (i=0; i<256; i = i + 1) begin @ (negedge clk); @ (posedge clk); end tty_tx_rdy = 1; if (tty_tx_ie) virq = 0; end //_____________________________________________________________________________ // // Clock generator // initial begin clk_f1 = 1; clk_f2 = 1; clk = 0; forever begin clk = 0; clk_f1 = 0; #(`SIM_CONFIG_CLOCK_HPERIOD); clk = 1; clk_f2 = 0; #(`SIM_CONFIG_CLOCK_HPERIOD); clk = 0; clk_f1 = 1; #(`SIM_CONFIG_CLOCK_HPERIOD); clk = 1; clk_f2 = 1; #(`SIM_CONFIG_CLOCK_HPERIOD); end end //_____________________________________________________________________________ // // Simulation time limit (first breakpoint) // initial begin #`SIM_CONFIG_TIME_LIMIT $finish; end //_____________________________________________________________________________ // initial begin tty_tx_ie = 0; tty_rx_ie = 0; tty_tx_rdy = 1; waki = 0; ar = 1; dclo = 0; aclo = 0; evnt = 1; virq = 1; halt = 1; ad_reg = ~16'h0000; ad_oe = 0; rply = 1; sack = 1; dmr = 1; // // CPU start // @ (negedge clk); @ (negedge clk); @ (negedge clk); @ (negedge clk); dclo = 1; @ (negedge clk); @ (negedge clk); @ (negedge clk); @ (negedge clk); @ (negedge clk); @ (negedge clk); @ (negedge clk); @ (negedge clk); #1 aclo = 1; $display("Processor ACLO and DCLO deasserted"); end //_____________________________________________________________________________ // vm2 cpu ( .pin_clk_p(clk_f1), // processor clock, rising edge f1 .pin_clk_n(~clk_f1), // processor clock, falling edge f1 .pin_init_n(init), // peripheral reset .pin_dclo_n(dclo), // processor reset .pin_aclo_n(aclo), // power fail notificaton .pin_halt_n(halt), // halt mode request .pin_evnt_n(evnt), // timer interrupt requests .pin_virq_n(virq), // vectored interrupt request // .pin_ar_n(ar) , // address ready strobe .pin_dmr_n(dmr), // bus request line .pin_sack_n(sack), // bus acknowlegement .pin_rply_n(rply), // transaction reply // .pin_waki_n(waki), // window access granted .pin_wrq_n(wrq), // window access request (open drain) .pin_clko(clko), // internal core clock output .pin_dmgo_n(dmgo), // bus granted output // .pin_ad_n(ad), // inverted address/data bus .pin_sel_n(sel), // register select outputs .pin_sync_n(sync), // address strobe .pin_wtbt_n(wtbt), // write/byte status .pin_dout_n(dout), // data output strobe .pin_din_n(din), // data input strobe .pin_iako_n(iako) // interrupt vector input ); `ifdef SIM_CONFIG_DEBUG_MC //_____________________________________________________________________________ // // PLM debug decoded log // reg [3*8:0] type; reg [4*8:0] word; reg [5*8:0] wdir; reg [4*8:0] op; reg [3*8:0] sh; reg [4*8:0] qbus; reg [6*8:0] xr; reg [6*8:0] yr; reg [6*8:0] vec; reg [4*8:0] sta; reg [5:0] pa; reg [2:0] pix; reg [2:0] pri; reg [5:0] pia; always @ (posedge cpu.core.mc_stb) begin pix = cpu.core.ix; pri = cpu.core.ri; pia = cpu.core.ia; end always @ (negedge cpu.core.mc_stb) begin if (cpu.core.plm[1]) word = "word"; else word = "byte"; vec = "------"; sta = "----"; case({cpu.core.plm[0], cpu.core.plm[8]}) 2'b00: type = "se0"; 2'b01: type = "se1"; 2'b10: type = "op2"; 2'b11: type = "op1"; default: type = "***"; endcase if (cpu.core.plm[0]) begin case({cpu.core.plm[26], cpu.core.plm[25]}) 2'b00: sta = "nzvc"; 2'b01: sta = "nzv-"; 2'b10: sta = cpu.core.na[0] ? "cend" : "wsta"; default: sta = "****"; endcase case({cpu.core.plm[2], cpu.core.plm[3], cpu.core.plm[29]}) 3'b000: wdir = " *y"; 3'b001: wdir = " *x"; 3'b010: wdir = " ac"; 3'b011: wdir = "f2 *x"; 3'b100: wdir = "-----"; 3'b101: wdir = "fr *x"; 3'b110: wdir = "fw ac"; 3'b111: wdir = "fw *x"; default: type = "******"; endcase case({cpu.core.plm[13], cpu.core.plm[14], cpu.core.plm[15], cpu.core.plm[16], cpu.core.plm[17]}) 5'b00000: op = " y"; 5'b00001: op = " ~y"; 5'b01000: op = " ~x"; 5'b01001: op = " x"; 5'b10001: op = " x-y"; 5'b11000: op = " y-x"; 5'b11001: op = " x+y"; 5'b10000: op = " x^y"; 5'b10010: op = " x|y"; 5'b10100: op = " x&y"; 5'b10101: op = "x&~y"; default: op = "****"; endcase case({cpu.core.plm[18], cpu.core.plm[19], cpu.core.plm[20]}) 3'b001: sh = "asl"; 3'b010: sh = "asr"; 3'b011: sh = "---"; 3'b101: sh = "rol"; 3'b110: sh = "ror"; 3'b110: sh = "xch"; default: sh = "***"; endcase // // Inverted IOS opcode (related to original docs) // case({~cpu.core.plm[21], ~cpu.core.plm[22], ~cpu.core.plm[23], ~cpu.core.plm[24]}) 4'b0000: qbus = "----"; 4'b0010: qbus = "wdat"; 4'b0100: qbus = "rcmd"; 4'b0101: qbus = "rdat"; 4'b0111: qbus = " rmw"; 4'b1001: qbus = "dcop"; 4'b1010: qbus = "wsha"; 4'b1100: qbus = "rsha"; 4'b1101: qbus = "runa"; 4'b1110: qbus = "pref"; 4'b1111: qbus = "ivec"; default: qbus = "****"; endcase case({cpu.core.plm[4], cpu.core.plm[5], cpu.core.plm[6], cpu.core.plm[7]}) 4'b0000: xr = " R0 "; 4'b0001: xr = " R1 "; 4'b0010: xr = " R2 "; 4'b0011: xr = " R3 "; 4'b0100: xr = " R4 "; 4'b0101: xr = " R5 "; 4'b0110: xr = " R6 "; 4'b0111: xr = " R7 "; 4'b1000: xr = "EA_RA1"; 4'b1001: xr = "EA_RA2"; 4'b1010: xr = "EA_CNT"; 4'b1011: xr = " R_SRC"; 4'b1100: xr = " PSW "; 4'b1101: xr = " ACC "; 4'b1110: xr = " AREG "; 4'b1111: xr = " QREG "; default: xr = "******"; endcase if (cpu.core.plm[8]) begin case({cpu.core.plm[9], cpu.core.plm[10], cpu.core.plm[11], cpu.core.plm[12]}) 4'b0000: yr = " cond"; 4'b0001: yr = "bxxoff"; 4'b0010: yr = "000001"; 4'b0011: yr = "000002"; 4'b0100: yr = "000000"; 4'b0101: yr = "000004"; 4'b0110: yr = "00cpsw"; 4'b0111: yr = " bir"; 4'b1000: yr = "br_off"; 4'b1001: yr = "signex"; 4'b1010: yr = "00000c"; 4'b1011: yr = "000020"; 4'b1100: yr = "vector"; 4'b1101: yr = "000024"; 4'b1110: yr = " cpc"; 4'b1111: yr = " cpc"; default: yr = "******"; endcase if ({cpu.core.plm[9], cpu.core.plm[10], cpu.core.plm[11], cpu.core.plm[12]} == 4'b1100) begin case(cpu.core.vsel[3:0]) 4'b0000: vec = "000030"; 4'b0001: vec = "000020"; 4'b0010: vec = "000010"; 4'b0011: vec = "000014"; 4'b0100: vec = "000004"; 4'b0101: vec = "000174"; 4'b0110: vec = "000000"; 4'b1000: vec = "000250"; 4'b1001: vec = "000024"; 4'b1010: vec = "000100"; 4'b1011: vec = "000170"; 4'b1100: vec = "000034"; 4'b1101: vec = "000274"; default: vec = "******"; endcase end end else case({cpu.core.plm[9], cpu.core.plm[10], cpu.core.plm[11], cpu.core.plm[12]}) 4'b0000: yr = " R0 "; 4'b0001: yr = " R1 "; 4'b0010: yr = " R2 "; 4'b0011: yr = " R3 "; 4'b0100: yr = " R4 "; 4'b0101: yr = " R5 "; 4'b0110: yr = " R6 "; 4'b0111: yr = " R7 "; 4'b1000: yr = "EA_RA1"; 4'b1001: yr = "EA_RA2"; 4'b1010: yr = "EA_CNT"; 4'b1011: yr = " R_SRC"; 4'b1100: yr = " PSW "; 4'b1101: yr = " ACC "; 4'b1110: yr = " AREG "; 4'b1111: yr = " QREG "; default: yr = "******"; endcase end else begin qbus =" n/a"; wdir =" n/a"; op =" n/a"; sh = "n/a"; xr = " n/a"; yr = " n/a"; end $display("plm[%02X->%02X] %s %s %s %s %s %s %s %s %s %s %06O %06O %06O ir:%06O %1O %1O %02X", pa, cpu.core.na, type, word, wdir, sta, xr, yr, vec, op, sh, qbus, cpu.core.cpc, cpu.core.pc1, cpu.core.pc2, cpu.core.ireg, pix, pri, pia); pa = cpu.core.na; end `endif //_____________________________________________________________________________ // endmodule