5. Типы в SystemVerilog
В SystemVerilog все данные представлены при помощи битвекторов, следует об этом помнить, ведь при работе на таком уровне абстракции бывает удобно взять какой-то конкретный бит — здесь это делать можно. Самих типов не так много, и часть из тех, что есть не может быть напрямую использованы в синтезируемой части кода. В таблице приведены все типы данных в SystemVerilog.
Из них не синтезируются без лишних телодвижений real
, realtime
, time
, shortreal
, которые ведут себя привычным для программистов образом, так что в колонках, связанных с особенностями поведения в аппаратуре стоят пропуски.
Можно заметить важную колонку "2 state/4 state", значения в которой отличаются для logic
и bit
.
Значения сигнала может принимать в общем 4 состояния, что показано на схеме.
-
С
0
и1
всё понятно: это отсутствие и наличие сигнала. -
x
означает, что сигнал неизвестен, например, при каком-то конфликте. -
z
означает высокоимпедансное состояние, то есть выход ведёт себя как разомкнутая цепь: внешние устройства, подключенные к этому выводу, могут изменять напряжение на нём по своему усмотрению (в некоторых рамках), не влияя на работу схемы; и наоборот — схема не мешает внешним устройствам менять напряжение на контакте. Используется при отключении от шины, например, в многоточечных интерфейсах передачи данных для изоляции сигналов и предотвращения короткого замыкания.
Работа с float-ми
"Из коробки" в SystemVerilog нет поддержки чисел с плавающей запятой в синтезируемой части кода (зато есть в тестбенчах). Так что для работы с ними нужно либо брать готовые модули (в открытом доступе, например, Berkeley HardFloat или IP производителя), либо писать самому.
По стандарту IEEE 754[3] 32-битные числа с плавающей запятой представлены следующим образом.

Со следующей формулой декодирования.
Умножение byte на float
При реализации свёртки удобно считать, что ядро свёртки — это матрица и float
-ов.
Однако обрабатываемая картинка в формате grayscale — это матрица из byte
-ов, таким образом необходимо продумать, как умножить byte
на float
.
Исходя из формулы для декодирования 32-битных чисел с плавающей запятой, можно лего выразить следующее.
Получившийся результат может быть отрицательным и "вылезать" даже за границы float
, необходимо привести результат к byte
, мы примем такие соглашения.
-
Если число больше 255, то считаем его равным 255.
-
Если число меньше нуля, то мы считаем его равным 0.
Учитывая всё вышесказанное, реализуем модуль для умножения float
на byte
.
module float_mult_byte (
input logic [31:0] float_in,
input byte unsigned uint8_in,
output byte unsigned uint8_out
);
logic sign;
byte unsigned exponent; (1)
logic [22:0] mantissa;
byte exp_shift;
logic [31:0] scaled_mantissa;
logic [31:0] mantissa_to_uint;
logic [31:0] uint_extended;
logic [32:0] fraction_sum;
always_comb begin
sign = float_in[31];
exponent = float_in[30:23];
mantissa = float_in[22:0];
exp_shift = exponent - 8'd127;
if ((exp_shift - 23) >= 0) (2)
scaled_mantissa =
((mantissa * uint8_in) << (exp_shift - 23)) +
(uint8_in << exp_shift); (3)
else if (exp_shift >= 0) begin
mantissa_to_uint = mantissa * uint8_in;
uint_extended = uint8_in;
scaled_mantissa =
(mantissa_to_uint >> (-(exp_shift - 23))) +
(uint_extended << exp_shift);
end else begin
mantissa_to_uint = mantissa * uint8_in;
uint_extended = uint8_in;
if ((uint_extended << (32 + exp_shift)) == 0)
fraction_sum = (mantissa_to_uint << (32 + (exp_shift - 23))); (4)
else
fraction_sum =
(mantissa_to_uint << (32 + (exp_shift - 23))) +
(uint_extended << (32 + exp_shift));
scaled_mantissa =
(mantissa_to_uint >> -(exp_shift - 23)) +
(uint_extended >> -exp_shift) + fraction_sum[32]; (5)
end
if (scaled_mantissa > 255) begin (6)
uint8_out = 8'd255;
end else if (scaled_mantissa == 0 || sign) begin
uint8_out = 8'd0;
end else begin
uint8_out = scaled_mantissa[7:0];
end
end
endmodule
1 | byte в SystemVerilog — это знаковое 8-ми битное целое число, если нужно беззнаковое, то надо указать отдельно. |
2 | Так как мы умножаем на степень двойки, то эффективнее делать просто сдвиги. Для этого нужно понимать, куда сдвигать. |
3 | Если умножить результат сложения, то из-за сдвигов "потеряется" часть информации, и ответ будет неверный. |
4 | Единственная ситуация, когда из суммы дробных частей может появится дополнительная единичка: оба слагаемых умножаются на отрицательную степень двойки, тогда возникает две дробные части.
Их надо учесть, fraction_sum — это сумма дробных частей чисел.
Заметим, что оно 33-битное: в старшем бите лежит результат переполнения после выполнения сложения. |
5 | Учитываем целую часть суммы дробных частей. |
6 | Результат нужно привести к байту. |
Обязательно порешайте упражнения. |