From 4923f0132269302a69fc0083f5a441053d616992 Mon Sep 17 00:00:00 2001 From: Zer0Nu11 Date: Fri, 19 Jun 2026 08:00:00 +0300 Subject: [PATCH] upd: TB automation, DA->AD delay line for signal integrity --- designs/adc_dac_synchoronizer/sync_top.sv | 25 +- designs/adc_dac_synchoronizer/tb_sync_top.sv | 349 ++++++++++--------- 2 files changed, 205 insertions(+), 169 deletions(-) diff --git a/designs/adc_dac_synchoronizer/sync_top.sv b/designs/adc_dac_synchoronizer/sync_top.sv index ea5f28a..1fa1cf6 100644 --- a/designs/adc_dac_synchoronizer/sync_top.sv +++ b/designs/adc_dac_synchoronizer/sync_top.sv @@ -6,7 +6,8 @@ module sync_top 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 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, @@ -16,9 +17,9 @@ module sync_top input start, input out_of_range, 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 [15:0] pulse_num, // DAC counter limit + input [15:0] pulse_num, input [31:0] smp_num, // ADC counter limit output [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata, output m_axis_tvalid @@ -29,15 +30,29 @@ module sync_top //------------------------------------------------------------ wire dac_done, dac_request, adc_done, adc_request; 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; generate 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 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 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 diff --git a/designs/adc_dac_synchoronizer/tb_sync_top.sv b/designs/adc_dac_synchoronizer/tb_sync_top.sv index 7737d93..526ea79 100644 --- a/designs/adc_dac_synchoronizer/tb_sync_top.sv +++ b/designs/adc_dac_synchoronizer/tb_sync_top.sv @@ -5,16 +5,20 @@ module tb_top; //------------------------------------------------------------ // Параметры //------------------------------------------------------------ + parameter string ZERO_LEVEL_PARAM = "logic"; // "logic" VS "true" + localparam DAC_DATA_WIDTH = 14; localparam ADC_DATA_WIDTH = 12; localparam PACK_FACTOR = 1; 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_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), .PACK_FACTOR(PACK_FACTOR), .PROCESS_MODE(PROCESS_MODE), - .ZERO_LEVEL(ZERO_LEVEL) + .ZERO_LEVEL(ZERO_LEVEL), + .USE_DELAY_LINE(USE_DELAY_LINE) ) dut ( .clk_adc(clk_adc), .clk_dac(clk_dac), @@ -77,6 +82,11 @@ module tb_top; end // === Таски для тестирования === + // Функция модуля + function automatic real fabs(real val); + return (val < 0.0) ? -val : val; + endfunction + // Таска сброса DAC DUT task automatic reset_dut_dac( input int rst_duration // сколько тактов держать сброс @@ -113,194 +123,205 @@ module tb_top; input logic [31:0] sn // число сэмплов ); // Задаем конфигурационные регистры - @(posedge clk_dac); pulse_width <= w; pulse_period <= p; pulse_num <= n; pulse_height <= h; smp_num <= sn; endtask + + // Основная таска проверки DUT + task automatic run_test_case( + input int pulse_width, + input int pulse_period, + input int pulse_height, + input int pulse_num, + input int sample_num, + 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; - // // Таска проверки устойчивости к долгим управляющим импульсам - // task automatic check_impulses; - // // Локальные переменные для хранения случайных параметров - // int rand_start_duration; - // int rand_delay; - // int rand_ack; - // bit rand_first; - // int total_impulse_cycles = 0; + out_of_range = out_of_range_val; - // int pulse_w = 11; - // int pulse_p = 31; - // int pulse_n = 5; - // int pulse_h = 1024; + 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 - // $display("[TB] -check_impulses- Check system stability under random latencies"); + if (!skip_reset) + fork + reset_dut_adc(1); + reset_dut_dac(1); + join - // // Установка конфигурации - // set_config( - // .w(pulse_w), - // .p(pulse_p), - // .n(pulse_n), - // .h(pulse_h) - // ); + set_config( + .w(pulse_width), + .p(pulse_period), + .n(pulse_num), + .h(pulse_height), + .sn(sample_num) + ); - // reset_dut(5); - // repeat(2) @(posedge clk); + if (randomize_start_timing) + start_hold_time = $urandom_range(1, 15); + fork // warning: check not forever + start_dut(start_hold_time); + begin + @(posedge clk_dac); + // старт первой синхронизации + sync_start_time = $realtime; + end + join_none - // // Старт норме 1 такт. Сделаем случайным от 5 до 25 тактов. - // rand_start_duration = $urandom_range(5, 25); - // $display("[TB] Long start: %0d clocks", rand_start_duration); + for (int i = 0; i < pulse_num; i++) begin + @(posedge m_axis_tvalid); + // Старт цикла. Завершение синхронизации + sync_time_stats.push_back($realtime - sync_start_time); + pulse_start_time = $realtime; + + // Начало импульса + if (pulse_height != ZERO_LEVEL) begin + wait(m_axis_tdata != ZERO_LEVEL); + if (m_axis_tdata != pulse_height) begin + $display("[ERROR] -run_test_case- Wrong pulse height: %d. Must be: %d", m_axis_tdata, pulse_height); + $finish; + end + // Фактическое начало импульса. Поступление высокого уровня + pulse_update_val_time = $realtime; + pulse_delay_time_stats.push_back(pulse_update_val_time - pulse_start_time); + wait(m_axis_tdata == ZERO_LEVEL); + pulse_width_time_stats.push_back($realtime - pulse_update_val_time); + end + // Конец импульса + + @(negedge m_axis_tvalid); + // Завершение цикла. Старт синхронизации + pulse_period_time_stats.push_back($realtime - pulse_start_time); + sync_start_time = $realtime; + end - // // Фоновый процесс подсчета тактов импульса - // fork - // begin : counter_proc - // forever begin - // @(negedge clk); // 180 deg. phase shift for "DAC strobing signal" - // if (dac_out == pulse_h) begin - // total_impulse_cycles++; - // end - // end - // end - // join_none + fork // Проверка с таймаутом на лишние циклы + @(posedge m_axis_tvalid); + repeat(20) @(posedge clk_adc); + join_any + if (m_axis_tvalid == 1) begin + $display("[ERROR] -run_test_case- Extra pulse cycle num. More than must be."); + $finish; + end + + if (randomize_out_of_range) + disable randomize_out_of_range_proc; + + // Проверка по статистике. Подсчет средних значений + if (pulse_delay_time_stats.size() != pulse_num) begin + $display("[ERROR] -run_test_case- Size of pulse_delay_time_stats samples not equal to pulse_num"); + $finish; + end + if (pulse_width_time_stats.size() != pulse_num) begin + $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 + + 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; + foreach (pulse_width_time_stats[i]) + average_pulse_width += pulse_width_time_stats[i]; + average_pulse_width /= pulse_num; + + average_pulse_period = 0; + foreach (pulse_period_time_stats[i]) + average_pulse_period += pulse_period_time_stats[i]; + average_pulse_period /= pulse_num; + + average_sync_time = 0; + foreach (sync_time_stats[i]) + average_sync_time += sync_time_stats[i]; + average_sync_time /= pulse_num; - // // Параллельный запуск длинного старта и обработки синхронизации - // fork - // // Поток 1: Удерживаем старт аномально долго - // begin - // start_dut(rand_start_duration); - // end - // // Поток 2: Обслуживаем n=4 циклов синхронизации со случайными задержками - // begin - // repeat(pulse_n) begin - // // Рандомизируем параметры для каждого из 4-х рукопожатий - // rand_first = $urandom; // Случайно: Самплер первый (1) или Генератор первый (0) - // rand_delay = $urandom_range(1, 8); // Случайная задержка ожидания (1..8 тактов) - // rand_ack = $urandom_range(5, 10); // Аномально долгий удерживаемый импульс sampler_done (10..30 тактов) - - // synchronize( - // .sampler_first(rand_first), - // .delay_before_ack(rand_delay), - // .ack_duration(rand_ack) - // ); - // 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( - // input int pulse_w, - // input int pulse_p, - // input int pulse_n, - // input int pulse_h, - // input bit skip_reset, // skip reset sequence on demand - // input bit count_level // count ticks of amplitude == pulse_h or amplitude != pulse_h - // ); - // int total_impulse_cycles = 0; - - // if (!skip_reset) begin - // reset_dut(1); - // @(posedge clk); - // end - - // set_config( - // .w(pulse_w), - // .p(pulse_p), - // .n(pulse_n), - // .h(pulse_h) - // ); - // @(posedge clk); - - // start_dut(1); + $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); - // // Фоновый процесс подсчета тактов импульса - // fork - // begin : counter_proc - // forever begin - // @(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 - // synchronize( - // .sampler_first(0), - // .delay_before_ack(1), - // .ack_duration(2) - // ); - // end - // repeat(pulse_p+5) @(posedge clk); - // disable counter_proc; - // repeat(10) @(posedge clk); - - // if (count_level) begin - // if (total_impulse_cycles == pulse_w*pulse_n) - // $display("[TB] -run_test_case- Pulse generation CORRECT"); - // else begin - // $display("[ERROR] -run_test_case- Pulse generation INCORRECT. Total number of pulses: %d, must be: %d", total_impulse_cycles, pulse_w*pulse_n); - // $finish; - // end - // end - // else begin - // if (total_impulse_cycles == 0) - // $display("[TB] -run_test_case- Pulse generation CORRECT"); - // else begin - // $display("[ERROR] -run_test_case- Pulse generation INCORRECT. Total number of pulses: %d, must be: %d", total_impulse_cycles, 0); - // $finish; - // end - // end - // endtask + if (avearge_pulse_delay > CLOCK_DEVIATION * CLK_ADC_PERIOD) begin + $display("[ERROR] -run_test_case- avearge_pulse_delay too big: %0.3f", avearge_pulse_delay); + error_flag = 1; + end + if (fabs(average_pulse_width - pulse_width * CLK_DAC_PERIOD) > CLOCK_DEVIATION * CLK_ADC_PERIOD) begin + $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); + error_flag = 1; + end + if (fabs(average_pulse_period - sample_num * CLK_ADC_PERIOD) > CLOCK_DEVIATION * CLK_ADC_PERIOD) begin + $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 $display("[TB] Tests start"); // Инициализация - dac_start = 0; + dac_start = 0; pulse_width = 0; pulse_period = 0; pulse_height = 0; pulse_num = 0; smp_num = 0; out_of_range = 0; - fork - reset_dut_adc(3); - reset_dut_dac(6); - join - @(posedge clk_dac); - @(posedge clk_adc); - set_config( - .w(50), - .p(125), - .n(5), - .h(1024), - .sn(65) + rst_adc = 0; + rst_dac = 0; + + $display("[TB] Test 1. Simple test"); + // run_test_case( + // .pulse_width(50), + // .pulse_period(125), + // .pulse_height(2**(ADC_DATA_WIDTH-1)), + // .pulse_num(5), + // .sample_num(65), + // .skip_reset(0), + // .randomize_start_timing(0), + // .out_of_range_val(0), + // .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) ); - start_dut(1); - - repeat(1000) @(posedge clk_adc); - + $display("[TB] Test 1 complete"); + + $display("[TB] ALL PASSED"); $finish; end