Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> Verilog

Verilog generiert Block

Ein generate block ermöglicht es, Modulinstanzen zu multiplizieren oder eine bedingte Instanziierung eines beliebigen Moduls durchzuführen. Es bietet die Möglichkeit, das Design basierend auf Verilog-Parametern zu erstellen. Diese Anweisungen sind besonders praktisch, wenn die gleiche Operation oder Modulinstanz mehrere Male wiederholt werden muss oder wenn bestimmter Code basierend auf gegebenen Verilog-Parametern bedingt eingeschlossen werden muss.

A generate Block darf nicht Port, Parameter, specparam enthalten Deklarationen oder specify Blöcke. Andere Modulelemente und andere Generierungsblöcke sind jedoch zulässig. Alle generierten Instanziierungen werden innerhalb eines module codiert und zwischen den Schlüsselwörtern generate und endgenerate .

Generierte Instanziierungen können entweder Module, fortlaufende Zuweisungen, always haben oder initial Blöcke und benutzerdefinierte Primitive. Es gibt zwei Arten von generierten Konstrukten - Schleifen und Bedingungen.

For-Schleife erzeugen

Ein Halbaddierer wird N-mal in einem anderen Top-Level-Designmodul namens my_design unter Verwendung eines generate instanziiert für Schleifenkonstrukt. Die Schleifenvariable muss mit dem Schlüsselwort genvar deklariert werden die dem Tool mitteilt, dass diese Variable speziell während der Ausarbeitung des Generierungsblocks verwendet werden soll.

  
  
// Design for a half-adder
module ha ( input   a, b,
            output  sum, cout);
 
  assign sum  = a ^ b;
  assign cout = a & b;
endmodule

// A top level design that contains N instances of half adder
module my_design 
	#(parameter N=4) 
		(	input [N-1:0] a, b,
			output [N-1:0] sum, cout);
			
	// Declare a temporary loop variable to be used during
	// generation and won't be available during simulation
	genvar i;
	
	// Generate for loop to instantiate N times
	generate 
		for (i = 0; i < N; i = i + 1) begin
          ha u0 (a[i], b[i], sum[i], cout[i]);
		end
	endgenerate
endmodule

  

Testbench

Der Testbench-Parameter wird verwendet, um die Anzahl der Halbaddiererinstanzen im Design zu steuern. Wenn N gleich 2 ist, hat my_design zwei Instanzen des Halbaddierers.

  
  
module tb;
	parameter N = 2;
  reg  [N-1:0] a, b;
  wire [N-1:0] sum, cout;
  
  // Instantiate top level design with N=2 so that it will have 2
  // separate instances of half adders and both are given two separate
  // inputs
  my_design #(.N(N)) md( .a(a), .b(b), .sum(sum), .cout(cout));
  
  initial begin
    a <= 0;
    b <= 0;
    
    $monitor ("a=0x%0h b=0x%0h sum=0x%0h cout=0x%0h", a, b, sum, cout);
    
    #10 a <= 'h2;
    		b <= 'h3;
    #20 b <= 'h4;
    #10 a <= 'h5;
  end
endmodule

  

a[0] und b[0] ergeben die Ausgabe sum[0] und cout[0], während a[1] und b[1] die Ausgabe sum[1] und cout[1] liefern.

Simulationsprotokoll
ncsim> run
a=0x0 b=0x0 sum=0x0 cout=0x0
a=0x2 b=0x3 sum=0x1 cout=0x2
a=0x2 b=0x0 sum=0x2 cout=0x0
a=0x1 b=0x0 sum=0x1 cout=0x0
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

Beachten Sie, dass die ausgearbeitete RTL tatsächlich zwei Halbaddierer-Instanzen hat, die von generate generiert werden blockieren.

Erzeuge wenn

Unten sehen Sie ein Beispiel mit if else innerhalb eines generate Konstrukt, um zwischen zwei verschiedenen Multiplexer-Implementierungen auszuwählen. Das erste Design verwendet einen assign -Anweisung zum Implementieren eines Mux, während das zweite Design einen case verwendet Aussage. Ein Parameter namens USE_CASE ist im Designmodul der obersten Ebene definiert, um zwischen den beiden Möglichkeiten auszuwählen.

  
  
// Design #1: Multiplexer design uses an "assign" statement to assign 
// out signal 
module mux_assign ( input a, b, sel,
                   output out);
  assign out = sel ? a : b;
  
  // The initial display statement is used so that 
  // we know which design got instantiated from simulation
  // logs  
  initial
  	$display ("mux_assign is instantiated");
endmodule

// Design #2: Multiplexer design uses a "case" statement to drive
// out signal
module mux_case (input a, b, sel,
                 output reg out);
  always @ (a or b or sel) begin
  	case (sel)
    	0 : out = a;
   	 	1 : out = b;
  	endcase
  end
  
  // The initial display statement is used so that 
  // we know which design got instantiated from simulation
  // logs
  initial 
    $display ("mux_case is instantiated");
endmodule

// Top Level Design: Use a parameter to choose either one
module my_design (	input a, b, sel,
         			output out);
  parameter USE_CASE = 0;
  
  // Use a "generate" block to instantiate either mux_case
  // or mux_assign using an if else construct with generate
  generate
  	if (USE_CASE) 
      mux_case mc (.a(a), .b(b), .sel(sel), .out(out));
    else
      mux_assign ma (.a(a), .b(b), .sel(sel), .out(out));
  endgenerate
    
endmodule

  

Testbench

Testbench instanziiert das Top-Level-Modul my_design und setzt den Parameter USE_CASE auf 1, sodass es das Design mit case instanziiert Aussage.

  
  
module tb;
	// Declare testbench variables
  reg a, b, sel;
  wire out;
  integer i;
  
  // Instantiate top level design and set USE_CASE parameter to 1 so that
  // the design using case statement is instantiated
  my_design #(.USE_CASE(1)) u0 ( .a(a), .b(b), .sel(sel), .out(out));
  
  initial begin
  	// Initialize testbench variables
  	a <= 0;
    b <= 0;
    sel <= 0;
    
    // Assign random values to DUT inputs with some delay
    for (i = 0; i < 5; i = i + 1) begin
      #10 a <= $random;
      	  b <= $random;
          sel <= $random;
      $display ("i=%0d a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out);
    end
  end
endmodule

  

Wenn der Parameter USE_CASE 1 ist, ist aus dem Simulationsprotokoll ersichtlich, dass das Multiplexer-Design mit case Anweisung wird instanziiert. Und wenn USE_CASE null ist, verwendet das Multiplexer-Design assign Anweisung wird instanziiert. Dies ist aus der Display-Anweisung ersichtlich, die im Simulationsprotokoll gedruckt wird.

Simulationsprotokoll
// When USE_CASE = 1
ncsim> run
mux_case is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
i=1 a=0x0 b=0x1 sel=0x1 out=0x1
i=2 a=0x1 b=0x1 sel=0x1 out=0x1
i=3 a=0x1 b=0x0 sel=0x1 out=0x0
i=4 a=0x1 b=0x0 sel=0x1 out=0x0
ncsim: *W,RNQUIE: Simulation is complete.

// When USE_CASE = 0
ncsim> run
mux_assign is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
i=1 a=0x0 b=0x1 sel=0x1 out=0x0
i=2 a=0x1 b=0x1 sel=0x1 out=0x1
i=3 a=0x1 b=0x0 sel=0x1 out=0x1
i=4 a=0x1 b=0x0 sel=0x1 out=0x1
ncsim: *W,RNQUIE: Simulation is complete.

Fall generieren

Ein generierter Fall ermöglicht es, Module, Anfangs- und Always-Blöcke in einem anderen Modul basierend auf einem case zu instanziieren Ausdruck, um eine der vielen Möglichkeiten auszuwählen.

  
  
// Design #1: Half adder
module ha (input a, b,
           output reg sum, cout);
  always @ (a or b)
  {cout, sum} = a + b;
  
  initial
    $display ("Half adder instantiation");
endmodule

// Design #2: Full adder
module fa (input a, b, cin,
           output reg sum, cout);
  always @ (a or b or cin)
  {cout, sum} = a + b + cin;
  
    initial
      $display ("Full adder instantiation");
endmodule

// Top level design: Choose between half adder and full adder
module my_adder (input a, b, cin,
                 output sum, cout);
  parameter ADDER_TYPE = 1;
  
  generate
    case(ADDER_TYPE)
      0 : ha u0 (.a(a), .b(b), .sum(sum), .cout(cout));
      1 : fa u1 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
    endcase
  endgenerate
endmodule

  

Testbench

  
  
module tb;
  reg a, b, cin;
  wire sum, cout;
  
  my_adder #(.ADDER_TYPE(0)) u0 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
  
  initial begin
    a <= 0;
    b <= 0;
    cin <= 0;
    
    $monitor("a=0x%0h b=0x%0h cin=0x%0h cout=0%0h sum=0x%0h",
             a, b, cin, cout, sum);
    
    for (int i = 0; i < 5; i = i + 1) begin
      #10 a <= $random;
      b <= $random;
      cin <= $random;
    end
  end
endmodule

  

Beachten Sie, dass cin keinen Einfluss auf die Ausgänge sum und cout hat, da ein Halbaddierer instanziiert wird.

Simulationsprotokoll
ncsim> run
Half adder instantiation
a=0x0 b=0x0 cin=0x0 cout=00 sum=0x0
a=0x0 b=0x1 cin=0x1 cout=00 sum=0x1
a=0x1 b=0x1 cin=0x1 cout=01 sum=0x0
a=0x1 b=0x0 cin=0x1 cout=00 sum=0x1
ncsim: *W,RNQUIE: Simulation is complete.


Verilog

  1. Verilog-Tutorial
  2. Verilog-Verkettung
  3. Verilog - In einer Nussschale
  4. Verilog-Aufgaben
  5. Verilog-Blockierung und Nicht-Blockierung
  6. Verilog-Funktionen
  7. Verilog-Aufgabe
  8. Verilog-Taktgenerator
  9. Verilog Math-Funktionen
  10. Verilog-Zeitformat