mirror of
https://github.com/rdolbeau/NuBusFPGA.git
synced 2024-12-22 10:29:53 +00:00
476 lines
15 KiB
Systemverilog
476 lines
15 KiB
Systemverilog
`timescale 1 ns / 1 ps
|
|
|
|
module nubus_slave_tb ();
|
|
|
|
`include "nubus_tb.svh"
|
|
|
|
parameter TEST_CARD_ID = 'hc;
|
|
parameter TEST_ADDR = 'hFc000000;
|
|
parameter TEST_DATA = 'h87654321;
|
|
parameter [1:0] MEMORY_WAIT_CLOCKS = 1;
|
|
parameter DEBUG_NUBUS_START = 0;
|
|
parameter ROM_ADDR = 'hFcFFF000;
|
|
parameter PING_ADDR = 'hFcB00000;
|
|
|
|
// Clock (rising is driving edge, faling is sampling)
|
|
tri1 bd_clk48;
|
|
|
|
// Slot Identification
|
|
tri1 [3:0] nub_idn;
|
|
// Clock (rising is driving edge, faling is sampling)
|
|
tri1 nub_clkn;
|
|
// Clock 90 (rising is driving edge, faling is sampling)
|
|
tri1 nub_clk2xn;
|
|
// Reset [Open Collector]
|
|
tri1 nub_resetn;
|
|
// Power Fail Warning [Control]
|
|
//tri1 nub_pfwn;
|
|
// Address/Data [Address/Data]
|
|
tri1 [31:0] nub_adn;
|
|
// Transfer Mode [Control]
|
|
tri1 nub_tm0n;
|
|
tri1 nub_tm1n;
|
|
tri1 nub_tm2n;
|
|
// Start [Control]
|
|
tri1 nub_startn;
|
|
// Request [Open Collector]
|
|
tri1 nub_rqstn;
|
|
// Acknowledge [Control]
|
|
tri1 nub_ackn;
|
|
// Arbitration [Open Collector]
|
|
tri1 [3:0] nub_arbn;
|
|
// Non-Master Request [Open Collector]
|
|
tri1 nub_nmrqn;
|
|
// System Parity [Address/Data]
|
|
//tri1 nub_spn;
|
|
// System Parity Valid [Address/Data]
|
|
//tri1 nub_spvn;
|
|
|
|
tri1 [1:0] leds;
|
|
|
|
tri unused0, unused1, unused2;
|
|
tri nubus_oe, nubus_ad_dir;
|
|
tri reset_n_3v3, clk_n_3v3, tm0_n_3v3, tm1_n_3v3, start_n_3v3, ack_n_3v3, rqst_n_3v3;
|
|
tri [3:0] id_n_3v3;
|
|
tri [3:0] arb_n_3v3;
|
|
tri [31:0] ad_n_3v3;
|
|
tri [3:0] arb_o_n;
|
|
tri tm0_o_n, tm1_o_n, tmx_oe_n;
|
|
tri start_o_n, start_oe_n;
|
|
tri ack_o_n, ack_oe_n;
|
|
tri rqst_o_n;
|
|
|
|
|
|
tri clk2x_n_3v3;
|
|
tri tm2_n_3v3, tm2_o_n, tm2_oe_n;
|
|
|
|
|
|
|
|
assign nub_idn = ~ TEST_CARD_ID;
|
|
//assign nub_arbn = 'b1111;
|
|
|
|
// actually 74lvt245, same digital function
|
|
sn74fct245 shifters_b0(.data_5v(nub_adn[ 7: 0]),
|
|
.data_3v3(ad_n_3v3[ 7: 0]),
|
|
.nubus_oe(nubus_oe),
|
|
.nubus_ad_dir(nubus_ad_dir));
|
|
sn74fct245 shifters_b1(.data_5v(nub_adn[15: 8]),
|
|
.data_3v3(ad_n_3v3[15: 8]),
|
|
.nubus_oe(nubus_oe),
|
|
.nubus_ad_dir(nubus_ad_dir));
|
|
sn74fct245 shifters_b2(.data_5v(nub_adn[23:16]),
|
|
.data_3v3(ad_n_3v3[23:16]),
|
|
.nubus_oe(nubus_oe),
|
|
.nubus_ad_dir(nubus_ad_dir));
|
|
sn74fct245 shifters_b3(.data_5v(nub_adn[31:24]),
|
|
.data_3v3(ad_n_3v3[31:24]),
|
|
.nubus_oe(nubus_oe),
|
|
.nubus_ad_dir(nubus_ad_dir));
|
|
|
|
tri1 nmrq_n_3v3;
|
|
|
|
|
|
sn74lvt145_quarter driver_u1a(.oe_n(nmrq_n_3v3),
|
|
.in(0),
|
|
.out(nub_nmrqn));
|
|
sn74lvt145_quarter driver_u1b(.oe_n(rqst_o_n),
|
|
.in(0),
|
|
.out(nub_rqstn));
|
|
sn74lvt145_quarter driver_u1c(.oe_n(start_oe_n),
|
|
.in(start_o_n),
|
|
.out(nub_startn));
|
|
sn74lvt145_quarter driver_u1d(.oe_n(ack_oe_n),
|
|
.in(ack_o_n),
|
|
.out(nub_ackn));
|
|
|
|
sn74lvt145_quarter driver_u3a(.oe_n(arb_o_n[0]),
|
|
.in(0),
|
|
.out(nub_arbn[0]));
|
|
sn74lvt145_quarter driver_u3b(.oe_n(arb_o_n[1]),
|
|
.in(0),
|
|
.out(nub_arbn[1]));
|
|
sn74lvt145_quarter driver_u3c(.oe_n(arb_o_n[3]),
|
|
.in(0),
|
|
.out(nub_arbn[3]));
|
|
sn74lvt145_quarter driver_u3d(.oe_n(arb_o_n[2]),
|
|
.in(0),
|
|
.out(nub_arbn[2]));
|
|
|
|
sn74lvt145_quarter driver_u2a(.oe_n(tmx_oe_n),
|
|
.in(tm1_o_n),
|
|
.out(nub_tm1n));
|
|
sn74lvt145_quarter driver_u2b(.oe_n(tmx_oe_n),
|
|
.in(tm0_o_n),
|
|
.out(nub_tm0n));
|
|
sn74lvt145_quarter driver_u2c(.oe_n(tm2_oe_n),
|
|
.in(tm2_o_n),
|
|
.out(nub_tm2n));
|
|
|
|
tri1 [2:0] u13_throwaway;
|
|
tri1 [2:0] u13_zero;
|
|
sn74cb3t3245 shifters_u13(.oe_n('h0),
|
|
.A({clk2x_n_3v3, clk_n_3v3, start_n_3v3, ack_n_3v3, rqst_n_3v3, u13_throwaway}),
|
|
.B({nub_clk2xn, nub_clkn, nub_startn, nub_ackn, nub_rqstn, u13_zero}));
|
|
sn74cb3t3245 shifters_u14(.oe_n('h0),
|
|
.A({id_n_3v3, arb_n_3v3 }),
|
|
.B({nub_idn, nub_arbn }));
|
|
tri1 [3:0] u15_throwaway;
|
|
tri1 [3:0] u15_zero;
|
|
sn74cb3t3245 shifters_u15(.oe_n('h0),
|
|
.A({ reset_n_3v3, tm2_n_3v3, tm0_n_3v3, tm1_n_3v3, u15_throwaway }),
|
|
.B({ nub_resetn, nub_tm2n, nub_tm0n, nub_tm1n, u15_zero }));
|
|
|
|
ztex213_nubus_V1_2 UNuBus (
|
|
// NuBus lines only
|
|
.clk48(bd_clk48),
|
|
.clk_3v3_n(clk_n_3v3),
|
|
.reset_3v3_n(reset_n_3v3),
|
|
.user_led0(leds[0]),
|
|
.user_led1(leds[1]),
|
|
.id_3v3_n(id_n_3v3),
|
|
.ad_3v3_n(ad_n_3v3),
|
|
.tm0_3v3_n(tm0_n_3v3),
|
|
.tm1_3v3_n(tm1_n_3v3),
|
|
.tm0_o_n(tm0_o_n),
|
|
.tm1_o_n(tm1_o_n),
|
|
.tmx_oe_n(tmx_oe_n),
|
|
.start_3v3_n(start_n_3v3),
|
|
.start_o_n(start_o_n),
|
|
.start_oe_n(start_oe_n),
|
|
.rqst_3v3_n(rqst_n_3v3),
|
|
.rqst_o_n(rqst_o_n),
|
|
.nmrq_3v3_n(nmrq_n_3v3), // output only, direct to driver
|
|
.ack_3v3_n(ack_n_3v3),
|
|
.ack_o_n(ack_o_n),
|
|
.ack_oe_n(ack_oe_n),
|
|
.arb_3v3_n(arb_n_3v3),
|
|
.arb_o_n(arb_o_n),
|
|
.nubus_ad_dir(nubus_ad_dir),
|
|
.nubus_oe(nubus_oe),
|
|
.clk2x_3v3_n(clk2x_n_3v3),
|
|
.tm2_3v3_n(tm2_n_3v3),
|
|
.tm2_o_n(tm2_o_n),
|
|
.tm2_oe_n(tm2_oe_n)
|
|
);
|
|
|
|
|
|
// State machine of test bench
|
|
reg tst_clkn;
|
|
reg tst_clk2xn;
|
|
reg tst_clk48;
|
|
reg tst_resetn;
|
|
reg tst_startn;
|
|
reg tst_ackn; // half clkn delayed ackn
|
|
reg [1:0] tst_tmn;
|
|
reg [1:0] tst_statusn;
|
|
reg [31:0] tst_addrn;
|
|
reg [31:0] tst_wdatan;
|
|
reg [31:0] tst_rdatan;
|
|
reg tst_rqstn;
|
|
|
|
reg mastermode_start;
|
|
reg mastermode_tmack;
|
|
|
|
assign nub_clkn = tst_clkn;
|
|
assign nub_clk2xn = tst_clk2xn;
|
|
assign bd_clk48 = tst_clk48;
|
|
assign nub_resetn = tst_resetn;
|
|
assign nub_rqstn = tst_rqstn;
|
|
// Drive NuBus signals
|
|
assign nub_startn = mastermode_start ? 'bZ: tst_startn;
|
|
assign nub_tm0n = (tst_startn & ~mastermode_tmack) ? 'bZ : tst_tmn[0];
|
|
assign nub_tm1n = (tst_startn & ~mastermode_tmack) ? 'bZ : tst_tmn[1];
|
|
assign nub_ackn = (tst_startn & ~mastermode_tmack) ? 'bZ : tst_ackn;
|
|
|
|
// Drive NuBus address/data lines
|
|
wire [31:0] tst_adn = tst_startn ? tst_wdatan : tst_addrn;
|
|
wire tst_nuboen = (tst_startn & tst_tmn[1]) | mastermode_start;
|
|
assign nub_adn = tst_nuboen ? 'bZ : tst_adn;
|
|
|
|
// Inverted verions of registers
|
|
wire [31:0] tst_rdata = ~tst_rdatan;
|
|
wire [31:0] tst_addr = ~tst_addrn;
|
|
|
|
initial begin
|
|
$display ("Start virtual master (vm) writes and reads to/from NuBus slave memory module");
|
|
$dumpfile("nubus_slave_tb.vcd");
|
|
$dumpvars;
|
|
#1;
|
|
|
|
mastermode_start <= 0;
|
|
mastermode_tmack <= 0;
|
|
|
|
tst_clkn <= 1;
|
|
tst_resetn <= 0;
|
|
tst_rqstn <= 'bz;
|
|
tst_addrn <= 'hFFFFFFFF;
|
|
tst_wdatan <= 'hFFFFFFFF;
|
|
tst_rdatan <= 'hFFFFFFFF;
|
|
tst_startn <= 1;
|
|
tst_statusn<= TMN_TRY_AGAIN_LATER;
|
|
tst_tmn <= TMN_NOP;
|
|
|
|
@ (posedge nub_clkn);
|
|
@ (posedge nub_clkn);
|
|
tst_resetn <= 1;
|
|
|
|
#2000;
|
|
|
|
@ (posedge nub_clkn);
|
|
$display ("%g: %b", $time, nub_startn);
|
|
|
|
$display ("WORD ---------------------------");
|
|
write_word(TMADN_WR_WORD, TEST_ADDR+0, TEST_DATA);
|
|
read_word (TMADN_RD_WORD, TEST_ADDR+0);
|
|
check_word(TMADN_RD_WORD, TEST_DATA);
|
|
$display ("HALF 0 -------------------------");
|
|
write_word(TMADN_WR_HALF_0, TEST_ADDR+4, TEST_DATA);
|
|
read_word (TMADN_RD_HALF_0, TEST_ADDR+4);
|
|
check_word(TMADN_RD_HALF_0, TEST_DATA);
|
|
$display ("HALF 1 -------------------------");
|
|
write_word(TMADN_WR_HALF_1, TEST_ADDR+8, TEST_DATA);
|
|
read_word (TMADN_RD_HALF_1, TEST_ADDR+8);
|
|
check_word(TMADN_RD_HALF_1, TEST_DATA);
|
|
|
|
$display ("BYTE 0 -------------------------");
|
|
write_word(TMADN_WR_BYTE_0, TEST_ADDR+12, TEST_DATA);
|
|
read_word (TMADN_RD_BYTE_0, TEST_ADDR+12);
|
|
check_word(TMADN_RD_BYTE_0, TEST_DATA);
|
|
$display ("BYTE 1 -------------------------");
|
|
write_word(TMADN_WR_BYTE_1, TEST_ADDR+16, TEST_DATA);
|
|
read_word (TMADN_RD_BYTE_1, TEST_ADDR+16);
|
|
check_word(TMADN_RD_BYTE_1, TEST_DATA);
|
|
$display ("BYTE 2 -------------------------");
|
|
write_word(TMADN_WR_BYTE_2, TEST_ADDR+20, TEST_DATA);
|
|
read_word (TMADN_RD_BYTE_2, TEST_ADDR+20);
|
|
check_word(TMADN_RD_BYTE_2, TEST_DATA);
|
|
$display ("BYTE 3 -------------------------");
|
|
write_word(TMADN_WR_BYTE_3, TEST_ADDR+24, TEST_DATA);
|
|
read_word (TMADN_RD_BYTE_3, TEST_ADDR+24);
|
|
check_word(TMADN_RD_BYTE_3, TEST_DATA);
|
|
|
|
// $display ("BLOCK2 -------------------------");
|
|
// read_block2 (TMADN_RD_BLOCK, TEST_ADDR);
|
|
|
|
#500
|
|
|
|
// Check Rom
|
|
$display ("ROM ---------------------------");
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+4092);
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+4088);
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+4084);
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+4080);
|
|
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+0);
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+4);
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+8);
|
|
read_word (TMADN_RD_WORD, ROM_ADDR+12);
|
|
|
|
#1000;
|
|
|
|
// check PingMaster
|
|
$display ("PING ---------------------------");
|
|
write_word(TMADN_WR_WORD, PING_ADDR+0, 'h00C0FFEE);
|
|
read_word (TMADN_RD_WORD, PING_ADDR+0);
|
|
write_word(TMADN_WR_WORD, PING_ADDR+4, 'h00096240);
|
|
//read_word (TMADN_RD_WORD, ROM_ADDR+0);
|
|
|
|
mastermode_start <= 1;
|
|
mastermode_tmack <= 0;
|
|
tst_ackn <= 1;
|
|
|
|
@ (negedge nub_startn);
|
|
#1
|
|
$display ("GOT START ---------------------------");
|
|
$display ("%g (received ) address: $%h", $time, ~nub_adn);
|
|
@ (negedge nub_clkn);
|
|
#1
|
|
@ (negedge nub_clkn);
|
|
#1
|
|
@ (negedge nub_clkn);
|
|
#1
|
|
$display ("%g (received ) data: $%h", $time, ~nub_adn);
|
|
@ (posedge nub_clkn);
|
|
mastermode_tmack <= 1;
|
|
tst_ackn <= 0;
|
|
tst_tmn <= TMN_COMPLETE;
|
|
|
|
@ (posedge nub_clkn);
|
|
mastermode_start <= 0;
|
|
mastermode_tmack <= 0;
|
|
|
|
#2000;
|
|
|
|
|
|
$finish;
|
|
end
|
|
|
|
|
|
// ======================================================
|
|
// Write task
|
|
// ======================================================
|
|
|
|
task write_word;
|
|
input [3:0] tmadn;
|
|
input [31:0] addr;
|
|
input [31:0] data;
|
|
begin
|
|
@ (posedge nub_clkn);
|
|
tst_wdatan <= ~data;
|
|
tst_addrn[31:2] <= ~addr[31:2];
|
|
tst_addrn[ 1:0] <= tmadn[1:0];
|
|
tst_tmn <= tmadn[3:2];
|
|
tst_startn <= 0;
|
|
tst_ackn <= 1;
|
|
//tst_statusn <= TMN_TRY_AGAIN_LATER;
|
|
@ (posedge nub_clkn);
|
|
tst_startn <= 1;
|
|
tst_ackn <= nub_ackn;
|
|
do begin
|
|
@ (negedge nub_clkn);
|
|
tst_ackn <= nub_ackn;
|
|
tst_statusn <= { nub_tm1n, nub_tm0n };
|
|
//@ (posedge nub_clkn);
|
|
end while (tst_ackn) ;
|
|
$display ("%g (write) address: $%h tm: $%h data: $%h stat: %s", $time, addr, tmadn, data, get_status_str(tst_statusn));
|
|
end
|
|
endtask
|
|
|
|
// ======================================================
|
|
// Read task
|
|
// ======================================================
|
|
|
|
task read_word;
|
|
input [3:0] tmadn;
|
|
input [31:0] addr;
|
|
begin
|
|
@ (posedge nub_clkn);
|
|
tst_tmn <= tmadn[3:2];
|
|
tst_addrn[ 1:0] <= tmadn[1:0];
|
|
tst_addrn[31:2] <= ~addr[31:2];
|
|
tst_startn <= 0;
|
|
tst_ackn <= 1;
|
|
//tst_statusn <= TMN_TRY_AGAIN_LATER;
|
|
@ (posedge nub_clkn);
|
|
tst_startn <= 1;
|
|
tst_ackn <= nub_ackn;
|
|
do begin
|
|
@ (negedge nub_clkn);
|
|
tst_rdatan <= nub_adn;
|
|
tst_ackn <= nub_ackn;
|
|
tst_statusn <= { nub_tm1n, nub_tm0n };
|
|
//@ (posedge nub_clkn);
|
|
end while (tst_ackn) ;
|
|
$display ("%g (read ) address: $%h tm: $%h data: $%h stat: %s", $time, addr, tmadn, tst_rdata, get_status_str(tst_statusn));
|
|
end
|
|
endtask
|
|
|
|
// ======================================================
|
|
// Verify data writen to memory with read from
|
|
// asume memory befor write was $00000000
|
|
// ======================================================
|
|
|
|
task check_word
|
|
(
|
|
input [3:0] tm,
|
|
input [31:0] data_wr
|
|
);
|
|
reg [31:0] expected;
|
|
begin
|
|
expected = (data_wr & get_mask(tm));
|
|
if (tst_rdata == expected)
|
|
$display (":) PASSED");
|
|
else
|
|
$display (":( FAILED expected: $%h found: $%h", expected, tst_rdata);
|
|
$display(" ");
|
|
end
|
|
endtask // verify
|
|
|
|
// ======================================================
|
|
// Read block2 task
|
|
// Currently unsupported (introduced with Q700/Q900, not in the NTC)
|
|
// ======================================================
|
|
|
|
task read_block2;
|
|
input [3:0] tmadn;
|
|
input [31:0] addr;
|
|
begin
|
|
@ (posedge nub_clkn);
|
|
tst_tmn <= tmadn[3:2];
|
|
tst_addrn[ 1:0] <= tmadn[1:0];
|
|
tst_addrn[ 2:2] <= 1; // this indicates size 2
|
|
tst_addrn[31:3] <= ~addr[31:3];
|
|
tst_startn <= 0;
|
|
//tst_statusn <= TMN_TRY_AGAIN_LATER;
|
|
@ (posedge nub_clkn);
|
|
tst_startn <= 1;
|
|
tst_ackn <= nub_ackn;
|
|
do begin
|
|
@ (negedge nub_clkn);
|
|
tst_rdatan <= nub_adn;
|
|
tst_ackn <= nub_ackn;
|
|
tst_statusn <= { nub_tm1n, nub_tm0n };
|
|
//@ (posedge nub_clkn);
|
|
end while (tst_statusn[0]) ;
|
|
$display ("%g (block0/2) address: $%h tm: $%h data: $%h stat: %s", $time, addr, tmadn, tst_rdata, get_status_str(tst_statusn));
|
|
do begin
|
|
@ (negedge nub_clkn);
|
|
tst_rdatan <= nub_adn;
|
|
tst_ackn <= nub_ackn;
|
|
tst_statusn <= { nub_tm1n, nub_tm0n };
|
|
//@ (posedge nub_clkn);
|
|
end while (tst_ackn) ;
|
|
$display ("%g (block1/2) address: $%h tm: $%h data: $%h stat: %s", $time, addr, tmadn, tst_rdata, get_status_str(tst_statusn));
|
|
end
|
|
endtask // read block2
|
|
|
|
// ======================================================
|
|
// Clock generators
|
|
// ======================================================
|
|
|
|
always begin
|
|
tst_clkn <= 1;
|
|
#75.075;
|
|
tst_clkn <= 0;
|
|
if (DEBUG_NUBUS_START) begin
|
|
if (~nub_startn)
|
|
$display ("%g (NuBus Start) /ad: $%h {/tmadn}: %b%b%b%b", $time, nub_adn, nub_tm1n, nub_tm0n, nub_adn[1], nub_adn[0]);
|
|
end
|
|
#25.025;
|
|
end
|
|
always begin
|
|
tst_clk2xn <= 0;
|
|
#25.025;
|
|
tst_clk2xn <= 1;
|
|
#25.025;
|
|
end
|
|
|
|
always begin
|
|
tst_clk48 <= 0;
|
|
#10.41666666;
|
|
tst_clk48 <= 1;
|
|
#10.41666666;
|
|
end
|
|
|
|
endmodule
|