Commit 3e8e0ae4 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

testbench: uploaded system-level testbenench for SVEC

parent a21d0b54
`timescale 10fs/10fs
`include "acam_model.svh"
`include "tunable_clock_gen.svh"
`include "random_pulse_gen.svh"
`include "jittery_delay.svh"
`include "mc100ep195.vh"
`include "wb/simdrv_defs.svh"
`include "wb/if_wb_master.svh"
`timescale 10fs/10fs
module trivial_spi_gpio(input sclk, cs_n, mosi, output reg [7:0] gpio);
int bit_count = 0;
reg [7:0] sreg;
always@(negedge cs_n)
bit_count <= 0;
always@(posedge sclk)
begin
bit_count <= bit_count + 1;
sreg <= { sreg[6:0], mosi };
end
always@(posedge cs_n)
if(bit_count == 24)
gpio <= sreg[7:0];
initial gpio = 0;
endmodule // trivial_spi
/* Board-level wrapper */
interface IFineDelayFMC;
wire tdc_start_p;
wire tdc_start_n;
wire clk_ref_p;
wire clk_ref_n;
wire trig_a;
wire tdc_cal_pulse;
wire [27:0] tdc_d;
wire tdc_emptyf;
wire tdc_alutrigger;
wire tdc_wr_n;
wire tdc_rd_n;
wire tdc_oe_n;
wire led_trig;
wire tdc_start_dis;
wire tdc_stop_dis;
wire spi_cs_dac_n;
wire spi_cs_pll_n;
wire spi_cs_gpio_n;
wire spi_sclk;
wire spi_mosi;
wire spi_miso;
wire [3:0] delay_len;
wire [9:0] delay_val;
wire [3:0] delay_pulse;
wire dmtd_clk;
wire dmtd_fb_in;
wire dmtd_fb_out;
wire pll_status;
wire ext_rst_n;
wire onewire;
modport board
(
output tdc_start_p, tdc_start_n, clk_ref_p, clk_ref_n, trig_a, spi_miso,
tdc_emptyf, dmtd_fb_in, dmtd_fb_out, pll_status,
input tdc_cal_pulse, tdc_wr_n, tdc_rd_n, tdc_oe_n, tdc_alutrigger, led_trig, tdc_start_dis,
tdc_stop_dis, spi_cs_dac_n, spi_cs_pll_n, spi_cs_gpio_n, spi_sclk, spi_mosi,
delay_len, delay_val, delay_pulse, dmtd_clk, ext_rst_n,
inout onewire, tdc_d);
modport core
(
input tdc_start_p, tdc_start_n, clk_ref_p, clk_ref_n, trig_a, spi_miso,
tdc_emptyf, dmtd_fb_in, dmtd_fb_out, pll_status,
output tdc_cal_pulse, tdc_wr_n, tdc_rd_n, tdc_oe_n, tdc_alutrigger, led_trig, tdc_start_dis,
tdc_stop_dis, spi_cs_dac_n, spi_cs_pll_n, spi_cs_gpio_n, spi_sclk, spi_mosi,
delay_len, delay_val, delay_pulse, dmtd_clk, ext_rst_n,
inout onewire, tdc_d);
endinterface // IFineDelayFMC
module fdelay_board (
input trig_i,
output [3:0] out_o,
IFineDelayFMC.board fmc
);
reg clk_ref_250 = 0;
reg clk_ref_125 = 0;
reg clk_tdc = 0;
reg [3:0] tdc_start_div = 0;
reg tdc_start;
always #(4ns / 2) clk_ref_250 <= ~clk_ref_250;
always@(posedge clk_ref_250) clk_ref_125 <= ~clk_ref_125;
always #(32ns / 2) clk_tdc <= ~clk_tdc;
assign fmc.clk_ref_p = clk_ref_125;
assign fmc.clk_ref_n = ~clk_ref_125;
always@(posedge clk_ref_125) begin
tdc_start_div <= tdc_start_div + 1;
tdc_start <= tdc_start_div[3];
end
assign fmc.tdc_start_p = tdc_start;
assign fmc.tdc_start_n = ~tdc_start;
wire trig_a_muxed;
wire [7:0] spi_gpio_out;
wire trig_cal_sel = 1'b1;
assign trig_a_muxed = (trig_cal_sel ? trig_i : fmc.tdc_cal_pulse);
trivial_spi_gpio
SPI_GPIO (
.sclk(fmc.spi_sclk),
.cs_n(fmc.spi_cs_gpio_n),
.mosi(fmc.spi_mosi),
.gpio(spi_gpio_out));
acam_model
#(
.g_verbose(0)
) ACAM (
.PuResN(fmc.ext_rst_n),
.Alutrigger(fmc.tdc_alutrigger),
.RefClk (clk_tdc),
.WRN(fmc.tdc_wr_n),
.RDN(fmc.tdc_rd_n),
.CSN(1'b0),
.OEN(fmc.tdc_oe_n),
.Adr(spi_gpio_out[3:0]),
.D(fmc.tdc_d),
.DStart(tdc_start_delayed),
.DStop1(trig_a_muxed),
.DStop2(1'b0),
.TStart(1'b0),
.TStop(1'b0),
.StartDis(fmc.tdc_start_dis),
.StopDis(fmc.tdc_stop_dis),
.IrFlag(),
.ErrFlag(),
.EF1 (fmc.tdc_emptyf),
.LF1 ()
);
jittery_delay
#(
.g_delay(3ns),
.g_jitter(10ps)
)
DLY_TRIG
(
.in_i(trig_a_muxed),
.out_o(trig_a_n_delayed)
);
assign fmc.trig_a = trig_a_n_delayed;
jittery_delay
#(
.g_delay(2.2ns),
.g_jitter(10ps)
)
DLY_TDC_START
(
.in_i(tdc_start),
.out_o(tdc_start_delayed)
);
genvar gg;
generate
for(gg=0;gg<4;gg++)
begin
// assign out_o[gg] = fmc.delay_pulse[gg];
mc100ep195
U_delay_line(
.len(fmc.delay_len[gg]),
.i(fmc.delay_pulse[gg]),
.delay(fmc.delay_val),
.o(out_o[gg])
);
end
endgenerate
endmodule // main
`define WIRE_FINE_DELAY_PINS(fmc_index,iface) \
.fd``fmc_index``_tdc_start_p_i (iface.core.tdc_start_p), \
.fd``fmc_index``_tdc_start_n_i (iface.core.tdc_start_n), \
.fd``fmc_index``_clk_ref_p_i (iface.core.clk_ref_p), \
.fd``fmc_index``_clk_ref_n_i (iface.core.clk_ref_n), \
.fd``fmc_index``_trig_a_i (iface.core.trig_a), \
.fd``fmc_index``_tdc_cal_pulse_o (iface.core.tdc_cal_pulse), \
.fd``fmc_index``_tdc_d_b (iface.core.tdc_d), \
.fd``fmc_index``_tdc_emptyf_i (iface.core.tdc_emptyf), \
.fd``fmc_index``_tdc_alutrigger_o (iface.core.tdc_alutrigger), \
.fd``fmc_index``_tdc_wr_n_o (iface.core.tdc_wr_n), \
.fd``fmc_index``_tdc_rd_n_o (iface.core.tdc_rd_n), \
.fd``fmc_index``_tdc_oe_n_o (iface.core.tdc_oe_n), \
.fd``fmc_index``_led_trig_o (iface.core.led_trig), \
.fd``fmc_index``_tdc_start_dis_o (iface.core.tdc_start_dis), \
.fd``fmc_index``_tdc_stop_dis_o (iface.core.tdc_stop_dis), \
.fd``fmc_index``_spi_cs_dac_n_o (iface.core.spi_cs_dac_n), \
.fd``fmc_index``_spi_cs_pll_n_o (iface.core.spi_cs_pll_n), \
.fd``fmc_index``_spi_cs_gpio_n_o (iface.core.spi_cs_gpio_n), \
.fd``fmc_index``_spi_sclk_o (iface.core.spi_sclk), \
.fd``fmc_index``_spi_mosi_o (iface.core.spi_mosi), \
.fd``fmc_index``_spi_miso_i (iface.core.spi_miso), \
.fd``fmc_index``_delay_len_o (iface.core.delay_len), \
.fd``fmc_index``_delay_val_o (iface.core.delay_val), \
.fd``fmc_index``_delay_pulse_o (iface.core.delay_pulse), \
.fd``fmc_index``_dmtd_clk_o (iface.core.dmtd_clk), \
.fd``fmc_index``_dmtd_fb_in_i (iface.core.dmtd_fb_in), \
.fd``fmc_index``_dmtd_fb_out_i (iface.core.dmtd_fb_out), \
.fd``fmc_index``_pll_status_i (iface.core.pll_status), \
.fd``fmc_index``_ext_rst_n_o (iface.core.ext_rst_n), \
.fd``fmc_index``_onewire_b (iface.core.onewire)
`include "vme64x_bfm.svh"
`include "svec_vme_buffers.svh"
`include "regs/fd_main_regs.vh"
`include "regs/fd_channel_regs.vh"
`include "fdelay_board.svh"
`include "simdrv_fine_delay.svh"
module delay_meas(input enable, input a, input b);
mailbox tag_a, tag_b;
event q_notempty;
initial begin
tag_a = new(1024);
tag_b = new(1024);
end
always@(posedge a) begin
if(enable) tag_a.put($time);
end
always@(posedge b) begin
if(enable) tag_b.put($time);
end
initial forever begin
wait(tag_a.num() > 0 && tag_b.num() > 0);
while(tag_a.num() > 0 && tag_b.num() > 0)
begin
longint ta, tb, delta;
tag_a.get(ta);
tag_b.get(tb);
delta = tb - ta;
$display("Delay: %.3f ns", real'(delta) / real'(1ns) );
end
end
endmodule // delay_meas
module main;
......@@ -17,6 +58,7 @@ module main;
rst_n = 1;
end
IFineDelayFMC I_fmc0(), I_fmc1();
IVME64X VME(rst_n);
......@@ -31,42 +73,85 @@ module main;
.clk_125m_gtp_p_i(clk_125m),
.clk_125m_gtp_n_i(~clk_125m),
.clk_20m_vcxo_i(clk_20m),
.fd0_clk_ref_p_i(clk_125m),
.fd0_clk_ref_n_i(~clk_125m),
.fd1_clk_ref_p_i(clk_125m),
.fd1_clk_ref_n_i(~clk_125m),
.rst_n_i(rst_n),
`WIRE_VME_PINS(8)
`WIRE_VME_PINS(8),
`WIRE_FINE_DELAY_PINS(0, I_fmc0),
`WIRE_FINE_DELAY_PINS(1, I_fmc1)
);
wire trig0, trig1;
wire [3:0] out0, out1;
reg pulse_enable = 0;
random_pulse_gen
#(
.g_pulse_width (50ns),
.g_min_spacing (1001ns),
.g_max_spacing (1001.1ns))
U_Gen0
(
.enable_i(pulse_enable),
.pulse_o(trig0)
);
fdelay_board U_Board0
(
.trig_i(trig0),
.out_o(out0),
.fmc(I_fmc0.board)
);
delay_meas U_DMeas0 (pulse_enable, trig0, out0[0]);
task automatic init_vme64x_core(ref CBusAccessor_VME64x acc);
/* map func0 to 0x80000000, A32 */
acc.write('h7ff63, 'h80, A32|CR_CSR|D08Byte3);
acc.write('h7ff67, 0, CR_CSR|A32|D08Byte3);
acc.write('h7ff6b, 0, CR_CSR|A32|D08Byte3);
acc.write('h7ff6f, 36, CR_CSR|A32|D08Byte3);
acc.write('h7ff33, 1, CR_CSR|A32|D08Byte3);
acc.write('h7fffb, 'h10, CR_CSR|A32|D08Byte3); /* enable module (BIT_SET = 0x10) */
endtask // init_vme64x_core
initial begin
uint64_t d, abuf[16], dbuf[16];
CBusAccessor_VME64x acc = new(VME.master);
CBusAccessor acc_casted = CBusAccessor'(acc);
Timestamp dly;
int i, result;
CSimDrv_FineDelay drv0;
uint64_t d;
CBusAccessor_VME64x acc = new(VME.master);
#20us;
acc.read('h40004, d, A32|SINGLE|D32);
$display("IDR0: %x\n", d);
acc.write('h40000 + `ADDR_FD_RSTR, 'hdeadffff, A32|SINGLE|D32); /* Un-reset the card */
#10us;
init_vme64x_core(acc);
acc_casted.set_default_xfer_size(A32|SINGLE|D32);
acc.write('h40100, 'hdeadbeef, A32|SINGLE|D32);
drv0 = new(acc, 'h40000);
drv0.init();
acc.read('h50004, d, A32|SINGLE|D32);
$display("IDR1: %x\n", d);
acc.write('h50000 + `ADDR_FD_RSTR, 'hdeadffff, A32|SINGLE|D32); /* Un-reset the card */
#10us;
acc.write('h50100, 'hdeadbeef, A32|SINGLE|D32);
dly=new;
dly.from_ps(600000);
drv0.config_output(0, CSimDrv_FineDelay::DELAY, 1, dly, 200000);
$display("Init done");
pulse_enable = 1;
forever begin
drv0.rbuf_update();
if(drv0.poll())
begin
Timestamp ts;
ts = drv0.get();
// $display("TS: %.3f", ts.flatten());
end
#1us;
end
end
......
......@@ -6,4 +6,4 @@ set NumericStdNoWarnings 1
do wave.do
radix -hexadecimal
run 40us
\ No newline at end of file
run 100us
\ No newline at end of file
`include "regs/fd_main_regs.vh"
`include "regs/fd_channel_regs.vh"
`include "wb/simdrv_defs.svh"
`include "timestamp.svh"
const int SPI_PLL = 0;
const int SPI_GPIO = 1;
const int SPI_DAC = 2;
int dly_seed= 10;
class CSimDrv_FineDelay;
protected CBusAccessor m_acc;
protected Timestamp ts_queue[$];
protected uint64_t m_base;
const real c_acam_bin = 27.012; // [ps]
const real c_ref_period = 8000; // [ps]
const int c_frac_bits = 12;
const int c_scaler_shift = 12;
const int c_acam_start_offset = 10000;
const int c_acam_merge_c_threshold = 1;
const int c_acam_merge_f_threshold = 2000;
task writel(uint64_t addr, uint64_t data);
m_acc.write(addr + m_base, data);
endtask // writel
task readl(uint64_t addr, ref uint64_t data);
m_acc.read(addr + m_base, data);
endtask // writel
function new(CBusAccessor acc, uint64_t base_addr);
m_acc = acc;
m_base = base_addr;
endfunction // new
/* fixme - maybe use real mcp23s17 model instead of this stub? */
task sgpio_write(int value);
uint64_t scr;
scr = `FD_SCR_SEL_GPIO | `FD_SCR_CPOL | (value << `FD_SCR_DATA_OFFSET);
writel(`ADDR_FD_SCR, scr);
writel(`ADDR_FD_SCR, scr | `FD_SCR_START);
while(1)
begin
readl(`ADDR_FD_SCR, scr);
if(scr & `FD_SCR_READY)
break;
end
endtask // sgpio_write
task acam_write(int addr, int value);
sgpio_write(addr);
#10ns;
writel(`ADDR_FD_TDR, value);
writel(`ADDR_FD_TDCSR, `FD_TDCSR_WRITE);
endtask // acam_write
task acam_read(int addr, output int value);
uint64_t rval;
sgpio_write(addr);
#10ns;
writel(`ADDR_FD_TDR, (addr<<28));
writel(`ADDR_FD_TDCSR, `FD_TDCSR_READ);
#(500ns);
readl(`ADDR_FD_TDR, rval);
value = rval;
endtask // acam_read
task get_time(ref Timestamp t);
uint64_t tcr, secl, sech, cycles;
readl(`ADDR_FD_TCR, tcr);
writel(`ADDR_FD_TCR, tcr | `FD_TCR_CAP_TIME);
readl(`ADDR_FD_TM_SECL, secl);
readl(`ADDR_FD_TM_SECH, sech);
readl(`ADDR_FD_TM_CYCLES, cycles);
t.utc = (sech << 32) | secl;
t.coarse = cycles;
t.frac = 0;
endtask // get_time
task set_time(Timestamp t);
uint64_t tcr;
readl(`ADDR_FD_TCR, tcr);
writel(`ADDR_FD_TM_SECL, t.utc & 32'hffffffff);
writel(`ADDR_FD_TM_SECH, t.utc >> 32);
writel(`ADDR_FD_TM_CYCLES, t.coarse);
writel(`ADDR_FD_TCR, tcr | `FD_TCR_SET_TIME);
endtask // set_time
task set_reference(int wr);
if(wr)
begin
uint64_t rval;
$display("Enabling White Rabbit time reference...");
writel(`ADDR_FD_TCR, `FD_TCR_WR_ENABLE);
forever begin
readl(`ADDR_FD_TCR, rval);
if(rval & `FD_TCR_WR_LOCKED) break;
end
$display("WR Locked");
end
else begin
Timestamp t = new(0,0,0);
set_time(t);
end
endtask // set_reference
task rbuf_update();
Timestamp ts;
uint64_t utc, coarse, seq_frac, stat, sech, secl;
readl(`ADDR_FD_TSBCR, stat);
if((stat & `FD_TSBCR_EMPTY) == 0) begin
writel(`ADDR_FD_TSBR_ADVANCE, 1);
readl(`ADDR_FD_TSBR_SECH, sech);
readl(`ADDR_FD_TSBR_SECL, secl);
readl(`ADDR_FD_TSBR_CYCLES, coarse);
readl(`ADDR_FD_TSBR_FID, seq_frac);
ts = new (0,0,0);
ts.source = seq_frac & 'h7;
ts.utc = (sech << 32) | secl;
ts.coarse = coarse & 'hfffffff;
ts.seq_id = (seq_frac >> 16) & 'hffff;
ts.frac = (seq_frac>>4) & 'hfff;
ts_queue.push_back(ts);
end
endtask // rbuf_read
function int poll();
return (ts_queue.size() > 0);
endfunction // poll
function Timestamp get();
return ts_queue.pop_front();
endfunction // get
typedef enum
{
DELAY = 0,
PULSE_GEN = 1
} channel_mode_t;
task config_output( int channel,channel_mode_t mode, int enable, Timestamp start_delay, uint64_t width_ps, uint64_t delta_ps=0, int rep_count=1);
uint64_t dcr, base, rep;
Timestamp t_start, t_end, t_delta, t_width;
t_width = new;
t_width.unflatten(int'(real'(width_ps) * 4096.0 / 8000.0));
t_start = start_delay;
t_end = start_delay.add(t_width);
t_delta = new;
t_delta.unflatten(int'(real'(delta_ps) * 4096.0 / 8000.0));
base = 'h100 + 'h100 * channel;
writel(base + `ADDR_FD_FRR, 800);
writel(base + `ADDR_FD_U_STARTH, t_start.utc >> 32);
writel(base + `ADDR_FD_U_STARTL, t_start.utc & 'hffffffff);
writel(base + `ADDR_FD_C_START, t_start.coarse);
writel(base + `ADDR_FD_F_START, t_start.frac);
writel(base + `ADDR_FD_U_ENDH, t_end.utc >> 32);
writel(base + `ADDR_FD_U_ENDL, t_end.utc & 'hffffffff);
writel(base + `ADDR_FD_C_END, t_end.coarse);
writel(base + `ADDR_FD_F_END, t_end.frac);
writel(base + `ADDR_FD_U_DELTA, t_delta.utc & 'hf);
writel(base + `ADDR_FD_C_DELTA, t_delta.coarse);
writel(base + `ADDR_FD_F_DELTA, t_delta.frac);
if(rep_count < 0)
rep = `FD_RCR_CONT;
else
rep = (rep_count-1) << `FD_RCR_REP_CNT_OFFSET;
writel(base + `ADDR_FD_RCR, rep);
dcr = (enable? `FD_DCR_ENABLE : 0) | `FD_DCR_UPDATE ;
if(mode == PULSE_GEN)
dcr |= `FD_DCR_MODE;
if((width_ps < 200000) || (((delta_ps-width_ps) < 150000) && (rep_count > 1)))
dcr |= `FD_DCR_NO_FINE;
writel('h100 + 'h100 * channel + `ADDR_FD_DCR, dcr);
if(mode == PULSE_GEN)
writel('h100 + 'h100 * channel + `ADDR_FD_DCR, dcr | `FD_DCR_PG_ARM);
endtask // config_output
task init();
int rval;
uint64_t idr;
Timestamp t = new;
readl(`ADDR_FD_IDR, idr); /* Un-reset the card */
$display("FD @ 0x%x: idr = 0x%x", m_base, idr);
if(idr != 32'hf19ede1a)
$error("Can't detect an FD core @ 0x%x\n", m_base);
writel(`ADDR_FD_RSTR, 'hdeadffff); /* Un-reset the card */
writel(`ADDR_FD_TDCSR, `FD_TDCSR_START_DIS | `FD_TDCSR_STOP_DIS);
writel(`ADDR_FD_GCR, `FD_GCR_BYPASS);
acam_write(5, c_acam_start_offset); // set StartOffset
acam_read(5, rval);
sgpio_write(8); /* permanently select FIFO1 */
// Clear the ring buffer
writel(`ADDR_FD_TSBCR, `FD_TSBCR_ENABLE | `FD_TSBCR_PURGE | `FD_TSBCR_RST_SEQ | (3 << `FD_TSBCR_CHAN_MASK_OFFSET));
writel(`ADDR_FD_ADSFR, int' (real'(1<< (c_frac_bits + c_scaler_shift)) * c_acam_bin / c_ref_period));
writel(`ADDR_FD_ASOR, c_acam_start_offset * 3);
writel(`ADDR_FD_ATMCR, c_acam_merge_c_threshold | (c_acam_merge_f_threshold << 4));
// Enable trigger input
writel(`ADDR_FD_GCR, 0);
t.utc = 0;
t.coarse = 0;
set_time(t);
// Enable trigger input
writel(`ADDR_FD_GCR, `FD_GCR_INPUT_EN);
endtask // init
task force_cal_pulse(int channel, int delay_setpoint);
writel(`ADDR_FD_FRR + (channel * 'h20), delay_setpoint);
writel(`ADDR_FD_DCR + (channel * 'h20), `FD_DCR_FORCE_DLY);
writel(`ADDR_FD_CALR, `FD_CALR_CAL_PULSE | ((1<<channel) << `FD_CALR_PSEL_OFFSET));
endtask // force_cal_pulse
endclass // CSimDrv_FineDelay
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /main/DUT/vme_master_in
add wave -noupdate /main/DUT/vme_master_out
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/c_dl
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/c_al
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/c_sell
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/c_psizel
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/clk_i
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/reset_i
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/transfer_done_o
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/sl_dat_i
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/sl_dat_o
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/sl_adr_i
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/sl_cyc_i
add wave -noupdate /main/DUT/U_VME_Core/U_Wrapped_VME/Uwb_dma/sl_err_o