737 lines
19 KiB
Systemverilog
737 lines
19 KiB
Systemverilog
`timescale 1ns/1ps
|
|
|
|
module tb_control;
|
|
|
|
localparam int unsigned DAC_DATA_WIDTH = 12;
|
|
|
|
//------------------------------------------------------------
|
|
// Clocks / reset
|
|
//------------------------------------------------------------
|
|
logic ctrl_clk;
|
|
logic dac_clk_in;
|
|
logic adc_clk_in;
|
|
logic rst_n;
|
|
|
|
initial begin
|
|
ctrl_clk = 1'b0;
|
|
forever #(1 * 4.000) ctrl_clk = ~ctrl_clk; // 125 MHz
|
|
end
|
|
|
|
initial begin
|
|
dac_clk_in = 1'b0;
|
|
forever #(1 * 3.846153846) dac_clk_in = ~dac_clk_in; // ~130 MHz
|
|
end
|
|
|
|
initial begin
|
|
adc_clk_in = 1'b0;
|
|
forever #(1 * 7.692307692) adc_clk_in = ~adc_clk_in; // ~65 MHz
|
|
end
|
|
|
|
//------------------------------------------------------------
|
|
// DUT inputs
|
|
//------------------------------------------------------------
|
|
|
|
logic rst_soft;
|
|
|
|
logic finish;
|
|
|
|
logic [159:0] cfg_bus_input;
|
|
logic cfg_bus_valid;
|
|
|
|
logic start;
|
|
|
|
//------------------------------------------------------------
|
|
// DUT outputs
|
|
//------------------------------------------------------------
|
|
|
|
logic busy;
|
|
|
|
logic [31:0] dac_pulse_width;
|
|
logic [31:0] dac_pulse_period;
|
|
logic [DAC_DATA_WIDTH-1:0] dac_pulse_height;
|
|
logic [15:0] dac_pulse_num;
|
|
|
|
logic [31:0] adc_pulse_period;
|
|
logic [15:0] adc_pulse_num;
|
|
logic [31:0] adc_window_size;
|
|
|
|
logic dac_start;
|
|
logic adc_start;
|
|
logic dac_rst;
|
|
logic adc_rst;
|
|
|
|
//------------------------------------------------------------
|
|
// DUT
|
|
//------------------------------------------------------------
|
|
|
|
control #(
|
|
.DAC_DATA_WIDTH(DAC_DATA_WIDTH)
|
|
) dut (
|
|
.ctrl_clk (ctrl_clk),
|
|
.dac_clk_in (dac_clk_in),
|
|
.adc_clk_in (adc_clk_in),
|
|
|
|
.rst_n (rst_n),
|
|
.rst_soft (rst_soft),
|
|
|
|
.finish (finish),
|
|
|
|
.cfg_bus_input (cfg_bus_input),
|
|
.cfg_bus_valid (cfg_bus_valid),
|
|
|
|
.start (start),
|
|
|
|
.busy (busy),
|
|
|
|
.dac_pulse_width (dac_pulse_width),
|
|
.dac_pulse_period (dac_pulse_period),
|
|
.dac_pulse_height (dac_pulse_height),
|
|
.dac_pulse_num (dac_pulse_num),
|
|
|
|
.adc_pulse_period (adc_pulse_period),
|
|
.adc_pulse_num (adc_pulse_num),
|
|
.adc_window_size (adc_window_size),
|
|
|
|
.dac_start (dac_start),
|
|
.adc_start (adc_start),
|
|
.dac_rst (dac_rst),
|
|
.adc_rst (adc_rst)
|
|
);
|
|
|
|
//------------------------------------------------------------
|
|
// pulse counters and monitors for testing
|
|
//------------------------------------------------------------
|
|
|
|
int dac_rst_count;
|
|
int adc_rst_count;
|
|
int dac_start_count;
|
|
int adc_start_count;
|
|
|
|
always_ff @(posedge dac_clk_in) begin
|
|
if (!rst_n) begin
|
|
dac_rst_count <= 0;
|
|
dac_start_count <= 0;
|
|
end
|
|
else begin
|
|
if (dac_rst)
|
|
dac_rst_count <= dac_rst_count + 1;
|
|
|
|
if (dac_start)
|
|
dac_start_count <= dac_start_count + 1;
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge adc_clk_in) begin
|
|
if (!rst_n) begin
|
|
adc_rst_count <= 0;
|
|
adc_start_count <= 0;
|
|
end
|
|
else begin
|
|
if (adc_rst)
|
|
adc_rst_count <= adc_rst_count + 1;
|
|
|
|
if (adc_start)
|
|
adc_start_count <= adc_start_count + 1;
|
|
end
|
|
end
|
|
|
|
//------------------------------------------------------------
|
|
// helpers
|
|
//------------------------------------------------------------
|
|
|
|
task automatic send_cmd(input logic [7:0] cmd);
|
|
begin
|
|
@(negedge ctrl_clk);
|
|
|
|
case (cmd)
|
|
|
|
8'h0F: begin
|
|
rst_soft <= 1'b1;
|
|
|
|
@(posedge ctrl_clk);
|
|
|
|
rst_soft <= 1'b0;
|
|
end
|
|
|
|
8'hF0: begin
|
|
start <= 1'b1;
|
|
|
|
@(posedge ctrl_clk);
|
|
|
|
start <= 1'b0;
|
|
end
|
|
|
|
default:
|
|
$fatal(1, "Unsupported command %h", cmd);
|
|
|
|
endcase
|
|
end
|
|
endtask
|
|
|
|
task automatic send_set_data(
|
|
input logic [31:0] pulse_width,
|
|
input logic [31:0] pulse_period,
|
|
input logic [15:0] pulse_num,
|
|
input logic [15:0] pulse_height_raw,
|
|
input logic [31:0] pulse_period_adc,
|
|
input logic [31:0] window_size
|
|
);
|
|
|
|
logic [159:0] payload;
|
|
|
|
begin
|
|
|
|
payload = {
|
|
window_size,
|
|
pulse_period_adc,
|
|
pulse_height_raw,
|
|
pulse_num,
|
|
pulse_period,
|
|
pulse_width
|
|
};
|
|
|
|
@(negedge ctrl_clk);
|
|
|
|
cfg_bus_input <= payload;
|
|
cfg_bus_valid <= 1'b1;
|
|
|
|
@(posedge ctrl_clk);
|
|
|
|
cfg_bus_valid <= 1'b0;
|
|
cfg_bus_input <= '0;
|
|
|
|
end
|
|
endtask
|
|
|
|
task automatic pulse_finish;
|
|
begin
|
|
@(posedge adc_clk_in);
|
|
finish <= 1'b1;
|
|
|
|
@(posedge adc_clk_in);
|
|
finish <= 1'b0;
|
|
end
|
|
endtask
|
|
|
|
//------------------------------------------------------------
|
|
// waiters
|
|
//------------------------------------------------------------
|
|
|
|
task automatic wait_dac_rst_count(input int expected, input int max_cycles = 100);
|
|
int i;
|
|
begin
|
|
for (i = 0; i < max_cycles; i++) begin
|
|
@(posedge dac_clk_in);
|
|
|
|
if (dac_rst_count >= expected)
|
|
return;
|
|
end
|
|
|
|
$fatal(1,
|
|
"Timeout waiting for dac_rst_count >= %0d, current=%0d",
|
|
expected,
|
|
dac_rst_count
|
|
);
|
|
end
|
|
endtask
|
|
|
|
task automatic wait_adc_rst_count(input int expected, input int max_cycles = 100);
|
|
int i;
|
|
begin
|
|
for (i = 0; i < max_cycles; i++) begin
|
|
@(posedge adc_clk_in);
|
|
|
|
if (adc_rst_count >= expected)
|
|
return;
|
|
end
|
|
|
|
$fatal(1,
|
|
"Timeout waiting for adc_rst_count >= %0d, current=%0d",
|
|
expected,
|
|
adc_rst_count
|
|
);
|
|
end
|
|
endtask
|
|
|
|
task automatic wait_dac_start_count(input int expected, input int max_cycles = 100);
|
|
int i;
|
|
begin
|
|
for (i = 0; i < max_cycles; i++) begin
|
|
@(posedge dac_clk_in);
|
|
|
|
if (dac_start_count >= expected)
|
|
return;
|
|
end
|
|
|
|
$fatal(1,
|
|
"Timeout waiting for dac_start_count >= %0d, current=%0d",
|
|
expected,
|
|
dac_start_count
|
|
);
|
|
end
|
|
endtask
|
|
|
|
task automatic wait_adc_start_count(input int expected, input int max_cycles = 100);
|
|
int i;
|
|
begin
|
|
for (i = 0; i < max_cycles; i++) begin
|
|
@(posedge adc_clk_in);
|
|
|
|
if (adc_start_count >= expected)
|
|
return;
|
|
end
|
|
|
|
$fatal(1,
|
|
"Timeout waiting for adc_start_count >= %0d, current=%0d",
|
|
expected,
|
|
adc_start_count
|
|
);
|
|
end
|
|
endtask
|
|
|
|
task automatic wait_cfg_applied(
|
|
|
|
input logic [31:0] exp_pulse_width,
|
|
input logic [31:0] exp_pulse_period,
|
|
input logic [15:0] exp_pulse_num,
|
|
input logic [15:0] exp_pulse_height_raw,
|
|
input logic [31:0] exp_pulse_period_adc,
|
|
input logic [31:0] exp_window_size,
|
|
input int max_cycles = 200
|
|
);
|
|
|
|
logic [DAC_DATA_WIDTH-1:0] exp_height;
|
|
|
|
int i;
|
|
|
|
begin
|
|
|
|
exp_height = exp_pulse_height_raw[DAC_DATA_WIDTH-1:0];
|
|
|
|
for(i=0;i<max_cycles;i++) begin
|
|
|
|
@(posedge ctrl_clk);
|
|
|
|
if(
|
|
|
|
dac_pulse_width == exp_pulse_width &&
|
|
dac_pulse_period == exp_pulse_period &&
|
|
dac_pulse_num == exp_pulse_num &&
|
|
dac_pulse_height == exp_height &&
|
|
|
|
adc_pulse_period == exp_pulse_period_adc &&
|
|
adc_pulse_num == exp_pulse_num &&
|
|
adc_window_size == exp_window_size
|
|
|
|
)
|
|
return;
|
|
|
|
end
|
|
|
|
$fatal("Configuration timeout");
|
|
|
|
end
|
|
endtask
|
|
|
|
//------------------------------------------------------------
|
|
// Test sequence
|
|
//------------------------------------------------------------
|
|
|
|
logic [31:0] test_pulse_width;
|
|
logic [31:0] test_pulse_period;
|
|
logic [15:0] test_pulse_num;
|
|
logic [15:0] test_pulse_height_raw;
|
|
logic [31:0] test_pulse_period_adc;
|
|
logic [31:0] test_window_size;
|
|
|
|
initial begin
|
|
// defaults
|
|
rst_n = 1'b0;
|
|
rst_soft = 1'b0;
|
|
|
|
start = 1'b0;
|
|
finish = 1'b0;
|
|
|
|
cfg_bus_input = '0;
|
|
cfg_bus_valid = 1'b0;
|
|
|
|
test_pulse_width = 32'h11223344;
|
|
test_pulse_period = 32'h55667788;
|
|
test_pulse_num = 16'hA1B2;
|
|
test_pulse_height_raw = 16'h0CDE; // for DAC_DATA_WIDTH=12 => 12'hCDE
|
|
test_pulse_period_adc = 32'h50607080;
|
|
test_window_size = 32'hABCDEF55;
|
|
|
|
repeat (10) @(posedge ctrl_clk);
|
|
rst_n = 1'b1;
|
|
|
|
repeat (10) @(posedge ctrl_clk);
|
|
|
|
//--------------------------------------------------------
|
|
// TEST 1: soft_reset
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] TEST 1: soft_reset", $time);
|
|
|
|
send_cmd(8'h0F);
|
|
|
|
wait_dac_rst_count(1);
|
|
wait_adc_rst_count(1);
|
|
|
|
if (dac_rst_count != 1) begin
|
|
$fatal(1,
|
|
"Expected exactly one dac_rst pulse after first soft_reset, got %0d",
|
|
dac_rst_count
|
|
);
|
|
end
|
|
|
|
if (adc_rst_count != 1) begin
|
|
$fatal(1,
|
|
"Expected exactly one adc_rst pulse after first soft_reset, got %0d",
|
|
adc_rst_count
|
|
);
|
|
end
|
|
|
|
$display("[%0t] TEST 1 passed", $time);
|
|
|
|
//--------------------------------------------------------
|
|
// TEST 2: set_data
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] TEST 2: set_data", $time);
|
|
|
|
send_set_data(
|
|
|
|
test_pulse_width,
|
|
test_pulse_period,
|
|
test_pulse_num,
|
|
test_pulse_height_raw,
|
|
test_pulse_period_adc,
|
|
test_window_size
|
|
|
|
);
|
|
|
|
wait_cfg_applied(
|
|
|
|
test_pulse_width,
|
|
test_pulse_period,
|
|
test_pulse_num,
|
|
test_pulse_height_raw,
|
|
test_pulse_period_adc,
|
|
test_window_size
|
|
|
|
);
|
|
|
|
if (dac_pulse_width !== 32'h11223344) begin
|
|
$fatal(1,
|
|
"dac_pulse_width mismatch: got %h expected %h",
|
|
dac_pulse_width,
|
|
32'h11223344
|
|
);
|
|
end
|
|
|
|
if (dac_pulse_period !== 32'h55667788) begin
|
|
$fatal(1,
|
|
"dac_pulse_period mismatch: got %h expected %h",
|
|
dac_pulse_period,
|
|
32'h55667788
|
|
);
|
|
end
|
|
|
|
if (dac_pulse_num !== 16'hA1B2) begin
|
|
$fatal(1,
|
|
"dac_pulse_num mismatch: got %h expected %h",
|
|
dac_pulse_num,
|
|
16'hA1B2
|
|
);
|
|
end
|
|
|
|
if (dac_pulse_height !== 12'hCDE) begin
|
|
$fatal(1,
|
|
"dac_pulse_height mismatch: got %h expected %h",
|
|
dac_pulse_height,
|
|
12'hCDE
|
|
);
|
|
end
|
|
|
|
if (adc_pulse_period !== 32'h50607080) begin
|
|
$fatal(1,
|
|
"adc_pulse_period mismatch: got %h expected %h",
|
|
adc_pulse_period,
|
|
32'h50607080
|
|
);
|
|
end
|
|
|
|
if (adc_pulse_num !== 16'hA1B2) begin
|
|
$fatal(1,
|
|
"adc_pulse_num mismatch: got %h expected %h",
|
|
adc_pulse_num,
|
|
16'hA1B2
|
|
);
|
|
end
|
|
|
|
if(adc_window_size != test_window_size)
|
|
|
|
$fatal(
|
|
"adc_window_size mismatch. got=%h expected=%h",
|
|
adc_window_size,
|
|
test_window_size
|
|
);
|
|
|
|
$display("[%0t] TEST 2 passed", $time);
|
|
|
|
repeat (20) @(posedge ctrl_clk);
|
|
|
|
//--------------------------------------------------------
|
|
// TEST 3: start
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] TEST 3: start", $time);
|
|
|
|
send_cmd(8'hF0);
|
|
|
|
wait_dac_start_count(1);
|
|
wait_adc_start_count(1);
|
|
|
|
if (dac_start_count != 1) begin
|
|
$fatal(1,
|
|
"Expected exactly one dac_start pulse after first start, got %0d",
|
|
dac_start_count
|
|
);
|
|
end
|
|
|
|
if (adc_start_count != 1) begin
|
|
$fatal(1,
|
|
"Expected exactly one adc_start pulse after first start, got %0d",
|
|
adc_start_count
|
|
);
|
|
end
|
|
|
|
if (!busy) begin
|
|
$fatal(1,
|
|
"busy must be asserted after start"
|
|
);
|
|
end
|
|
|
|
$display("[%0t] TEST 3 start pulses passed", $time);
|
|
|
|
//--------------------------------------------------------
|
|
// release busy by finish pulse from ADC domain
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] Sending finish pulse", $time);
|
|
|
|
pulse_finish();
|
|
|
|
repeat (20) @(posedge ctrl_clk);
|
|
|
|
if (busy) begin
|
|
$fatal(1,
|
|
"busy was not cleared after finish pulse"
|
|
);
|
|
end
|
|
|
|
//--------------------------------------------------------
|
|
// sanity check that commands are accepted again after finish
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] TEST 4: soft_reset after finish", $time);
|
|
|
|
send_cmd(8'h0F);
|
|
|
|
wait_dac_rst_count(2);
|
|
wait_adc_rst_count(2);
|
|
|
|
if (dac_rst_count != 2) begin
|
|
$fatal(1,
|
|
"Expected exactly two dac_rst pulses total, got %0d",
|
|
dac_rst_count
|
|
);
|
|
end
|
|
|
|
if (adc_rst_count != 2) begin
|
|
$fatal(1,
|
|
"Expected exactly two adc_rst pulses total, got %0d",
|
|
adc_rst_count
|
|
);
|
|
end
|
|
|
|
$display("[%0t] TEST 4 passed", $time);
|
|
|
|
//--------------------------------------------------------
|
|
|
|
$display("==============================================");
|
|
$display("ALL BASIC TESTS PASSED");
|
|
$display("dac_rst_count = %0d", dac_rst_count);
|
|
$display("adc_rst_count = %0d", adc_rst_count);
|
|
$display("dac_start_count = %0d", dac_start_count);
|
|
$display("adc_start_count = %0d", adc_start_count);
|
|
$display("==============================================");
|
|
|
|
#50;
|
|
|
|
//--------------------------------------------------------
|
|
// TEST 2.1: set_data
|
|
//--------------------------------------------------------
|
|
|
|
test_pulse_width = 32'h12121212;
|
|
test_pulse_period = 32'h34343434;
|
|
test_pulse_num = 16'h1A2B;
|
|
test_pulse_height_raw = 16'hC0ED; // for DAC_DATA_WIDTH=12 => 12'hCDE
|
|
test_pulse_period_adc = 32'h56565656;
|
|
test_window_size = 32'h01020304;
|
|
|
|
$display("[%0t] TEST 2.1: set_data", $time);
|
|
|
|
send_set_data(
|
|
test_pulse_width,
|
|
test_pulse_period,
|
|
test_pulse_num,
|
|
test_pulse_height_raw,
|
|
test_pulse_period_adc,
|
|
test_window_size
|
|
);
|
|
|
|
wait_cfg_applied(
|
|
test_pulse_width,
|
|
test_pulse_period,
|
|
test_pulse_num,
|
|
test_pulse_height_raw,
|
|
test_pulse_period_adc,
|
|
test_window_size
|
|
);
|
|
|
|
if (dac_pulse_width !== 32'h12121212) begin
|
|
$fatal(1,
|
|
"dac_pulse_width mismatch: got %h expected %h",
|
|
dac_pulse_width,
|
|
32'h12121212
|
|
);
|
|
end
|
|
|
|
if (dac_pulse_period !== 32'h34343434) begin
|
|
$fatal(1,
|
|
"dac_pulse_period mismatch: got %h expected %h",
|
|
dac_pulse_period,
|
|
32'h34343434
|
|
);
|
|
end
|
|
|
|
if (dac_pulse_num !== 16'h1A2B) begin
|
|
$fatal(1,
|
|
"dac_pulse_num mismatch: got %h expected %h",
|
|
dac_pulse_num,
|
|
16'h1A2B
|
|
);
|
|
end
|
|
|
|
if (dac_pulse_height !== 12'hC0ED) begin
|
|
$fatal(1,
|
|
"dac_pulse_height mismatch: got %h expected %h",
|
|
dac_pulse_height,
|
|
12'hC0ED
|
|
);
|
|
end
|
|
|
|
if (adc_pulse_period !== 32'h56565656) begin
|
|
$fatal(1,
|
|
"adc_pulse_period mismatch: got %h expected %h",
|
|
adc_pulse_period,
|
|
32'h56565656
|
|
);
|
|
end
|
|
|
|
$display("[%0t] TEST 2.1 passed", $time);
|
|
|
|
repeat (20) @(posedge ctrl_clk);
|
|
|
|
//--------------------------------------------------------
|
|
// TEST 3.1: start
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] TEST 3.1: start", $time);
|
|
|
|
send_cmd(8'hF0);
|
|
|
|
wait_dac_start_count(2);
|
|
wait_adc_start_count(2);
|
|
|
|
if (dac_start_count != 2) begin
|
|
$fatal(1,
|
|
"Expected exactly one dac_start pulse after first start, got %0d",
|
|
dac_start_count
|
|
);
|
|
end
|
|
|
|
if (adc_start_count != 2) begin
|
|
$fatal(1,
|
|
"Expected exactly one adc_start pulse after first start, got %0d",
|
|
adc_start_count
|
|
);
|
|
end
|
|
|
|
if (!busy) begin
|
|
$fatal(1,
|
|
"busy must be asserted after start"
|
|
);
|
|
end
|
|
|
|
$display("[%0t] TEST 3.1 start pulses passed", $time);
|
|
|
|
//--------------------------------------------------------
|
|
// release busy by finish pulse from ADC domain
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] Sending finish pulse", $time);
|
|
|
|
pulse_finish();
|
|
|
|
repeat (20) @(posedge ctrl_clk);
|
|
|
|
if (busy) begin
|
|
$fatal(1,
|
|
"busy was not cleared after finish pulse"
|
|
);
|
|
end
|
|
|
|
//--------------------------------------------------------
|
|
// sanity check that commands are accepted again after finish
|
|
//--------------------------------------------------------
|
|
|
|
$display("[%0t] TEST 4.1: soft_reset after finish", $time);
|
|
|
|
send_cmd(8'h0F);
|
|
|
|
wait_dac_rst_count(2);
|
|
wait_adc_rst_count(2);
|
|
|
|
if (dac_rst_count != 2) begin
|
|
$fatal(1,
|
|
"Expected exactly two dac_rst pulses total, got %0d",
|
|
dac_rst_count
|
|
);
|
|
end
|
|
|
|
if (adc_rst_count != 2) begin
|
|
$fatal(1,
|
|
"Expected exactly two adc_rst pulses total, got %0d",
|
|
adc_rst_count
|
|
);
|
|
end
|
|
|
|
$display("[%0t] TEST 4.1 passed", $time);
|
|
|
|
//--------------------------------------------------------
|
|
|
|
$display("==============================================");
|
|
$display("ALL BASIC TESTS PASSED");
|
|
$display("dac_rst_count = %0d", dac_rst_count);
|
|
$display("adc_rst_count = %0d", adc_rst_count);
|
|
$display("dac_start_count = %0d", dac_start_count);
|
|
$display("adc_start_count = %0d", adc_start_count);
|
|
$display("==============================================");
|
|
$finish;
|
|
|
|
end
|
|
|
|
endmodule |