DocsTech
/
VERILOG HDL
/

~ cd 12. модули(module). переопределение параметров(defparam).

12. Иерархические структуры

Verilog HDL поддерживает иерархическую структуру описания аппаратного обеспечения, позволяя модулям встраиваться в другие модули. Модули более высокого уровня создают экземпляры модулей более низкого уровня и взаимодействуют с ними через порты ввода, вывода и двунаправленные порты. Эти порты ввода/вывода (I/O) модулей могут быть скалярными или векторными.

В качестве примера иерархии модулей рассмотрим систему, состоящую из печатных плат (PCB). Система будет представлена как модуль верхнего уровня и будет создавать экземпляры модулей, представляющих платы. Модули плат, в свою очередь, создают экземпляры модулей, представляющих интегральные схемы (ИС). ИС, в свою очередь, могут создавать экземпляры таких модулей, как fip-flop, мультиплексоры и АЛУ.

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

12.1 Модули

В этом подпункте приводится формальный синтаксис определения модуля, а затем синтаксис экземпляра модуля, а также пример определения модуля и экземпляра модуля.

Определение модуля должно быть заключено между ключевыми словами module и endmodule. Идентификатор, следующий за ключевым словом module, должен быть именем определяемого модуля. Необязательный список определений параметров должен содержать упорядоченный список параметров модуля. Необязательный список портов или деклараций портов должен определять упорядоченный список портов для модуля. Порядок, используемый при определении списка параметров в списке module_parameter_port_list и в списке портов, может иметь значение при объявлении экземпляра модуля (см. 12.2.2.1 и 12.3.5). Идентификаторы в этом списке должны быть объявлены в операторах input, output и inout определении модуля. Порты, объявленные в списке объявлений портов, не должны быть повторно объявлены в теле модуля. Элементы модуля определяют, что представляет собой модуль, и включают множество различных типов объявлений и определений, многие из которых уже были представлены.

Ключевое слово macromodule может использоваться как взаимозаменяемое с ключевым словом module для определения модуля. Реализация может по-разному относиться к определениям модулей, начинающимся с ключевого слова macromodule.

...
Копировать
module_declaration ::=
        { attribute_instance } module_keyword module_identifier [ module_parameter_port_list ]
        list_of_ports ; { module_item }
    endmodule
        | { attribute_instance } module_keyword module_identifier [ module_parameter_port_list ]
        [ list_of_port_declarations ] ; { non_port_module_item }
    endmodule
module_keyword ::= module | macromodule
module_parameter_port_list ::=
    # ( parameter_declaration { , parameter_declaration } )
list_of_ports ::= ( port { , port } )
list_of_port_declarations ::= ( port_declaration { , port_declaration } ) | ( )
port ::= [ port_expression ] | . port_identifier ( [ port_expression ] )
port_expression ::= port_reference | { port_reference { , port_reference } }
port_reference ::= port_identifier [ [ constant_range_expression ] ]
port_declaration ::= {attribute_instance} inout_declaration
    | {attribute_instance} input_declaration
    | {attribute_instance} output_declaration
module_item ::=
    port_declaration ;
    | non_port_module_item
module_or_generate_item ::=
    { attribute_instance } module_or_generate_item_declaration
    | { attribute_instance } local_parameter_declaration ;
    | { attribute_instance } parameter_override
    | { attribute_instance } continuous_assign
    | { attribute_instance } gate_instantiation
    | { attribute_instance } udp_instantiation
    | { attribute_instance } module_instantiation
    | { attribute_instance } initial_construct
    | { attribute_instance } always_construct
    | { attribute_instance } loop_generate_construct
    | { attribute_instance } conditional_generate_construct
module_or_generate_item_declaration ::=
    net_declaration
    | reg_declaration
    | integer_declaration
    | real_declaration
    | time_declaration
    | realtime_declaration
    | event_declaration
    | genvar_declaration
    | task_declaration
    | function_declaration
non_port_module_item ::=
    module_or_generate_item
    | generate_region
    | specify_block
    | { attribute_instance } parameter_declaration ;
    | { attribute_instance } specparam_declaration
parameter_override ::= defparam list_of_defparam_assignments ;
Синтаксис 12-1. Синтаксис для модуля

Определения портов см. в разделе 12.3.

12.1.1 Модули верхнего уровня

Модули верхнего уровня — это модули, которые включены в исходный текст, но не появляются ни в одном объявлении экземпляра модуля, как описано в 12.1.2. Это применимо, даже если объявление экземпляра модуля появляется в блоке generate, который сам не объявляется экземпляр (см. 12.4). Модель должна содержать по крайней мере один модуль верхнего уровня.

12.1.2 Объявление экземпляра модуля

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

Например, модуль счетчика может объявить модуль D-flip-flop для создания нескольких экземпляров flip-flop.

В синтаксисе 12-2 приведен синтаксис для указания объявления экземпляра модулей.

...
Копировать
module_instantiation ::=
    module_identifier [ parameter_value_assignment ]
    module_instance { , module_instance } ;
parameter_value_assignment ::=
    # ( list_of_parameter_assignments )
list_of_parameter_assignments ::=
    ordered_parameter_assignment { , ordered_parameter_assignment }
    | named_parameter_assignment { , named_parameter_assignment }
ordered_parameter_assignment ::=
    expression
named_parameter_assignment ::=
    . parameter_identifier ( [ mintypmax_expression ] )
module_instance ::=
    name_of_module_instance ( [ list_of_port_connections ] )
name_of_module_instance ::=
    module_instance_identifier [ range ]
list_of_port_connections ::=
    ordered_port_connection { , ordered_port_connection }
    | named_port_connection { , named_port_connection }
ordered_port_connection ::=
    { attribute_instance } [ expression ]
named_port_connection ::=
    { attribute_instance } . port_identifier ( [ expression ] )
Синтаксис 12-2. Синтаксис для объявления экземпляра модуля

Объявление экземпляров модулей может содержать спецификацию диапазона. Это позволяет создать массив экземпляров. Массив экземпляров описан в разделе 7.1. Синтаксис и семантика массивов экземпляров, определенные для вентилей и примитивов, применимы и к модулям.

Один или несколько экземпляров модуля (идентичные копии модуля) могут быть объявлены одном операторе экземпляре модуля.

Список соединений портов должен быть предоставлен только для модулей, определенных с портами. Круглые скобки, однако, всегда обязательны. Когда список соединений портов приводится с использованием метода упорядоченного соединения портов, первый элемент списка должен соединяться с первым портом, объявленным в модуле, второй — со вторым портом и так далее. Более подробное обсуждение портов и правил подключения портов см. в разделе 12.3.

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

При подключении портов по имени, неподключенный порт можно указать либо пропуская его в списке портов, либо не указывая выражения в круглых скобках [т.е. .port_name ()].

Например:

Пример 1 — Следующий пример иллюстрирует схему (модуль нижнего уровня), управляемую простым описанием формы сигнала (модуль верхнего уровня), где экземпляр модуля схемы внутри модуля формы сигнала:
...
Копировать
// Модуль нижнего уровня:
// модульное описание схемы nand flip-flop
module ffnand (q, qbar, preset, clear);
    output q, qbar;     //объявляет 2 выходные сети схемы
    input preset, clear; //объявляет 2 входные сети схемы

    // объявление двух nand-гейтов и их взаимосвязей
    nand g1 (q, qbar, preset),
        g2 (qbar, q, clear);
endmodule

// Модуль более высокого уровня:
// описание формы сигнала для флип-флопа nand
module ffnand_wave;
    wire out1, out2; //выходы из схемы
    reg in1, in2; //переменные для управления схемой
    parameter d = 10;

    // инстанцируйте цепь ffnand, назовите ее "ff",
    // и укажите взаимосвязи портов ввода-вывода ffnand
    ffnand ff(out1, out2, in1, in2);

    // определить форму волны для стимулирования схемы
    initial begin
        #d in1 = 0; in2 = 1;
        #d in1 = 1;
        #d in2 = 0;
        #d in2 = 1;
    end
endmodule
Пример 2 — В следующем примере создаются два экземпляра модуля flip-flop ffnand, определенного в примере 1. Он подключается только выходу q в одном экземпляре и выходу qbar в другом экземпляре.
...
Копировать
// описание формы волны для тестирования
// nand flip-flop, без выходных портов
module ffnand_wave;
    reg in1, in2; //переменные для управления схемой
    parameter d = 10;

    // сделайте две копии схемы ffnand
    // ff1 имеет qbar неподключенных, ff2 имеет q неподключенных
    ffnand ff1(out1, , in1, in2),
    ff2(.qbar(out2), .clear(in2), .preset(in1), .q());
    // ff3(.q(out3),.clear(in1),,,); является неправльным

    // определить форму волны для стимулирования схемы
    initial begin
        #d in1 = 0; in2 = 1;
        #d in1 = 1;
        #d in2 = 0;
        #d in2 = 1;
    end
endmodule

12.2 Переопределение значений параметров модуля

Существует два различных способа определения параметров. Первый — это module_parameter_port_list(список параметров порта) (см. 12.1), а второй — как module_item(элемент модуля) (см. 4.10). Объявление модуля может содержать определения параметров одного или обоих типов или не содержать определений параметров.

Параметр модуля может иметь спецификацию типа и спецификацию диапазона. Влияние переопределения параметров на тип и диапазон параметра должно соответствовать следующим правилам:

Например:
...
Копировать
module generic_fifo
    #(parameter MSB=3, LSB=0, DEPTH=4)
            //Эти параметры могут быть переопределены
    (input [MSB:LSB] in,
    input clk, read, write, reset,
    output [MSB:LSB] out,
    output full, empty );

    localparam FIFO_MSB = DEPTH*MSB;
    localparam FIFO_LSB = LSB;
        // Эти параметры являются локальными и не могут быть переопределены.
        // На них можно повлиять, изменив публичные параметры
        // выше, и модуль будет работать правильно.
    reg [FIFO_MSB:FIFO_LSB] fifo;
    reg [LOG2(DEPTH):0] depth;
    always @(posedge clk or reset) begin
        casex ({read,write,reset})
                // реализация fifo
    endcase
    end
endmodule

Существует два способа изменения нелокальных значений параметров: оператор defparam, который позволяет присваивать параметры, используя их иерархические имена, и присвоение значения параметру экземпляра модуля, которое позволяет присваивать значения в строке во время объявления экземпляра модуля. Если назначение defparam конфликтует с параметром экземпляра модуля, то параметр в модуле примет значение, указанное в defparam. Назначение значения параметра экземпляра модуля происходит в двух формах: по упорядоченному списку или по имени. Следующие два подраздела описывают эти два метода.

Существует два вида объявления параметров. Первый вид объявления параметров имеет спецификацию типа и/или диапазона, а второй — нет. Когда значение параметра без указания типа и диапазона переопределяется, параметр принимает размер и тип переопределяемого параметра.

Когда переопределяется типизированный и/или диапазонный параметр, новое значение преобразуется к типу и размеру назначения и присваивается этому параметру.

Например:
...
Копировать
module foo(a,b);
    real r1,r2;
    parameter [2:0] A = 3'h2;
    parameter B = 3'h2;
    initial begin
        r1 = A;
        r2 = B;
        $display("r1 is %f r2 is %f",r1,r2);
    end
endmodule // foo

module bar;
    wire a,b;
    defparam f1.A = 3.1415;
    defparam f1.B = 3.1415;
    foo f1(a,b);
endmodule // bar

Параметр A является типизированным и/или ранжированным параметром. Поэтому при переопределении его значения параметр сохраняет свой исходный тип и знак. Поэтому defparam параметра f1. A со значением 3.1415 выполняется путем преобразования числа с плавающей точкой 3.1415 в число с фиксированной точкой 3, а затем младшие 3 бита числа 3 присваиваются параметру A.

Параметр B не является типизированным и/или диапазоном. Поэтому, когда его значение переопределяется, тип и диапазон параметра принимают тип и диапазон нового значения. Таким образом, defparam f1. B со значением 3.1415 заменяет текущее значение B 3’h2 на число с плавающей точкой 3.1415.

12.2.1 Объявление defparam

С помощью оператора defparam значения параметров могут быть изменены в любом экземпляре модуля во всем проекте с использованием иерархического имени параметра. Иерархические имена см. в разделе 12.5.

Однако оператор defparam в иерархии в или под экземпляром генерирующего блока (см. 12.4) или массивом экземпляров (см. 7.1 и 12.1.2) не должен изменять значение параметра вне этой иерархии.

Каждый экземпляр блока generate считается отдельной областью иерархии. Поэтому это правило подразумевает, что оператор defparam в блоке generate не может ссылаться на параметр в другой экземпляра того же блока generate, даже если другой экземпляр создана той же конструкцией loop generate. Например, следующий код недопустим:
...
Копировать
genvar i;

generate
    for (i = 0; i < 8; i = i + 1) begin : somename
        flop my_flop(in[i], in1[i], out1[i]);
        defparam somename[i+1].my_flop.xyz = i ;
    end
endgenerate

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

Выражение в правой части присваивания defparam должно быть константным выражением, включающим только числа и ссылки на параметры. Ссылающиеся параметры (в правой части defparam) должны быть объявлены в том же модуле, что и оператор defparam.

Оператор defparam особенно полезен для группировки всех назначений переопределения значений параметров в одном модуле.

В случае нескольких defparam для одного параметра, параметр принимает значение последнего оператора defparam, встретившегося в исходном тексте. Если defparam встречается в нескольких исходных файлах, например, при поиске в библиотеке, то defparam, из которого параметр принимает свое значение, не определяется.

Например:
...
Копировать
module top;
    reg clk;
    reg [0:4] in1;
    reg [0:9] in2;
    wire [0:4] o1;
    wire [0:9] o2;

    vdff m1 (o1, in1, clk);
    vdff m2 (o2, in2, clk);
endmodule

module vdff (out, in, clk);
    parameter size = 1, delay = 1;
    input [0:size-1] in;
    input clk;
    output [0:size-1] out;
    reg [0:size-1] out;

    always @(posedge clk)
        # delay out = in;
endmodule

module annotate;
    defparam
        top.m1.size = 5,
        top.m1.delay = 10,
        top.m2.size = 10,
        top.m2.delay = 20;
endmodule

Модуль annotate имеет оператор defparam, который переопределяет значения параметров size и delay для экземпляров m1 и m2 в модуле верхнего уровня top. Модули top и annotate считаются модулями верхнего уровня.

12.2.2 Присвоение значения параметра экземпляра модуля

Альтернативным методом присвоения значений параметрам внутри экземпляров модуля является использование одной из двух форм присвоения значения параметра экземпляра модуля. Это присвоение по упорядоченному списку и присвоение по имени. Эти два типа присвоения значений параметров экземпляра модуля не должны смешиваться. Присвоение параметров конкретному экземпляру модуля должно быть полностью по порядку или полностью по имени.

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

Параметр, объявленный именованном блоке, задаче или функции, может быть переопределен только с помощью оператора defparam. Однако, если значение параметра зависит от второго параметра, то переопределение второго параметра приведет к обновлению значения и первого параметра (см. 12.2.3).

12.2.2.1 Присвоение значения параметра по упорядоченному списку

Порядок присвоения значений параметров экземпляра модуля по упорядоченному списку должен соответствовать порядку объявления параметров модуле. При использовании этого метода нет необходимости присваивать значения всем параметрам в модуле. Однако, пропустить какой-либо параметр невозможно. Поэтому, чтобы присвоить значения подмножеству параметров, объявленных в модуле, объявления параметров, составляющих это подмножество, должны предшествовать объявлениям остальных параметров. Альтернативой является присвоение значений всем параметрам, но использование значения по умолчанию (того же значения, которое было присвоено при объявлении параметра в определении модуля) для тех параметров, которые не нуждаются в новых значениях.

Например:

Рассмотрим следующий пример, где параметры внутри экземпляров модулей mod_a, mod_c и mod_d изменяются во время объявления экземпляра:
...
Копировать
module tb1;

    wire [9:0] out_a, out_d;
    wire [4:0] out_b, out_c;
    reg [9:0] in_a, in_d;
    reg [4:0] in_b, in_c;
    reg clk;

    // код генерации тактовых импульсов и стимулов для тестбенча ...

    // Четыре экземпляра vdff с присвоением значений параметров
    // по упорядоченному списку

    // mod_a имеет новые значения параметров size=10 и delay=15
    // mod_b имеет параметры по умолчанию (size=5, delay=1)
    // mod_c имеет один стандартный size=5 и один новый delay=12
    // Для того, чтобы изменить значение задержки,
    // необходимо также указать значение размера (по умолчанию).
    // mod_d имеет новое значение параметра size=10.
    // задержка сохраняет свое значение по умолчанию

    vdff #(10,15) mod_a (.out(out_a), .in(in_a), .clk(clk));
    vdff mod_b (.out(out_b), .in(in_b), .clk(clk));
    vdff #( 5,12) mod_c (.out(out_c), .in(in_c), .clk(clk));
    vdff #(10) mod_d (.out(out_d), .in(in_d), .clk(clk));

endmodule

module vdff (out, in, clk);
    parameter size=5, delay=1;
    output [size-1:0] out;
    input [size-1:0] in;
    input clk;
    reg [size-1:0] out;

    always @(posedge clk)
        #delay out = in;

endmodule
Локальные параметры не могут быть переопределены. Поэтому они не считаются частью упорядоченного списка для присвоения значения параметра. В следующем примере addr_width будет присвоено значение 12, а data_width — значение 16. Переменная mem_size не будет явно присвоено значение из-за упорядоченного списка, но будет иметь значение 4096 из-за выражения объявления.
...
Копировать
module my_mem (addr, data);

    parameter addr_width = 16;
    localparam mem_size = 1 << addr_width;
    parameter data_width = 8;
    ...
endmodule

module top;
    ...
    my_mem #(12, 16) m(addr,data);
endmodule

12.2.2.2 Присвоение значения параметра по имени

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

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

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

Рассмотрим следующий пример, где оба параметра mod_a и только один параметр mod_c и mod_d изменяются во время объявления экземпляра:
...
Копировать
module tb2;

    wire [9:0] out_a, out_d;
    wire [4:0] out_b, out_c;
    reg [9:0] in_a, in_d;
    reg [4:0] in_b, in_c;
    reg clk;

    // код генерации тактовых импульсов и стимулов для тестбенча ...

    // Четыре экземпляра vdff с присвоением значений параметров по имени

    // mod_a имеет новые значения параметров size=10 и delay=15
    // mod_b имеет параметры по умолчанию (size=5, delay=1)
    // mod_c имеет один стандартный size=5 и один новый delay=12
    // mod_d имеет новое значение параметра size=10.
    //задержка сохраняет свое значение по умолчанию

    vdff #(.size(10),.delay(15)) mod_a (.out(out_a),.in(in_a),.clk(clk));
    vdff mod_b (.out(out_b),.in(in_b),.clk(clk));
    vdff #(.delay(12)) mod_c (.out(out_c),.in(in_c),.clk(clk));
    vdff #(.delay( ),.size(10) ) mod_d (.out(out_d),.in(in_d),.clk(clk));

endmodule

module vdff (out, in, clk);
    parameter size=5, delay=1;
    output [size-1:0] out;
    input [size-1:0] in;
    input clk;
    reg [size-1:0] out;

    always @(posedge clk)
        #delay out = in;

endmodule
Должно быть законным экземпляре модулей, использующих различные типы переопределения параметров в одном модуле верхнего уровня. Рассмотрим следующий пример, где параметры mod_a изменяются с помощью переопределения параметров по упорядоченному списку. Второй параметр mod_c изменяется с помощью переопределения параметров по имени во время объявления экземпляра:
...
Копировать
module tb3;

    // декларации и код

    // легальная смесь экземпляра с позиционными параметрами и
    // другой экземпляр с именованными параметрами

    vdff #(10, 15) mod_a (.out(out_a), .in(in_a), .clk(clk));
    vdff mod_b (.out(out_b), .in(in_b), .clk(clk));
    vdff #(.delay(12)) mod_c (.out(out_c), .in(in_c), .clk(clk));
endmodule
Будет незаконным объявление экземпляра модуля, используя смесь переопределения параметров по порядку и по имени, как показано в экземпляре mod_a ниже:
...
Копировать
// экземпляр mod_a с НЕПРАВИЛЬНОЙ смесью назначений параметров vdff
vdff #(10, .delay(15)) mod_a (.out(out_a), .in(in_a), .clk(clk));

12.2.3 Зависимость параметров

Параметр (например, memory_size) может быть определен с помощью выражения, содержащего другой параметр (например, word_size). Однако переопределение параметра, будь то с помощью оператора defparam или в операторе экземпляра модуля, эффективно заменяет определение параметра новым выражением. Поскольку memory_size зависит от значения word_size, изменение word_size изменяет значение memory_size. Например, в следующем объявлении параметров обновление word_size, будь то в операторе defparam или в операторе экземпляра модуля, определившего эти параметры, автоматически обновляет memory_size. Если memory_size обновляется в результате оператора defparam или оператора экземпляра, то он принимает это значение, независимо от значения word_size.
...
Копировать
parameter
    word_size = 32,
    memory_size = word_size * 4096;
Главная
Курсы
Вебинары
3. Лексические правила(Синтаксис) Verilog HDL
4. Типы данных Verilog HDL
5. Выражения и Операторы Verilog HDL
6. Назначения (Assignments) в Verilog HDL
7. Моделирование на уровне вентилей и переключателей в Verilog HDL
8. Примитивы, объявляемые пользователем (UDP) Verilog HDL
9. Процедурные назначения. Поведенческое моделирование в Verilog HDL.
9. If, case for, while и repeat Verilog HDL
9. Initial, always, задержки, блоки Verilog HDL
10. Задачи(task) и функции (function) в Verilog HDL
11. Семантика планирования. Стек (stack) в Verilog HDL
12. Модули(module). Переопределение параметров(defparam).
12. Порты. Иерархические имена в Verilog HDL
12. Generate блоки Verilog HDL
12. Иерархические имена Verilog HDL
13. Конфигурирование содержимого конструкции
13. Использование библиотек. Конфигурирование содержимого конструкции в Verilog HDL
14. Specify блоки в Verilog HDL
15. Setup, hold, setuphold и recovery в Verilog HDL
15. Skew, period, width и nochange Verilog HDL
15. Проверка синхронизации сигналов в Verilog HDL
16. Бэканнотирование с использованием стандартного формата задержки (SDF) в Verilog HDL
17. Системные задачи и функции
17.2 Файлы. Запись и чтение файлов Verilog
17. Задачи временной шкалы, управления, PLA и стохастического анализа Verilog
$time, $stime и $realtime Verilog
17.8. Функции преобразования Verilog
17.9. Функции распределения вероятностей Verilog
17.10. Ввод командной строки. 17.11. Математические функции
18. Дамп файлы изменения значений (VCD)
18. Формат файла VCD расширенные и четырьмя состояниями
19. Директивы компилятора Verilog HDL
20. Обзор интерфейса языка программирования (PLI) Verilog
28. Зашифрованные оболочки
Закрыть