diff --git a/designs/adc_dac_synchoronizer/debug.xdc b/designs/adc_dac_synchoronizer/debug.xdc index 0dd108b..9df31b6 100644 --- a/designs/adc_dac_synchoronizer/debug.xdc +++ b/designs/adc_dac_synchoronizer/debug.xdc @@ -1,10 +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] - - -# Asynchronous clock groups - -set_clock_groups -name ASYNC_ETH_ACC -asynchronous \ - -group [get_clocks eth_clk] \ - -group [get_clocks acc_clk] \ No newline at end of file +create_clock -name acc_clk -period 15.385 [get_ports adc_clk_in] \ No newline at end of file diff --git a/designs/adc_dac_synchoronizer/sync_top.sv b/designs/adc_dac_synchoronizer/sync_top.sv index 22424d0..ea5f28a 100644 --- a/designs/adc_dac_synchoronizer/sync_top.sv +++ b/designs/adc_dac_synchoronizer/sync_top.sv @@ -2,113 +2,104 @@ module sync_top #( - parameter int unsigned DAC_DATA_WIDTH = 14, - parameter int unsigned ADC_DATA_WIDTH = 12, - parameter int unsigned PACK_FACTOR = 1, - parameter int unsigned PROCESS_MODE = 0 + parameter int unsigned DAC_DATA_WIDTH = 14, // DAC 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 PROCESS_MODE = 0, // representation format of ADC readings (0 - direct code, 1 - 2's completment) + parameter int unsigned ZERO_LEVEL = 0 ) ( - input adc_clk_in, - input adc_rst, - - input dac_clk_in, - input dac_rst, - - input dac_start, - + input clk_adc, + input rst_adc, + input clk_dac, + input rst_dac, + input start, + input out_of_range, input [31:0] pulse_width, input [31:0] pulse_period, input [DAC_DATA_WIDTH-1:0] pulse_height, - input [15:0] pulse_num, - input [31:0] smp_num, - - output logic [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata, - output logic m_axis_tvalid + input [15:0] pulse_num, // DAC counter limit + input [31:0] smp_num, // ADC counter limit + output [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata, + output m_axis_tvalid ); //------------------------------------------------------------ // Internal signals //------------------------------------------------------------ - - (* MARK_DEBUG="true" *) logic sample_req; - (* MARK_DEBUG="true" *) logic sample_req_sync1; - (* MARK_DEBUG="true" *) logic sample_req_sync2; - (* MARK_DEBUG="true" *) logic sample_req_sync3; - - (* MARK_DEBUG="true" *) logic sample_done; - (* MARK_DEBUG="true" *) logic sample_done_sync1; - (* MARK_DEBUG="true" *) logic sample_done_sync2; - (* MARK_DEBUG="true" *) logic sample_done_sync3; - - (* MARK_DEBUG="true" *) logic pulse; - (* MARK_DEBUG="true" *) logic [DAC_DATA_WIDTH-1:0] pulse_height_out; - - //------------------------------------------------------------ - // Simple DAC -> ADC test source - // - // generator output is directly connected to sampler input - // with width truncation: - // - // pulse_height_out[13:0] -> data_in[11:0] - //------------------------------------------------------------ - (* MARK_DEBUG="true" *) logic [ADC_DATA_WIDTH-1:0] data_in; - (* MARK_DEBUG="true" *) logic out_of_range; - - assign data_in = pulse_height_out[ADC_DATA_WIDTH-1:0]; - assign out_of_range = 1'b0; + wire dac_done, dac_request, adc_done, adc_request; + wire [DAC_DATA_WIDTH-1:0] dac_signal; + 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 }; + end + else begin : g_truncate + assign adc_singnal = dac_signal[ADC_DATA_WIDTH-1:0]; + end + endgenerate //------------------------------------------------------------ // DAC -> ADC CDC //------------------------------------------------------------ - always_ff @(posedge adc_clk_in or posedge adc_rst) begin - if (adc_rst) begin - sample_req <= 1'b0; - sample_req_sync2 <= 1'b0; - sample_req_sync3 <= 1'b0; - end + logic [2:0] stretch; // 125/65~=2. Чтобы поймать единичный импульс, растянем его во времени + logic [1:0] sync_DA; + wire dac_done_stretched; + + always_ff @(posedge clk_dac or posedge rst_dac) + begin + if (rst_dac) + stretch <= 0; else begin - sample_req_sync2 <= sample_req_sync1; - sample_req_sync3 <= sample_req_sync2; - sample_req <= sample_req_sync3; + stretch[0] <= dac_done; + stretch[1] <= stretch[0]; + stretch[2] <= stretch[1]; end end + assign dac_done_stretched = |stretch; + + always_ff @(posedge clk_adc or posedge rst_adc) begin + if (rst_adc) + sync_DA <= 0; + else begin + sync_DA[0] <= dac_done_stretched; + sync_DA[1] <= sync_DA[0]; + end + end + assign adc_request = sync_DA[1]; //------------------------------------------------------------ // ADC -> DAC CDC //------------------------------------------------------------ - always_ff @(posedge dac_clk_in or posedge dac_rst) begin - if (dac_rst) begin - sample_done <= 1'b0; - sample_done_sync2 <= 1'b0; - sample_done_sync3 <= 1'b0; - end + logic [1:0] sync_AD; + + always_ff @(posedge clk_dac or posedge rst_dac) begin + if (rst_dac) + sync_AD <= 0; else begin - sample_done_sync2 <= sample_done_sync1; - sample_done_sync3 <= sample_done_sync2; - sample_done <= sample_done_sync3; + sync_AD[0] <= adc_done; + sync_AD[1] <= sync_AD[0]; end end + assign dac_request = sync_AD[1]; //------------------------------------------------------------ // Generator //------------------------------------------------------------ generator #( - .DATA_WIDTH(DAC_DATA_WIDTH) + .DATA_WIDTH(DAC_DATA_WIDTH), + .ZERO_LEVEL(ZERO_LEVEL) ) generator_inst ( - .clk_in(dac_clk_in), - .rst(dac_rst), - .start(dac_start), - + .clk_dac(clk_dac), + .rst(rst_dac), + .start(start), .pulse_width(pulse_width), .pulse_period(pulse_period), .pulse_height(pulse_height), .pulse_num(pulse_num), - - .sample_done(sample_done), - - .pulse(pulse), - .pulse_height_out(pulse_height_out), - .sample_req(sample_req_sync1) + .dac_out(dac_signal), + .request(dac_request), + .done(dac_done) ); //------------------------------------------------------------ @@ -119,18 +110,15 @@ module sync_top .PACK_FACTOR(PACK_FACTOR), .PROCESS_MODE(PROCESS_MODE) ) sampler_inst ( - .clk_in(adc_clk_in), - .rst(adc_rst), - - .data_in(data_in), + .clk_in(clk_adc), + .rst(rst_adc), + .data_in(adc_singnal), .out_of_range(out_of_range), - .smp_num(smp_num), - .sample_req(sample_req), - .m_axis_tdata(m_axis_tdata), .m_axis_tvalid(m_axis_tvalid), - .sample_done(sample_done_sync1) + .request(adc_request), + .done(adc_done) ); endmodule \ 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 0fe7627..94e7c18 100644 --- a/designs/adc_dac_synchoronizer/tb_sync_top.sv +++ b/designs/adc_dac_synchoronizer/tb_sync_top.sv @@ -2,39 +2,45 @@ module tb_top; - localparam DAC_DATA_WIDTH = 14; - localparam ADC_DATA_WIDTH = 12; - localparam PACK_FACTOR = 1; - localparam PROCESS_MODE = 0; + //------------------------------------------------------------ + // Параметры + //------------------------------------------------------------ + 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 ZERO_LEVEL = LOGIC_ZERO_LEVEL; // "logic" VS "true" //------------------------------------------------------------ - // clocks / reset + // Тактовые сигналы и сброс //------------------------------------------------------------ - logic adc_clk_in; - logic adc_rst; - - logic dac_clk_in; - logic dac_rst; - + logic clk_dac; + logic rst_dac; + logic clk_adc; + logic rst_adc; //------------------------------------------------------------ - // control + // Управление и конфиг //------------------------------------------------------------ logic dac_start; - - logic [31:0] pulse_width; - logic [31:0] pulse_period; - logic [DAC_DATA_WIDTH-1:0] pulse_height; - logic [15:0] pulse_num; - logic [31:0] smp_num; - + logic [31:0] pulse_width; + logic [31:0] pulse_period; + logic [DAC_DATA_WIDTH-1:0] pulse_height; + logic [15:0] pulse_num; + logic [31:0] smp_num; //------------------------------------------------------------ - // outputs + // Входы //------------------------------------------------------------ - logic [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; - logic m_axis_tvalid; - - integer valid_count; - + reg out_of_range; + //------------------------------------------------------------ + // Выходы + //------------------------------------------------------------ + wire [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; + wire m_axis_tvalid; //------------------------------------------------------------ // DUT //------------------------------------------------------------ @@ -42,127 +48,260 @@ module tb_top; .DAC_DATA_WIDTH(DAC_DATA_WIDTH), .ADC_DATA_WIDTH(ADC_DATA_WIDTH), .PACK_FACTOR(PACK_FACTOR), - .PROCESS_MODE(PROCESS_MODE) + .PROCESS_MODE(PROCESS_MODE), + .ZERO_LEVEL(ZERO_LEVEL) ) dut ( - .adc_clk_in(adc_clk_in), - .adc_rst(adc_rst), - - .dac_clk_in(dac_clk_in), - .dac_rst(dac_rst), - - .dac_start(dac_start), - + .clk_adc(clk_adc), + .clk_dac(clk_dac), + .rst_adc(rst_adc), + .rst_dac(rst_dac), + .start(dac_start), .pulse_width(pulse_width), .pulse_period(pulse_period), .pulse_height(pulse_height), .pulse_num(pulse_num), .smp_num(smp_num), - .m_axis_tdata(m_axis_tdata), - .m_axis_tvalid(m_axis_tvalid) + .m_axis_tvalid(m_axis_tvalid), + .out_of_range(out_of_range) ); - //------------------------------------------------------------ - // ADC clock - //------------------------------------------------------------ + // Тактовые сигналы initial begin - adc_clk_in = 1'b0; - forever #5 adc_clk_in = ~adc_clk_in; // 100 MHz + clk_adc = 0; + forever #(CLK_ADC_PERIOD/2) clk_adc = ~clk_adc; + end + initial begin + clk_dac = 0; + forever #(CLK_DAC_PERIOD/2) clk_dac = ~clk_dac; end - //------------------------------------------------------------ - // DAC clock - //------------------------------------------------------------ + // === Таски для тестипрования === + // Таска сброса DAC DUT + task automatic reset_dut_dac( + input int rst_duration // сколько тактов держать сброс + ); + rst_dac <= 1; + repeat(rst_duration) @(posedge clk_dac); + rst_dac <= 0; + endtask + + // Таска сброса ADC DUT + task automatic reset_dut_adc( + input int rst_duration // сколько тактов держать сброс + ); + rst_adc <= 1; + repeat(rst_duration) @(posedge clk_adc); + rst_adc <= 0; + endtask + + // Таска запуска DUT + task automatic start_dut( + input int start_duration // сколько тактов держать импульс + ); + dac_start <= 1; + repeat(start_duration) @(posedge clk_dac); + dac_start <= 0; + endtask + + // Таска конфигурации DUT + task automatic set_config( + input logic [31:0] w, // ширина импульса + input logic [31:0] p, // период импульса + input logic [15:0] n, // количество импульсов + input logic [DAC_DATA_WIDTH-1:0] h, // высота импульса + input logic [31:0] sn // число сэмплов + ); + // Задаем конфигурационные регистры + @(posedge clk_dac); + pulse_width <= w; + pulse_period <= p; + pulse_num <= n; + pulse_height <= h; + smp_num <= sn; + endtask + + // // Таска проверки устойчивости к долгим управляющим импульсам + // task automatic check_impulses; + // // Локальные переменные для хранения случайных параметров + // int rand_start_duration; + // int rand_delay; + // int rand_ack; + // bit rand_first; + // int total_impulse_cycles = 0; + + // int pulse_w = 11; + // int pulse_p = 31; + // int pulse_n = 5; + // int pulse_h = 1024; + + // $display("[TB] -check_impulses- Check system stability under random latencies"); + + // // Установка конфигурации + // set_config( + // .w(pulse_w), + // .p(pulse_p), + // .n(pulse_n), + // .h(pulse_h) + // ); + + // reset_dut(5); + // repeat(2) @(posedge clk); + + // // Старт норме 1 такт. Сделаем случайным от 5 до 25 тактов. + // rand_start_duration = $urandom_range(5, 25); + // $display("[TB] Long start: %0d clocks", rand_start_duration); + + // // Фоновый процесс подсчета тактов импульса + // 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 + // // Поток 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); + + // // Фоновый процесс подсчета тактов импульса + // 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 + + // --- ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ --- initial begin - dac_clk_in = 1'b0; - forever #8 dac_clk_in = ~dac_clk_in; // slower domain - end - - //------------------------------------------------------------ - // monitor output stream - //------------------------------------------------------------ - always @(posedge adc_clk_in) begin - if (m_axis_tvalid) begin - valid_count = valid_count + 1; - - $display("[%0t] VALID: data=%0d", - $time, - m_axis_tdata); - end - end - - //------------------------------------------------------------ - // test - //------------------------------------------------------------ - initial begin - - adc_rst = 1'b1; - dac_rst = 1'b1; - - dac_start = 1'b0; + $display("[TB] Tests start"); + // Инициализация + 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) + ); + start_dut(1); - valid_count = 0; - - //-------------------------------------------------------- - // reset - //-------------------------------------------------------- - repeat (10) @(posedge adc_clk_in); - repeat (10) @(posedge dac_clk_in); - - adc_rst = 1'b0; - dac_rst = 1'b0; - - repeat (5) @(posedge dac_clk_in); - - //-------------------------------------------------------- - // config - //-------------------------------------------------------- - pulse_width = 32'd3; - pulse_period = 32'd8; - pulse_height = 14'd200; - pulse_num = 16'd4; - smp_num = 32'd8; - - //-------------------------------------------------------- - // start - //-------------------------------------------------------- - @(posedge dac_clk_in); - dac_start = 1'b1; - - @(posedge dac_clk_in); - dac_start = 1'b0; - - $display("=================================="); - $display("TEST START"); - $display("=================================="); - - //-------------------------------------------------------- - // wait - //-------------------------------------------------------- - repeat (600) @(posedge adc_clk_in); - - //-------------------------------------------------------- - // check - //-------------------------------------------------------- - if (valid_count > 0) begin - $display("=================================="); - $display("TEST PASSED"); - $display("valid_count = %0d", valid_count); - $display("=================================="); - end - else begin - $display("=================================="); - $display("TEST FAILED"); - $display("No valid output detected"); - $display("=================================="); - end + + $display("[TB] ALL PASSED"); $finish; end