2 Commits

Author SHA1 Message Date
17748a71b1 infra: add libs 2026-05-28 16:59:57 +03:00
0486e16484 Merge pull request 'dev/design' (#8) from dev/design into master
Reviewed-on: #8
2026-05-15 16:43:18 +03:00
10 changed files with 280 additions and 855 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "libs/rtl_libs"]
path = libs/rtl_libs
url = https://git.radiophotonics.ru/baulin.fa/rtl_libs.git
[submodule "libs/verilog-axi"]
path = libs/verilog-axi
url = https://github.com/alexforencich/verilog-axi.git

View File

@ -17,7 +17,6 @@ RTL_DIR = ../../rtl
include ../../scripts/vivado.mk include ../../scripts/vivado.mk
SYN_FILES += reflectometer.sv SYN_FILES += reflectometer.sv
SYN_FILES += tb_reflectometer.sv
SYN_FILES += $(sort $(shell find ../../rtl -type f \( -name '*.v' -o -name '*.sv' \))) SYN_FILES += $(sort $(shell find ../../rtl -type f \( -name '*.v' -o -name '*.sv' \)))
XCI_FILES = $(sort $(shell find ../../rtl/ethernet-udp/src -type f -name '*.xci')) XCI_FILES = $(sort $(shell find ../../rtl/ethernet-udp/src -type f -name '*.xci'))

View File

@ -1,267 +0,0 @@
`timescale 1ns / 1ps
module tb_reflectometer;
// parameters
localparam int unsigned DAC_DATA_WIDTH = 14;
localparam int unsigned ADC_DATA_WIDTH = 12;
localparam PACK_FACTOR = 1; // not used in TB
localparam PROCESS_MODE = 0; // 0 - uint, 1 - int
localparam ZERO_LEVEL = 8192; // DAC zero voltage representation (2^14 / 2)
localparam ACCUM_WIDTH = 32; // accumulator number bit witdth
localparam N_MAX = 4096; // max value of windows to average by experiments
localparam WINDOW_SIZE = 65; // fixed subwindow size to average by time
localparam PACKET_SIZE = 1024; // bytes per UDP packet
localparam int unsigned ADC_CLK_MHZ = 65;
localparam int unsigned DAC_CLK_MHZ = 125;
// may be changed for test purposes
localparam int unsigned PULSE_WIDTH = 2**6;
localparam int unsigned PULSE_PERIOD = 2**8;
localparam int unsigned PULSE_NUM = 10;
localparam int unsigned PULSE_HEIGHT = 2**12;
localparam int unsigned PULSE_PERIOD_ADC = (int'(real'(ADC_CLK_MHZ) / real'(DAC_CLK_MHZ) * real'(PULSE_PERIOD)) / int'(WINDOW_SIZE)) * int'(WINDOW_SIZE);
initial begin
if (PULSE_WIDTH <= 0)
$fatal(1, "PULSE_WIDTH should be positive");
if (PULSE_PERIOD <= 0)
$fatal(1, "PULSE_PERIOD should be positive");
if (PULSE_NUM <= 0)
$fatal(1, "PULSE_NUM should be positive");
if (PULSE_HEIGHT <= 0)
$fatal(1, "PULSE_HEIGHT should be positive");
if (PULSE_WIDTH >= 2**32-1)
$fatal(1, "PULSE_WIDTH too high");
if (PULSE_PERIOD >= 2**32-1)
$fatal(1, "PULSE_PERIOD too high");
if (PULSE_NUM >= 2**16-1)
$fatal(1, "PULSE_NUM too high");
if (PULSE_HEIGHT >= 2**DAC_DATA_WIDTH-1)
$fatal(1, "PULSE_HEIGHT too high");
if (PULSE_PERIOD_ADC % WINDOW_SIZE == 0)
$fatal(1, "PULSE_PERIOD_ADC isn't multiple of WINDOW_SIZE");
end
// DUT signals
logic clk200, clk_eth_phy_tx, clk_eth_phy_rx; // GMII clocks
logic rst_n;
wire [3:0] status_leds; // [ None, dac_start, m_axis_valid, clk_wiz_locked ]
wire dac_clk, dac_en;
wire [DAC_DATA_WIDTH-1:0] dac_data;
wire adc_clk;
logic adc_otr;
logic [ADC_DATA_WIDTH-1:0] adc_data;
wire [7:0] s_axis_tx_tdata;
wire s_axis_tx_tvalid;
logic s_axis_tx_tready;
wire s_axis_tx_tlast;
logic phy_ready;
wire accum_tx_start;
logic [7:0] m_axis_rx_tdata;
logic m_axis_rx_tvalid;
logic m_axis_rx_tlast;
logic m_axis_rx_tready;
logic [127:0] dut_config = 0;
// DUT
reflectometer_top #(
.DAC_DATA_WIDTH(DAC_DATA_WIDTH),
.ADC_DATA_WIDTH(ADC_DATA_WIDTH),
.PACK_FACTOR(PACK_FACTOR),
.PROCESS_MODE(PROCESS_MODE),
.ZERO_LEVEL(ZERO_LEVEL),
.ACCUM_WIDTH(ACCUM_WIDTH),
.N_MAX(N_MAX),
.WINDOW_SIZE(WINDOW_SIZE),
.PACKET_SIZE(PACKET_SIZE)
) DUT (
.sys_clk(clk200), // main clk 200 mhz
.rst_n(rst_n), // rst_n
.led(status_leds), // indication [3:0]
.gmii_rx_clk(clk_eth_phy_rx), // ext. clk from PHY
.gmii_tx_clk(clk_eth_phy_tx), // ext. clk from PHY
// accumulated data stream
.s_axis_tx_tdata(s_axis_tx_tdata),
.s_axis_tx_tvalid(s_axis_tx_tvalid),
.s_axis_tx_tready(s_axis_tx_tready),
.s_axis_tx_tlast(s_axis_tx_tlast),
// controller data stream
.m_axis_rx_tdata(m_axis_rx_tdata),
.m_axis_rx_tvalid(m_axis_rx_tvalid),
.m_axis_rx_tlast(m_axis_rx_tlast),
.m_axis_rx_tready(m_axis_rx_tready),
.req_ready(phy_ready), // AXI-stream requester ready
.send_req(accum_tx_start), // AXI-stream start transmit
.p2_clk(dac_clk), // DAC clk
.p2_data(dac_data), // DAC [DAC_DATA_WIDTH-1:0] data
.p2_wrt(dac_en), // DAC write enable
.ch2_clk(adc_clk), // ADC clk
.ch2_data(adc_data), // ADC [ADC_DATA_WIDTH-1:0] data
.ch2_otr(adc_otr) // ADC signal out-of-range
);
// clocks
initial begin
// 200 MHz
clk200 = 1'b0;
forever #2.5 clk200 = ~clk200;
end
initial begin
// 125 MHz
clk_eth_phy_tx = 1'b0;
forever #4 clk_eth_phy_tx = ~clk_eth_phy_tx;
end
initial begin
// 125 MHz
clk_eth_phy_rx = 1'b0;
forever #4 clk_eth_phy_rx = ~clk_eth_phy_rx;
end
// ADC input noise simulation
always @(posedge adc_clk or negedge rst_n) begin
if (!rst_n) begin
adc_data <= '0;
end else begin
adc_data <= $urandom() & ((1 << ADC_DATA_WIDTH) - 1);
end
end
assign adc_otr = 1'b0;
// AXIS tasks
task automatic axis_send_byte(
ref logic clk,
input logic [7:0] data,
input logic last,
ref logic tvalid,
ref logic [7:0] tdata,
ref logic tlast,
input logic tready
);
@(posedge clk);
tdata <= data;
tlast <= last;
tvalid <= 1'b1;
// Ждем готовности приемника
wait(tready === 1'b1);
@(posedge clk);
tvalid <= 1'b0;
tlast <= 1'b0;
endtask
task automatic dut_soft_reset();
axis_send_byte(
.clk(clk_eth_phy_rx),
.data(8'b00001111),
.last(1'b1),
.tvalid(m_axis_rx_tvalid),
.tdata(m_axis_rx_tdata),
.tlast(m_axis_rx_tlast),
.tready(m_axis_rx_tready)
);
endtask
task automatic dut_start();
axis_send_byte(
.clk(clk_eth_phy_rx),
.data(8'b11110000),
.last(1'b1),
.tvalid(m_axis_rx_tvalid),
.tdata(m_axis_rx_tdata),
.tlast(m_axis_rx_tlast),
.tready(m_axis_rx_tready)
);
endtask
// task automatic dut_send_config(
// input logic [127:0] ctrl_config
// );
// // команда set_data
// axis_send_byte(
// .clk(clk_eth_phy_rx),
// .data(8'b10001000),
// .last(1'b0),
// .tvalid(m_axis_rx_tvalid),
// .tdata(m_axis_rx_tdata),
// .tlast(m_axis_rx_tlast),
// .tready(m_axis_rx_tready)
// );
// // config burst
// for (int i = 0; i < 16; i++) begin
// logic [7:0] byte_to_send;
// logic is_last;
// // get byte
// byte_to_send = ctrl_config[i*8 +: 8];
// // tlast for last byte
// is_last = (i == 15);
// axis_send_byte(
// .clk(clk_eth_phy_rx),
// .data(byte_to_send),
// .last(is_last),
// .tvalid(m_axis_rx_tvalid),
// .tdata(m_axis_rx_tdata),
// .tlast(m_axis_rx_tlast),
// .tready(m_axis_rx_tready)
// );
// end
// endtask
// some helpers for controller axis
// GAME PLAN
// 1. setup reflectometer
// 2. create some reference signal with noise + virtual ADC
// 3. setup m_axis endpoint for controller to start reflectometer (create multiple tasks)
// 4. setup s_axis endpoint for data gathering and plotting
// 5. check standalone reflectometer
// 6. add reference signal averaging loop throw generator pulse posedge detection
// 7. visual comparision of reference VS reflectometer
// 8. add statistics for signal comparision (MSE/RMSE)
// main TB
initial begin
// setup
rst_n = 1'b0;
s_axis_tx_tready = 1'b0;
m_axis_rx_tdata = 1'b0;
m_axis_rx_tvalid = 1'b0;
m_axis_rx_tlast = 1'b0;
phy_ready = 1'b0;
// startup
#100;
rst_n = 1'b1;
wait(DUT.clk_wiz_ctrl_inst.locked == 1'b1);
#20;
$display("=== clocks ready / wiz. locked ===");
#40;
// ready to work
dut_config[31:0] = PULSE_WIDTH;
dut_config[63:32] = PULSE_PERIOD;
dut_config[79:64] = PULSE_NUM;
dut_config[79+DAC_DATA_WIDTH:80] = PULSE_HEIGHT;
dut_config[127:96] = PULSE_PERIOD_ADC;
// dut_send_config(dut_config);
dut_start();
// dut_start();
#1000;
// dut_soft_reset();
$display("=== ALL BASIC TESTS PASSED ===");
$finish;
end
endmodule

1
libs/rtl_libs Submodule

Submodule libs/rtl_libs added at 338f30c0d7

1
libs/verilog-axi Submodule

Submodule libs/verilog-axi added at 516bd5dadc

View File

@ -1,7 +1,7 @@
# Генератор # Генератор
Модуль выполняет задачу формирования последовательности импульсов заданной амплитуды, длительности и периода. Модуль выполняет задачу формирования последовательности импульсов заданной амплитуды, длительности и периода.
Дополнительно реализован механизм синхронизации с модулем сэмплера через сигналы `request` и `done`, позволяющий запускать сбор данных для каждого импульса и ожидать подтверждения завершения выборки перед переходом к следующему импульсу. Дополнительно реализован механизм синхронизации с модулем сэмплера через сигналы `sample_req` и `sample_done`, позволяющий запускать сбор данных для каждого импульса и ожидать подтверждения завершения выборки перед переходом к следующему импульсу.
--- ---
@ -22,7 +22,7 @@
## Список входных портов ## Список входных портов
### clk_dac ### clk_in
Сигнал тактирования модуля. Сигнал тактирования модуля.
### rst ### rst
@ -47,25 +47,25 @@
### [15:0] pulse_num ### [15:0] pulse_num
Количество импульсов, которое необходимо сгенерировать. Количество импульсов, которое необходимо сгенерировать.
### request ### sample_done
Сигнал запроса на синхронизацию от сэмплера для текущего импульса. Сигнал подтверждения от сэмплера о завершении выборки данных для текущего импульса.
--- ---
## Список выходных портов ## Список выходных портов
### dac_wrt pulse
Выходной сигнал разрешения записи сигнала Выходной сигнал разрешения записи сигнала
### [DATA_WIDTH-1:0] dac_out [DATA_WIDTH-1:0] pulse_height_out
Выходное значение амплитуды сигнала. Выходное значение амплитуды сигнала.
Во время активной части импульса равно `pulse_height`, вне импульса — `ZERO_LEVEL`. Во время активной части импульса равно `pulse_height`, вне импульса — `ZERO_LEVEL`.
### done sample_req
Сигнал запроса на запуск синхронизации с сэмплером для текущего импульса. Сигнал запроса на запуск выборки в модуле сэмплера.
Поднимается в начале каждого нового импульса и снимается после получения `request`. Поднимается в начале каждого нового импульса и снимается после получения `sample_done`.
--- ---
@ -74,11 +74,11 @@
После прихода сигнала `start` модуль: После прихода сигнала `start` модуль:
- фиксирует входные параметры генерации - фиксирует входные параметры генерации
- сбрасывает внутренние счетчики
- поднимает `enable = 1` - поднимает `enable = 1`
- выполняет `pulse_num` циклов работы - формирует первый `sample_req`
- - типичный цикл состоит в ожидании синхронизации (`synced`), после чего запуск генерации импульса
Синхронизация представляет из себя простое рукопожатие с внешним модулем, имеющим сигналы `request`/`done` работающими в соответствии с этими сигналами генератора. Один из модулей, входит в ожидание и ставит на свой done активный уровень, после чего ждет, пока второй, запаздывающий модуль не войдет в свой режим ожидания, и не выставит для своего done активный уровень. Для каждого из модулей, на следующий такт после выставления активного уровня, производится проверка своего request. Так, при получении активного request (иными словами активного done от внешнего модуля), модуль незамедлительно опускает уровень своего done и начинает работать. Done подымается до активного уровня хотя-бы на один такт работы соответствующего модуля. После этого начинается последовательная генерация импульсов.
--- ---

View File

@ -1,95 +1,105 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
module generator module generator
#( #(
parameter DATA_WIDTH = 14, parameter DATA_WIDTH = 14,
parameter ZERO_LEVEL = 8192 // 8192 or 0 parameter ZERO_LEVEL = 8192 // 8192 or 0
) )
( (
input clk_dac, input clk_in,
input rst, input rst,
input start, input start,
input [31:0] pulse_width, input [31:0] pulse_width,
input [31:0] pulse_period, input [31:0] pulse_period,
input [DATA_WIDTH-1:0] pulse_height, input [DATA_WIDTH-1:0] pulse_height,
input [15:0] pulse_num, input [15:0] pulse_num,
input request, input sample_done,
output dac_wrt, output pulse,
output logic [DATA_WIDTH-1:0] dac_out, output[DATA_WIDTH-1:0] pulse_height_out,
output logic done output logic sample_req
);
logic [DATA_WIDTH-1:0] pulse_height_reg;
logic [31:0] pulse_width_reg, pulse_period_reg;
logic [15:0] pulse_num_reg;
logic [15:0] cnt_pulse_num; );
logic [31:0] cnt_pulse_period;
logic enable, synced; (* MARK_DEBUG="true" *) logic [DATA_WIDTH-1:0] pulse_height_reg, pulse_height_out_reg;
always @(posedge clk_dac) begin (* MARK_DEBUG="true" *) logic [31:0] pulse_width_reg, pulse_period_reg;
(* MARK_DEBUG="true" *) logic [15:0] pulse_num_reg;
(* MARK_DEBUG="true" *) logic enable;
(* MARK_DEBUG="true" *) logic [15:0] cnt_pulse_num;
(* MARK_DEBUG="true" *) logic [31:0] cnt_period;
always @(posedge clk_in) begin
if (rst) begin if (rst) begin
pulse_height_reg <= ZERO_LEVEL; pulse_height_reg <= ZERO_LEVEL;
pulse_width_reg <= 0; pulse_height_out_reg <= ZERO_LEVEL;
pulse_period_reg <= 0; pulse_width_reg <= '0;
pulse_num_reg <= 0; pulse_period_reg <= '0;
cnt_pulse_num <= 0; pulse_num_reg <= '0;
cnt_pulse_period <= 0;
dac_out <= ZERO_LEVEL;
done <= 0;
enable <= 0; enable <= 0;
synced <= 0; cnt_pulse_num <= '0;
end cnt_period <= '0;
else begin sample_req <= 0;
// wait start for updating registers end else begin
if (start & !enable) begin if (start & !enable) begin
enable <= 1; enable <= 1'b1;
cnt_pulse_num <= '0;
cnt_period <= '0;
sample_req <= 1;
pulse_width_reg <= pulse_width; pulse_width_reg <= pulse_width;
pulse_period_reg <= pulse_period; pulse_period_reg <= pulse_period;
pulse_num_reg <= pulse_num; pulse_num_reg <= pulse_num;
pulse_height_reg <= pulse_height; pulse_height_reg <= pulse_height;
end end
// main work cycle
if (enable) begin if (enable) begin
if (cnt_pulse_num != pulse_num_reg) begin
// wait for synchronization with sampler if (!sample_req && (cnt_period == 0)) begin
if (!synced) begin pulse_height_out_reg <= ZERO_LEVEL;
if (request & done) begin if (sample_done) begin
synced <= 1; sample_req <= 1'b0;
done <= 0;
end end
else
done <= 1; if (!sample_done) begin
if (cnt_pulse_num == pulse_num_reg - 1) begin
enable <= 1'b0;
end end
else begin else begin
if (cnt_pulse_period != pulse_period_reg) begin cnt_pulse_num <= cnt_pulse_num + 1;
if (cnt_pulse_period < pulse_width_reg) sample_req <= 1'b1;
dac_out <= pulse_height_reg; cnt_period <= 1;
else
dac_out <= ZERO_LEVEL;
cnt_pulse_period++;
end
else if (cnt_pulse_period == pulse_period_reg) begin
cnt_pulse_num++;
cnt_pulse_period <= 0;
synced <= 0;
dac_out <= ZERO_LEVEL;
end end
end end
end end
else if (cnt_pulse_num == pulse_num_reg) begin else begin
cnt_pulse_num <= 0;
enable <= 0; if (cnt_period <= pulse_width_reg) begin
pulse_height_out_reg <= pulse_height_reg;
end else begin
pulse_height_out_reg <= ZERO_LEVEL;
end
if (cnt_period == pulse_period_reg) begin
cnt_period <= 0;
end else begin
cnt_period <= cnt_period + 1;
end
if (sample_req && sample_done) begin
sample_req <= 0;
end
end end
end end
end end
end end
// Gated DAC write signal from DAC clock. Needed for posedge
OBUF OBUF_pulse_clk ( OBUF OBUF_pulse_clk (
.I(clk_dac & enable), .I(clk_in),
.O(dac_wrt) .O(pulse)
); );
assign pulse_height_out = pulse_height_out_reg;
endmodule endmodule

View File

@ -1,363 +1,114 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
module generator_tb; module generator_tb;
// === Параметры ===
localparam DATA_WIDTH = 14;
localparam LOGIC_ZERO_LEVEL = 0; // DAC -5V for logic zero
localparam VOLTAGE_ZERO_LEVEL = 2**(DATA_WIDTH-1); // DAC 0V for logic zero
localparam CLK_PERIOD = 8;
parameter string ZERO_LEVEL = "logic"; // "logic" VS "true"
// === Сигналы === parameter DATA_WIDTH = 14;
// Системные сигналы parameter ZERO_LEVEL = 8192;
parameter CLK_PERIOD = 16;
logic clk; logic clk;
logic rst; logic rst;
logic start; logic start;
// Входные сигналы
logic [31:0] pulse_width; // config reg
logic [31:0] pulse_period; // config reg
logic [DATA_WIDTH-1:0] pulse_height; // config reg
logic [15:0] pulse_num; // config reg
logic sampler_done; // sampler request for synchronization
// Выходные сигналы
wire dac_wrt; // DAC wrt singnal
wire [DATA_WIDTH-1:0] dac_out; // DAC input logic signal
wire generator_done; // generator request for synchronization
// === Переменные === logic [31:0] pulse_width;
int current_zero_level; logic [31:0] pulse_period;
initial begin logic [DATA_WIDTH-1:0] pulse_height;
if (ZERO_LEVEL == "true") logic [15:0] pulse_num;
current_zero_level = VOLTAGE_ZERO_LEVEL;
else logic pulse;
current_zero_level = LOGIC_ZERO_LEVEL; logic [DATA_WIDTH-1:0] pulse_height_out;
end
// DUT // DUT
generate
if (ZERO_LEVEL == "true") begin : gen_dut_true
generator #( generator #(
.DATA_WIDTH(DATA_WIDTH), .DATA_WIDTH(DATA_WIDTH)
.ZERO_LEVEL(VOLTAGE_ZERO_LEVEL)
) dut ( ) dut (
.clk_dac(clk), .clk_in(clk),
.rst(rst), .rst(rst),
.start(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_wrt(dac_wrt), .pulse(pulse),
.dac_out(dac_out), .pulse_height_out(pulse_height_out)
.done(generator_done),
.request(sampler_done)
); );
initial $display("[TB] Generator compiled. ZERO_LEVEL: TRUE");
end
else if (ZERO_LEVEL == "logic") begin : gen_dut_logic
generator #(
.DATA_WIDTH(DATA_WIDTH),
.ZERO_LEVEL(LOGIC_ZERO_LEVEL)
) dut (
.clk_dac(clk),
.rst(rst),
.start(start),
.pulse_width(pulse_width),
.pulse_period(pulse_period),
.pulse_height(pulse_height),
.pulse_num(pulse_num),
.dac_wrt(dac_wrt),
.dac_out(dac_out),
.done(generator_done),
.request(sampler_done)
);
initial $display("[TB] Generator compiled. ZERO_LEVEL: LOGIC");
end
else begin : gen_dut_error
// защита от дурака
initial begin
$display("[ERROR] Unknown value ZERO_LEVEL: %s", ZERO_LEVEL);
$finish;
end
end
endgenerate
// Тактовые сигналы // Clock
initial begin initial begin
clk = 0; clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk; forever #(CLK_PERIOD/2) clk = ~clk;
end end
// === Таски для тестипрования ===
// Таска синхронизации, одно рукопожатие
task automatic synchronize(
input bit sampler_first, // 1 - выставить sampler_done ДО генератора, 0 - ПОСЛЕ
input int delay_before_ack, // Если sampler_first=0: задержка ПОСЛЕ gen_done. Если 1: задержка от НАЧАЛА цикла.
input int ack_duration // сколько тактов удерживать sampler_done после встречи сигналов
);
if (sampler_first) begin
// --- сэмплер готов до генератора ---
repeat(delay_before_ack) @(posedge clk);
sampler_done <= 1;
wait(generator_done == 1);
repeat(ack_duration) @(posedge clk);
sampler_done <= 0;
end
else begin
// --- генератора готов до сэмплер ---
wait(generator_done == 1);
repeat(delay_before_ack) @(posedge clk);
sampler_done <= 1;
repeat(ack_duration) @(posedge clk);
sampler_done <= 0;
end
endtask
// Таска сброса DUT
task automatic reset_dut(
input int rst_duration // сколько тактов держать сброс
);
rst <= 1;
repeat(rst_duration) @(posedge clk);
rst <= 0;
endtask
// Таска запуска DUT
task automatic start_dut(
input int start_duration // сколько тактов держать импульс
);
start <= 1;
repeat(start_duration) @(posedge clk);
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 [DATA_WIDTH-1:0] h // высота импульса
);
// Задаем конфигурационные регистры
@(posedge clk);
pulse_width <= w;
pulse_period <= p;
pulse_num <= n;
pulse_height <= h;
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
@(posedge dac_wrt);
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
@(posedge dac_wrt);
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"); $display("\n=== GENERATOR TEST ===\n");
// Инициализация
rst = 1; rst = 1;
start = 0; start = 0;
pulse_width = 0; pulse_width = 0;
pulse_period = 0; pulse_period = 0;
pulse_height = 0; pulse_height = 0;
pulse_num = 0; pulse_num = 0;
sampler_done = 0;
$display("[TB] Test 1. Random latency for control signals"); repeat(5) @(posedge clk);
check_impulses(); rst = 0;
$display("[TB] Test 1 complete");
$display("[TB] Test 2. Random configs"); // --- Test 1 ---
for (int i = 0; i < 25; i++) begin // 3 clk 1, 5 clk 0, 4 pulses
int r_w, r_p, r_n, r_h; repeat(2) @(posedge clk);
bit r_skip; pulse_width = 3;
pulse_period = 8;
pulse_num = 4;
pulse_height = 14'h3FF;
start = 1;
// Генерируем параметры repeat(1) @(posedge clk);
r_p = $urandom_range(5, 50); // Период от 5 до 50 start = 0;
r_w = $urandom_range(0, r_p); // Ширина не больше периода
r_n = $urandom_range(1, 10); // Количество импульсов
r_h = $urandom_range(1, 2**DATA_WIDTH-1); // Высота (для 14 бит)
r_skip = $urandom_range(0, 1); // Случайный сброс (0 - сброс, 1 - пропуск)
// Защита от "нулевого" импульса. Невозможно проверить длительность. repeat(50) @(posedge clk);
if (r_h == current_zero_level) begin
r_h += $urandom_range(1, 10);
end
$display("[TB] --- Test #%0d (Config: W=%0d, P=%0d, N=%0d, H=%0d, SkipReset=%0b) ---", // --- Test 2 ---
i+1, r_w, r_p, r_n, r_h, r_skip); $display("\n--- SECOND RUN ---\n");
run_test_case( @(posedge clk);
.pulse_w(r_w), pulse_width = 2;
.pulse_p(r_p), pulse_period = 5;
.pulse_n(r_n), pulse_num = 3;
.pulse_h(r_h), pulse_height = 14'h155;
.skip_reset(r_skip), start = 1;
.count_level(1)
);
end
$display("[TB] Test 2 complete");
$display("[TB] Test 3. Zero level of pulse height"); @(posedge clk);
run_test_case( start = 0;
.pulse_w(77),
.pulse_p(131),
.pulse_n(13),
.pulse_h(current_zero_level),
.skip_reset(0),
.count_level(0)
);
$display("[TB] Test 3 complete");
$display("[TB] ALL PASSED"); repeat(40) @(posedge clk);
pulse_width = 3;
pulse_period = 8;
pulse_num = 4;
pulse_height = 14'h3FF;
start = 1;
repeat(1) @(posedge clk);
start = 0;
repeat(5) @(posedge clk);
start = 1;
pulse_height = 14'h155;
repeat(1) @(posedge clk);
start = 0;
repeat(50) @(posedge clk);
$display("\n=== TEST FINISHED ===");
$finish; $finish;
end end
// Display
always @(posedge clk) begin
$display("t=%0t | pulse=%0b | height=%h",
$time, pulse, pulse_height_out);
end
endmodule endmodule

View File

@ -1,5 +1,7 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
module sampler module sampler
#( #(
parameter DATA_WIDTH = 12, parameter DATA_WIDTH = 12,
@ -12,16 +14,16 @@ module sampler
input [DATA_WIDTH-1:0] data_in, input [DATA_WIDTH-1:0] data_in,
input out_of_range, input out_of_range,
input [31:0] smp_num, input [31:0] smp_num,
input done, input sample_req,
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 request output logic sample_done
); );
(* MARK_DEBUG="true" *) logic [DATA_WIDTH-1:0] data_converted; (* MARK_DEBUG="true" *) logic [DATA_WIDTH-1:0] data_converted;
(* MARK_DEBUG="true" *) logic out_of_range_reg; (* MARK_DEBUG="true" *) logic out_of_range_reg;
(* MARK_DEBUG="true" *) logic [31:0] smp_num_reg, cnt_smp_num; (* MARK_DEBUG="true" *) logic [31:0] smp_num_reg, cnt_smp_num;
(* MARK_DEBUG="true" *) logic enable, enable_d; (* MARK_DEBUG="true" *) logic enable;
generate generate
if (PROCESS_MODE) begin if (PROCESS_MODE) begin
@ -66,22 +68,20 @@ module sampler
buffer_ready <= 0; buffer_ready <= 0;
cnt_smp_num <= '0; cnt_smp_num <= '0;
smp_num_reg <= '0; smp_num_reg <= '0;
enable <= 0; enable <= '0;
request <= 0; sample_done <= 0;
end end
else begin else begin
buffer_ready <= 0; buffer_ready <= 0;
enable_d <= enable; if (sample_done && !sample_req) begin
if (!enable) begin sample_done <= 1'b0;
if (request && done) begin end
if (!enable && sample_req && !sample_done) begin
enable <= 1; enable <= 1;
request <= 0;
cnt_smp_num <= 0; cnt_smp_num <= 0;
smp_num_reg <= smp_num; smp_num_reg <= smp_num;
end else begin
request <= 1;
end end
end else begin if (enable) begin
if (!out_of_range_reg) begin if (!out_of_range_reg) begin
if (cnt_smp_num != smp_num_reg) begin if (cnt_smp_num != smp_num_reg) begin
buffer <= data_converted; buffer <= data_converted;
@ -90,6 +90,8 @@ module sampler
end end
else begin else begin
cnt_smp_num <= '0; cnt_smp_num <= '0;
sample_done <= 1'b1;
buffer_ready <= 0;
buffer <= '0; buffer <= '0;
enable <= 0; enable <= 0;
end end
@ -106,21 +108,21 @@ module sampler
cnt_smp_num <= '0; cnt_smp_num <= '0;
smp_num_reg <= '0; smp_num_reg <= '0;
enable <= 0; enable <= 0;
request <= 0; sample_done <= 0;
end end
else begin else begin
buffer_ready <= 0; buffer_ready <= 0;
if (!enable) begin if (sample_done && !sample_req) begin
if (!request) request <= 1; sample_done <= 1'b0;
if (request && done) begin end
if (!enable && sample_req && !sample_done) begin
enable <= 1; enable <= 1;
request <= 0;
cnt_smp_num <= 0; cnt_smp_num <= 0;
smp_num_reg <= smp_num; smp_num_reg <= smp_num;
end end
end else begin if (enable) begin
if (!out_of_range_reg) begin if (!out_of_range_reg) 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 <= cnt_smp_num +1;
buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted};
if (cnt == PACK_FACTOR-1) begin if (cnt == PACK_FACTOR-1) begin
@ -133,6 +135,7 @@ module sampler
end end
end end
else begin else begin
sample_done <= 1'b1;
cnt_smp_num <= '0; cnt_smp_num <= '0;
buffer_ready <= 0; buffer_ready <= 0;
buffer <= '0; buffer <= '0;

View File

@ -2,209 +2,130 @@
module sampler_tb; module sampler_tb;
// ========================================================= parameter DATA_WIDTH = 12;
// PARAMETERS parameter PROCESS_MODE = 0;
// ========================================================= parameter CLK_PERIOD = 15.3846;
localparam DATA_WIDTH = 12; parameter TEST_NUM = 1000;
localparam PACK_FACTOR = 1;
localparam PROCESS_MODE = 0;
localparam CLK_PERIOD = 15.3846;
// =========================================================
// SIGNALS
// =========================================================
logic clk; logic clk;
logic rst; logic rst;
logic [DATA_WIDTH-1:0] data_in; logic [DATA_WIDTH-1:0] data_in;
logic out_of_range; logic out_of_range;
logic [31:0] smp_num; logic [DATA_WIDTH-1:0] m_axis_tdata;
logic done;
logic request;
logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata;
logic m_axis_tvalid; logic m_axis_tvalid;
// ========================================================= integer errors = 0;
// DUT
// =========================================================
sampler #( sampler #(
.DATA_WIDTH(DATA_WIDTH), .DATA_WIDTH(DATA_WIDTH),
.PACK_FACTOR(PACK_FACTOR),
.PROCESS_MODE(PROCESS_MODE) .PROCESS_MODE(PROCESS_MODE)
) dut ( ) dut (
.clk_in(clk), .clk_in(clk),
.rst(rst), .rst(rst),
.data_in(data_in), .data_in(data_in),
.out_of_range(out_of_range), .out_of_range(out_of_range),
.smp_num(smp_num),
.done(done),
.m_axis_tdata(m_axis_tdata), .m_axis_tdata(m_axis_tdata),
.m_axis_tvalid(m_axis_tvalid), .m_axis_tvalid(m_axis_tvalid)
.request(request)
); );
// =========================================================
// CLOCK
// =========================================================
initial begin initial begin
clk = 0; clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk; forever #(CLK_PERIOD/2) clk = ~clk;
end end
// ========================================================= function automatic [DATA_WIDTH-1:0] ref_convert(input [DATA_WIDTH-1:0] din);
// RESET (ONLY ONCE) if (PROCESS_MODE == 0)
// ========================================================= return din;
else if (din == {1'b1, {(DATA_WIDTH-1){1'b0}}})
return din;
else
return din[DATA_WIDTH-1] ?
{1'b1, (~din[DATA_WIDTH-2:0] + 1'b1)} :
din;
endfunction
task send(input [DATA_WIDTH-1:0] word, input bit oor);
@(posedge clk);
data_in <= word;
out_of_range <= oor;
endtask
logic [DATA_WIDTH-1:0] exp_d0, exp_d1, exp_d2;
logic oor_d0, oor_d1, oor_d2;
initial begin initial begin
$display("\n=== RANDOM SAMPLER TEST===\n");
rst = 1; rst = 1;
done = 0;
data_in = 0; data_in = 0;
out_of_range = 0; out_of_range = 0;
smp_num = 0;
exp_d0 = 0;
exp_d1 = 0;
exp_d2 = 0;
oor_d0 = 1;
oor_d1 = 1;
oor_d2 = 1;
repeat(5) @(posedge clk); repeat(5) @(posedge clk);
rst = 0; rst = 0;
repeat(2) @(posedge clk);
repeat (TEST_NUM) begin
logic [DATA_WIDTH-1:0] rand_data;
bit rand_oor;
rand_data = $urandom_range(0, (1 << DATA_WIDTH) - 1);
rand_oor = ($urandom_range(0, 99) < 20);
@(negedge clk);
if (!oor_d2) begin
if (m_axis_tvalid !== 1) begin
$display("ERROR: valid=0");
errors++;
end end
// ========================================================= if (m_axis_tdata !== exp_d2) begin
// CONFIG $display("ERROR: data mismatch");
// ========================================================= $display(" expected = %h", exp_d2);
task automatic set_config( $display(" got = %h", m_axis_tdata);
input int n, errors++;
input int init_delay
);
smp_num = n;
repeat(init_delay) @(posedge clk);
endtask
// =========================================================
// HANDSHAKE (DONE/REQUEST)
// =========================================================
task automatic synchronize_sampler(
input bit sampler_first,
input int delay_before_ack,
input int ack_duration
);
if (sampler_first) begin
repeat(delay_before_ack) @(posedge clk);
done <= 1'b1;
wait(request == 1'b1);
repeat(ack_duration) @(posedge clk);
done <= 1'b0;
end end
else begin
wait(request == 1'b1);
repeat(delay_before_ack) @(posedge clk);
done <= 1'b1;
repeat(ack_duration) @(posedge clk);
done <= 1'b0;
end end
endtask
// ========================================================= send(rand_data, rand_oor);
// DATA STREAM (STARTS AFTER SYNC)
// ========================================================= exp_d2 = exp_d1;
task automatic feed_data_stream( exp_d1 = exp_d0;
input int num_words, exp_d0 = ref_convert(rand_data);
input bit random_mode
); oor_d2 = oor_d1;
logic [DATA_WIDTH-1:0] val; oor_d1 = oor_d0;
val = 1; oor_d0 = rand_oor;
end
for (int i = 0; i < num_words; i++) begin
@(posedge clk); @(posedge clk);
if (random_mode)
val = $urandom_range(1, 2**DATA_WIDTH-1); if (!oor_d2) begin
if (m_axis_tdata !== exp_d2) begin
$display("ERROR: final mismatch");
$display(" expected = %h", exp_d2);
$display(" got = %h", m_axis_tdata);
errors++;
end
end
if (errors == 0)
$display("\n========== ALL PASSED ==========\n");
else else
val = val + 1; $display("\n========== FAILED: %0d errors ==========\n", errors);
data_in <= val;
out_of_range <= 0;
end
endtask
// =========================================================
// COUNTER
// =========================================================
int received_count;
always @(posedge clk) begin
if (m_axis_tvalid)
received_count++;
end
// =========================================================
// TEST CASE
// =========================================================
task automatic run_test_case(
input int n,
input int delay,
input int ack,
input bit sampler_first,
input bit random_stream
);
received_count = 0;
set_config(n, 2);
// 1) сначала синхронизация
synchronize_sampler(sampler_first, delay, ack);
// 2) СРАЗУ после sync - поток данных
feed_data_stream(n, random_stream);
repeat(20) @(posedge clk);
if (received_count == n)
$display("[OK] received %0d / %0d", received_count, n);
else
$display("[ERROR] received %0d / %0d", received_count, n);
endtask
// =========================================================
// RANDOM STRESS TEST
// =========================================================
task automatic check_random;
int n, d, a;
bit sf;
for (int i = 0; i < 20; i++) begin
n = $urandom_range(3, 10);
d = $urandom_range(0, 5);
a = $urandom_range(1, 5);
sf = $urandom_range(0, 1);
$display("\n--- TEST %0d --- n=%0d delay=%0d ack=%0d sf=%0b",
i, n, d, a, sf);
run_test_case(n, d, a, sf, 1);
end
endtask
// =========================================================
// MAIN
// =========================================================
initial begin
$display("\n=== SAMPLER TEST (FIXED FLOW: NO MULTI RESET) ===\n");
check_random();
$display("\n=== TEST FINISHED ===");
$finish; $finish;
end end