Grundlagen von Verilog (1) Grundlegende Syntax und Anmerkungen von Verilog

Grundwissen

 0.1 Modul (Modul)

        Ein Modul in Verilog kann als eine Blackbox mit Eingangs- und Ausgangsports betrachtet werden.Die Blackbox hat Eingangs- und Ausgangsschnittstellen (Signale), und eine bestimmteFunktion wird erreicht, indem bestimmte Operationen an der Eingabe in der Boxdurchgeführt werden. (ähnlich wie Funktionen in C-Sprache)

Abbildung 1 Schematische Darstellung des Moduls

0.1.1 Modulbeschreibung

Die in Abbildung 1 gezeigte Struktur des oberen Moduls (top_module) kann in Verilog wie folgt beschrieben werden:

module  top_module(
    input a,
    input b,
    output out
);

   ....... 

endmodule
  • Ein Modul beginnt mit module und endet mit endmodule
  • top_module ist der Modulname
  • input : ist der Eingangsport
  • Ausgang: ist der Ausgangsport
  • Der gesamte Code muss im Modul module stehen !

 In ähnlicher Weise kann die in Abbildung 1 gezeigte Untermodulstruktur (mod_a) in der Verilog-Sprache wie folgt beschrieben werden:

module  top_module(
    input in1,
    input in2,
    output out
);

   ....... 

endmodule

Hinweis : Jedes Modul sollte sich in einem separaten Block in einer .v-Datei befinden, und der Modulname ist der Dateiname (kanonischer Code!)

0.1.2 Eingangs- und Ausgangssignale des Moduls

  • Ausgabe: Ausgabe
  • Eingang: Eingang

Die Eingangs- und Ausgangsports des Moduls können das Signal des Moduls sehen. Wenn der Signaltyp nicht geschrieben ist, ist die Voreinstellung das Drahttypsignal !

// 以下这两个语句本质是一直的
input a;

input wire a;

Neben Drahtsignalen gibt es auch Reg-Signale, siehe Abschnitt 1.4 für Details !

0.1.3 Modul-Instanziierung

        Wie in Abbildung 1 gezeigt, sind die beiden Eingangsports des top_module mit den Eingangsports des Submoduls (mod_a) verbunden.Wie verwende ich die Funktion des mod_a-Moduls im top_module-Modul? Dies erfordert eine Instanziierung des Moduls. Top_module kann als Hauptfunktion in der Sprache C betrachtet werden, und das sekundäre Modul mod_a kann als gewöhnliche Funktion betrachtet werden, sodass andere Funktionen in der Hauptfunktion aufgerufen werden können, um die entsprechenden Funktionen zu vervollständigen!

Der Weg zum Instanziieren von mod_a in top_module ist:

Modulinstanzsyntax : Modulname Instanzname (definiert das mit dem Port verbundene Signal);

module  top_module(
    input a,
    input b,
    output out
);

	mod_a instance2 (.in1(a), .in2(b), .out(out));

endmodule
  • Instanziiere in Port-Reihenfolge definiert durch mod_a: mod_a instance1 (a, b, out);
  • Instanziieren nach mod_a Portname : mod_a instance2 (.in1(a), .in2(b), .out(out)); (dies wird empfohlen)

0.2 Logikblöcke (immer generieren)

0.2.1 immer Logikblock

        Der Always-Block kann kombinatorische Logikblöcke und sequentielle Logikblöcke bilden. Komplexe logische Operationen müssen in diesem Logikblock enthalten sein, wie if, case, for usw.

(1) Kombinationslogikblock

module top_module();

    always @(*) begin
        ....
    end

endmodule
  • Triggern Sie sofort, wenn sich ein Signal im Always-Logikblock ändert , führen Sie die Anweisung zwischen Beginn und Ende aus
  • begin - end wird verwendet, um mehrere Anweisungen zu einem Codeblock zusammenzufassen , und kann weggelassen werden, wenn es nur eine Anweisung gibt

(1) Sequentielle Logikschaltung

module top_module();

    always @(posedge clk) begin
        ....
    end

endmodule
  • Trigger auf ansteigende Flanke des clk -Signals
  • Posedge: steigende Flanke
  • Negede: fallende Flanke

0.2.2 Logikblock generieren

generate wird hauptsächlich in Verbindung mit der for-Schleife verwendet.

  • Wiederholen Sie die Operation an mehreren Bits in einem Vektor
  • Mehrere wiederholte Instanziierungen desselben Moduls (Hauptzweck)

(1) Operationsvektor

module top_module(input [7:0] in,  output [7:0] out);
    genvar i;        // genvar i; 也可以定义在generate内部
    generate
        for(i=0; i<8; i++) begin: bit
             assign out[i]=^in[8-1:i];
        end
    endgenerate
endmodule

(2) Das Modul wird mehrfach wiederholt instanziiert

module  top_module(
    input a,
    input b,
    output out
);
    genvar i;
    generate
        for(i=0; i<8; i++)  begin: gen_mod_a   //  gen_mod_a 为每个begin_end的结构的名称
            mod_a instance2 (.in1(a), .in2(b), .out(out));
        end
    endgenerate
endmodule
  • Hinweis: Wenn das Modul mehrmals instanziiert wird, muss der Name jeder begin_end-Struktur geschrieben werden (gen_mod_a).
  • Der Simulator identifiziert die generierte Struktur durch gen_mod_a: gen_mod_a[0], gen_mod_a[1]....

0.3 Zuordnungsmethode

Es gibt drei Zuweisungsmethoden in Verilog: fortlaufende Zuweisung, blockierende Zuweisung und nicht-blockierende Zuweisung

0.3.1 Kontinuierliche Zuweisung (Zuweisung)

assign x = y;
  • Diese Aussage bedeutet, die beiden Signale von x und y zu verbinden, die reale physikalische Verbindung !
  • kann nicht in Always-Blöcken verwendet werden

0.3.2 Sperrzuordnung (=)

// 组合块
always @(*)  begin
	out1 = a ;
    a = b ;
    out2 = a ;
end
  • Blockierende Zuweisung in einem kombinierten Always-Block
  • Ausführungsreihenfolge: Nacheinander gemäß der Reihenfolge im begin_end-Anweisungsblock ausführen Die obigen Ausgabeergebnisse sind: out1 = a, out2 = b

 0.3.3 Nicht blockierende Zuweisung (<=)

// 时序块
always @(posedge clk)  begin
	out1 <= a ;
    a <= b ;
    out2 <= a ;
end
  • Verwenden Sie nicht blockierende Zuweisungen in sequentiellen Always-Blöcken
  • Ausführungsreihenfolge: Alle Anweisungen in begin_end werden parallel ausgeführt , die obigen Ausgabeergebnisse sind: out1 = a, out2 = a


Kapitel 1 Grundlegende Grammatik

1.1 Identifikatoren

(1) Zweck: Bezeichner werden verwendet, um Konstanten, Variablen, Signale, Ports, Parameternamen, Modulnamen usw. zu definieren.

(2) Zusammensetzung: beliebige Kombination aus Buchstaben, Zahlen, $ und Unterstrichen

(3) Hinweise:

  • Groß-/Kleinschreibung (Verilog und Verilog sind unterschiedlich)
  • Das erste Zeichen darf nur ein Buchstabe oder ein Unterstrich sein (123demo ist eine illegale Kennung)

1.2 Logische Werte und logische Operationen

1.2.1 Logischer Wert

Es gibt 4 logische Werte in Verilog: 0, 1, x, z

  • 0: niedriger Pegel
  • 1: Hohes Niveau
  • x: bedeutet, dass der Zustand unbekannt ist
  • z: Zeigt den Hochwiderstandszustand an

Hinweis: Bei z und x wird hier nicht zwischen Groß- und Kleinschreibung unterschieden (X, Z können auch verwendet werden)

1.2.2 Logische Operationen

(1) Logische Operatoren: && (und), == (gleich), || (oder), != (ungleich)

  • Wie m&&n: beurteilen, ob m und n alle wahr sind (wenn nicht 0 wahr ist ), wenn wahr, 1'b1 ausgeben, andernfalls 1'b0 ausgeben (4'b1010&4'b0101 = 1'b1 )
  • Das endgültige Ausgabeergebnis ist nur 1 Bit

(2) Bitweise Operatoren: &, |, ~, ^, ~&, ~^, ~| 

  • Zum Beispiel m&n: Es soll die bitweise UND-Operation jedes Bits von m und jedes Bits von n ausführen (4'b1010&4'b0101 = 4'b0000 )
  • Das Ausgabeergebnis ist dasselbe wie die Anzahl von Bits von m/n

(3) Reduktionsoperatoren: &, |, ~, ^, &, ~^, ~| 

  • Wenn nur ein Parameter an der Operation beteiligt ist (& ist der unäre Operator), bedeutet dies die Reduktion und Operation, dh die interne Operation des Vektors
&a [3:0] // AND:a[3]&a[2]&a[1]&a [0]相当于(a[3:0]== 4'hf)
|b [3:0] // OR: b[3]|b[2]|b[1]|b [0]相当于(b[3:0]!= 4'h0)
^c [2:0] // XOR:c[2]^c[1]^c[0]
  • 即(&4'b0101 = 0&1&0&1 = 1'b0  )
  • Das endgültige Ausgabeergebnis ist nur 1 Bit

1.3 Darstellung von Konstanten

Ähnlich wie in der Sprache C gibt es drei Arten von Konstanten: Integer, Real und String.

1.3.1 Ganzzahlkonstanten als Dezimalzahlen darstellen

(1) Positive Zahl: 10 direkt schreiben, um eine Dezimalzahl mit einer Bitbreite von 32 Bit darzustellen (Systemstandard)

(2) Negative Zahlen: -10 muss im Zweierkomplement dargestellt werden, mit einem weiteren Vorzeichenbit ( 1 1010)

(3) In wissenschaftlicher Schreibweise ausdrücken: 12.345e3 bedeutet 12345

1.3.2 Darstellung ganzzahliger Konstanten in Radix

[换算成二进制数后的位宽]'[数制符号][与数制对应的值]

(1) Binär (b): 8'b1000_1100      

(2) Hexadezimal (h): 8'h8c

(3) Oktal (o): 8'o214

(4) Dezimal (d): 8'140

Vorsichtsmaßnahmen:

  • Bei der Darstellung im Binärformat ist es am besten alle 4 Bit einen Unterstrich zu schreiben, um die Lesbarkeit zu verbessern: z.B. 8'b1000_1100 ist dasselbe wie 8'b10001100
  • Wenn x in Radix -Notation angetroffen wird: 4 x hexadezimal, 3 x oktal  
  • Wenn die Bitbreite größer als die Binärzahl ist, wird die linke Seite automatisch mit 0 aufgefüllt, und wenn die Bitbreite kleiner als die Binärzahl ist, wird 2 von links abgeschnitten !

1.3.3 Strings (mit doppelten Anführungszeichen)

(1) Jedes Zeichen wird durch einen 8-Bit-ASCII-Codewert dargestellt, d. h. es wird 1 Byte Speicherplatz benötigt

(2) Beispiel: Die Zeichenkette „Hallo Welt“   besteht aus 11 ASCII-Symbolen und benötigt 11 Byte Speicherplatz

1.3 Annotationsverfahren

Es gibt zwei Haupttypen von Kommentaren in Verilog: Zeilenkommentare (//) und Blockkommentare (/* .... */) Die Darstellungsmethode ist konsistent mit der Sprache C!

// 行注释

/*
        块注释

*/

1.4 Variablen (wire, reg)

Es gibt zwei Haupttypen von Variablen in Verilog: Wire und Reg

1.4.1 Draht

(1) Drahttyp (Draht): Zeigt die physikalische Verbindung zwischen Schaltungen an, und die durch Draht definierten Variablen können auch als Signalports betrachtet werden

(2) Wenn zwei Drahtsignale nacheinander zugewiesen werden, werden sie auf reale physikalische Verbindungen im Logikblock abgebildet. Zu diesem Zeitpunkt werden die Änderungen dieser beiden Signalports synchronisiert !

wire a;

wire b;

assign b = a;    // 表示a与b之间生成实际的物理连线

1.4.2 Reg.-Nr

(1) Registertyp (reg): Repräsentiert eine abstrakte Datenspeichereinheit

(2) reg hat die Funktion, den Zustand zu einem bestimmten Zeitpunkt aufrechtzuerhalten

1.4.3 Verwendung und Vorsichtsmaßnahmen

(1) Die in den Always- und Initial- Anweisungen zugewiesenen Variablen (die Variablen links von der Zuweisungsnummer ) sind alle Reg- Variablen

(2) Die in der Zuweisungsanweisung zugewiesene Variable ist eine Drahttypvariable

1.5 Vektoren und Parameter (Konstanten)

1.5.1 Parameter Parameter (Konstante)

(1) Parameter ist eine Art Konstante, die normalerweise innerhalb des Moduls erscheint und häufig verwendet wird, um den Zustand, die Datenbitbreite usw. zu definieren.

parameter STATE = 1'b0;

(2) Funktioniert nur auf der deklarierten Datei und kann flexibel geändert werden !

(3) Lokaler Parameter localparam, wird nur in diesem Modul verwendet

localparam  STATE= 1'b1’;

(4) Der Name des Parameters wird im Allgemeinen in Großbuchstaben geschrieben , um andere Variablen zu unterscheiden 

1.5.2 Vektor (Vektor)

Vektor (Vektor) ist eine Sammlung einer Gruppe von Signalen , die als Drahtsignal mit einer Bitbreite von mehr als 1 Bit angesehen werden kann.

(1) Definition:

格式:input/output  wire/reg [upper:lower] vector_name

//输入输出型
input [7:0] a,b,
output reg [7:0] out

// 模块中间向量
wire [7:0] c, e;
reg [7:0] d;
  • [upper:lower] definiert die Bitbreite, z. B. [7:0] bedeutet, dass die Bitbreite 8 Bit beträgt, d. h. obere=7, untere=0
  • vector_name kann mehrere Vektoren gleichzeitig schreiben

1.5.3 Vektorchip-Auswahl

  • a[3:0] 0~4-Bit-Daten des Vektors a
  • b[n] n-te Bitdaten des Vektors b
  • c[-1:-2] Die niedrigsten 2 Bits des Vektors c
  • c[0:3] Die höchsten 4-Bit- Daten des Vektors c

Multiplexer-Anwendung: Implementieren Sie einen 256-zu-1-Selektor, das sel-Signal wird als Auswahlsignal verwendet, wenn sel = 0, wählen Sie in[3:0], wenn sel = 1, wählen Sie in[7:4] und so weiter An.

module top_module (
	input [1023:0] in,
	input [7:0] sel,
	output [3:0] out
);
	assign out = {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]};

	// assign out = in[sel*4 +: 4];		
	// assign out = in[sel*4+3 -: 4];	
endmodule
  • Der Eingang des Chipauswahlsignals sel ist eine n-Bit-Binärzahl, die automatisch in eine Dezimalzahl umgewandelt wird, wenn sie an der Operation teilnimmt und als Index dient.
  • Das für diese Frage ausgewählte Signalsegment ist: in[sel*4+3: sel*4] , aber dies entspricht nicht den Syntaxregeln für die Chipauswahl von Verilog, daher sollte es wie folgt geschrieben werden:
    in[sel*4 +: 4]   bedeutet, dass der Index bei sel* beginnt. Das hohe 4-Bit-Signal 4
    in[sel*4+3 –: 4] gibt das niedrige 4-Bit-Signal  beginnend bei sel*4+3 an
  • Oder wählen Sie direkt jedes benötigte Bit aus und verwenden Sie dann { }, um es zu einem neuen Vektor zu verketten:
    {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[ sel*4+0]}

Referenzartikel: HDLBits: Learning Verilog Online (Thirteen · Problem 60-64) – Knowing (zhihu.com)

1.6 Ternäre Ausdrücke

(1) Wie die Sprache C hat auch Verilog ternäre Ausdrücke :

condition ? if_true : if_false

Wenn die Bedingung wahr ist, wird der Ausdruck zu if_true ausgewertet, andernfalls wird der Ausdruck zu if_false ausgewertet.

(2) Bewerbung

(sel ? b : a)   // 一个二选一MUX,通过sel的值选择a或者b

always @(posedge clk)         // 一个T触发器
  q <= toggle ? ~q : q;

assign out = ena ? q : 1'bz;  // 三态缓冲器

(3) Referenzartikel:  HDLBits: Learning Verilog Online (8. Problem 35-39) – Zhihu (zhihu.com)

1.7 Verzweigungsanweisungen (if-else, case)

1.7.1 if-else-Anweisung

(1) Die gebräuchlichste Form : (Vorteil: alle möglichen Ausgaben werden geschrieben, es gibt keine unbekannte Pegelausgabe !)

if(<条件表达式 1>)
    语句或语句块 1;
else if(<条件表达式 2>)
    语句或语句块 2;
    ………
else
    语句或语句块 n;

(2) Es wird nicht empfohlen, if-else-Verschachtelung zu verwenden, da es zu Prioritätsproblemen kommt, die zu logischer Verwirrung führen,

(3)  Alle if-else-Anweisungen sollten in Form von (1) geschrieben werden !

(4) Vergleichen Sie sequentiell gemäß dem Bedingungsausdruck, es gibt eine Priorität !

1.7.2 Fallerklärung

(1) Schriftform:

case(<控制表达式>)
    <分支语句 1> : 语句块 1;
    <分支语句 2> : 语句块 2;
    <分支语句 3> : 语句块 3;
    ………
    <分支语句 n> : 语句块 n;

    default : 语句块 n+1;
endcase

Sind die Werte von <control expression> und <branch statement n> gleich, wird das entsprechende Statement ausgeführt, ansonsten wird das Statement after default ausgeführt!

(2) Aus der case-Anweisungsstruktur sofort nach Ausführung einer Verzweigungsanweisung springen und die Ausführung der case-Anweisung beenden.

(3)  Die Werte von <branch statement n> müssen sich voneinander unterscheiden !

(4) Beenden Sie den case-Anweisungsblock mit encase

(5) Es gibt keine Priorität zwischen den Verzweigungsanweisungen !

(6) Spezifische Anwendung: Anwendungsfall-Anweisung zum Aufbau eines Multiplexers (als Beispiel einen 9-zu-1-Multiplexer nehmen)

module top_module( 
    input [15:0] a, b, c, d, e, f, g, h, i,
    input [3:0] sel,
    output [15:0] out );
    always @(*) begin
        case(sel)
            4'h0:begin out = a; end
            4'h1:begin out = b; end
            4'h2:begin out = c; end
            4'h3:begin out = d; end
            4'h4:begin out = e; end
            4'h5:begin out = f; end
            4'h6:begin out = g; end
            4'h7:begin out = h; end
            4'h8:begin out = i; end
            default: out = 16'hffff;
        endcase
    end
endmodule

1.8 for-Schleife-Anweisung

(1) Schriftform:

integer i;
always @(*)  begin 
    for(i=0; i<n; i++)  begin: for_name
        <循环语句>
    end
end
  • Führen Sie <loop statement> n mal aus
  • for_name ist der Name jeder Schleife

1.9 Vergleichsoperatoren (>, <, >=, <=)

  • Gibt 1 zurück, wenn das Ergebnis der Operation wahr ist
  • Gibt 0 zurück, wenn das Ergebnis der Operation falsch ist
  • Wenn ein Operandenwert unbestimmt (x) ist, ist der Rückgabewert x

2.0 Verkettungsoperator ({ , })

2.0.1 Spleißen

Verwenden Sie ein Paar geschweifte Klammern und Kommas, um den Verkettungsoperator " { , } " zu bilden, und die durch Kommas getrennten Daten werden nacheinander zu neuen Daten verkettet !

wire [1:0] a;
wire [3:0] b;
wire [5:0] c;
wire [11:0] d = {a, b, c} 

2.0.2 Verschiebung durch Spleißen

Links spleißen, um sich nach rechts zu bewegen, und rechts spleißen, um sich nach links zu bewegen!

always @(posedge clk) begin
    if(rst_n == 1'b0)
        out <= 4'b0;
    else
        out <= {in, out[3:1]};    // 右移
end

2.0.2 Wiederholte Operationen in Konnektoren

Syntax:  {Anzahl der Wiederholungen{Vektor}}

{3{a}} = {a, a, a}
{3'd5, {2{3'd6}}}   // 9'b101_110_110.

3 binärer Volladdierer

  • a, b sind eingegebene 1-Bit-Daten
  • cin ist der Übertragseingang des vorherigen Addierers
  • cout ist der Übertragsausgang dieses Addierers
  • Summe = a+b

Code:

module add1 (
	input a,
	input b,
	input cin,	
	output sum,
	output cout
);
	assign sum = a^b^cin;
	assign cout = (a&b) | (a&cin) | (b&cin);
endmodule

4 hexadezimaler Volladdierer

In der obigen Abbildung ist der hexadezimale Volladdierer dargestellt, der im vorherigen Abschnitt aus 16 binären Volladdierern zusammengesetzt werden kann.

Der in Verilog implementierte hexadezimale Volladdierercode lautet:

module add16 (	
	input [15:0] a,
	input [15:0] b,
	input cin,
	
	output [15:0] sum,
	output cout
);
	wire [16:0] Add_cin;
	assign Add_cin[0] = cin;    // 上图中第一个二进制加法器进位输入为0 assign Add_cin[0] = 1b'0;

//  用 generate 进行模块多次实例化
// generate 应用范围:对矢量(vector)多个位重复操作,模块重复实例化
	genvar i;
	generate
		for(i=0; i<16; i++) begin: gen_add16		// gen_add16 为每个begin_end的结构,仿真器会通过他来标识生成结构,gen_add16[0],gen_add16[1]....
			add1 Add16(.a(a[i]), .b(b[i]), .cin(Add_cin[i]), .sum(sum[i]), .cout(Add_cin[i+1]));
		end
	
	endgenerate
	
	assign cout = Add_cin[16];

endmodule

5 Parameterübergabe in Modulen

5.1 Definieren eines Moduls, das Parameter übergeben kann

module counter
// 参数传递
#(
    parameter COUNT_MAX = 25'd24_999_999,
    parameter STATE     = 1'b0            // 多个参数用逗号隔开
)
(
    input  wire  sys_clk,
    output reg led_out
);
// 代码主体
endmodule

5.2 Instanziierung eines Blocks mit Parametern

// 参数传递
#(
   .COUNT_NUM( 25'd24_999_999),        // 传入参数
   .STATE(1'b0)
)
counter1_init      // 实例化模块的名称位置
(
    .sys_clk   (sys_clk),
    .led_out(led_out)
); 

 

Verweise:

[1] Wildfire „FPGA Verilog Development Practical Guide“: [Wildfire] FPGA Verilog Development Practical Guide – Basierend auf dem Altera EP4CE10 Journey Pro Development Board – [Wildfire] FPGA Verilog Development Practical Guide – Basierend auf der Altera EP4CE10 Journey Pro Development Board Documentation (embedfire .com) https://doc.embedfire.com/fpga/altera/ep4ce10_pro/en/latest/index.html

[2] HDLBits Chinese guide: HDLBits Chinese guide-zhihu (zhihu.com)

Supongo que te gusta

Origin blog.csdn.net/jac_chao/article/details/123744724
Recomendado
Clasificación