Тестбенч и CI

Итак, мы написали простой сумматор и посмотрели, что он выводит что-то адекватное. Такой способ тестирования, конечно, никуда не годится. В более привычных языках программирования принято писать unit-тесты, тестбенч[1] нужен в том числе для этого.

Разберём простейший тестбенч:

src/02-combination-logic/testbenches/adder_1_bit_tb.sv
module adder_1_bit_tb;

  logic c_in, c_out, a, b, sum;

  const logic [7:0] CInParams = 8'b0000_1111;  (1)
  const logic [7:0] AParams = 8'b0011_0011;
  const logic [7:0] BParams = 8'b0101_0101;

  const logic [7:0] SumExpected = 8'b0110_1001;  (2)
  const logic [7:0] COutExpected = 8'b0001_0111;

  adder_logic_1_bit add (
      .a(a),
      .b(b),
      .sum(sum),
      .c_in(c_in),
      .c_out(c_out)
  );

  initial begin
    for (
        int i = 0; i < 8; i++
    )  (3)
        begin
      c_in = CInParams[i]; (4)
      a    = AParams[i];
      b    = BParams[i];
      #10;
      assert (SumExpected[i] === sum & COutExpected[i] === c_out)  (5)
        $display(
            "%b (из переполнения) + %b + %b = %b (%b в переполнении)",
            c_in,
            a,
            b,
            sum,
            c_out
        );
      else begin
        $display({"\nЧто-то тут не так:",
                  "\nc_in = %b, a = %b, b = %b, sum = %b, c_out = %b",
                  "\nsum_expect = %b, c_out_expect = %b"}, c_in, a, b, sum, c_out, SumExpected[i],
                   COutExpected[i]);
        $fatal;  (6)
      end
    end
  end
endmodule
1 Задаём входные данные в виде векторов, которые будем считывать побитно. Примечателен формат целых чисел: <количество знаков>'<система счисления><знак1>…​<знакN>, в данном случае у нас 8-ми битные числа.
2 Задаём ожидаемые результаты.
3 Задаём цикл, тут всё, как у людей, настоящий си-подобный синтаксис.
4 Считываем очередной бит входных данных.
5 Проверяем, что актуальные результаты совпали с ожидаемыми, для этого используем assert[2].
6 Если условие не выполнено, то падаем с ошибкой.

Это больше похоже на привычные нам тесты. И раз они есть, значит можно сделать любимый всеми программистами CI. Во-первых, раз мы настроили линтер, то нужно запускать его автоматически. Как это сделали мы, можно посмотреть[3] в файле .github/workflows/lint.yaml. Во-вторых, запускать всё вручную неприятно, напишем Makefile, на который можно посмотреть в файле src/02-adder/Makefile. Ну и в конце запустим всё это в CI. Опять, подробности можно посмотреть здесь: .github/workflows/tests.yaml. Готово, код проверяется, проект собирается, тесты запускаются.

Последовательность действий ничем не отличается от того, к чему вы привыкли при работе с другими инструментами, что не может не радовать. Настоятельно рекомендуем посмотреть на все приведённые выше файлы и реализовать что-то подобное для своих проектов.

Обязательно пройдите упражнения.