`timescale 1ns / 1ps 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" // === Сигналы === // Системные сигналы logic clk; logic rst; 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 // DUT generate if (ZERO_LEVEL == "true") begin : gen_dut_true generator #( .DATA_WIDTH(DATA_WIDTH), .ZERO_LEVEL(VOLTAGE_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] Скомпилирован генератор 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] Скомпилирован генератор ZERO_LEVEL: LOGIC"); end else begin : gen_dut_error // защита от дурака initial begin $display("[ERROR] Неизвестное значение ZERO_LEVEL: %s", ZERO_LEVEL); $finish; end end endgenerate // Тактовые сигналы initial begin clk = 0; forever #(CLK_PERIOD/2) clk = ~clk; 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; $display("[TB] -check_impulses- Проверка устойчивости к долгим управляющим импульсам"); // 1. Установка базовой конфигурации set_config( .w(3), .p(10), .n(4), .h(1024) ); reset_dut(5); repeat(2) @(posedge clk); // 3. Генерируем случайную БОЛЬШУЮ длительность для импульса START // В норме он 1 такт. Сделаем случайным от 5 до 25 тактов. rand_start_duration = $urandom_range(5, 25); $display("[TB] Сгенерирован долгий импульс START: %0d тактов", rand_start_duration); // 4. Параллельный запуск длинного старта и обработки синхронизации fork // Поток 1: Удерживаем старт аномально долго begin start_dut(rand_start_duration); end // Поток 2: Обслуживаем n=4 циклов синхронизации со случайными задержками begin repeat(4) begin // Рандомизируем параметры для каждого из 4-х рукопожатий rand_first = $urandom; // Случайно: Самплер первый (1) или Генератор первый (0) rand_delay = $urandom_range(1, 8); // Случайная задержка ожидания (1..8 тактов) rand_ack = $urandom_range(10, 30); // Аномально долгий удерживаемый импульс sampler_done (10..30 тактов) synchronize( .sampler_first(rand_first), .delay_before_ack(rand_delay), .ack_duration(rand_ack) ); end end join // Ожидание завершения переходных процессов repeat(10) @(posedge clk); $display("[TB] -check_impulses- Тест на долгие импульсы пройден"); endtask // --- ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ --- initial begin $display("[TB] Старт тестов"); // Инициализация rst = 1; start = 0; pulse_width = 0; pulse_period = 0; pulse_height = 0; pulse_num = 0; sampler_done = 0; check_impulses(); $display("[TB] Все тесты выполнены!"); $finish; end endmodule