update: new synchronizer + half-baked TB

This commit is contained in:
2026-06-10 16:40:53 +03:00
parent bb65aea4f1
commit c165d346a0
3 changed files with 334 additions and 214 deletions

View File

@ -1,10 +1,3 @@
# Primary clocks # Primary clocks
create_clock -name eth_clk -period 8.000 [get_ports dac_clk_in] 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] 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]

View File

@ -2,113 +2,104 @@
module sync_top module sync_top
#( #(
parameter int unsigned DAC_DATA_WIDTH = 14, parameter int unsigned DAC_DATA_WIDTH = 14, // DAC bit-width
parameter int unsigned ADC_DATA_WIDTH = 12, parameter int unsigned ADC_DATA_WIDTH = 12, // ADC bit-width
parameter int unsigned PACK_FACTOR = 1, parameter int unsigned PACK_FACTOR = 1, // number of ADC readings per transaction
parameter int unsigned PROCESS_MODE = 0 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 clk_adc,
input adc_rst, input rst_adc,
input clk_dac,
input dac_clk_in, input rst_dac,
input dac_rst, input start,
input out_of_range,
input dac_start,
input [31:0] pulse_width, input [31:0] pulse_width,
input [31:0] pulse_period, input [31:0] pulse_period,
input [DAC_DATA_WIDTH-1:0] pulse_height, input [DAC_DATA_WIDTH-1:0] pulse_height,
input [15:0] pulse_num, input [15:0] pulse_num, // DAC counter limit
input [31:0] smp_num, input [31:0] smp_num, // ADC counter limit
output [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata,
output logic [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata, output m_axis_tvalid
output logic m_axis_tvalid
); );
//------------------------------------------------------------ //------------------------------------------------------------
// Internal signals // Internal signals
//------------------------------------------------------------ //------------------------------------------------------------
wire dac_done, dac_request, adc_done, adc_request;
(* MARK_DEBUG="true" *) logic sample_req; wire [DAC_DATA_WIDTH-1:0] dac_signal;
(* MARK_DEBUG="true" *) logic sample_req_sync1; wire [ADC_DATA_WIDTH-1:0] adc_singnal;
(* MARK_DEBUG="true" *) logic sample_req_sync2; generate
(* MARK_DEBUG="true" *) logic sample_req_sync3; if (ADC_DATA_WIDTH > DAC_DATA_WIDTH) begin : g_pad_zeros
assign adc_singnal = { {(ADC_DATA_WIDTH - DAC_DATA_WIDTH){1'b0}}, dac_signal };
(* MARK_DEBUG="true" *) logic sample_done; end
(* MARK_DEBUG="true" *) logic sample_done_sync1; else begin : g_truncate
(* MARK_DEBUG="true" *) logic sample_done_sync2; assign adc_singnal = dac_signal[ADC_DATA_WIDTH-1:0];
(* MARK_DEBUG="true" *) logic sample_done_sync3; end
endgenerate
(* 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;
//------------------------------------------------------------ //------------------------------------------------------------
// DAC -> ADC CDC // DAC -> ADC CDC
//------------------------------------------------------------ //------------------------------------------------------------
always_ff @(posedge adc_clk_in or posedge adc_rst) begin logic [2:0] stretch; // 125/65~=2. Чтобы поймать единичный импульс, растянем его во времени
if (adc_rst) begin logic [1:0] sync_DA;
sample_req <= 1'b0; wire dac_done_stretched;
sample_req_sync2 <= 1'b0;
sample_req_sync3 <= 1'b0; always_ff @(posedge clk_dac or posedge rst_dac)
end begin
if (rst_dac)
stretch <= 0;
else begin else begin
sample_req_sync2 <= sample_req_sync1; stretch[0] <= dac_done;
sample_req_sync3 <= sample_req_sync2; stretch[1] <= stretch[0];
sample_req <= sample_req_sync3; stretch[2] <= stretch[1];
end end
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 // ADC -> DAC CDC
//------------------------------------------------------------ //------------------------------------------------------------
always_ff @(posedge dac_clk_in or posedge dac_rst) begin logic [1:0] sync_AD;
if (dac_rst) begin
sample_done <= 1'b0; always_ff @(posedge clk_dac or posedge rst_dac) begin
sample_done_sync2 <= 1'b0; if (rst_dac)
sample_done_sync3 <= 1'b0; sync_AD <= 0;
end
else begin else begin
sample_done_sync2 <= sample_done_sync1; sync_AD[0] <= adc_done;
sample_done_sync3 <= sample_done_sync2; sync_AD[1] <= sync_AD[0];
sample_done <= sample_done_sync3;
end end
end end
assign dac_request = sync_AD[1];
//------------------------------------------------------------ //------------------------------------------------------------
// Generator // Generator
//------------------------------------------------------------ //------------------------------------------------------------
generator #( generator #(
.DATA_WIDTH(DAC_DATA_WIDTH) .DATA_WIDTH(DAC_DATA_WIDTH),
.ZERO_LEVEL(ZERO_LEVEL)
) generator_inst ( ) generator_inst (
.clk_in(dac_clk_in), .clk_dac(clk_dac),
.rst(dac_rst), .rst(rst_dac),
.start(dac_start), .start(start),
.pulse_width(pulse_width), .pulse_width(pulse_width),
.pulse_period(pulse_period), .pulse_period(pulse_period),
.pulse_height(pulse_height), .pulse_height(pulse_height),
.pulse_num(pulse_num), .pulse_num(pulse_num),
.dac_out(dac_signal),
.sample_done(sample_done), .request(dac_request),
.done(dac_done)
.pulse(pulse),
.pulse_height_out(pulse_height_out),
.sample_req(sample_req_sync1)
); );
//------------------------------------------------------------ //------------------------------------------------------------
@ -119,18 +110,15 @@ module sync_top
.PACK_FACTOR(PACK_FACTOR), .PACK_FACTOR(PACK_FACTOR),
.PROCESS_MODE(PROCESS_MODE) .PROCESS_MODE(PROCESS_MODE)
) sampler_inst ( ) sampler_inst (
.clk_in(adc_clk_in), .clk_in(clk_adc),
.rst(adc_rst), .rst(rst_adc),
.data_in(adc_singnal),
.data_in(data_in),
.out_of_range(out_of_range), .out_of_range(out_of_range),
.smp_num(smp_num), .smp_num(smp_num),
.sample_req(sample_req),
.m_axis_tdata(m_axis_tdata), .m_axis_tdata(m_axis_tdata),
.m_axis_tvalid(m_axis_tvalid), .m_axis_tvalid(m_axis_tvalid),
.sample_done(sample_done_sync1) .request(adc_request),
.done(adc_done)
); );
endmodule endmodule

View File

@ -2,39 +2,45 @@
module tb_top; module tb_top;
//------------------------------------------------------------
// Параметры
//------------------------------------------------------------
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_ADC_PERIOD = 15.385;
localparam ZERO_LEVEL = LOGIC_ZERO_LEVEL; // "logic" VS "true"
//------------------------------------------------------------ //------------------------------------------------------------
// clocks / reset // Тактовые сигналы и сброс
//------------------------------------------------------------ //------------------------------------------------------------
logic adc_clk_in; logic clk_dac;
logic adc_rst; logic rst_dac;
logic clk_adc;
logic dac_clk_in; logic rst_adc;
logic dac_rst;
//------------------------------------------------------------ //------------------------------------------------------------
// control // Управление и конфиг
//------------------------------------------------------------ //------------------------------------------------------------
logic dac_start; logic dac_start;
logic [31:0] pulse_width; logic [31:0] pulse_width;
logic [31:0] pulse_period; logic [31:0] pulse_period;
logic [DAC_DATA_WIDTH-1:0] pulse_height; logic [DAC_DATA_WIDTH-1:0] pulse_height;
logic [15:0] pulse_num; logic [15:0] pulse_num;
logic [31:0] smp_num; logic [31:0] smp_num;
//------------------------------------------------------------ //------------------------------------------------------------
// outputs // Входы
//------------------------------------------------------------ //------------------------------------------------------------
logic [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; reg out_of_range;
logic m_axis_tvalid; //------------------------------------------------------------
// Выходы
integer valid_count; //------------------------------------------------------------
wire [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata;
wire m_axis_tvalid;
//------------------------------------------------------------ //------------------------------------------------------------
// DUT // DUT
//------------------------------------------------------------ //------------------------------------------------------------
@ -42,127 +48,260 @@ module tb_top;
.DAC_DATA_WIDTH(DAC_DATA_WIDTH), .DAC_DATA_WIDTH(DAC_DATA_WIDTH),
.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)
) dut ( ) dut (
.adc_clk_in(adc_clk_in), .clk_adc(clk_adc),
.adc_rst(adc_rst), .clk_dac(clk_dac),
.rst_adc(rst_adc),
.dac_clk_in(dac_clk_in), .rst_dac(rst_dac),
.dac_rst(dac_rst), .start(dac_start),
.dac_start(dac_start),
.pulse_width(pulse_width), .pulse_width(pulse_width),
.pulse_period(pulse_period), .pulse_period(pulse_period),
.pulse_height(pulse_height), .pulse_height(pulse_height),
.pulse_num(pulse_num), .pulse_num(pulse_num),
.smp_num(smp_num), .smp_num(smp_num),
.m_axis_tdata(m_axis_tdata), .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 initial begin
adc_clk_in = 1'b0; clk_adc = 0;
forever #5 adc_clk_in = ~adc_clk_in; // 100 MHz forever #(CLK_ADC_PERIOD/2) clk_adc = ~clk_adc;
end end
//------------------------------------------------------------
// DAC clock
//------------------------------------------------------------
initial begin initial begin
dac_clk_in = 1'b0; clk_dac = 0;
forever #8 dac_clk_in = ~dac_clk_in; // slower domain forever #(CLK_DAC_PERIOD/2) clk_dac = ~clk_dac;
end end
//------------------------------------------------------------ // === Таски для тестипрования ===
// monitor output stream // Таска сброса DAC DUT
//------------------------------------------------------------ task automatic reset_dut_dac(
always @(posedge adc_clk_in) begin input int rst_duration // сколько тактов держать сброс
if (m_axis_tvalid) begin );
valid_count = valid_count + 1; rst_dac <= 1;
repeat(rst_duration) @(posedge clk_dac);
rst_dac <= 0;
endtask
$display("[%0t] VALID: data=%0d", // Таска сброса ADC DUT
$time, task automatic reset_dut_adc(
m_axis_tdata); input int rst_duration // сколько тактов держать сброс
end );
end rst_adc <= 1;
repeat(rst_duration) @(posedge clk_adc);
rst_adc <= 0;
endtask
//------------------------------------------------------------ // Таска запуска DUT
// test 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 initial begin
$display("[TB] Tests start");
adc_rst = 1'b1; // Инициализация
dac_rst = 1'b1; dac_start = 0;
dac_start = 1'b0;
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;
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; $finish;
end end