Сводка
Модуль — это иерархическая единица Verilog описания аппаратного обеспечения. Модуль объявляется в глобальном пространстве через ключевое слово «module» и заканчивается ключевым словом «endmodule«. В объявления модуля входит идентификатор, порты(необязательно) и параметры(необязательно). Объявление портов может содержать входные, выходные и двунаправленные порты. Объявление параметров может содержать параметры любого типа. Модуль может входить в другой модуль, но объявляется в качестве экземпляра. Различают 2 вида модулей:
- Модуль верхнего уровня — это модуль, который не входит в состав другого модуля проекта в качестве экземпляра. Т.е. имеет одно объявление в глобальной области и содержит в себе экземпляры других модулей, если таковы есть.
- Модуль низкого уровня — это модуль объявляемый в глобальном пространстве и в качестве экземпляра в другом модуле.
Объявления в глобальном пространстве модуля верхнего и низкого уровня ничем не отличаются.
Синтаксис
Объявления модуля в глобальном пространстве
Существует 4 вида объявления модуля Verilog(см. 12) в глобальном пространстве. Поэтому объявление параметров модуля может происходить в строке модуля, так и внутри блока модуля. Такая же ситуация обстоит и с портами. Если объявление портов происходит внутри блока, то строке модуля прописывается список портов.
Синтаксис объявления модуля outside в глобальном пространстве приведен в синтаксисе 1.
module #(/* объявление параметров */) identifier (/* объявление портов */);
// другие объявления внутри блока
endmodule
Синтаксис объявления модуля inside в глобальном пространстве приведен в синтаксисе 2.
module identifier(/* список портов */);
/* объявление параметров */
/* объявление портов*/
// другие объявления внутри блока
endmodule
Объявления экземпляра модуля
Экземпляр модуля — это объявление модуля внутри блока другого модуля. Объявление портов и параметров имеет два вида: простая и точечная нотация. Если используется простая нотация, необходимо присваивать цепи и регистры в той последовательности, в какой объявлены порты или параметры. Если используется точечная нотация, то присваивание можно объявлять в любом порядке.
Синтаксис экземпляра модуля с простой нотацией приведен в синтаксисе 3.
identifier_module #(/* значения параметра через запятую */)
identifier_module_local(/* список портов через запятую */);
identifier_module — это идентификатор модуля в глобальном пространстве, а identifier_module_local — это новый идентификатор модуля в локальном пространстве объявления.
Синтаксис экземпляра модуля с точечной нотацией приведен в синтаксисе 4.
identifier_module #(.PARAM_NAME(VALUE_PARAM), .PARAM_NAME(VALUE_PARAM), /* и тд */)
identifier_module_local (.PORT_NAME(WIRE_NAME), .PORT_NAME(WIRE_NAME), /* и тд */);
Примеры объявления модуля
Примеры модуля в глобальном пространстве
module TWISE_PORTS_RAM
#(
parameter BITS_DATA = 8, // кол-во битов в линии данных
parameter BITS_ADDR = 6 // кол-во битов в линии адреса
)
(
input wire [(BITS_DATA - 1): 0] data, // линия данных
input wire [(BITS_ADDR - 1): 0] addr_we, // линия адреса для записи
input wire [(BITS_ADDR - 1): 0] addr_re, // линия адреса для чтения
input wire we, // линия включения записи
input wire clk, // тактовый сигнал
output reg [(BITS_DATA - 1): 0] q // линия выхода
);
// регистр, использующий примитив FPGA
reg [(BITS_DATA - 1): 0] ram [((2**BITS_ADDR) - 1): 0];
always @(posedge clk) begin
// запись
if (we)
ram[addr_we] <= data;
// чтение
q <= ram[addr_re];
end
endmodule
В этом модуле реализован механизм простой двухпортовой однотактовой памяти(см. 4.9.3 Память). При приходе восходящего фронта в тактовом сигнале clk и высоком сигнале записи write, происходит запись в память по адресу addr_we. При такой же ситуации тактовым сигналом, то происходит чтение с памяти по адресу addr_re. RTL модель показана на рисунке 1.

module TWISE_PORTS_RAM(data, addr_we, addr_re, we, clk, q);
parameter BITS_DATA = 8; // кол-во битов в линии данных
parameter BITS_ADDR = 6; // кол-во битов в линии адреса
input wire [(BITS_DATA - 1): 0] data; // линия данных
input wire [(BITS_ADDR - 1): 0] addr_we; // линия адреса для записи
input wire [(BITS_ADDR - 1): 0] addr_re; // линия адреса для чтения
input wire we; // линия включения записи
input wire clk; // тактовый сигнал
output reg [(BITS_DATA - 1): 0] q; // линия выхода
// регистр, использующий примитив FPGA
reg [(BITS_DATA - 1): 0] ram [((2**BITS_ADDR) - 1): 0];
always @(posedge clk) begin
// запись
if (we)
ram[addr_we] <= data;
// чтение
q <= ram[addr_re];
end
endmodule
Примеры объявления экземпляра модуля
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 #(.delay(10), .size(15)) mod_a (.out(out_a), .in(in_a), .clk(clk));
// тоже самое в другом формате
// vdff #(15, 10) mod_a (out_a, in_a, clk);
endmodule
module vdff (out, in, clk);
parameter size=5, delay=1;
// Объявление внутри блока
endmodule