upd: TB automation,

DA->AD delay line for signal integrity
This commit is contained in:
Zer0Nu11
2026-06-19 08:00:00 +03:00
parent fd9280737d
commit 4923f01322
2 changed files with 205 additions and 169 deletions

View File

@ -6,7 +6,8 @@ module sync_top
parameter int unsigned ADC_DATA_WIDTH = 12, // ADC bit-width parameter int unsigned ADC_DATA_WIDTH = 12, // ADC bit-width
parameter int unsigned PACK_FACTOR = 1, // number of ADC readings per transaction parameter int unsigned PACK_FACTOR = 1, // number of ADC readings per transaction
parameter int unsigned PROCESS_MODE = 0, // representation format of ADC readings (0 - direct code, 1 - 2's completment) parameter int unsigned PROCESS_MODE = 0, // representation format of ADC readings (0 - direct code, 1 - 2's completment)
parameter int unsigned ZERO_LEVEL = 0 parameter int unsigned ZERO_LEVEL = 0,
parameter int unsigned USE_DELAY_LINE = 0
) )
( (
input clk_adc, input clk_adc,
@ -16,9 +17,9 @@ module sync_top
input start, input start,
input out_of_range, input out_of_range,
input [31:0] pulse_width, input [31:0] pulse_width,
input [31:0] pulse_period, input [31:0] pulse_period, // DAC counter limit
input [DAC_DATA_WIDTH-1:0] pulse_height, input [DAC_DATA_WIDTH-1:0] pulse_height,
input [15:0] pulse_num, // DAC counter limit input [15:0] pulse_num,
input [31:0] smp_num, // ADC counter limit input [31:0] smp_num, // ADC counter limit
output [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata, output [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata,
output m_axis_tvalid output m_axis_tvalid
@ -29,15 +30,29 @@ module sync_top
//------------------------------------------------------------ //------------------------------------------------------------
wire dac_done, dac_request, adc_done, adc_request; wire dac_done, dac_request, adc_done, adc_request;
wire [DAC_DATA_WIDTH-1:0] dac_signal; wire [DAC_DATA_WIDTH-1:0] dac_signal;
wire [ADC_DATA_WIDTH-1:0] internal_wire_singnal;
wire [ADC_DATA_WIDTH-1:0] adc_singnal; wire [ADC_DATA_WIDTH-1:0] adc_singnal;
generate generate
if (ADC_DATA_WIDTH > DAC_DATA_WIDTH) begin : g_pad_zeros if (ADC_DATA_WIDTH > DAC_DATA_WIDTH) begin : g_pad_zeros
assign adc_singnal = { {(ADC_DATA_WIDTH - DAC_DATA_WIDTH){1'b0}}, dac_signal }; assign internal_wire_singnal = { {(ADC_DATA_WIDTH - DAC_DATA_WIDTH){1'b0}}, dac_signal };
end end
else begin : g_truncate else begin : g_truncate
assign adc_singnal = dac_signal[ADC_DATA_WIDTH-1:0]; assign internal_wire_singnal = dac_signal[ADC_DATA_WIDTH-1:0];
end end
endgenerate endgenerate
generate
if (USE_DELAY_LINE) begin
localparam int DELAY_LENGTH = 0;
logic [DELAY_LENGTH:0][ADC_DATA_WIDTH-1:0] signal_delay_line;
always_ff @(posedge clk_dac) begin
signal_delay_line[0] <= internal_wire_singnal;
for (int i = 0; i < DELAY_LENGTH; i++)
signal_delay_line[i+1] <= signal_delay_line[i];
end
assign adc_singnal = signal_delay_line[DELAY_LENGTH];
end else
assign adc_singnal = internal_wire_singnal;
endgenerate
//------------------------------------------------------------ //------------------------------------------------------------
// DAC -> ADC CDC // DAC -> ADC CDC

View File

@ -5,16 +5,20 @@ module tb_top;
//------------------------------------------------------------ //------------------------------------------------------------
// Параметры // Параметры
//------------------------------------------------------------ //------------------------------------------------------------
parameter string ZERO_LEVEL_PARAM = "logic"; // "logic" VS "true"
localparam DAC_DATA_WIDTH = 14; localparam DAC_DATA_WIDTH = 14;
localparam ADC_DATA_WIDTH = 12; localparam ADC_DATA_WIDTH = 12;
localparam PACK_FACTOR = 1; localparam PACK_FACTOR = 1;
localparam PROCESS_MODE = 0; localparam PROCESS_MODE = 0;
localparam LOGIC_ZERO_LEVEL = 0; // DAC -5V for logic zero
localparam VOLTAGE_ZERO_LEVEL = 2**(DAC_DATA_WIDTH-1); // DAC 0V for logic zero
localparam CLK_DAC_PERIOD = 8; localparam CLK_DAC_PERIOD = 8;
localparam CLK_ADC_PERIOD = 15.385; localparam CLK_ADC_PERIOD = 15.385;
localparam USE_DELAY_LINE = 1;
localparam LOGIC_ZERO_LEVEL = 0; // DAC -5V for logic zero
localparam VOLTAGE_ZERO_LEVEL = 2**(DAC_DATA_WIDTH-1); // DAC 0V for logic zero
localparam ZERO_LEVEL = (ZERO_LEVEL_PARAM == "logic") ? LOGIC_ZERO_LEVEL : VOLTAGE_ZERO_LEVEL;
localparam ZERO_LEVEL = LOGIC_ZERO_LEVEL; // "logic" VS "true" localparam CLOCK_DEVIATION = 1; // Maximum clock deviation of pulse stats
//------------------------------------------------------------ //------------------------------------------------------------
// Тактовые сигналы и сброс // Тактовые сигналы и сброс
@ -49,7 +53,8 @@ module tb_top;
.ADC_DATA_WIDTH(ADC_DATA_WIDTH), .ADC_DATA_WIDTH(ADC_DATA_WIDTH),
.PACK_FACTOR(PACK_FACTOR), .PACK_FACTOR(PACK_FACTOR),
.PROCESS_MODE(PROCESS_MODE), .PROCESS_MODE(PROCESS_MODE),
.ZERO_LEVEL(ZERO_LEVEL) .ZERO_LEVEL(ZERO_LEVEL),
.USE_DELAY_LINE(USE_DELAY_LINE)
) dut ( ) dut (
.clk_adc(clk_adc), .clk_adc(clk_adc),
.clk_dac(clk_dac), .clk_dac(clk_dac),
@ -77,6 +82,11 @@ module tb_top;
end end
// === Таски для тестирования === // === Таски для тестирования ===
// Функция модуля
function automatic real fabs(real val);
return (val < 0.0) ? -val : val;
endfunction
// Таска сброса DAC DUT // Таска сброса DAC DUT
task automatic reset_dut_dac( task automatic reset_dut_dac(
input int rst_duration // сколько тактов держать сброс input int rst_duration // сколько тактов держать сброс
@ -113,7 +123,6 @@ module tb_top;
input logic [31:0] sn // число сэмплов input logic [31:0] sn // число сэмплов
); );
// Задаем конфигурационные регистры // Задаем конфигурационные регистры
@(posedge clk_dac);
pulse_width <= w; pulse_width <= w;
pulse_period <= p; pulse_period <= p;
pulse_num <= n; pulse_num <= n;
@ -121,185 +130,197 @@ module tb_top;
smp_num <= sn; smp_num <= sn;
endtask endtask
// // Таска проверки устойчивости к долгим управляющим импульсам // Основная таска проверки DUT
// task automatic check_impulses; task automatic run_test_case(
// // Локальные переменные для хранения случайных параметров input int pulse_width,
// int rand_start_duration; input int pulse_period,
// int rand_delay; input int pulse_height,
// int rand_ack; input int pulse_num,
// bit rand_first; input int sample_num,
// int total_impulse_cycles = 0; input bit skip_reset,
input bit randomize_start_timing,
input bit out_of_range_val,
input bit randomize_out_of_range
);
int error_flag = 0;
int start_hold_time = 1;
realtime sync_start_time, pulse_start_time, pulse_update_val_time;
realtime sync_time_stats[$], pulse_width_time_stats[$], pulse_period_time_stats[$], pulse_delay_time_stats[$];
realtime avearge_pulse_delay, average_pulse_width, average_pulse_period, average_sync_time;
// int pulse_w = 11; out_of_range = out_of_range_val;
// int pulse_p = 31;
// int pulse_n = 5;
// int pulse_h = 1024;
// $display("[TB] -check_impulses- Check system stability under random latencies"); if (randomize_out_of_range)
fork
begin : randomize_out_of_range_proc
forever begin
@(posedge clk_adc);
out_of_range = $urandom_range(0, 1);
end
end
join_none
// // Установка конфигурации if (!skip_reset)
// set_config( fork
// .w(pulse_w), reset_dut_adc(1);
// .p(pulse_p), reset_dut_dac(1);
// .n(pulse_n), join
// .h(pulse_h)
// );
// reset_dut(5); set_config(
// repeat(2) @(posedge clk); .w(pulse_width),
.p(pulse_period),
.n(pulse_num),
.h(pulse_height),
.sn(sample_num)
);
// // Старт норме 1 такт. Сделаем случайным от 5 до 25 тактов. if (randomize_start_timing)
// rand_start_duration = $urandom_range(5, 25); start_hold_time = $urandom_range(1, 15);
// $display("[TB] Long start: %0d clocks", rand_start_duration); fork // warning: check not forever
start_dut(start_hold_time);
begin
@(posedge clk_dac);
// старт первой синхронизации
sync_start_time = $realtime;
end
join_none
// // Фоновый процесс подсчета тактов импульса for (int i = 0; i < pulse_num; i++) begin
// fork @(posedge m_axis_tvalid);
// begin : counter_proc // Старт цикла. Завершение синхронизации
// forever begin sync_time_stats.push_back($realtime - sync_start_time);
// @(negedge clk); // 180 deg. phase shift for "DAC strobing signal" pulse_start_time = $realtime;
// if (dac_out == pulse_h) begin
// total_impulse_cycles++;
// end
// end
// end
// join_none
// // Параллельный запуск длинного старта и обработки синхронизации // Начало импульса
// fork if (pulse_height != ZERO_LEVEL) begin
// // Поток 1: Удерживаем старт аномально долго wait(m_axis_tdata != ZERO_LEVEL);
// begin if (m_axis_tdata != pulse_height) begin
// start_dut(rand_start_duration); $display("[ERROR] -run_test_case- Wrong pulse height: %d. Must be: %d", m_axis_tdata, pulse_height);
// end $finish;
// // Поток 2: Обслуживаем n=4 циклов синхронизации со случайными задержками end
// begin // Фактическое начало импульса. Поступление высокого уровня
// repeat(pulse_n) begin pulse_update_val_time = $realtime;
// // Рандомизируем параметры для каждого из 4-х рукопожатий pulse_delay_time_stats.push_back(pulse_update_val_time - pulse_start_time);
// rand_first = $urandom; // Случайно: Самплер первый (1) или Генератор первый (0) wait(m_axis_tdata == ZERO_LEVEL);
// rand_delay = $urandom_range(1, 8); // Случайная задержка ожидания (1..8 тактов) pulse_width_time_stats.push_back($realtime - pulse_update_val_time);
// rand_ack = $urandom_range(5, 10); // Аномально долгий удерживаемый импульс sampler_done (10..30 тактов) end
// Конец импульса
// synchronize( @(negedge m_axis_tvalid);
// .sampler_first(rand_first), // Завершение цикла. Старт синхронизации
// .delay_before_ack(rand_delay), pulse_period_time_stats.push_back($realtime - pulse_start_time);
// .ack_duration(rand_ack) sync_start_time = $realtime;
// ); end
// end
// end
// join
// repeat(pulse_p+5) @(posedge clk);
// disable counter_proc;
// // Ожидание завершения переходных процессов
// repeat(10) @(posedge clk);
// if (total_impulse_cycles == pulse_w*pulse_n)
// $display("[TB] -check_impulses- Pulse generation CORRECT");
// else begin
// $display("[ERROR] -check_impulses- Pulse generation INCORRECT. Total number of pulses: %d, must be: %d", total_impulse_cycles, pulse_w*pulse_n);
// $finish;
// end
// $display("[TB] -check_impulses- Done");
// endtask
// task automatic run_test_case( fork // Проверка с таймаутом на лишние циклы
// input int pulse_w, @(posedge m_axis_tvalid);
// input int pulse_p, repeat(20) @(posedge clk_adc);
// input int pulse_n, join_any
// input int pulse_h, if (m_axis_tvalid == 1) begin
// input bit skip_reset, // skip reset sequence on demand $display("[ERROR] -run_test_case- Extra pulse cycle num. More than must be.");
// input bit count_level // count ticks of amplitude == pulse_h or amplitude != pulse_h $finish;
// ); end
// int total_impulse_cycles = 0;
// if (!skip_reset) begin if (randomize_out_of_range)
// reset_dut(1); disable randomize_out_of_range_proc;
// @(posedge clk);
// end
// set_config( // Проверка по статистике. Подсчет средних значений
// .w(pulse_w), if (pulse_delay_time_stats.size() != pulse_num) begin
// .p(pulse_p), $display("[ERROR] -run_test_case- Size of pulse_delay_time_stats samples not equal to pulse_num");
// .n(pulse_n), $finish;
// .h(pulse_h) end
// ); if (pulse_width_time_stats.size() != pulse_num) begin
// @(posedge clk); $display("[ERROR] -run_test_case- Size of pulse_width_time_stats samples not equal to pulse_num");
$finish;
end
if (pulse_period_time_stats.size() != pulse_num) begin
$display("[ERROR] -run_test_case- Size of pulse_period_time_stats samples not equal to pulse_num");
$finish;
end
if (sync_time_stats.size() != pulse_num) begin
$display("[ERROR] -run_test_case- Size of sync_time_stats samples not equal to pulse_num");
$finish;
end
// start_dut(1); avearge_pulse_delay = 0;
foreach (pulse_delay_time_stats[i])
avearge_pulse_delay += pulse_delay_time_stats[i];
avearge_pulse_delay /= pulse_num;
// // Фоновый процесс подсчета тактов импульса average_pulse_width = 0;
// fork foreach (pulse_width_time_stats[i])
// begin : counter_proc average_pulse_width += pulse_width_time_stats[i];
// forever begin average_pulse_width /= pulse_num;
// @(negedge clk); // 180 deg. phase shift for "DAC strobing signal"
// if (count_level) begin
// if (dac_out == pulse_h) begin
// total_impulse_cycles++;
// end
// end
// else begin
// if (dac_out != current_zero_level) begin
// total_impulse_cycles++;
// end
// end
// end
// end
// join_none
// repeat(pulse_n) begin average_pulse_period = 0;
// synchronize( foreach (pulse_period_time_stats[i])
// .sampler_first(0), average_pulse_period += pulse_period_time_stats[i];
// .delay_before_ack(1), average_pulse_period /= pulse_num;
// .ack_duration(2)
// );
// end
// repeat(pulse_p+5) @(posedge clk);
// disable counter_proc;
// repeat(10) @(posedge clk);
// if (count_level) begin average_sync_time = 0;
// if (total_impulse_cycles == pulse_w*pulse_n) foreach (sync_time_stats[i])
// $display("[TB] -run_test_case- Pulse generation CORRECT"); average_sync_time += sync_time_stats[i];
// else begin average_sync_time /= pulse_num;
// $display("[ERROR] -run_test_case- Pulse generation INCORRECT. Total number of pulses: %d, must be: %d", total_impulse_cycles, pulse_w*pulse_n);
// $finish; $display("[TB] -run_test_case- Pulse test stats:\n\tavearge_pulse_delay: %0.3f\n\taverage_pulse_width: %0.3f\n\taverage_pulse_period: %0.3f\n\taverage_sync_time: %0.3f", avearge_pulse_delay, average_pulse_width, average_pulse_period, average_sync_time);
// end
// end if (avearge_pulse_delay > CLOCK_DEVIATION * CLK_ADC_PERIOD) begin
// else begin $display("[ERROR] -run_test_case- avearge_pulse_delay too big: %0.3f", avearge_pulse_delay);
// if (total_impulse_cycles == 0) error_flag = 1;
// $display("[TB] -run_test_case- Pulse generation CORRECT"); end
// else begin if (fabs(average_pulse_width - pulse_width * CLK_DAC_PERIOD) > CLOCK_DEVIATION * CLK_ADC_PERIOD) begin
// $display("[ERROR] -run_test_case- Pulse generation INCORRECT. Total number of pulses: %d, must be: %d", total_impulse_cycles, 0); $display("[ERROR] -run_test_case- average_pulse_width deviates from choosen pulse_width. Deviation: %0.3f > %0.3f ns", fabs(average_pulse_width - pulse_width * CLK_DAC_PERIOD), CLOCK_DEVIATION * CLK_ADC_PERIOD);
// $finish; error_flag = 1;
// end end
// end if (fabs(average_pulse_period - sample_num * CLK_ADC_PERIOD) > CLOCK_DEVIATION * CLK_ADC_PERIOD) begin
// endtask $display("[ERROR] -run_test_case- average_pulse_period deviates from choosen pulse_width. Deviation: %0.3f > %0.3f ns", fabs(average_pulse_period - sample_num * CLK_ADC_PERIOD), CLOCK_DEVIATION * CLK_ADC_PERIOD);
error_flag = 1;
end
if (error_flag)
$finish;
endtask
// Таска
// --- ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ --- // --- ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ ---
initial begin initial begin
$display("[TB] Tests start"); $display("[TB] Tests start");
// Инициализация // Инициализация
dac_start = 0; dac_start = 0;
pulse_width = 0; pulse_width = 0;
pulse_period = 0; pulse_period = 0;
pulse_height = 0; pulse_height = 0;
pulse_num = 0; pulse_num = 0;
smp_num = 0; smp_num = 0;
out_of_range = 0; out_of_range = 0;
fork rst_adc = 0;
reset_dut_adc(3); rst_dac = 0;
reset_dut_dac(6);
join $display("[TB] Test 1. Simple test");
@(posedge clk_dac); // run_test_case(
@(posedge clk_adc); // .pulse_width(50),
set_config( // .pulse_period(125),
.w(50), // .pulse_height(2**(ADC_DATA_WIDTH-1)),
.p(125), // .pulse_num(5),
.n(5), // .sample_num(65),
.h(1024), // .skip_reset(0),
.sn(65) // .randomize_start_timing(0),
); // .out_of_range_val(0),
start_dut(1); // .randomize_out_of_range(0)
// );
run_test_case(
.pulse_width(25),
.pulse_period(110),
.pulse_height(2**(ADC_DATA_WIDTH-1)),
.pulse_num(10),
.sample_num(50),
.skip_reset(0),
.randomize_start_timing(0),
.out_of_range_val(0),
.randomize_out_of_range(0)
);
$display("[TB] Test 1 complete");
repeat(1000) @(posedge clk_adc);
$display("[TB] ALL PASSED"); $display("[TB] ALL PASSED");
$finish; $finish;