upd:
fix axi-stream interface definition; successful TB DUT launch
This commit is contained in:
@ -14,40 +14,117 @@ interface axis_if #(
|
||||
logic tlast;
|
||||
logic tready;
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Clocking block для Testbench (Driver/Monitor)
|
||||
// Защищает от Race Conditions в симуляции
|
||||
//------------------------------------------------------------
|
||||
initial begin // Default values
|
||||
tdata = 'x;
|
||||
tvalid = 1'b0;
|
||||
tlast = 1'b0;
|
||||
tready = 1'b0;
|
||||
end
|
||||
|
||||
// Master Clocking Block (для отправки данных из TB)
|
||||
clocking drv_cb @(posedge clk);
|
||||
default input #1step output #100ps;
|
||||
output tdata, tvalid, tlast;
|
||||
input tready;
|
||||
endclocking
|
||||
|
||||
// Slave Clocking Block (для приема данных в TB с генерацией tready)
|
||||
clocking slv_cb @(posedge clk);
|
||||
default input #1step output #100ps;
|
||||
input tdata, tvalid, tlast;
|
||||
output tready;
|
||||
endclocking
|
||||
|
||||
// Passive Monitor Clocking Block
|
||||
clocking mon_cb @(posedge clk);
|
||||
default input #1step;
|
||||
input tdata, tvalid, tlast, tready;
|
||||
endclocking
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Modports для автоматического подключения одной строчкой
|
||||
//------------------------------------------------------------
|
||||
modport master (
|
||||
output tdata, tvalid, tlast,
|
||||
input tready
|
||||
);
|
||||
|
||||
modport slave (
|
||||
input tdata, tvalid, tlast,
|
||||
output tready
|
||||
);
|
||||
|
||||
// Modport для тестбенча (чтобы работать через функции)
|
||||
modport master (output tdata, tvalid, tlast, input tready);
|
||||
modport slave (input tdata, tvalid, tlast, output tready);
|
||||
|
||||
// Модпорт для тестбенча с тасками
|
||||
modport tb (
|
||||
clocking drv_cb,
|
||||
clocking mon_cb
|
||||
clocking slv_cb,
|
||||
clocking mon_cb,
|
||||
import master_send,
|
||||
import slave_recv,
|
||||
import monitor_recv
|
||||
);
|
||||
|
||||
// Отправка пакета (Тестбенч выступает как Master)
|
||||
task automatic master_send(input logic [DATA_WIDTH-1:0] payload[]);
|
||||
if (payload.size() == 0) return;
|
||||
|
||||
@(drv_cb);
|
||||
|
||||
for (int i = 0; i < payload.size(); i++) begin
|
||||
drv_cb.tdata <= payload[i];
|
||||
drv_cb.tvalid <= 1'b1;
|
||||
drv_cb.tlast <= (i == payload.size() - 1);
|
||||
|
||||
forever begin // Ждем подтверждение от слейва пока не получим
|
||||
@(drv_cb);
|
||||
if (drv_cb.tready === 1'b1) begin
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
// Сбрасываем сигналы после отправки пакета
|
||||
drv_cb.tvalid <= 1'b0;
|
||||
drv_cb.tlast <= 1'b0;
|
||||
drv_cb.tdata <= 'x;
|
||||
endtask
|
||||
|
||||
// Прием пакета (Тестбенч выступает как Slave и управляет tready). Не применять если есть реальный Slave (его tready опустится насильно)
|
||||
task automatic slave_recv(output logic [DATA_WIDTH-1:0] payload[]);
|
||||
logic [DATA_WIDTH-1:0] local_queue[$]; // Внутри таски очередь использовать можно
|
||||
|
||||
slv_cb.tready <= 1'b1; // Показываем, что готовы принимать
|
||||
|
||||
forever begin
|
||||
@(slv_cb);
|
||||
if (slv_cb.tvalid === 1'b1) begin
|
||||
local_queue.push_back(slv_cb.tdata);
|
||||
if (slv_cb.tlast === 1'b1) begin
|
||||
break; // Пакет закончился
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
slv_cb.tready <= 1'b0; // Снимаем готовность
|
||||
|
||||
// Перекладываем из очереди в динамический массив
|
||||
payload = new[local_queue.size()](local_queue);
|
||||
endtask
|
||||
|
||||
// Прием пакета (Тестбенч выступает как пассивный наблюдатель без tready)
|
||||
task automatic monitor_recv(output logic [DATA_WIDTH-1:0] payload[]);
|
||||
logic [DATA_WIDTH-1:0] local_queue[$]; // Внутри таски очередь использовать можно
|
||||
|
||||
forever begin
|
||||
if (mon_cb.tready === 1'b1) begin
|
||||
break; // Дождались слейва
|
||||
end
|
||||
@(slv_cb);
|
||||
end
|
||||
|
||||
forever begin
|
||||
@(mon_cb);
|
||||
if (mon_cb.tvalid === 1'b1) begin
|
||||
local_queue.push_back(mon_cb.tdata);
|
||||
if (mon_cb.tlast === 1'b1) begin
|
||||
break; // Пакет закончился
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Перекладываем из очереди в динамический массив
|
||||
payload = new[local_queue.size()](local_queue);
|
||||
endtask
|
||||
|
||||
endinterface
|
||||
|
||||
`endif // AXIS_INTERFACE_SVH
|
||||
`endif // AXIS_INTERFACE_SVH`
|
||||
@ -149,99 +149,37 @@ module reflectometer_tb;
|
||||
//------------------------------------------------------------
|
||||
// Таски для тестирования
|
||||
//------------------------------------------------------------
|
||||
|
||||
// AXI-S таски
|
||||
task automatic axis_send_packet(
|
||||
virtual axis_if.tb vif,
|
||||
logic [7:0] payload[]
|
||||
);
|
||||
int packet_size = payload.size();
|
||||
|
||||
if (packet_size == 0) begin
|
||||
$display("[WARNING] -axis_send_packet- Попытка отправить пустой пакет.");
|
||||
return;
|
||||
end
|
||||
|
||||
foreach (payload[i]) begin
|
||||
$display("%1", payload[i]);
|
||||
// vif.drv_cb.tdata <= payload[i];
|
||||
// vif.drv_cb.tvalid <= 1'b1;
|
||||
// vif.drv_cb.tlast <= (i == packet_size - 1); // tlast активен только на последнем байте
|
||||
|
||||
// // Шагаем на следующий фронт клока, чтобы симулятор применил выходы
|
||||
// @(vif.drv_cb);
|
||||
|
||||
// // Ждем на последующих фронтах, пока tready не станет равен 1.
|
||||
// while (vif.drv_cb.tready !== 1'b1) begin
|
||||
// @(vif.drv_cb);
|
||||
// end
|
||||
end
|
||||
|
||||
// Снимаем валидность на следующем дельта-цикле
|
||||
vif.drv_cb.tvalid <= 1'b0;
|
||||
vif.drv_cb.tlast <= 1'b0;
|
||||
vif.drv_cb.tdata <= '0;
|
||||
task automatic dut_soft_reset(virtual axis_if#(8).tb vif);
|
||||
logic [7:0] tx_packet[];
|
||||
tx_packet = '{8'h0f};
|
||||
vif.master_send(tx_packet);
|
||||
endtask
|
||||
|
||||
task automatic dut_soft_reset();
|
||||
logic [7:0] cmd[] = '{8'b00001111};
|
||||
axis_send_packet(
|
||||
.vif(axis_control_if.tb),
|
||||
.payload(cmd)
|
||||
);
|
||||
task automatic dut_start(virtual axis_if#(8).tb vif);
|
||||
logic [7:0] tx_packet[];
|
||||
tx_packet = '{8'hf0};
|
||||
vif.master_send(tx_packet);
|
||||
endtask
|
||||
|
||||
task automatic dut_start();
|
||||
logic [7:0] cmd[] = '{8'b11110000};
|
||||
axis_send_packet(
|
||||
.vif(axis_control_if.tb),
|
||||
.payload(cmd)
|
||||
);
|
||||
endtask
|
||||
|
||||
task automatic dut_send_system_config(
|
||||
virtual axis_if#(8).tb vif,
|
||||
input logic [31:0] pulse_width,
|
||||
input logic [31:0] pulse_period,
|
||||
input logic [15:0] pulse_num,
|
||||
input logic [13:0] pulse_height,
|
||||
input logic [13:0] pulse_height, // achtung! p_height strictly must have 14 bits of width
|
||||
input logic [31:0] pulse_period_adc
|
||||
);
|
||||
// Выделяем 17 байт: 1 байт команды + 16 байт регистров
|
||||
logic [7:0] full_packet[17];
|
||||
|
||||
// 1. Сначала пишем команду в нулевой байт пакета
|
||||
full_packet[0] = 8'b10001000; // Твоя команда set_data
|
||||
|
||||
// 2. Упаковываем регистр pulse_width [31:0] (байты 1..4)
|
||||
full_packet[1] = pulse_width[7:0];
|
||||
full_packet[2] = pulse_width[15:8];
|
||||
full_packet[3] = pulse_width[23:16];
|
||||
full_packet[4] = pulse_width[31:24];
|
||||
|
||||
// 3. Упаковываем регистр pulse_period [63:32] (байты 5..8)
|
||||
full_packet[5] = pulse_period[7:0];
|
||||
full_packet[6] = pulse_period[15:8];
|
||||
full_packet[7] = pulse_period[23:16];
|
||||
full_packet[8] = pulse_period[31:24];
|
||||
|
||||
// 4. Упаковываем регистр pulse_num [79:64] (байты 9..10)
|
||||
full_packet[9] = pulse_num[7:0];
|
||||
full_packet[10] = pulse_num[15:8];
|
||||
|
||||
// 5. Упаковываем регистр pulse_height [79+dac_width:80] (байты 11..12)
|
||||
full_packet[11] = pulse_height[7:0];
|
||||
full_packet[12] = {2'b00, pulse_height[13:8]}; // дополняем до честного байта
|
||||
|
||||
// 6. Упаковываем регистр pulse_period_adc [127:96] (байты 13..16)
|
||||
full_packet[13] = pulse_period_adc[7:0];
|
||||
full_packet[14] = pulse_period_adc[15:8];
|
||||
full_packet[15] = pulse_period_adc[23:16];
|
||||
full_packet[16] = pulse_period_adc[31:24];
|
||||
|
||||
axis_send_packet(
|
||||
.vif(axis_control_if.tb),
|
||||
.payload(full_packet)
|
||||
);
|
||||
// Создаем временный фиксированный массив и упаковываем всё одной строкой
|
||||
logic [7:0] tx_packet[];
|
||||
|
||||
tx_packet = '{
|
||||
8'h88, // Команда
|
||||
pulse_width[7:0], pulse_width[15:8], pulse_width[23:16], pulse_width[31:24],
|
||||
pulse_period[7:0], pulse_period[15:8], pulse_period[23:16], pulse_period[31:24],
|
||||
pulse_num[7:0], pulse_num[15:8], pulse_height[7:0], 8'({2'b00, pulse_height[13:8]}),
|
||||
pulse_period_adc[7:0], pulse_period_adc[15:8], pulse_period_adc[23:16], pulse_period_adc[31:24]
|
||||
};
|
||||
|
||||
vif.master_send(tx_packet);
|
||||
endtask
|
||||
|
||||
// Таски сбора и обработки статистики
|
||||
@ -251,24 +189,31 @@ module reflectometer_tb;
|
||||
// ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ
|
||||
//------------------------------------------------------------
|
||||
initial begin
|
||||
automatic virtual axis_if.tb control_vif = axis_control_if.tb;
|
||||
automatic virtual axis_if.tb accumulator_vif = axis_accumulator_if.tb;
|
||||
|
||||
$display("[TB] Tests start");
|
||||
|
||||
|
||||
// Инициализация
|
||||
rst_n = 0;
|
||||
#100;
|
||||
rst_n = 1;
|
||||
wait(mmcm_locked === 1'b1);
|
||||
#10;
|
||||
$display("[TB] MMCM locked");
|
||||
|
||||
dut_soft_reset();
|
||||
// dut_send_system_config(
|
||||
// .pulse_width(32'd350),
|
||||
// .pulse_period(32'd1250),
|
||||
// .pulse_num(16'd10),
|
||||
// .pulse_height(14'd14000), // 0V
|
||||
// .pulse_period_adc(32'd650)
|
||||
// );
|
||||
// dut_start();
|
||||
dut_soft_reset(control_vif);
|
||||
#100;
|
||||
dut_send_system_config(
|
||||
.vif(control_vif),
|
||||
.pulse_width(32'd350),
|
||||
.pulse_period(32'd1250),
|
||||
.pulse_num(16'd10),
|
||||
.pulse_height(14'd14000), // 0V
|
||||
.pulse_period_adc(32'd650)
|
||||
);
|
||||
#100;
|
||||
dut_start(control_vif);
|
||||
|
||||
$display("[TB] ALL PASSED");
|
||||
$finish;
|
||||
|
||||
Reference in New Issue
Block a user