Программирование

Множественные присваивания в always_comb SystemVerilog

Поведение множественных присваиваний одной переменной в блоках always_comb SystemVerilog, стандарты и особенности синтеза в Vivado.

4 ответа 2 просмотра

Как работает множественное присваивание одной переменной внутри блока always_comb в SystemVerilog? При разработке блоков комбинаторной логики с использованием always_comb я столкнулся с рекомендацией присваивать ‘значение по умолчанию’ переменной внутри блока, чтобы избежать вывода латчей. Однако в стандарте SystemVerilog 1800-2017 я не нашел информации о поведении при множественном присваивании одной переменной внутри любого типа блока always. Уточните, пожалуйста:

  1. Применимо ли это поведение к любому блоку always или только к always_comb?
  2. Стандартизировано ли это поведение или зависит от компилятора?
  3. Одинаково ли поведение для блокирующих и неблокирующих присваиваний в этом контексте?
  4. Поддерживается ли это поведение в синтезе, по крайней мере в Vivado?

В SystemVerilog множественное присваивание одной переменной внутри блока always_comb работает так, что последнее присваивание в выполненной ветви определяет финальное значение переменной. Это поведение является стандартным и применяется для всех блоков always, но always_comb имеет дополнительные ограничения для предотвращения латчей. Компиляторы требуют, чтобы переменная была присвоена на всех возможных путях выполнения, иначе генерируется предупреждение о потенциальном латче.


Содержание


Основы работы always_comb в SystemVerilog

always_comb - это современный синтаксис SystemVerilog, предназначенный для описания чисто комбинаторной логики. В отличие от традиционного always @(*), always_comb предоставляет дополнительные возможности для проверки корректности кода. Основное отличие заключается в автоматическом создании списка чувствительности и требованиях к полноте присваивания.

Когда вы используете always_comb, компилятор автоматически определяет все сигналы, которые используются в правых частях выражений, и включает их в список чувствительности. Это избавляет от необходимости вручную перечислять все входные сигналы. Но есть и важное требование - все переменные, присваиваемые внутри блока, должны быть присвоены на каждом возможном пути выполнения.

В противном случае, синтезатор выдаст предупреждение о возможном формировании латча (latch). Именно поэтому рекомендуется всегда присваивать значение по умолчанию в начале блока, чтобы гарантировать, что переменная получит значение на всех путях выполнения.

Множественные присваивания в always_comb: поведение и стандарты

Множественные присваивания одной переменной внутри блока always_comb - это полностью допустимая конструкция, и ее поведение строго определено. Когда переменной присваивается значение несколько раз в одном блоке, финальное значение определяется последним выполненным присваиванием в той ветви кода, которая будет выполнена при заданных входных условиях.

Это поведение является стандартным и соответствует стандарту IEEE 1800-2017. Важно понимать, что порядок выполнения операторов соответствует порядку их написания в коде. Компилятор анализирует все возможные пути выполнения и гарантирует, что в каждом из них переменная будет присвоена значение.

Как отмечается в документации Xilinx, при синтезе комбинаторной логики Vivado корректно обрабатывает множественные присваивания одной переменны внутри always блоков. Последнее присваивание в выполненной ветви определяет значение переменной, что соответствует стандарту IEEE 1800-2017.

Сравнение с другими блоками always

Отвечая на ваш первый вопрос: поведение множественных присваиваний применимо не только к always_comb, но и к любым другим блокам always в SystemVerilog. Однако ключевые различия заключаются в том, как компилятор проверяет корректность кода.

Для always_comb компилятор автоматически проверяет, что все переменные, присваиваемые внутри блока, получают значение на всех возможных путях выполнения. Для традиционного always @(*) такой проверки нет, и компилятор не будет выдавать предупреждения, если переменная не присваивается на каком-то пути.

Это приводит к тому, что в always_comb при неполном присваивании генерируется предупреждение о возможном латче, в то время как в always @(*) синтез может пройти без предупреждений, но результат будет содержать нежелательные латчи.

Для always @(posedge clk) и других блоков с тактированием поведение множественных присваиваний аналогично - последнее присваивание в ветви определяет значение. Но для синхронных блоков используются неблокирующие присваивания (<=), чтобы избежать проблем с моделированием.

Блокирующие vs неблокирующие присваивания в always_comb

Отвечая на ваш третий вопрос: в контексте always_comb следует использовать только блокирующие присваивания (=). Неблокирующие присваивания (<=) в комбинаторных блоках не имеют смысема и могут привести к ошибкам синтеза или неожиданному поведению.

В always_comb всегда используется блокирующее присваивание (=), потому что оно моделирует мгновенное распространение сигнала. Неблокирующие присваивания (<=) предназначены для синхронных блоков always @(posedge clk), где они моделируют регистровое поведение.

Если вы используете неблокирующие присваивания в always_comb, синтезатор может интерпретировать это как попытку создать регистровую логику, что приведет к ошибкам или предупреждениям. В некоторых случаях синтезатор может пропустить такие присваивания или создать неверную логику.

Таким образом, в always_comb всегда используйте только блокирующие присваивания (=), а неблокирующие (<=) оставьте для синхронных блоков с тактированием.

Поддержка в синтезе: особенности Vivado и других инструментов

Отвечая на ваш четвертый вопрос: да, это поведение полностью поддерживается в синтезе, включая Vivado. Современные инструменты синтеза, такие как Vivado от Xilinx, корректно обрабатывают множественные присваивания в комбинаторных блоках.

Как отмечается в документации Xilinx, при синтезе комбинаторной логики Vivado корректно обрабатывает множественные присваивания одной переменны внутри always блоков. Порядок выполнения присваиваний соответствует порядку операторов в коде. Для always_comb блоков компилятор гарантирует, что все пути выполнения присваивают переменную, а при нарушении этого условия генерируется предупреждение о латче.

Важно отметить, что различные инструменты синтеза могут иметь небольшие различия в обработке пограничных случаев, но основное поведение последнего присваивания в ветви является стандартным и поддерживается всеми основными инструментами, включая Vivado, Quartus, Synopsys DC и другие.

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

Практические рекомендации и примеры кода

Правильное использование always_comb с множественными присваиваниями

systemverilog
always_comb begin
 // Всегда присваивайте значение по умолчанию
 result = 0;
 
 if (enable) begin
 if (mode == 0)
 result = a + b;
 else
 result = a - b;
 end
 
 // Дополнительные условия могут переопределить result
 if (reset)
 result = 0;
end

Неправильный пример без присваивания по умолчанию

systemverilog
// ОШИБКА: переменная result не присваивается, когда enable=0 и reset=0
always_comb begin
 if (enable) begin
 if (mode == 0)
 result = a + b;
 else
 result = a - b;
 end
 
 if (reset)
 result = 0;
end

Сравнение с always @(*)

systemverilog
// Традиционный блок без автоматической проверки
always @(*) begin
 // Компилятор не проверит, что result присвоен на всех путях
 if (enable)
 result = a + b;
 
 // Если enable=0, result сохранит предыдущее значение (латч!)
end

Использование в контексте systemverilog верификация

При разработке testbench для проверки комбинаторной логики важно понимать, как работает множественное присваивание. В модулях systemverilog testbench вы можете использовать always_comb для моделирования сложных комбинационных зависимостей между сигналами.

systemverilog
module testbench;
 logic [7:0] in1, in2;
 logic [7:0] out1, out2;
 
 // Пример множественного присваивания в testbench
 always_comb begin
 // Значение по умолчанию
 out1 = 8'h00;
 out2 = 8'h00;
 
 // Комбинаторная логика
 if (in1 > in2) begin
 out1 = in1 + 1;
 out2 = in2 - 1;
 end else begin
 out1 = in1 - 1;
 out2 = in2 + 1;
 end
 end
 
 // Остальной код testbench
endmodule

Работа с systemverilog interface

При использовании systemverilog interface множественные присваивания могут быть особенно полезны для обработки данных из различных полей интерфейса:

systemverilog
interface data_if;
 logic [31:0] data;
 logic valid;
 logic [1:0] mode;
endinterface

module processor;
 data_if bus;
 
 always_comb begin
 // Множественные присваивания из интерфейса
 if (bus.valid) begin
 case (bus.mode)
 2'b00: result = bus.data[7:0] + bus.data[15:8];
 2'b01: result = bus.data[15:8] + bus.data[23:16];
 2'b10: result = bus.data[23:16] + bus.data[31:24];
 2'b11: result = bus.data[31:24] + bus.data[7:0];
 endcase
 end else begin
 result = 32'h00000000;
 end
 end
endmodule

Источники

  1. Xilinx UG901 HDL Coding Techniques — Руководство по техникам кодирования HDL для Vivado: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2023_1/ug901-vivado-hdl-coding-techniques.pdf
  2. Xilinx UG900 Logic Synthesis — Документация по синтезу логики в Vivado: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2023_2/ug900-vivado-logic-synthesis.pdf
  3. Xilinx Support Community Answer 53015 — Ответ сообщества о множественных присваиваниях в SystemVerilog: https://www.xilinx.com/support/answers/53015.html

Заключение

Множественные присваивания одной переменной внутри always_comb в SystemVerilog работают предсказуемо: последнее присваивание в выполненной ветви определяет финальное значение. Это поведение стандартизировано и применимо ко всем блокам always, но always_comb обеспечивает дополнительную проверку полноты присваивания. В синтезе, включая Vivado, это поведение поддерживается корректно. Для комбинаторной логики всегда используйте блокирующие присваивания (=), а значения по умолчанию присваивайте в начале блока, чтобы избежать генерации латчей. Понимание этих принципов критически важно для качественного проектирования цифровых схем с использованием systemverilog.

В SystemVerilog блок always_comb автоматически создает список чувствительности, включающий все сигналы, используемые в правых частицах присваиваний. Для избежания латчей все переменные, присваиваемые внутри блока, должны быть присвоены на каждом возможном пути выполнения. Множественные присваивания одной переменны разрешены, но последнее присваивание в выполненной ветви определяет финальное значение. Если переменная не присваивается на каком-либо пути, синтезатор генерирует латч с предупреждением. Всегда присваивайте значение по умолчанию в начале блока always_comb.

При синтезе комбинаторной логики Vivado корректно обрабатывает множественные присваивания одной переменной внутри always блоков. Порядок выполнения присваиваний соответствует порядку операторов в коде. Для always_comb блоков компилятор гарантирует, что все пути выполнения присваивают переменную, а при нарушении этого условия генерируется предупреждение о латче. Последнее присваивание в выполненной ветви определяет значение переменной, что соответствует стандарту IEEE 1800-2017.

W

В always_comb множественные присваивания работают как в обычном always - последнее присваивание в ветви определяет значение. Ключевое отличие - компилятор требует, чтобы переменная была присвоена на всех путях, поэтому всегда добавляйте присваивание по умолчанию. В Vivado при синтезе always_comb с неполными присваиваниями генерируется предупреждение о латче. Для комбинаторной логики используйте только блокирующие присваивания (=). Неблокирующие (<=) в always_comb приведут к ошибкам синтеза или неожиданному поведению.

Авторы
W
Топ-участник сообщества
D
Топ-участник сообщества
M
Топ-участник сообщества
Источники
Портал документации
Проверено модерацией
НейроОтветы
Модерация