12. Иерархические имена Verilog HDL

Описывается иерархия имен
Содержание

12.5 Иерархические имена

Каждый идентификатор в описании Verilog HDL должен иметь уникальное имя иерархического пути. Иерархия модулей и определение элементов, таких как задачи и именованные блоки внутри модулей, должны определять эти имена. Иерархия имен может рассматриваться как древовидная структура, где каждый экземпляр модуля, экземпляр блока генерации(generate), задачи(task), функции(function) или именованный блок begin-end или fork-join определяет новый иерархический уровень, или область видимости, в определенной ветви дерева.

Описание конструкции содержит один или несколько модулей верхнего уровня (см. 12.1.1). Каждый такой модуль образует вершину иерархии имен. Этот корневой (модуля верхнего уровня) или эти параллельные корневые модули составляют одну или несколько иерархий в описании конструкции или описании. Внутри любого модуля каждый экземпляр модуля(module) (включая массив экземпляров), экземпляр блока генерации(generate), объявление задачи(task), объявление функции(function) и именованный блок «begin-end» или «fork-join» определяют новую ветвь иерархии. Именованные блоки внутри именованных блоков, а также внутри задач и функций должны создавать новые ветви.

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

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

На любой именованный объект Verilog или иерархическую ссылку на имя можно однозначно ссылаться в полной форме путем конкатенации имен модулей(module), имен экземпляров модулей(module, macromodule), блоков генерации(generate), задач(task), функций(function) или именованных блоков, которые его содержат. Символ точки должен использоваться для разделения каждого имени в иерархии, за исключением экранированных идентификаторов, встроенных в иерархическую ссылку на имя, за которыми следуют разделители, состоящие из пробела и символа точки. Полное имя пути к любому объекту должно начинаться с модуля верхнего уровня (корневого). Это имя пути может быть использовано с любого уровня иерархии или из параллельной иерархии. Имя первого узла в имени пути также может быть вершиной иерархии, которая начинается с уровня, на котором используется путь (что позволяет и дает возможность ссылаться на объекты по нисходящей). Объекты, объявленные в автоматических задачах и функциях, являются исключениями и не могут быть доступны по иерархическим ссылкам имен. Объекты, объявленные в неименованных генерирующих блоках, также являются исключениями. На них можно ссылаться по иерархическим именам только внутри блока и внутри любой иерархии, объявленном экземпляре блока.

За именами в имени иерархического пути, которые ссылаются на массивы экземпляров или циклов блока generate, может сразу следовать константное выражение в квадратных скобках. Это выражение выбирает конкретный экземпляр массива и поэтому называется выбором экземпляра. Выражение должно соответствовать одному из законных значений индекса массива. Если имя массива не является последним элементом пути в иерархическом имени, то выражение instance select является обязательным.

Синтаксис для имен иерархических путей приведен в Синтаксисе 12-6.

escaped_identifier ::= \ {Any_ASCII_character_except_white_space} white_space hierarchical_identifier ::= { identifier [ [ constant_expression ] ] . } identifier identifier ::= simple_identifier | escaped_identifier simple_identifier ::= [ a-zA-Z_ ] { [ a-zA-Z0-9_$ ] } white_space ::= space | tab | newline | eof
Синтаксис 12-6-Синтаксис для имен иерархических путей

Например:

Пример 1 — Код в этом примере определяет иерархию экземпляров модулей и именованных блоков.
module mod(in); input in; always @(posedge in) begin : keep reg hold; hold = in; end endmodule module cct(stim1, stim2); input stim1, stim2; //экземпляр mod mod amod(stim1), bmod(stim2); endmodule module wave; reg stim1, stim2; cct a(stim1, stim2); // экземпляр cct initial begin :wave1 #100 fork :innerwave reg hold; join #150 begin stim1 = 0; end end endmodule

Рисунок 12-1 иллюстрирует иерархию, подразумеваемую в этом коде Verilog.

Глава 12.5 Иерархические имена Рисунок 12-1 Иллюстрирует иерархию примера кода Verilog

Рисунок 12-1 Иерархия в модели

Рисунок 12-2 представляет собой список иерархических форм имен всех объектов, определенных в коде.
wave wave.stim1 wave.stim2 wave.a wave.a.stim1 wave.a.stim2 wave.a.amod wave.a.amod.in wave.a.amod.keep wave.a.amod.keep.hold wave.a.bmod wave.a.bmod.in wave.a.bmod.keep wave.a.bmod.keep.hold wave.wave1 wave.wave1.innerwave wave.wave1.innerwave.hold

Рисунок 12-2 Иерархические имена путей в модели

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

Пример 2 — Следующий пример показывает, как пара именованных блоков может ссылаться на элементы, объявленные друг в друге.
begin fork :mod_1 reg x; mod_2.x = 1; join fork :mod_2 reg x; mod_1.x = 0; join end

12.6 Ссылка на имя верхней области

Имена модуля или экземпляра модуля достаточно для идентификации модуля и его местоположения в иерархии. Модуль нижнего уровня может ссылаться на элементы модуля, находящегося выше него в иерархии. На переменные могут ссылаться, если известно имя модуля более высокого уровня или имя его экземпляра. Для задач(task), функций(function), именованных блоков и блоков генерации(generate) Verilog должен искать имя в объемлющем модуле, пока оно не будет найдено или пока не будет достигнут корень иерархии(модуля верхнего уровня). Он должен искать имя только в более высоких объемлющих модулях, но не в экземплярах.

Синтаксис для ссылки на верхние области приведен в Синтаксисе 12-7.

upward_name_reference ::= module_identifier.item_name item_name ::= function_identifier | block_identifier | net_identifier | parameter_identifier | port_identifier | task_identifier | variable_identifier
Синтаксис 12-7-Синтаксис для ссылки на имя верхних областей

Возрастающем ссылки на имена также могут быть сделаны с именами вида имяобласти.имяэлемента(scope_name.item_name), где имя области является либо именем экземпляра модуля, либо именем блока generate. Имя такой формы должно быть передаваться следующим образом:

  1. Поиск в текущей области видимости область видимости с именем области. Если она не найдена и текущая область видимости не является областью видимости модуля, ищите имя в верхней области видимости, повторяя при необходимости, пока имя не будет найдено или не будет достигнута область видимости модуля. Если имя все еще не найдено, перейдите к шагу 2). В противном случае эта ссылка на имя должна рассматриваться как нисходящая ссылка из области видимости, в которой найдено имя.
  2. Ищите в самой внешней области видимости родительского модуля области видимости с scope_name видимости. Если он найден, имя элемента будет передаваться из этой области видимости.
  3. Повторите шаг 2), поднимаясь вверх по иерархии.

Существует исключение из этих правил для иерархических имен в левой части объявления defparam. Подробности см. в разделе 12.8.

Например:

В этом примере есть четыре модуля, a, b, c и d. Каждый модуль содержит целое число i. Модулями высшего уровня в этом сегменте иерархии модели являются a и d. Есть две копии модуля b, потому что модули a и d объявляется экземпляр b. Есть четыре копии c.i, потому что каждая из двух копий b объявляется экземпляр c дважды.
module a; integer i; b a_b1(); endmodule module b; integer i; c b_c1(), b_c2(); initial // нисходящий путь ссылается на 2 копии i: #10 b_c1.i = 2; // a.a_b1.b_c1.i, d.d_b1.b_c1.i endmodule module c; integer i; initial begin // локальное имя ссылается на 4 копии i: i = 1; // a.a_b1.b_c1.i, a.a_b1.b_c2.i, // d.d_b1.b_c1.i, d.d_b1.b_c2.i b.i = 1; // Возрастающем пути ссылается на 2 копии i: // a.a_b1.i, d.d_b1.i end endmodule module d; integer i; b d_b1(); initial begin // полное имя пути ссылается на каждую копию i a.i = 1; d.i = 5; a.a_b1.i = 2; d.d_b1.i = 6; a.a_b1.b_c1.i = 3; d.d_b1.b_c1.i = 7; a.a_b1.b_c2.i = 4; d.d_b1.b_c2.i = 8; end endmodule

12.7 Правила области

Следующие элементы определяют новую область видимости в Verilog:

  • module
  • task
  • function
  • Именованные блоки
  • Блоки generate

Идентификатор должен использоваться для объявления только одного элемента в пределах области видимости. Это правило означает, что объявлять две или более переменных с одинаковым именем, или называть задачу так же, как переменную в том же модуле, или давать экземпляру вентиля то же имя, что и имя сети, подключенной к его выходу, запрещено. Для блоков generate это правило применяется независимо от того, инстанцирован ли блок generate. Исключение сделано для блоков generate в условной конструкции generate. Обсуждение именования условных блоков генерации см. в разделе 12.4.3.

Если на идентификатор ссылаются непосредственно (без иерархического пути) внутри task, function, именованного блока или блока generate, то он должен быть объявлен либо внутри task, function, именованного блока или блока generate локально, либо внутри module, task, function, именованного блока или блока generate, который находится выше в той же ветви дерева имен, которая содержит task, function, именованный блок или блок generate. Если элемент объявлен локально, то используется локальный элемент. Если нет, то поиск продолжается вверх, пока не будет найден элемент с таким именем или пока не будет встречена граница модуля. Если элемент является переменной, то поиск останавливается на границе модуля. Если элемент является task, function, именованным блоком или блоком generate, то поиск продолжается в модулях более высокого уровня до тех пор, пока он не будет найден. Этот факт означает, что task и function могут использовать и изменять переменные внутри содержащего модуля по имени, не проходя через его порты.

Если на идентификатор ссылается иерархическое имя, путь может начинаться с имени модуля, имени экземпляра, задачи(task), функции(function), именованного блока или именованного блока generate. Имена должны искаться сначала на текущем уровне, а затем в модулях более высокого уровня, пока не будут найдены. Поскольку могут использоваться как имена модулей, так и имена экземпляров, приоритет отдается именам экземпляров, если существует модуль с тем же именем, что и имя экземпляра.

Из-за восходящего поиска можно использовать имена путей, которые не находятся строго на нисходящем пути. Например:

Пример 1 — На рисунке 12-3 каждый прямоугольник представляет локальную область поиска. Область видимости, доступная для восходящего поиска, распространяется на все содержащие прямоугольники — с границей модуля A в качестве внешней границы. Таким образом, блок G может напрямую ссылаться на идентификаторы в F, E и A. Он не может напрямую ссылаться на идентификаторы в H, B, C и D.

12.5 Иерархические имена Рисунок 12-3 Области, доступные для ссылки на имя (из примера) Verilog HDL

Рисунок 12-3 Области, доступные для ссылки на имя

Пример 2 — Следующий пример показывает, как к переменным можно обращаться напрямую или с помощью иерархических имен:
task t; reg s; begin : b reg r; t.b.r = 0;// Эти три строки обращаются к одной и той же переменной r b.r = 0; r = 0; t.s = 0;/ Эти две строки обращаются к одной и той же s = 0; end endtask

12.8 Элаборация

Элаборация — это процесс, который происходит между разбором и моделированием. Он связывает модули с экземплярами модулей, строит иерархию модели, вычисляет значения параметров, передает иерархические имена, устанавливает сетевые связи и готовит все это к моделированию. С добавлением конструкций generate порядок выполнения этих задач становится важным.

12.8.1 Порядок Элаборации

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

Окончательная иерархия модели может зависеть от порядка, в котором оцениваются конструкции defparam и generate.

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

  1. Список начальных точек инициализируется списком модулей верхнего уровня.
  2. Иерархия под каждой начальной точкой расширяется настолько, насколько это возможно без детализации общих конструкций. Все параметры, встречающиеся во время этого расширения, получают свои окончательные значения путем применения начальных значений, переопределения параметров и объявление defparam. Другими словами, любое объявление defparam, цель которого может быть определена в иерархии, элаборации до сих пор, должно иметь свою цель и свое значение. Объявление defparam, цель которых не может быть определена, откладываются до следующей итерации этого шага. Поскольку ни один defparam в иерархии ниже конструкции generate не может ссылаться на параметр вне конструкции generate, возможно, что параметры получат свои окончательные значения до перехода к шагу 2).
  3. Каждая конструкция generate, встреченная на шаге 2), просматривается, и схема generate оценивается. Полученные экземпляры блока generate составляют новый список начальных точек. Если новый список начальных точек не пуст, перейдите к шагу 2).

12.8.2 Раннее передача иерархических имен

Чтобы соответствовать этому алгоритму, иерархические имена в некоторых объявлениях defparam должны быть передача до полной элаборации иерархии. Возможно, что после завершения элаборации правила передачи имен будут диктовать, что иерархическое имя в объявления defparam передавалась бы иначе, если бы не требовалось ранняя передача. Это может привести к ситуации, когда идентичное иерархическое имя в каком-либо другом объявлении в той же области видимости будет передаваться иначе, чем в объявление defparam. Ниже приведен пример конструкции с такой проблемой:
module m; m1 n(); endmodule module m1; parameter p = 2; defparam m.n.p = 1; initial $display(m.n.p); generate if (p == 1) begin : m m2 n(); end endgenerate endmodule module m2; parameter p = 3; endmodule

В этом примере defparam должен быть оценен до того, как будет элаборация условной конструкции generate. На этом этапе элаборации имя передается в parameter p в модуле m1, и этот параметр используется в схеме generate. Результатом действия defparam является присвоение этого параметра в 1. Следовательно, условие generate истинно. После элаборации иерархии под конструкцией generate правила передач иерархического имени диктуют, что имя должно было передаваться в parameter p в модуле m2. На самом деле, идентичное имя в операторе $display будет передаваться на этот другой параметр.

Будет ошибкой, если иерархическое имя в defparam будет начинать передачу до того, как иерархия будет полностью проработана, и это имя будет трактоваться по-другому после того, как модель будет полностью проработана.

Такая ситуация возникает очень редко. Чтобы вызвать ошибку, должен существовать именованный блок generate, который имеет то же имя, что и одна из областей видимости в ее полном иерархическом имени. Кроме того, должны существовать два экземпляра с одинаковым именем, один в блоке generate, а другой в другой области видимости с тем же именем, что и блок generate. Затем внутри этих экземпляров должны быть параметры с тем же именем. Если такая проблема возникла, ее можно легко устранить, изменив имя блока generate.