Вопрос или проблема
Когда вы присваиваете узкий битовый вектор более широкому в SystemVerilog вот так:
logic [3:0] narrow;
logic [7:0] wide;
assign wide = narrow;
он будет расширен нулями narrow
. Есть ли хороший способ сделать единичное расширение вместо этого? Я не имею в виду знаковое расширение, и не хочу явно указывать размер. Я думаю о чем-то вроде этого:
assign wide = { '1, narrow };
Но это не работает, поскольку в конкатенации не допускаются несформатированные значения. Есть ли хорошее решение для этого?
Вы можете использовать $bits
для вычисления размера и использовать оператор повторенной конкатенации:
assign wide = { {($bits(wide) - $bits(narrow)){1'b1}}, narrow };
Ссылайтесь на стандарт IEEE Std 1800-2023 раздел 20.6.2 Функция системного размера выражений.
Если бы вы использовали параметры в объявлении сигналов, то вы могли бы сделать расчет с использованием параметров вместо $bits
.
Хорошо, я нашел удобный способ:
assign wide = ~{~narrow};
Если вы просто сделаете это:
assign wide = ~narrow;
то, весьма не интуитивно, SV будет расширять narrow
до ширины wide
перед инверсией. Боже мой, нам нужен разумный заменитель SV. В любом случае, в этом случае мы можем использовать этот факт. Конкатенация {...}
защищает ~narrow
внутри нее от аналогичного расширения. Так что это эквивалентно (в более разумном языке):
wide = ~(zero_extend(~(narrow)));
что, конечно, эквивалентно единичному расширению. Это добавляет инверторы подряд, но если ваш оптимизатор не может их устранить, то у вас есть более серьезные проблемы.
Также уровень “WTF SV?” довольно высок, так что если вы действительно используете это, обязательно добавьте комментарий, объясняющий это!
Ответ или решение
Расширение значения с единицами в SystemVerilog
При работе с системами проектирования на основе SystemVerilog разработчики часто сталкиваются с задачей расширения битовых векторов. В данной статье мы обсудим, как можно эффективно расширить узкий битовый вектор, заполняя его единицами, в отличие от стандартного ноль-расширения, которое применяется по умолчанию.
Пример кода
Рассмотрим следующий код, где мы объявляем узкий и широкий битовые векторы:
logic [3:0] narrow;
logic [7:0] wide;
assign wide = narrow;
В приведенном примере произойдет ноль-расширение значения narrow
при присвоении его wide
. Таким образом, narrow
будет расширен нулями до ширины wide
. Однако в некоторых случаях может потребоваться заполнение расширенной части единицами, что часто называют "расширением единиц".
Решение с использованием функции $bits
Первый способ решения данной задачи заключается в использовании функции $bits
, которая позволяет вычислить размер битового вектора. Затем можно воспользоваться оператором конкатенации с повторением:
assign wide = { {($bits(wide) - $bits(narrow)){1'b1}}, narrow };
Здесь мы вычисляем количество единиц, которые необходимо добавить, вычитая размер narrow
из размера wide
. Данный подход является универсальным и не требует явного указания размеров векторов, что упрощает код и делает его более читабельным.
Альтернативный способ с инвертированием
Другой интересный способ расширения narrow
единицами – это использование операции инверсии:
assign wide = ~{~narrow};
На первый взгляд, такой код может показаться непонятным. Принимаем во внимание, что если использовать простое ~narrow
, то произойдет ноль-расширение narrow
перед инверсией. Тем не менее, использование фигурных скобок {...}
позволяет сохранить оригинальное значение narrow
, а затем инвертировать его.
В этом случае:
wide = ~(zero_extend(~(narrow)));
Таким образом, мы достигаем той же цели, что и в первом случае, но с меньшим количеством явного кода. Следует отметить, что два инвертора можно оптимизировать на уровне компиляции, если оптимизатор поддерживает такой функционал.
Рекомендации
Хотя оба подхода работают корректно, рекомендуется всегда добавлять комментарии к коду, если вы используете нестандартные методы, особенно такие, как инверсия в данном контексте. Это поможет другим разработчикам лучше понять вашу логику и избежать путаницы.
Заключение
Расширение битовых векторов с единицами в SystemVerilog может быть выполнено различными способами. Использование $bits
и операторов конкатенации является стандартным и безопасным методом, тогда как инверсия предоставляет более изящное, хоть и менее очевидное, решение. Выбор подхода должен основываться на специфике вашего проекта и предпочтениях команды разработки.