Verilog: как создать экземпляр модуля

Если у меня есть модуль Verilog «верхний» и «субкомпонент» модуля Verilog, как мне создать экземпляр субкомпонента сверху?

верхняя:

module top(
   input        clk,
   input        rst_n,
   input        enable,
   input  [9:0] data_rx_1,
   input  [9:0] data_rx_2,
   output [9:0] data_tx_2
);

подкомпонент:

module subcomponent(
   input        clk,
   input        rst_n,
   input  [9:0] data_rx,
   output [9:0] data_tx
);

Примечание. Это был общий вопрос, который время от времени возникает снова и снова, он соответствует формат самостоятельного ответа. Дополнительные ответы и обновления приветствуются.


person Morgan    schedule 19.11.2013    source источник
comment
Одна часть информации, которую я не смог найти в стандарте (я смотрю на старую версию), относится к экземплярам с пустыми списками портов: должны ли круглые скобки присутствовать, как в subcomponent subcomponent_instance_name ();, или опущены, как в subcomponent subcomponent_instance_name; ? Или это должен быть отдельный вопрос?   -  person FriendFX    schedule 12.04.2017
comment
@FriendFX Зачем нужен модуль с пустым списком портов? ранние версии инструментов требовали скобок ();, но современные инструменты, скорее всего, могут обрабатывать только ;   -  person Morgan    schedule 17.04.2017
comment
Примерами являются списки соединений Verilog микросхем, которые имеют экземпляры блоков, на которых нет логических портов, например развязывающие конденсаторы или логотипы микросхемы, которые не влияют на функциональность. Методом проб и ошибок я обнаружил, что действительно старые инструменты не принимают просто ; и требуют ();. Тем не менее, я ищу исчерпывающий справочник по этому делу.   -  person FriendFX    schedule 18.04.2017


Ответы (3)


Все это обычно рассматривается в разделе 23.3.2 документа SystemVerilog IEEE Std 1800-2012 .

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

module top(
   input        clk,
   input        rst_n,
   input        enable,
   input  [9:0] data_rx_1,
   input  [9:0] data_rx_2,
   output [9:0] data_tx_2
);

subcomponent subcomponent_instance_name (
  clk, rst_n, data_rx_1, data_tx ); 

endmodule

Это описано в разделе 23.3.2.1 документа SystemVerilog IEEE Std 1800-2012. .

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

module subcomponent(
  input        rst_n,       
  input        clk,
  ...

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

module top(
   input        clk,
   input        rst_n,
   input        enable,
   input  [9:0] data_rx_1,
   input  [9:0] data_rx_2,
   output [9:0] data_tx_2
);

subcomponent subcomponent_instance_name (
  .clk(clk), .rst_n(rst_n), .data_rx(data_rx_1), .data_tx(data_tx) ); 

endmodule

Это описано в разделе 23.3.2.2 документа SystemVerilog IEEE Std 1800-2012. .

Предоставление каждому порту отдельной строки и правильного отступа повышает удобочитаемость и качество кода.

subcomponent subcomponent_instance_name (
  .clk      ( clk       ), // input
  .rst_n    ( rst_n     ), // input
  .data_rx  ( data_rx_1 ), // input  [9:0]
  .data_tx  ( data_tx   )  // output [9:0]
);

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

clk_gen( 
  .clk      ( clk_sub   ), // output
  .en       ( enable    )  // input

subcomponent subcomponent_instance_name (
  .clk      ( clk_sub   ), // input
  .rst_n    ( rst_n     ), // input 
  .data_rx  ( data_rx_1 ), // input  [9:0]
  .data_tx  ( data_tx   )  // output [9:0]
);

Это номинально работает, поскольку автоматически создается провод для clk_sub, есть опасность полагаться на это. по умолчанию он будет создавать только 1-битный провод. Пример, где это проблема, касается данных:

Обратите внимание, что имя экземпляра для второго компонента было изменено

subcomponent subcomponent_instance_name (
  .clk      ( clk_sub   ), // input
  .rst_n    ( rst_n     ), // input 
  .data_rx  ( data_rx_1 ), // input  [9:0]
  .data_tx  ( data_temp )  // output [9:0]
);
subcomponent subcomponent_instance_name2 (
  .clk      ( clk_sub   ), // input
  .rst_n    ( rst_n     ), // input 
  .data_rx  ( data_temp ), // input  [9:0]
  .data_tx  ( data_tx   )  // output [9:0]
);

Проблема с приведенным выше кодом заключается в том, что data_temp имеет ширину всего 1 бит, при компиляции может появиться предупреждение о несоответствии ширины порта. Необходимо создать провод подключения и указать ширину. Я бы рекомендовал явно выписать все провода подключения.

wire [9:0] data_temp
subcomponent subcomponent_instance_name (
  .clk      ( clk_sub   ), // input
  .rst_n    ( rst_n     ), // input 
  .data_rx  ( data_rx_1 ), // input  [9:0]
  .data_tx  ( data_temp )  // output [9:0]
);
subcomponent subcomponent_instance_name2 (
  .clk      ( clk_sub   ), // input
  .rst_n    ( rst_n     ), // input 
  .data_rx  ( data_temp ), // input  [9:0]
  .data_tx  ( data_tx   )  // output [9:0]
);

При переходе на SystemVerilog доступно несколько приемов, позволяющих сэкономить на вводе нескольких символов. Я считаю, что они затрудняют читаемость кода и затрудняют поиск ошибок.

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

module top(
   input        clk,
   input        rst_n,
   input        enable,
   input  [9:0] data_rx_1,
   input  [9:0] data_rx_2,
   output [9:0] data_tx_2
);

subcomponent subcomponent_instance_name (
  .clk,                    // input **Auto connect**
  .rst_n,                  // input **Auto connect**
  .data_rx  ( data_rx_1 ), // input  [9:0]
  .data_tx  ( data_tx   )  // output [9:0]
);

endmodule

Это описано в разделе 23.3.2.3 документа SystemVerilog IEEE Std 1800-2012. .

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

subcomponent subcomponent_instance_name (
  .*,                      // **Auto connect**
  .data_rx  ( data_rx_1 ), // input  [9:0]
  .data_tx  ( data_tx   )  // output [9:0]
);

Это описано в разделе 23.3.2.4 документа SystemVerilog IEEE Std 1800-2012. .

person Morgan    schedule 19.11.2013
comment
Ваши ссылки не работают. Вы знаете другое место, где размещаются стандарты? - person gobernador; 27.02.2018
comment
@gobernador обновил ссылки, думаю, вам нужно зарегистрироваться, но они все еще доступны бесплатно. standard.ieee.org/findstds/standard/1800-2012.html - person Morgan; 28.02.2018

Не забудьте проверить verilog-mode и особенно verilog-auto. http://www.veripool.org/wiki/verilog-mode/ Это режим Verilog для emacs, но плагины существуют, например, для vi (m?).

Создание экземпляра можно автоматизировать с помощью AUTOINST. Комментарий расширяется с помощью M-x verilog-auto и впоследствии может быть отредактирован вручную.

subcomponent subcomponent_instance_name(/*AUTOINST*/);

Расширенный

subcomponent subcomponent_instance_name (/*AUTOINST*/
  //Inputs
  .clk,         (clk)           
  .rst_n,       (rst_n)
  .data_rx      (data_rx_1[9:0]),
  //Outputs
  .data_tx      (data_tx[9:0])
);

Неявные связи можно автоматизировать с помощью /*AUTOWIRE*/. Перейдите по ссылке для получения дополнительной информации.

person Moberg    schedule 14.04.2014

Одна вещь, не упомянутая в ответах (и фактически не заданная в вопросе), - это как создать экземпляр модуля с помощью параметра. Мне всегда трудно запомнить порядок и синтаксис, так что вот:

<component_name> #(.parameter_name( parameter_value ), ... ) <instance_name>(.port_name( wire/reg_name ), ...)

Итак, если у вас есть модуль fooBar с параметром N, и вы хотите вместо этого создать fooBarInstance с другим значением параметра:

module fooBar #(parameter N = 8) (input [N-1:0] foo, output[N-1:0] bar)
endmodule

// Instantiate fooBar with N=12
fooBar #(.N(12)) fooBarInstance(.foo(fooWire), .bar(barReg));
person am9417    schedule 12.05.2021