Verilog module basis: Structuur & syntaxis
Een module is een blok Verilog-code dat een bepaalde functionaliteit implementeert. Modules kunnen in andere modules genest worden; een hoger-niveau module communiceert met lagere modules via hun in- en uitgangspoorten.
Syntaxis
Een module moet omsloten zijn door module en endmodule sleutelwoorden. De modulenaam volgt direct na module en een optionele lijst met poorten kan gedeclareerd worden. Let op: poorten gedeclareerd in de poortlijst mogen niet opnieuw binnen de module-body gedeclareerd worden.
module ([poort_lijst]);
// Inhoud van de module
endmodule
// Een module kan een lege poortlijst hebben
module naam;
// Inhoud van de module
endmodule Alle variabele declaraties, dataflow-statements, functies, taken en lagere module-instanties moeten binnen module en endmodule gedefinieerd zijn. Er kunnen meerdere modules met verschillende namen in hetzelfde bestand staan, in willekeurige volgorde.
Voorbeeld
De module dff vertegenwoordigt een D-flipflop met drie invoerpoorten d, clk, rstn en één uitvoerpoort q. De inhoud beschrijft hoe een D-flipflop zich gedraagt voor verschillende invoercombinaties. Hier wordt invoer d altijd aan uitvoer q toegewezen op de positieve flank van clk als rstn hoog is, omdat het een active-low reset is.
// Module "dff" met 3 inputs en 1 output poort
module dff (
input d, input clk, input rstn, output reg q);
always @ (posedge clk) begin
if (!rstn) q <= 0;
else q <= d;
end
endmoduleHardwareschema
Deze module wordt tijdens synthetise omgezet in het volgende digitale circuit:
Let op: Je kunt geen code buiten een module schrijven!
Wat is het doel van een module?
Een module vertegenwoordigt een ontwerp-eenheid die bepaalde gedragskarakteristieken implementeert en tijdens synthetise omgezet wordt in een digitaal circuit. Elke combinatie van invoeren kan aan de module gegeven worden en levert een corresponderende uitvoer. Dit stelt dezelfde module in staat herbruikbaar te zijn om grotere modules te vormen die complexere hardware implementeren.
Bijvoorbeeld: de bovenstaande DFF kan aan elkaar geketend worden om een schuifregister te vormen.
module shift_reg (
input d, input clk, input rstn, output q);
wire [2:0] q_net;
dff u0 (.d(d), .clk(clk), .rstn(rstn), .q(q_net[0]));
dff u1 (.d(q_net[0]), .clk(clk), .rstn(rstn), .q(q_net[1]));
dff u2 (.d(q_net[1]), .clk(clk), .rstn(rstn), .q(q_net[2]));
dff u3 (.d(q_net[2]), .clk(clk), .rstn(rstn), .q(q));
endmoduleHardwareschema
De DFF-instanties zijn verbonden met draden zoals beschreven door de Verilog RTL-module.
In plaats van op te bouwen vanuit kleinere blokken, kan ook het omgekeerde gedaan worden. Overweeg de opsplitsing van een simpele GPU-engine in kleinere componenten, elk vertegenwoordigd als een module die een specifieke functionaliteit implementeert. De GPU-engine hieronder kan verdeeld worden in vijf sub-blokken. De bus-interface haalt data van buitenaf, een andere eenheid haalt instructies eruit. Verdere eenheden verwerken data van de vorige eenheid.
Elk sub-blok kan een module zijn met een set in- en uitgangssignalen voor communicatie, en verder verdeeld in fijner blokken.
Wat zijn top-level modules?
Een top-level module is een module die alle andere modules bevat. Een top-level module wordt niet geïnstantieerd in een andere module.
Bijvoorbeeld: ontwerpmodules worden normaal geïnstantieerd in top-level testbench-modules zodat simulatie kan draaien met invoerstimuli. De testbench wordt niet in een andere module geïnstantieerd omdat het alles encapsuleert en dus de top-level module is.
Design Top Level
De onderstaande ontwerpcode heeft een top-level module design omdat het alle andere sub-modules bevat. Submodules kunnen geneste submodules hebben zoals mod3 in mod1 en mod4 in mod2. Allemaal worden opgenomen in de top-level module wanneer mod1 en mod2 geïnstantieerd worden. Dit maakt het design compleet en is de top-level module voor het ontwerp.
//---------------------------------
// Ontwerpcode
//---------------------------------
module mod3 ( [poort_lijst] );
reg c; // Ontwerpcode
endmodule
module mod4 ( [poort_lijst] );
wire a; // Ontwerpcode
endmodule
module mod1 ( [poort_lijst] );
wire y;
mod3 mod_inst1 ( ... ); // Eerste instantie van module "mod3" met naam "mod_inst1"
mod3 mod_inst2 ( ... ); // Tweede instantie ook van module "mod3" met naam "mod_inst2"
endmodule
module mod2 ( [poort_lijst] );
mod4 mod_inst1 ( ... );
mod4 mod_inst2 ( ... );
endmodule
// Top-level module
module design ( [poort_lijst]);
wire _net;
mod1 mod_inst1 ( ... ); // bevat alle andere modules en sub-modules
mod2 mod_inst2 ( ... );
endmoduleTestbench Top Level
De testbench-module bevat stimuli om de functionaliteit van het ontwerp te controleren en wordt gebruikt voor functionele verificatie met simulatietools. Het ontwerp wordt geïnstantieerd en d0 genoemd binnen de testbench. Vanuit simulatorperspectief is de testbench de top-level module.
//-----------------------------------------------------------
// Testbench code
// Vanuit simulatieperspectief is dit de top-level module
// omdat 'design' geïnstantieerd is binnen deze module
//-----------------------------------------------------------
module testbench;
design d0 ( [poort_verbindingen] );
// Rest van testbench code
endmoduleHiërarchische namen
Een hiërarchische structuur ontstaat wanneer modules in elkaar geïnstantieerd worden; de top-level module heet de root. Omdat elke lagere module-instantie binnen een gegeven module een unieke identifiernaam moet hebben, is er geen ambiguïteit bij het benaderen van signalen. Een hiërarchische naam wordt opgebouwd uit een lijst van deze identifiers gescheiden door punten . voor elk hiërarchieniveau. Elk signaal kan vanuit elke module benaderd worden via het hiërarchische pad.
// Neem het voorbeeld van top-level modules
design.mod_inst1 // Toegang tot module-instantie mod_inst1
design.mod_inst1.y // Toegang tot signaal "y" binnen mod_inst1
design.mod_inst2.mod_inst2.a // Toegang tot signaal "a" binnen mod4-module
testbench.d0._net; // Top-level signaal _net binnen design-module benaderd vanuit testbench
Verilog
- Verilog beheersen: Complete tutorial voor digitale schakelingontwerp
- Verilog concatenatie beheersen: Een praktische gids
- Verilog Essentials: Een beknopt overzicht
- Verilog-toewijzingen begrijpen: types, syntax en best practices
- Verilog Blocking vs. Non-Blocking: belangrijkste verschillen uitgelegd
- Verilog-functies beheersen voor efficiënt RTL-ontwerp
- Verilog Tasks begrijpen: Functions vs Tasks, Syntax en Gebruik
- Verilog Hiërarchische Referentie Scope: Naamconflicten in Modules en Blokken vermijden
- Verilog Clock Generator: Ontwerp & Implementatie Gids
- Verilog Math Functions beheersen voor efficiënt Hardware-ontwerp
- Verilog $timeformat beheersen: Precisie en Weergave-controle