Altifigence Academy

24 / 37 · Практика

Практика: тестовый стенд, самостоятельно сообщающий ошибки

Разделите DUT, стимулы, эталон и сравнения для проверки сброса, enable и возврата без гонок.

Разделяйте реализацию и проверяющий код

Стенд задаёт стимулы, считает ожидания и сообщает различия. Это код моделирования, не синтезируемая схема. Сохраните modulo_tick из урока параметров как modulo_tick.sv, а этот полный стенд как tb.sv. Синхронный сброс приоритетен; без enable, count сохраняется, pulse очищается; при enable счёт идёт по модулю N, pulse=1 только при возврате. Используйте целочисленный эталон вместо чтения внутренних состояний.

SystemVerilog
`timescale 1ns/1ps
module tb #(parameter integer N = 5);
  localparam integer CW = (N <= 1) ? 1 : $clog2(N);
  logic clk = 1'b0;
  logic rst = 1'b0;
  logic enable = 1'b0;
  wire [CW-1:0] count;
  wire pulse;
  integer expected_count = 0;
  logic expected_pulse = 1'b0;
  integer checks = 0;
  modulo_tick #(.N(N)) dut (
    .clk(clk), .rst(rst), .enable(enable),
    .count(count), .pulse(pulse)
  );
  always #5 clk = ~clk;

  task automatic step(input logic reset_value, enable_value);
    begin
      @(negedge clk);
      rst = reset_value;
      enable = enable_value;
      if (reset_value) begin
        expected_count = 0;
        expected_pulse = 1'b0;
      end else begin
        expected_pulse = 1'b0;
        if (enable_value) begin
          expected_count = expected_count + 1;
          if (expected_count == N) begin
            expected_count = 0;
            expected_pulse = 1'b1;
          end
        end
      end
      @(posedge clk);
      #1;
      if (count !== CW'(expected_count) || pulse !== expected_pulse)
        $fatal(1, "Mismatch check=%0d count=%0d expected=%0d pulse=%b expected=%b",
          checks, count, expected_count, pulse, expected_pulse);
      checks = checks + 1;
    end
  endtask

  initial begin
    if (N < 1) $fatal(1, "N must be positive");
    step(1'b1, 1'b1);
    repeat (N-1) step(1'b0, 1'b1);
    step(1'b0, 1'b0);
    step(1'b0, 1'b1);
    step(1'b0, 1'b0);
    repeat (2*N+1) step(1'b0, 1'b1);
    step(1'b1, 1'b1);
    step(1'b0, 1'b0);
    $display("PASS N=%0d checks=%0d", N, checks);
    $finish;
  end
  initial begin
    #10000;
    $fatal(1, "Timeout");
  end
endmodule

Избегайте гонки на активном фронте

Задача меняет входы на спадающем фронте, за половину периода до захвата, и сравнивает после неблокирующих обновлений нарастающего. #1 — учебная задержка наблюдения с заданным timescale и тактом 10ns, не физический анализ clock-to-Q, setup или hold. Повторно используемые среды могут применять clocking blocks или дисциплину областей событий. Первый сброс определяет DUT; инициализация эталона не инициализирует DUT. !== отмечает X/Z против известного ожидания.

Компилируйте с явным верхним модулем

В оболочке с установленным Icarus:

text
iverilog -g2012 -s tb -o sim.vvp modulo_tick.sv tb.sv
vvp sim.vvp

Компилируйте отдельно с -P tb.N=1,-P tb.N=2,-P tb.N=5,-P tb.N=8. Официальная документация Icarus объясняет язык и верхний модуль. Запишите версию: поддержка SystemVerilog различается.

Намеренно вызовите отказ

Измените сравнение для возврата на один фронт раньше или удалите очистку pulse по умолчанию. Сценарий должен завершиться ошибкой с ненулевым статусом до timeout. Затем восстановите правильный исходник. PASS, не обнаруживающий значимую мутацию, является слабым подтверждением. Запишите hash, N, команды, статусы, PASS и первое расхождение. Локальный тест не является выполнением DDS:#delay и initial универсального стенда не равны ограниченному лабораторному интерфейсу одного такта.

Выбор действует в этом браузере. Его можно изменить внизу страницы.