3. Многобитный сумматор
Напишем модуль, который может складывать двухбитные числа.
Для этого переиспользуем модуль adder_logic_1_bit
, написанный в прошлый раз.
Что логично, нужно использовать его два раза.
В первый раз, запишем бит переполнения во временную переменную.
После учтём её.
module adder_reuse_without_generate (
input logic [1:0] a,
input logic [1:0] b,
input logic c_in,
output logic c_out,
output logic [1:0] sum
);
logic carry_tmp;
adder_logic_1_bit add1(
.a(a[0]),
.b(b[0]),
.c_in(c_in),
.c_out(carry_tmp),
.sum(sum[0])
);
adder_logic_1_bit add2(
.a(a[1]),
.b(b[1]),
.c_in(carry_tmp),
.c_out(c_out),
.sum(sum[1])
);
endmodule
Можно синтезировать схему написанного модуля. Рисунок 1. Схема двухбитного сложения
Как видно, вспомогательная переменная исчезла.
Точнее не исчезла, она представляет собой проводок из |
В этом коде нет ничего нового, чего бы мы не знали.
Однако если мы попробуем написать сложение для n
-ой размерности, то придётся n
раз прописывать использование однобитного сложения.
Что никуда не годится, программисты придумали для такого циклы.
В SystemVerilog они тоже есть.
module adder_multibits_reuse #(
parameter int COUNT_OF_BITS = 4 (1)
) (
input logic [COUNT_OF_BITS-1:0] a, (2)
input logic [COUNT_OF_BITS-1:0] b,
input logic c_in,
output logic c_out,
output logic [COUNT_OF_BITS-1:0] sum
);
logic [COUNT_OF_BITS:0] carry; (3)
assign carry[0] = c_in; (4)
assign c_out = carry[COUNT_OF_BITS];
genvar i; (5)
generate (6)
for (i = 0; i < COUNT_OF_BITS; i++)
adder_logic_1_bit add( (7)
.a(a[i]),
.b(b[i]),
.c_in(carry[i]),
.c_out(carry[i + 1]), (8)
.sum(sum[i])
);
endgenerate
endmodule
1 | Модули можно параметризовать[1] и использовать параметр в любом месте модуля. Потом при инстанциации можно поставить то значение, которое хочется. |
2 | Параметры можно использовать даже при описании входных и выходных данных. Очень мощный механизм, который обычно отсутствует (ну или является инструментом продвинутого уровня) в других языках. |
3 | Задаём шину промежуточных битов переполнения. |
4 | Начальный бит переполнения нужно учесть в промежуточной шине. |
5 | Переменная, нужная только для генерации. |
6 | Сейчас нужно много раз инстанциировать модуль adder_logic_1_bit , в SystemVerilog для решения этой проблемы можно генерировать код[2]. |
7 | Для каждого бита используем модуль однобитного сложения. |
8 | Ну, и, что вполне логично, запоминаем бит переполнения для использования на следующем шаге (в школе про это говорилось "а один держим в уме"). |
Интересно посмотреть, во что синтезируется этот модуль.
Тут видно, что блок модуля однобитного сложения используется четыре раза. Что ожидаемо: он был вызван для каждого бита четырёхбитной шины. Это напоминает нам о том, что, хоть они и похожи, но инстанциация модуля в SystemVerilog и вызов функции в привычных языках — разные вещи.
Можно добавить флаг Рисунок 3. Развёрнутая схема многобитного сумматора
|
Ну, не стоит забывать, что всё это делается в учебных целях, и сложение битветоров реализовано, конечно, из коробки.
module adder_multibits_bitvectors #(
parameter int COUNT_OF_BITS = 4
) (
input logic [COUNT_OF_BITS-1:0] a,
input logic [COUNT_OF_BITS-1:0] b,
input logic c_in,
output logic c_out,
output logic [COUNT_OF_BITS-1:0] sum
);
assign {c_out, sum} = a + b + c_in;
endmodule
Схема для этого модуля будет выглядеть довольно просто.
Человеку привычно воспринимать числа в десятичной записи, и в нормальных языках для такого есть отдельный тип int
.
В этом смысле SystemVerilog тоже нормальный[3].
Так что наш модуль в некотором смысле вырождается.
module adder_multibits_int(
input int a,
input int b,
output int sum
);
assign sum = a + b;
endmodule
По умолчанию int
— знаковые числа, чтобы сделать их беззнаковыми, нужно писать int unsigned
.
У int другое поведение при переполнении: для знаковых чисел оно меняет знак, для беззнаковых — уходит в 0.
|
Обязательно порешайте упражнения. |