diff --git a/designs/adc_dac_synchoronizer/debug.xdc b/designs/adc_dac_synchoronizer/debug.xdc index 9df31b6..2a50dfc 100644 --- a/designs/adc_dac_synchoronizer/debug.xdc +++ b/designs/adc_dac_synchoronizer/debug.xdc @@ -1,3 +1,3 @@ # Primary clocks -create_clock -name eth_clk -period 8.000 [get_ports dac_clk_in] -create_clock -name acc_clk -period 15.385 [get_ports adc_clk_in] \ No newline at end of file +create_clock -name geneartor_clk -period 8.000 [get_ports clk_dac] +create_clock -name sampler_clk -period 15.385 [get_ports clk_adc] \ No newline at end of file diff --git a/designs/adc_dac_synchoronizer/tb_sync_top.sv b/designs/adc_dac_synchoronizer/tb_sync_top.sv index 68c3cf8..851c266 100644 --- a/designs/adc_dac_synchoronizer/tb_sync_top.sv +++ b/designs/adc_dac_synchoronizer/tb_sync_top.sv @@ -19,7 +19,7 @@ module tb_top; 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 CLOCK_DEVIATION = 1; // Maximum clock deviation of pulse stats + localparam CLOCK_DEVIATION = 2; // Maximum clock deviation of pulse stats //------------------------------------------------------------ // Тактовые сигналы и сброс @@ -88,6 +88,8 @@ module tb_top; return (val < 0.0) ? -val : val; endfunction + `define MIN(x, y) (((x) < (y)) ? (x) : (y)) + // Таска сброса DAC DUT task automatic reset_dut_dac( input int rst_duration // сколько тактов держать сброс @@ -151,6 +153,10 @@ module tb_top; out_of_range = out_of_range_val; + if (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Starting test case"); + end + if (randomize_out_of_range) fork begin : randomize_out_of_range_proc @@ -186,60 +192,96 @@ module tb_top; end join_none + if (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Starting pulse generation"); + end + for (int i = 0; i < pulse_num; i++) begin + if (VERBOSE >= 3) begin + $display("[TB] -run_test_case- Start sync for pulse #%d", i); + end @(posedge m_axis_tvalid); + if (VERBOSE >= 3) begin + $display("[TB] -run_test_case- Found valid pulse response data positive front"); + end // Старт цикла. Завершение синхронизации 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; + fork + // Поток будет запущен для ненулевых импульсов и гарантированно завершится как только зафиксирует статистику импульса + // Начало импульса + if (pulse_height != ZERO_LEVEL && pulse_width != 0) begin + if (VERBOSE >= 4) begin + $display("[TB] -run_test_case- Wait until pulse become high"); + end + wait(m_axis_tdata != ZERO_LEVEL); + // Проверим что высота импульса совпала с заданной. Т.к. OTR != 0 влияет на выходные данные сэмплера, то не будем проверять такие случаи. + // Будем считать что из-за OTR данные изменились (по условию OTR + MSB), проверка пропускается, т.к. сложно понять точное значение OTR в момент обработки данных от tdata + if (m_axis_tdata != pulse_height && (randomize_out_of_range || out_of_range_val)) 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); + if (VERBOSE >= 4) begin + $display("[TB] -run_test_case- Wait until pulse become low"); + end + wait(m_axis_tdata == ZERO_LEVEL); + pulse_width_time_stats.push_back($realtime - pulse_update_val_time); 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 - // Конец импульса + // Конец импульса + join_none @(negedge m_axis_tvalid); + if (VERBOSE >= 3) begin + $display("[TB] -run_test_case- Found valid pulse response data negative front"); + end // Завершение цикла. Старт синхронизации pulse_period_time_stats.push_back($realtime - pulse_start_time); sync_start_time = $realtime; end + if (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Stop pulse generation"); + end + fork // Проверка с таймаутом на лишние циклы @(posedge m_axis_tvalid); - repeat(20) @(posedge clk_adc); + repeat(30) @(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) + if (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Exit waiting via timeout"); + end + + if (randomize_out_of_range) begin disable randomize_out_of_range_proc; + if (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Stop randomize_out_of_range_proc"); + end + end // Проверка по статистике. Подсчет средних значений - 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"); + if (pulse_delay_time_stats.size() != pulse_num && pulse_height != ZERO_LEVEL && pulse_width != 0) begin // Detected with pulse level. Skip if pulse level undetectable + $display("[ERROR] -run_test_case- Size of pulse_delay_time_stats samples not equal to pulse_num: %d VS %d", pulse_delay_time_stats.size(), 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"); + if (pulse_width_time_stats.size() != pulse_num && pulse_height != ZERO_LEVEL && pulse_width != 0) begin // Detected with pulse level. Skip if pulse level undetectable + $display("[ERROR] -run_test_case- Size of pulse_width_time_stats samples not equal to pulse_num: %d VS %d", pulse_width_time_stats.size(), 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"); + $display("[ERROR] -run_test_case- Size of pulse_period_time_stats samples not equal to pulse_num: %d VS %d", pulse_period_time_stats.size(), 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"); + $display("[ERROR] -run_test_case- Size of sync_time_stats samples not equal to pulse_num: %d VS %d", sync_time_stats.size(), pulse_num); $finish; end @@ -271,7 +313,7 @@ module tb_top; $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 + if (fabs(average_pulse_width - pulse_width * CLK_DAC_PERIOD * (pulse_height != ZERO_LEVEL)) > CLOCK_DEVIATION * CLK_ADC_PERIOD && sample_num * CLK_ADC_PERIOD >= pulse_width * CLK_DAC_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 @@ -279,8 +321,17 @@ module tb_top; $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 (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Pass error processing"); + end + if (error_flag) $finish; + + if (VERBOSE >= 2) begin + $display("[TB] -run_test_case- Passed checks"); + end endtask // Таска @@ -299,7 +350,7 @@ module tb_top; rst_adc = 0; rst_dac = 0; - $display("[TB] Test 1. Simple test. (1/2)"); + $display("[TB] Test 1. Simple test. (1/3)"); run_test_case( .pulse_width(50), .pulse_period(125), @@ -307,26 +358,182 @@ module tb_top; .pulse_num(5), .sample_num(65), .skip_reset(0), - .randomize_start_timing(0), + .randomize_start_timing(1), .out_of_range_val(0), .randomize_out_of_range(0) ); - $display("[TB] Test 1. Simple test. (2/2)"); + $display("[TB] Test 1. Simple test. (2/3)"); run_test_case( .pulse_width(25), .pulse_period(125), .pulse_height(2**(ADC_DATA_WIDTH-1)), .pulse_num(10), .sample_num(65), - .skip_reset(0), - .randomize_start_timing(0), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(0), + .randomize_out_of_range(0) + ); + $display("[TB] Test 1. Simple test. (3/3)"); + run_test_case( + .pulse_width(10), + .pulse_period(50), + .pulse_height(ZERO_LEVEL), + .pulse_num(4), + .sample_num(25), + .skip_reset(1), + .randomize_start_timing(1), .out_of_range_val(0), .randomize_out_of_range(0) ); $display("[TB] Test 1 complete"); + $display("[TB] Test 2. Edge cases. Pulse width 0%%. (1/7)"); + run_test_case( + .pulse_width(0), + .pulse_period(125), + .pulse_height(2**(ADC_DATA_WIDTH-1)), + .pulse_num(5), + .sample_num(65), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(0), + .randomize_out_of_range(0) + ); + $display("[TB] Test 2. Edge cases. Pulse width 100%%. (2/7)"); + run_test_case( + .pulse_width(10), + .pulse_period(10), + .pulse_height(2**(ADC_DATA_WIDTH-1)), + .pulse_num(5), + .sample_num(65), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(0), + .randomize_out_of_range(0) + ); + $display("[TB] Test 2. Edge cases. Pulse height == ZERO_LEVEL. (3/7)"); + run_test_case( + .pulse_width(10), + .pulse_period(125), + .pulse_height(ZERO_LEVEL), + .pulse_num(5), + .sample_num(65), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(0), + .randomize_out_of_range(0) + ); + $display("[TB] Test 2. Edge cases. Pulse num == 0. (4/7)"); + run_test_case( + .pulse_width(10), + .pulse_period(125), + .pulse_height(2**(ADC_DATA_WIDTH-3)), + .pulse_num(0), + .sample_num(65), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(0), + .randomize_out_of_range(0) + ); + + $display("[TB] Test 2. Edge cases. Sample num time << Pulse width time. (5/7)"); + run_test_case( + .pulse_width(10), + .pulse_period(125), + .pulse_height(2**(ADC_DATA_WIDTH-1)), + .pulse_num(5), + .sample_num(2), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(0), + .randomize_out_of_range(0) + ); + + // Ожидание окончания работы генератора. Т.к. конец работы определяется по tvalid сэмплера, а он завершается сильно раньше. Чтобы не пропустить start следующей таски, ждем + wait(dut.generator_inst.enable == 0); + #50; + + $display("[TB] Test 2. Edge cases. Sample num == 0. (6/7)"); + // Запустим в работу вручную, т.к. run_test_case обязательно ждет pulse num циклов. Детекция цикла производится по активности сэплера. Ее не должно быть при sample num = 0 + set_config( + .w(10), + .p(125), + .h(2**(ADC_DATA_WIDTH-1)), + .n(5), + .sn(0) + ); + start_dut(3); + fork + begin : wait_sampler_active_proc + @(posedge m_axis_tvalid); + $display("[ERROR] Sampler active with sample num == 0"); + $finish; + end + begin + @(negedge dut.generator_inst.enable); + end + join_any + disable wait_sampler_active_proc; + repeat(30) @(posedge clk_adc); + + // Данный тест должен приводить к тому, что сэмплер будет давать крайние значения вместо заданного pulse height из-за OTR=1 + // TODO известно что при таких настройках OTR = 1 постоянно. Легко предсказать уровень + // Дописать авто тест + $display("[TB] Test 2. Edge cases. OTR == 1. (7/7)"); + run_test_case( + .pulse_width(10), + .pulse_period(125), + .pulse_height(10), + .pulse_num(5), + .sample_num(65), + .skip_reset(1), + .randomize_start_timing(1), + .out_of_range_val(1), + .randomize_out_of_range(0) + ); + $display("[TB] Test 2 complete"); + + // $finish; // TODO remove + + $display("[TB] Test 3. Random tests"); + for (int i = 0; i < 100; i++) begin + int r_w, r_p, r_n, r_h, r_sn; + bit r_skip, r_otr, r_otr_rand; + + // Генерируем параметры + r_p = $urandom_range(50, 150); // Период от 5 до 50 + r_w = $urandom_range(10, r_p); // Ширина не больше периода + r_n = $urandom_range(1, 10); // Количество импульсов + r_h = $urandom_range(0, 2**(`MIN(ADC_DATA_WIDTH, DAC_DATA_WIDTH))-1); // Высота импульса + r_sn = $urandom_range(1, 40); // Число сэмплов + r_skip = $urandom_range(0, 1); // Случайный сброс (0 - сброс, 1 - пропуск) + r_otr = $urandom_range(0, 1); // Out Of Range стартовое значение + r_otr_rand = $urandom_range(0, 1); // Сделать OTR случайным + + if (VERBOSE >= 1) + $display("[TB] --- Test #%0d (Config: W=%0d, P=%0d, N=%0d, H=%0d, SN=%0d, SkipReset=%0b) ---", + i+1, r_w, r_p, r_n, r_h, r_sn, r_skip); + + run_test_case( + .pulse_width(r_w), + .pulse_period(r_p), + .pulse_height(r_h), + .pulse_num(r_n), + .sample_num(r_sn), + .skip_reset(r_skip), + .randomize_start_timing(0), + .out_of_range_val(r_otr), + .randomize_out_of_range(r_otr_rand) + ); + + wait(dut.generator_inst.enable == 0); // Проверка на завершение работы + #50; + end + $display("[TB] Test 3 complete"); $display("[TB] ALL PASSED"); + $display("[TB] Maximum clock deviation of stats %0.2f", CLOCK_DEVIATION); $finish; end diff --git a/rtl/sampler/src/sampler.sv b/rtl/sampler/src/sampler.sv index 94d5c38..ccc36b7 100644 --- a/rtl/sampler/src/sampler.sv +++ b/rtl/sampler/src/sampler.sv @@ -83,9 +83,9 @@ module sampler if (cnt_smp_num != smp_num_reg) begin cnt_smp_num <= cnt_smp_num +1; buffer_ready <= 1; - if (!out_of_range_reg) begin + // if (!out_of_range_reg) begin buffer <= data_converted; - end + // end end else begin cnt_smp_num <= '0; buffer_ready <= 0; @@ -120,16 +120,16 @@ module sampler if (cnt_smp_num != smp_num_reg) begin cnt_smp_num <= cnt_smp_num +1; buffer_ready <= 1; - if (!out_of_range_reg) begin + // if (!out_of_range_reg) begin buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; if (cnt == PACK_FACTOR-1) begin cnt <= 0; buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; end - else begin - cnt <= cnt + 1; - end - end + else begin + cnt <= cnt + 1; + end + // end end else begin cnt_smp_num <= '0; buffer_ready <= 0;