Merge branch 'dev/design' into integration_new_controller

This commit is contained in:
otroubi
2026-07-28 12:54:48 +03:00
5 changed files with 318 additions and 227 deletions
+53 -56
View File
@@ -3,7 +3,7 @@
module tb_top; module tb_top;
//------------------------------------------------------------ //------------------------------------------------------------
// // Параметры
//------------------------------------------------------------ //------------------------------------------------------------
parameter string ZERO_LEVEL_PARAM = "logic"; // "logic" VS "true" parameter string ZERO_LEVEL_PARAM = "logic"; // "logic" VS "true"
parameter VERBOSE = 1; parameter VERBOSE = 1;
@@ -22,14 +22,14 @@ module tb_top;
localparam CLOCK_DEVIATION = 3; // Maximum clock deviation of pulse stats localparam CLOCK_DEVIATION = 3; // Maximum clock deviation of pulse stats
//------------------------------------------------------------ //------------------------------------------------------------
// // Тактовые сигналы и сброс
//------------------------------------------------------------ //------------------------------------------------------------
logic clk_dac; logic clk_dac;
logic rst_dac; logic rst_dac;
logic clk_adc; logic clk_adc;
logic rst_adc; logic rst_adc;
//------------------------------------------------------------ //------------------------------------------------------------
// // Управление и конфиг
//------------------------------------------------------------ //------------------------------------------------------------
logic dac_start; logic dac_start;
logic [31:0] pulse_width; logic [31:0] pulse_width;
@@ -38,11 +38,11 @@ module tb_top;
logic [15:0] pulse_num; logic [15:0] pulse_num;
logic [31:0] smp_num; logic [31:0] smp_num;
//------------------------------------------------------------ //------------------------------------------------------------
// // Входы
//------------------------------------------------------------ //------------------------------------------------------------
reg out_of_range; reg out_of_range;
//------------------------------------------------------------ //------------------------------------------------------------
// // Выходы
//------------------------------------------------------------ //------------------------------------------------------------
wire [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; wire [ADC_DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata;
wire m_axis_tvalid; wire m_axis_tvalid;
@@ -72,7 +72,7 @@ module tb_top;
.out_of_range(out_of_range) .out_of_range(out_of_range)
); );
// // Тактовые сигналы
initial begin initial begin
clk_adc = 0; clk_adc = 0;
forever #(CLK_ADC_PERIOD/2) clk_adc = ~clk_adc; forever #(CLK_ADC_PERIOD/2) clk_adc = ~clk_adc;
@@ -82,17 +82,17 @@ module tb_top;
forever #(CLK_DAC_PERIOD/2) clk_dac = ~clk_dac; forever #(CLK_DAC_PERIOD/2) clk_dac = ~clk_dac;
end end
// === === // === Таски для тестирования ===
// // Функция модуля
function automatic real fabs(real val); function automatic real fabs(real val);
return (val < 0.0) ? -val : val; return (val < 0.0) ? -val : val;
endfunction endfunction
`define MIN(x, y) (((x) < (y)) ? (x) : (y)) `define MIN(x, y) (((x) < (y)) ? (x) : (y))
// DAC DUT // Таска сброса DAC DUT
task automatic reset_dut_dac( task automatic reset_dut_dac(
input int rst_duration // input int rst_duration // сколько тактов держать сброс
); );
@(negedge clk_dac); @(negedge clk_dac);
rst_dac <= 1; rst_dac <= 1;
@@ -100,9 +100,9 @@ module tb_top;
rst_dac <= 0; rst_dac <= 0;
endtask endtask
// ADC DUT // Таска сброса ADC DUT
task automatic reset_dut_adc( task automatic reset_dut_adc(
input int rst_duration // input int rst_duration // сколько тактов держать сброс
); );
@(negedge clk_adc); @(negedge clk_adc);
rst_adc <= 1; rst_adc <= 1;
@@ -110,9 +110,9 @@ module tb_top;
rst_adc <= 0; rst_adc <= 0;
endtask endtask
// DUT // Таска запуска DUT
task automatic start_dut( task automatic start_dut(
input int start_duration // input int start_duration // сколько тактов держать импульс
); );
@(negedge clk_dac); // to make signal stable @(negedge clk_dac); // to make signal stable
dac_start <= 1; dac_start <= 1;
@@ -120,15 +120,15 @@ module tb_top;
dac_start <= 0; dac_start <= 0;
endtask endtask
// DUT // Таска конфигурации DUT
task automatic set_config( task automatic set_config(
input logic [31:0] w, // input logic [31:0] w, // ширина импульса
input logic [31:0] p, // input logic [31:0] p, // период импульса
input logic [15:0] n, // input logic [15:0] n, // количество импульсов
input logic [DAC_DATA_WIDTH-1:0] h, // input logic [DAC_DATA_WIDTH-1:0] h, // высота импульса
input logic [31:0] sn // input logic [31:0] sn // число сэмплов
); );
// // Задаем конфигурационные регистры
pulse_width <= w; pulse_width <= w;
pulse_period <= p; pulse_period <= p;
pulse_num <= n; pulse_num <= n;
@@ -136,7 +136,7 @@ module tb_top;
smp_num <= sn; smp_num <= sn;
endtask endtask
// DUT // Основная таска проверки DUT
task automatic run_test_case( task automatic run_test_case(
input int pulse_width, input int pulse_width,
input int pulse_period, input int pulse_period,
@@ -192,7 +192,7 @@ module tb_top;
start_dut(start_hold_time); start_dut(start_hold_time);
begin begin
@(posedge clk_dac); @(posedge clk_dac);
// // старт первой синхронизации
sync_start_time = $realtime; sync_start_time = $realtime;
end end
join_none join_none
@@ -209,28 +209,27 @@ module tb_top;
if (VERBOSE >= 3) begin if (VERBOSE >= 3) begin
$display("[TB] -run_test_case- Found valid pulse response data positive front"); $display("[TB] -run_test_case- Found valid pulse response data positive front");
end end
// . // Старт цикла. Завершение синхронизации
sync_time_stats.push_back($realtime - sync_start_time); sync_time_stats.push_back($realtime - sync_start_time);
pulse_start_time = $realtime; pulse_start_time = $realtime;
fork fork
// // Поток будет запущен для ненулевых импульсов и гарантированно завершится как только зафиксирует статистику импульса
// // Начало импульса
if (pulse_height != ZERO_LEVEL && pulse_width != 0) begin if (pulse_height != ZERO_LEVEL && pulse_width != 0) begin
if (VERBOSE >= 4) begin if (VERBOSE >= 4) begin
$display("[TB] -run_test_case- Wait until pulse become high"); $display("[TB] -run_test_case- Wait until pulse become high");
end end
wait(m_axis_tdata != ZERO_LEVEL); wait(m_axis_tdata != ZERO_LEVEL);
// . // Фактическое начало импульса. Поступление высокого уровня
pulse_update_val_time = $realtime; pulse_update_val_time = $realtime;
pulse_delay_time_stats.push_back(pulse_update_val_time - pulse_start_time); pulse_delay_time_stats.push_back(pulse_update_val_time - pulse_start_time);
// . .. OTR != 0 , . // Проверим что высота импульса совпала с заданной. Т.к. OTR != 0 влияет на выходные данные сэмплера, то не будем проверять такие случаи.
@(posedge clk_adc); @(posedge clk_adc);
#1.5; // #1.5; // Ожидание завершения переходных процессов
// todo fix // Будем считать что из-за OTR данные изменились (по условию OTR + MSB), проверка пропускается, т.к. сложно понять точное значение OTR в момент обработки данных от tdata
// - OTR ( OTR + MSB), , .. OTR tdata if (m_axis_tdata != (pulse_height >> 2) && (randomize_out_of_range || out_of_range_val)) begin
if (m_axis_tdata != (pulse_height >> 2) && !(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 >> 2); $display("[ERROR] -run_test_case- Wrong pulse height: %d. Must be: %d", m_axis_tdata, pulse_height >> 2);
$finish; $finish;
end end
@@ -241,14 +240,14 @@ module tb_top;
wait(m_axis_tdata == ZERO_LEVEL); wait(m_axis_tdata == ZERO_LEVEL);
pulse_width_time_stats.push_back($realtime - pulse_update_val_time); pulse_width_time_stats.push_back($realtime - pulse_update_val_time);
end end
// // Конец импульса
join_none join_none
@(negedge m_axis_tvalid); @(negedge m_axis_tvalid);
if (VERBOSE >= 3) begin if (VERBOSE >= 3) begin
$display("[TB] -run_test_case- Found valid pulse response data negative front"); $display("[TB] -run_test_case- Found valid pulse response data negative front");
end end
// . // Завершение цикла. Старт синхронизации
pulse_period_time_stats.push_back($realtime - pulse_start_time); pulse_period_time_stats.push_back($realtime - pulse_start_time);
sync_start_time = $realtime; sync_start_time = $realtime;
end end
@@ -257,7 +256,7 @@ module tb_top;
$display("[TB] -run_test_case- Stop pulse generation"); $display("[TB] -run_test_case- Stop pulse generation");
end end
fork // fork // Проверка с таймаутом на лишние циклы
@(posedge m_axis_tvalid); @(posedge m_axis_tvalid);
repeat(30) @(posedge clk_adc); repeat(30) @(posedge clk_adc);
join_any join_any
@@ -278,8 +277,7 @@ module tb_top;
end end
out_of_range = 0; out_of_range = 0;
// . // Проверка по статистике. Подсчет средних значений
// todo randomize_out_of_range || out_of_range_val
if (pulse_delay_time_stats.size() != pulse_num && pulse_height != ZERO_LEVEL && pulse_width != 0 && !(randomize_out_of_range || out_of_range_val)) begin // Detected with pulse level. Skip if pulse level undetectable if (pulse_delay_time_stats.size() != pulse_num && pulse_height != ZERO_LEVEL && pulse_width != 0 && !(randomize_out_of_range || out_of_range_val)) 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); $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; $finish;
@@ -325,7 +323,7 @@ module tb_top;
$display("[ERROR] -run_test_case- avearge_pulse_delay too big: %0.3f", avearge_pulse_delay); $display("[ERROR] -run_test_case- avearge_pulse_delay too big: %0.3f", avearge_pulse_delay);
error_flag = 1; error_flag = 1;
end end
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 && !(randomize_out_of_range || out_of_range_val)) 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); $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; error_flag = 1;
end end
@@ -345,13 +343,13 @@ module tb_top;
$display("[TB] -run_test_case- Passed checks"); $display("[TB] -run_test_case- Passed checks");
end end
endtask 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;
@@ -477,12 +475,12 @@ module tb_top;
.randomize_out_of_range(0) .randomize_out_of_range(0)
); );
// . .. tvalid , . start , // Ожидание окончания работы генератора. Т.к. конец работы определяется по tvalid сэмплера, а он завершается сильно раньше. Чтобы не пропустить start следующей таски, ждем
wait(dut.generator_inst.enable == 0); wait(dut.generator_inst.enable == 0);
#50; #50;
$display("[TB] Test 2. Edge cases. Sample num == 0. (6/7)"); $display("[TB] Test 2. Edge cases. Sample num == 0. (6/7)");
// , .. run_test_case pulse num . . sample num = 0 // Запустим в работу вручную, т.к. run_test_case обязательно ждет pulse num циклов. Детекция цикла производится по активности сэплера. Ее не должно быть при sample num = 0
set_config( set_config(
.w(10), .w(10),
.p(125), .p(125),
@@ -504,9 +502,8 @@ module tb_top;
disable wait_sampler_active_proc; disable wait_sampler_active_proc;
repeat(30) @(posedge clk_adc); repeat(30) @(posedge clk_adc);
// , pulse height - OTR=1 // Данный тест должен приводить к тому, что сэмплер будет давать крайние значения вместо заданного pulse height из-за OTR=1
// TODO OTR = 1 . // Дописать авто тест
//
$display("[TB] Test 2. Edge cases. OTR == 1. (7/7)"); $display("[TB] Test 2. Edge cases. OTR == 1. (7/7)");
run_test_case( run_test_case(
.pulse_width(10), .pulse_width(10),
@@ -538,15 +535,15 @@ module tb_top;
int r_w, r_p, r_n, r_h, r_sn; int r_w, r_p, r_n, r_h, r_sn;
bit r_skip, r_otr, r_otr_rand; bit r_skip, r_otr, r_otr_rand;
// // Генерируем параметры
r_p = $urandom_range(50, 150); // 5 50 r_p = $urandom_range(50, 150); // Период от 5 до 50
r_w = $urandom_range(10, r_p); // r_w = $urandom_range(10, r_p); // Ширина не больше периода
r_n = $urandom_range(1, 10); // r_n = $urandom_range(1, 10); // Количество импульсов
r_h = $urandom_range(0, 2**(`MIN(ADC_DATA_WIDTH, DAC_DATA_WIDTH))-1); // r_h = $urandom_range(0, 2**(`MIN(ADC_DATA_WIDTH, DAC_DATA_WIDTH))-1); // Высота импульса
r_sn = $urandom_range(2, 40); // r_sn = $urandom_range(2, 40); // Число сэмплов
r_skip = $urandom_range(0, 1); // (0 - , 1 - ) r_skip = $urandom_range(0, 1); // Случайный сброс (0 - сброс, 1 - пропуск)
r_otr = 0; // Out Of Range r_otr = 0; // Out Of Range стартовое значение
r_otr_rand = 0; // OTR r_otr_rand = 0; // Сделать OTR случайным
if (VERBOSE >= 1) if (VERBOSE >= 1)
$display("[TB] --- Test #%0d (Config: W=%0d, P=%0d, N=%0d, H=%0d, SN=%0d, SkipReset=%0b) ---", $display("[TB] --- Test #%0d (Config: W=%0d, P=%0d, N=%0d, H=%0d, SN=%0d, SkipReset=%0b) ---",
@@ -564,7 +561,7 @@ module tb_top;
.randomize_out_of_range(r_otr_rand) .randomize_out_of_range(r_otr_rand)
); );
wait(dut.generator_inst.enable == 0); // wait(dut.generator_inst.enable == 0); // Проверка на завершение работы
#50; #50;
end end
$display("[TB] Test 3 complete"); $display("[TB] Test 3 complete");
+73 -31
View File
@@ -11,13 +11,13 @@ module reflectometer_tb;
localparam LOGIC_ZERO_LEVEL = 0; // DAC -5V for logic zero 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 VOLTAGE_ZERO_LEVEL = 2**(DAC_DATA_WIDTH-1); // DAC 0V for logic zero
localparam PACK_FACTOR = 1; // not used in TB localparam PACK_FACTOR = 1; // not used in TB
localparam PROCESS_MODE = 0; // 0 - uint, 1 - int localparam PROCESS_MODE = 1; // 0 - uint, 1 - int
localparam ACCUM_WIDTH = 32; // accumulator number bit witdth localparam ACCUM_WIDTH = 32; // accumulator number bit witdth
localparam N_MAX = 4096; // max value of windows to average by experiments localparam N_MAX = 4096; // max value of windows to average by experiments
localparam WINDOW_SIZE = 65; // fixed subwindow size to average by time localparam WINDOW_SIZE = 65; // fixed subwindow size to average by time
localparam PACKET_SIZE = 1024; // bytes per UDP packet localparam PACKET_SIZE = 1024; // bytes per UDP packet
localparam ZERO_LEVEL = VOLTAGE_ZERO_LEVEL; // "logic" VS "true" localparam ZERO_LEVEL = LOGIC_ZERO_LEVEL; // "logic" VS "voltage"
localparam CLK_ETH_PHY_PERIOD = 8.000; // 125 MHz localparam CLK_ETH_PHY_PERIOD = 8.000; // 125 MHz
localparam CLK_REF_PERIOD = 5.000; // 200 MHz localparam CLK_REF_PERIOD = 5.000; // 200 MHz
@@ -63,7 +63,7 @@ module reflectometer_tb;
wire [ADC_DATA_WIDTH-1:0] adc_data; wire [ADC_DATA_WIDTH-1:0] adc_data;
// Интерфейс хендшейка с MAC-PHY // Интерфейс хендшейка с MAC-PHY
wire send_request; wire send_request;
logic request_ready = 1'b0; logic request_ready;
// Сигналы ЦАП и АЦП // Сигналы ЦАП и АЦП
real signal_voltage; real signal_voltage;
@@ -72,6 +72,7 @@ module reflectometer_tb;
//------------------------------------------------------------ //------------------------------------------------------------
virtual_dac_model #( // default voltage range is +/- 5V virtual_dac_model #( // default voltage range is +/- 5V
.DAC_DATA_WIDTH(DAC_DATA_WIDTH) .DAC_DATA_WIDTH(DAC_DATA_WIDTH)
// ,.VOLTAGE_GAIN(2)
) virtual_dac ( ) virtual_dac (
.clk_i(clk_dac), .clk_i(clk_dac),
.wrt_i(dac_wrt), .wrt_i(dac_wrt),
@@ -149,6 +150,7 @@ module reflectometer_tb;
//------------------------------------------------------------ //------------------------------------------------------------
// Таски для тестирования // Таски для тестирования
//------------------------------------------------------------ //------------------------------------------------------------
// Таски работы с AXI-Stream
task automatic dut_soft_reset(virtual axis_if#(8).tb vif); task automatic dut_soft_reset(virtual axis_if#(8).tb vif);
logic [7:0] tx_packet[]; logic [7:0] tx_packet[];
tx_packet = '{8'h0f}; tx_packet = '{8'h0f};
@@ -160,6 +162,7 @@ module reflectometer_tb;
tx_packet = '{8'hf0}; tx_packet = '{8'hf0};
vif.master_send(tx_packet); vif.master_send(tx_packet);
endtask endtask
task automatic dut_send_system_config( task automatic dut_send_system_config(
virtual axis_if#(8).tb vif, virtual axis_if#(8).tb vif,
input logic [31:0] pulse_width, input logic [31:0] pulse_width,
@@ -183,60 +186,99 @@ module reflectometer_tb;
endtask endtask
// Таски сбора и обработки статистики // Таски сбора и обработки статистики
task automatic dut_read_output(
virtual axis_if#(8).tb vif,
input int sample_num,
input bit randomize_recv_delays,
output int output_data[]
);
logic [7:0] rx_packet[];
logic [ACCUM_WIDTH-1:0] data_packet[];
int packet_num = $ceil(real'(sample_num / WINDOW_SIZE) / real'(PACKET_SIZE));
int numbers_per_packet = PACKET_SIZE/(ACCUM_WIDTH/8);
int idx = 0;
if (sample_num % WINDOW_SIZE) begin
$display("[TB] -dut_read_output- Error, sample_num must be multiple of WINDOW_SIZE: %0d %% %0d = %0d", sample_num, WINDOW_SIZE, sample_num % WINDOW_SIZE);
$finish;
end
data_packet = new[numbers_per_packet];
output_data = new[numbers_per_packet * packet_num];
// count send_request posedge todo
// timeout todo
// recv loop
for (int i = 0; i < packet_num; i++) begin
// randomize_recv_delays todo
request_ready = 1;
vif.slave_recv(rx_packet);
request_ready = 0;
// unpack values
data_packet = {<< byte {rx_packet}};
data_packet = {<< ACCUM_WIDTH {data_packet}};
// copy and convert values
for (int j = 0; j < data_packet.size(); j++) begin
output_data[i * data_packet.size() + j] = int'(data_packet[j]);
end
end
// wait for dut.finish posedge todo
// timeout todo
// error handling todo
endtask
//------------------------------------------------------------ //------------------------------------------------------------
// ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ // ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ
//------------------------------------------------------------ //------------------------------------------------------------
initial begin initial begin
logic [7:0] rx_packet[]; int output_data[];
automatic virtual axis_if.tb control_vif = axis_control_if.tb; automatic virtual axis_if.tb control_vif = axis_control_if.tb;
automatic virtual axis_if.tb accumulator_vif = axis_accumulator_if.tb; automatic virtual axis_if.tb accumulator_vif = axis_accumulator_if.tb;
$display("[TB] Tests start"); $display("[TB] DUT initializaton");
// Инициализация // Инициализация
request_ready = 0;
rst_n = 0; rst_n = 0;
#100; #100;
rst_n = 1; rst_n = 1;
wait(mmcm_locked === 1'b1); wait(mmcm_locked === 1'b1);
#150; #150;
$display("[TB] MMCM locked"); $display("[TB] MMCM locked");
dut_soft_reset(control_vif); dut_soft_reset(control_vif);
#100; #100;
// Тесты
$display("[TB] Tests start");
dut_send_system_config( dut_send_system_config(
.vif(control_vif), .vif(control_vif),
.pulse_width(32'd12345), .pulse_width(32'd123),
.pulse_period(32'd50000), .pulse_period(32'd5000),
.pulse_num(16'd10), .pulse_num(16'd1),
.pulse_height(14'd16000), // 0V .pulse_height(14'd15000), // 0V
.pulse_period_adc(32'd26000) .pulse_period_adc(32'd2600)
); );
#100; #100;
dut_start(control_vif); dut_start(control_vif);
#1000; dut_read_output(
request_ready = 1'b1; .vif(accumulator_vif),
accumulator_vif.slave_recv(rx_packet); .sample_num(2600),
$display("receive %0d bytes", rx_packet.size()); .randomize_recv_delays(0),
for (int i = 0; i < rx_packet.size(); i+=4) begin .output_data(output_data)
$write("%0d ", {rx_packet[i], rx_packet[i+1], rx_packet[i+2], rx_packet[i+3]}); );
$display("Received %0d numbers", output_data.size());
for (int i = 0; i < output_data.size(); i++) begin
$write("%0d ", output_data[i]);
end end
$display(""); $display("");
#1000;
request_ready = 1'b1;
accumulator_vif.slave_recv(rx_packet);
$display("receive %0d bytes", rx_packet.size());
for (int i = 0; i < rx_packet.size(); i+=4) begin
$write("%0d ", {rx_packet[i], rx_packet[i+1], rx_packet[i+2], rx_packet[i+3]});
end
$display("");
// wait();
// #100000;
// #93000;
// rst_n = 0;
#10000
$display("[TB] ALL PASSED"); $display("[TB] ALL PASSED");
$finish; $finish;
end end
+8
View File
@@ -27,6 +27,14 @@ module generator
logic enable, synced; logic enable, synced;
initial begin
cnt_pulse_period = '0;
cnt_pulse_num = '0;
enable = 0;
synced = 0;
dac_out = ZERO_LEVEL;
end
always @(posedge clk_dac) begin always @(posedge clk_dac) begin
if (rst) begin if (rst) begin
pulse_height_reg <= ZERO_LEVEL; pulse_height_reg <= ZERO_LEVEL;
+22 -25
View File
@@ -13,38 +13,38 @@ module sampler
input [31:0] smp_num, input [31:0] smp_num,
input request, input request,
output logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata, output logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata,
output logic m_axis_tvalid, output logic m_axis_tvalid,
output logic done output logic done
); );
// WARNING: number of samples smp_num must be multiple of PACK_FACTOR
// Last (smp_num % PACK_FACTOR) will be lost and not transmitted
logic [DATA_WIDTH-1:0] data_converted; logic [DATA_WIDTH-1:0] data_converted;
logic [31:0] smp_num_reg, cnt_smp_num; logic [31:0] smp_num_reg, cnt_smp_num;
logic synced; logic synced;
logic [$clog2(PACK_FACTOR):0] pack_cnt; logic [$clog2(PACK_FACTOR):0] pack_cnt;
always_comb begin always_comb begin
data_converted = '0;
if (PROCESS_MODE) begin if (PROCESS_MODE) begin
if (out_of_range) begin if (out_of_range) begin
if (data_in[DATA_WIDTH-1]==1'b1) data_converted = {1'b1, {(DATA_WIDTH-2){1'b0}}}; data_converted = {~data_in[DATA_WIDTH-1], {(DATA_WIDTH-1){data_in[DATA_WIDTH-1]}}};
else if (data_in[DATA_WIDTH-1]==1'b0) data_converted = {1'b0, {(DATA_WIDTH-2){1'b1}}};
end else begin end else begin
if (data_in == {1'b1, {(DATA_WIDTH-1){1'b0}}}) data_converted = {~data_in[DATA_WIDTH-1], data_in[DATA_WIDTH-2:0]};
data_converted = data_in;
else
data_converted = data_in[DATA_WIDTH-1] ?{1'b1, (~data_in[DATA_WIDTH-2:0] + 1'b1)}:data_in;
end end
end else begin end else begin
if (out_of_range) begin if (out_of_range) begin
if (data_in[DATA_WIDTH-1]==1'b1) data_converted = '1; data_converted = {DATA_WIDTH{data_in[DATA_WIDTH-1]}};
else if (data_in[DATA_WIDTH-1]==1'b0) data_converted = '0;
end else begin end else begin
data_converted = data_in; data_converted = data_in;
end end
end end
end end
initial begin
synced = 0;
m_axis_tdata = '0;
m_axis_tvalid = 0;
end
always_ff @(posedge clk_in) begin always_ff @(posedge clk_in) begin
if (rst) begin if (rst) begin
@@ -56,10 +56,9 @@ module sampler
synced <= 0; synced <= 0;
done <= 0; done <= 0;
end else begin end else begin
m_axis_tvalid <= 0;
if (!synced) begin if (!synced) begin
if (done && request) begin if (done && request) begin
synced <= 1; synced <= 1;
done <= 0; done <= 0;
cnt_smp_num <= 0; cnt_smp_num <= 0;
smp_num_reg <= smp_num; smp_num_reg <= smp_num;
@@ -68,23 +67,21 @@ module sampler
end end
end else begin end else begin
if (cnt_smp_num != smp_num_reg) begin if (cnt_smp_num != smp_num_reg) begin
cnt_smp_num <= cnt_smp_num +1; cnt_smp_num++;
m_axis_tdata[pack_cnt*DATA_WIDTH +: DATA_WIDTH] <= data_converted; m_axis_tdata[pack_cnt*DATA_WIDTH +: DATA_WIDTH] <= data_converted;
if (pack_cnt == PACK_FACTOR-1) begin if (pack_cnt == PACK_FACTOR-1) begin
pack_cnt <= 0; pack_cnt <= 0;
m_axis_tvalid <= 1; m_axis_tvalid <= 1;
end else begin end else begin
pack_cnt <= pack_cnt + 1; pack_cnt++;
m_axis_tvalid <= 0;
end end
end else begin end else begin
cnt_smp_num <= '0; pack_cnt <= '0;
pack_cnt <= '0;
synced <= 0; synced <= 0;
done <= 1; m_axis_tvalid <= 0;
m_axis_tdata <= '0;
end end
end end
end end
end end
endmodule endmodule
+162 -115
View File
@@ -1,17 +1,24 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
module sampler_tb; module sampler_tb #(
// Параметры тестбенча
localparam DATA_WIDTH = 12; parameter int DATA_WIDTH = 12, // bitwidth of sampled data. Equal to ADC bitwidth
localparam PACK_FACTOR = 1; parameter int PACK_FACTOR = 1, // packing of several numbers in one axi-stream tdata transaction
localparam PROCESS_MODE = 0; parameter bit PROCESS_MODE = 0, // signed (1) / unsigned (0)
localparam CLK_PERIOD = 15.3846; parameter real CLK_PERIOD = 15.3846,
parameter int OTR_OFFSET = 0 // Out of Range offset number clipping
);
// Вычислимые константы
// Диапазон значений семплера
localparam int MIN_VALUE = PROCESS_MODE ? -2**(DATA_WIDTH-1) : 0;
localparam int MAX_VALUE = PROCESS_MODE ? 2**(DATA_WIDTH-1)-1 : (2**DATA_WIDTH)-1;
logic clk; logic clk;
logic rst; logic rst_dut;
logic rst_gen;
logic [DATA_WIDTH-1:0] data_in; wire [DATA_WIDTH-1:0] data_in;
logic out_of_range; wire out_of_range;
logic [31:0] smp_num; logic [31:0] smp_num;
@@ -21,15 +28,13 @@ module sampler_tb;
logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata;
logic m_axis_tvalid; logic m_axis_tvalid;
int received_count;
sampler #( sampler #(
.DATA_WIDTH (DATA_WIDTH), .DATA_WIDTH (DATA_WIDTH),
.PACK_FACTOR (PACK_FACTOR), .PACK_FACTOR (PACK_FACTOR),
.PROCESS_MODE(PROCESS_MODE) .PROCESS_MODE(PROCESS_MODE)
) dut ( ) dut (
.clk_in (clk), .clk_in (clk),
.rst (rst), .rst (rst_dut),
.data_in (data_in), .data_in (data_in),
.out_of_range (out_of_range), .out_of_range (out_of_range),
@@ -54,141 +59,183 @@ module sampler_tb;
// ===================================================== // =====================================================
// RESET // RESET
// ===================================================== // =====================================================
initial begin task automatic reset_dut();
rst = 1; @(posedge clk);
data_in = 0; rst_dut = 1;
out_of_range = 0; @(posedge clk);
done = 0; rst_dut = 0;
smp_num = 0; endtask
repeat(5) @(posedge clk);
rst = 0;
end
// =====================================================
// OUTPUT COUNTER
// =====================================================
always @(posedge clk) begin
if (m_axis_tvalid)
received_count++;
end
// ===================================================== // =====================================================
// FEED DATA // FEED DATA
// ===================================================== // =====================================================
task automatic feed_data_stream( triangle_wave_gen #(
input int num_words, .DATA_WIDTH(DATA_WIDTH),
input bit random_data, .OFFSET(OTR_OFFSET), // offset of bits from '0 and '1 for OTR
input bit random_out_of_range .STEP(1) // LSB bit per clock
) signal_gen (
.clk(clk),
.rst(rst_gen),
.signal(data_in),
.otr(out_of_range)
); );
logic [DATA_WIDTH-1:0] value;
bit oor;
begin
value = 1;
for (int i = 0; i < num_words; i++) begin
if (random_data)
value = $urandom_range(1, (1<<DATA_WIDTH)-1);
else
value = value + 1;
if (random_out_of_range)
oor = ($urandom_range(0,3) == 0);
else
oor = 0;
data_in = value;
out_of_range = oor;
@(posedge clk);
end
out_of_range = 0;
end
endtask
// ===================================================== // =====================================================
// TEST CASE // TEST TASKS
// ===================================================== // =====================================================
task automatic run_test_case( task automatic run_test_case(
input int n, input int sample_num
input bit random_data,
input bit random_out_of_range
); );
begin logic [DATA_WIDTH-1:0] input_values[$];
received_count = 0; logic [DATA_WIDTH-1:0] output_values[$];
bit flag;
int tmp_input_val, tmp_output_val;
data_in = 0; // Startup sequence
out_of_range = 0; rst_gen <= 1;
done = 0; smp_num <= sample_num;
request <= 1;
smp_num = n;
// handshake
@(posedge clk); @(posedge clk);
done <= 1'b1;
wait(request == 1'b1); // syncronize
@(posedge clk); while (!done)
done <= 1'b0; @(posedge clk);
request <= 0;
// Wait for sync and start signal generator
// while (!dut.synced)
// @(posedge clk);
wait(dut.synced === 1);
rst_gen <= 0;
// wait enable // Wait for valid data
wait(dut.enable == 1'b1); fork
begin : stat_mon_proc
forever begin
@(posedge clk);
input_values.push_back(data_in);
if (m_axis_tvalid) begin
for (int i = 0; i < PACK_FACTOR; i++)
output_values.push_back(m_axis_tdata[DATA_WIDTH*i +: DATA_WIDTH]);
end
end
end
join_none
// feed data // Wait until end
feed_data_stream(n + 10, random_data, random_out_of_range); // while (dut.synced)
// @(posedge clk);
wait(dut.synced === 0);
// wait completion disable stat_mon_proc;
wait(dut.enable == 1'b0); // Pop last input value. Signal is ahead of tdata for 1 cycle
input_values.pop_back();
$display("Expected smp_num=%0d Received=%0d", smp_num, received_count); // check data length correct
if ((input_values.size() - (input_values.size() % PACK_FACTOR)) != output_values.size()) begin
$display("[ERROR] -run_test_case- Input queue size don't equal to output queue size: %0d vs %0d", input_values.size(), output_values.size());
$finish;
end
flag = 0;
for (int i = 0; i < output_values.size(); i++) begin
// clip value for OTR
tmp_input_val = int'(input_values[i]) + MIN_VALUE;
tmp_output_val = PROCESS_MODE ?
int'($signed({output_values[i][DATA_WIDTH-1], output_values[i]})) :
int'(output_values[i]);
if (received_count == smp_num) if ((tmp_input_val < MIN_VALUE + OTR_OFFSET) ||
$display("[OK]"); (tmp_input_val > MAX_VALUE - OTR_OFFSET)) begin
else // need clipping
$display("[ERROR]"); tmp_input_val = ((tmp_input_val - MIN_VALUE) < 2**(DATA_WIDTH-1)) ?
MIN_VALUE : MAX_VALUE;
end
repeat(10) @(posedge clk); if (tmp_input_val != tmp_output_val)
end flag = 1;
endtask end
// $display("Total: %0d", output_values.size());
// ===================================================== if (flag) begin
// RANDOM TESTS $display("[ERROR] -run_test_case- Sampled data not correct and not equal to generated signal.");
// ===================================================== $finish;
task automatic random_stress_test;
int n;
begin
for (int i = 0; i < 20; i++) begin
n = $urandom_range(5,20);
$display("\n--- TEST %0d --- n=%0d", i, n);
run_test_case(
n,
1,
1
);
end end
end
endtask endtask
// ===================================================== // =====================================================
// MAIN // MAIN
// ===================================================== // =====================================================
initial begin initial begin
int random_number;
$display("\n=== BASIC TEST ==="); $display("\n=== BASIC TEST ===");
run_test_case(10, 0, 0);
$display("\n=== OUT_OF_RANGE TEST ==="); rst_dut = 1;
run_test_case(20, 1, 1); rst_gen = 1;
smp_num = 0;
repeat(2) @(posedge clk);
rst_dut = 0;
repeat(2) @(posedge clk);
$display("basic test #1");
run_test_case(10);
run_test_case(100);
run_test_case(10000);
$display("basic test #2");
run_test_case(0);
$display("\n=== RANDOM STRESS TEST ==="); $display("\n=== RANDOM STRESS TEST ===");
random_stress_test(); for (int i = 0; i < 10; i++) begin
$display("Random test #%0d", i);
random_number = $urandom_range(0, 3 * 2**(DATA_WIDTH)); // up to 1.5 triangle waves
run_test_case(random_number);
end
$display("\n=== TEST FINISHED ==="); $display("\n=== TEST FINISHED ===");
$finish; $finish;
end end
endmodule endmodule
module triangle_wave_gen #(
parameter DATA_WIDTH = 12,
parameter OFFSET = 100, // offset of bits from '0 and '1 for OTR
parameter STEP = 1 // LSB bit per clock
) (
input clk,
input rst,
output logic [DATA_WIDTH-1:0] signal,
output logic otr
);
logic [DATA_WIDTH:0] counter;
logic direction;
initial begin
counter = '0;
direction = 1;
end
assign signal = counter > (2**DATA_WIDTH-1) ? (2**DATA_WIDTH-1) : counter;
always_comb begin
if (signal < OFFSET || (2**DATA_WIDTH - signal) <= OFFSET)
otr <= 1;
else
otr <= 0;
end
always @(posedge clk) begin
if (rst) begin
counter = 0;
direction = 1;
end else begin
if (direction)
counter += STEP;
else
counter -= STEP;
if (counter >= 2**DATA_WIDTH-1 || counter <= 0)
direction <= ~direction;
end
end
endmodule