Combinatorische logica implementeren met Verilog's always-blok
Het Verilog always blok kan gebruikt worden voor zowel sequentiële als combinatorische logica. Eerder werden ontwerpvoorbeelden getoond met een assign statement. Dezelfde ontwerpen worden nu verkend met een always blok.
Voorbeeld #1: Eenvoudige combinatorische logica
De onderstaande code implementeert een simpele digitale combinatorische logica met een uitvoersignaal z van type reg dat wordt bijgewerkt wanneer een van de signalen in de gevoeligheidslijst zijn waarde verandert. De gevoeligheidslijst wordt gedeclareerd binnen haakjes na de @ operator.
module combo ( input a, b, c, d, e, output reg z);
always @ ( a or b or c or d or e) begin
z = ((a & b) | (c ^ d) & ~e);
end
endmoduleDe module combo wordt geëlaboreerd in het volgende hardwareschema met synthetise-tools, waar de combinatorische logica geïmplementeerd is met digitale gates.
Gebruik blokkerende toewijzingen bij het modelleren van combinatorische logica met een always-blok
Testbench
De testbench simuleert het ontwerp om te verifiëren dat het zich gedraagt zoals verwacht. Alle invoercombinaties worden aangedreven met een for lus met een vertraging van 10 tijdseenheden.
module tb;
reg a, b, c, d, e; wire z; integer i;
combo u0 ( .a(a), .b(b), .c(c), .d(d), .e(e), .z(z));
initial begin
a <= 0; b <= 0; c <= 0; d <= 0; e <= 0;
$monitor ("a=%0b b=%0b c=%0b d=%0b e=%0b z=%0b", a, b, c, d, e, z);
for (i = 0; i < 32; i = i + 1) begin {a, b, c, d, e} = i; #10; end
end
endmodule Simulation Log ncsim> run a=0 b=0 c=0 d=0 e=0 z=0 a=0 b=0 c=0 d=0 e=1 z=0 a=0 b=0 c=0 d=1 e=0 z=1 a=0 b=0 c=0 d=1 e=1 z=0 a=0 b=0 c=1 d=0 e=0 z=1 a=0 b=0 c=1 d=0 e=1 z=0 a=0 b=0 c=1 d=1 e=0 z=0 a=0 b=0 c=1 d=1 e=1 z=0 a=0 b=1 c=0 d=0 e=0 z=0 a=0 b=1 c=0 d=0 e=1 z=0 a=0 b=1 c=0 d=1 e=0 z=1 a=0 b=1 c=0 d=1 e=1 z=0 a=0 b=1 c=1 d=0 e=0 z=1 a=0 b=1 c=1 d=0 e=1 z=0 a=0 b=1 c=1 d=1 e=0 z=0 a=0 b=1 c=1 d=1 e=1 z=0 a=1 b=0 c=0 d=0 e=0 z=0 a=1 b=0 c=0 d=0 e=1 z=0 a=1 b=0 c=0 d=1 e=0 z=1 a=1 b=0 c=0 d=1 e=1 z=0 a=1 b=0 c=1 d=0 e=0 z=1 a=1 b=0 c=1 d=0 e=1 z=0 a=1 b=0 c=1 d=1 e=0 z=0 a=1 b=0 c=1 d=1 e=1 z=0 a=1 b=1 c=0 d=0 e=0 z=1 a=1 b=1 c=0 d=0 e=1 z=1 a=1 b=1 c=0 d=1 e=0 z=1 a=1 b=1 c=0 d=1 e=1 z=1 a=1 b=1 c=1 d=0 e=0 z=1 a=1 b=1 c=1 d=0 e=1 z=1 a=1 b=1 c=1 d=1 e=0 z=1 a=1 b=1 c=1 d=1 e=1 z=1 ncsim: *W,RNQUIE: Simulation is complete.
Let op: zowel assign als always methodes worden geïmplementeerd in dezelfde hardwarelogica.
Voorbeeld #2: Half Adder
De half adder module accepteert twee scalaire invoeren a en b en gebruikt combinatorische logica om de uitgangen sum en carry-bit cout toe te wijzen. De som wordt gedreven door een XOR tussen a en b, de carry-bit door een AND.
module ha ( input a, b, output sum, cout);
always @ (a or b) begin
{cout, sum} = a + b;
end
endmodule Testbench
module tb;
reg a, b; wire sum, cout; integer i;
ha u0 ( .a(a), .b(b), .sum(sum), .cout(cout));
initial begin
a <= 0; b <= 0;
$monitor("a=%0b b=%0b sum=%0b cout=%0b", a, b, sum, cout);
for (i = 0; i < 4; i = i + 1) begin {a, b} = i; #10; end
end
endmodule Simulation Log ncsim> run a=0 b=0 sum=0 cout=0 a=0 b=1 sum=1 cout=0 a=1 b=0 sum=1 cout=0 a=1 b=1 sum=0 cout=1 ncsim: *W,RNQUIE: Simulation is complete.
Voorbeeld #3: Full Adder
Een always-blok kan het gedrag van een full adder beschrijven om de uitgangen sum en cout te drijven.
module fa ( input a, b, cin, output reg sum, cout);
always @ (a or b or cin) begin
{cout, sum} = a + b + cin;
end
endmodule Testbench
module tb;
reg a, b, cin; wire sum, cout; integer i;
fa u0 ( .a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
initial begin
a <= 0; b <= 0;
$monitor("a=%0b b=%0b cin=%0b cout=%0b sum=%0b", a, b, cin, cout, sum);
for (i = 0; i < 8; i = i + 1) begin {a, b, cin} = i; #10; end
end
endmodule Simulation Log ncsim> run a=0 b=0 cin=0 cout=0 sum=0 a=0 b=0 cin=1 cout=0 sum=1 a=0 b=1 cin=0 cout=0 sum=1 a=0 b=1 cin=1 cout=1 sum=0 a=1 b=0 cin=0 cout=0 sum=1 a=1 b=0 cin=1 cout=1 sum=0 a=1 b=1 cin=0 cout=1 sum=0 a=1 b=1 cin=1 cout=1 sum=1 ncsim: *W,RNQUIE: Simulation is complete.
Voorbeeld #4: 2x1 Multiplexer
De simpele 2x1 multiplexer gebruikt een ternaire operator om te beslissen welke invoer aan de uitvoer c toegewezen wordt. Als sel 1 is, wordt uitvoer gedreven door a, anders door b.
module mux_2x1 (input a, b, sel, output reg c);
always @ ( a or b or sel) begin
c = sel ? a : b;
end
endmodule Testbench
module tb;
reg a, b, sel; wire c; integer i;
mux_2x1 u0 ( .a(a), .b(b), .sel(sel), .c(c));
initial begin
a <= 0; b <= 0; sel <= 0;
$monitor("a=%0b b=%0b sel=%0b c=%0b", a, b, sel, c);
for (i = 0; i < 3; i = i + 1) begin {a, b, sel} = i; #10; end
end
endmodule Simulation Log ncsim> run a=0 b=0 sel=0 c=0 a=0 b=0 sel=1 c=0 a=0 b=1 sel=0 c=1 ncsim: *W,RNQUIE: Simulation is complete.
Voorbeeld #5: 1x4 Demultiplexer
De demultiplexer gebruikt een combinatie van sel en f om de verschillende uitvoersignalen te drijven. Elk uitvoersignaal is van type reg en gebruikt een always blok dat wordt bijgewerkt op basis van veranderingen in de signalen in de gevoeligheidslijst.
module demux_1x4 ( input f, input [1:0] sel, output reg a, b, c, d);
always @ ( f or sel) begin
a = f & ~sel[1] & ~sel[0];
b = f & sel[1] & ~sel[0];
c = f & ~sel[1] & sel[0];
d = f & sel[1] & sel[0];
end
endmodule Testbench
module tb;
reg f; reg [1:0] sel; wire a, b, c, d; integer i;
demux_1x4 u0 ( .f(f), .sel(sel), .a(a), .b(b), .c(c), .d(d));
initial begin
f <= 0; sel <= 0;
$monitor("f=%0b sel=%0b a=%0b b=%0b c=%0b d=%0b", f, sel, a, b, c, d);
for (i = 0; i < 8; i = i + 1) begin {f, sel} = i; #10; end
end
endmodule Simulation Log ncsim> run f=0 sel=0 a=0 b=0 c=0 d=0 f=0 sel=1 a=0 b=0 c=0 d=0 f=0 sel=10 a=0 b=0 c=0 d=0 f=0 sel=11 a=0 b=0 c=0 d=0 f=1 sel=0 a=1 b=0 c=0 d=0 f=1 sel=1 a=0 b=0 c=1 d=0 f=1 sel=10 a=0 b=1 c=0 d=0 f=1 sel=11 a=0 b=0 c=0 d=1 ncsim: *W,RNQUIE: Simulation is complete.
Voorbeeld #6: 4x16 Decoder
module dec_3x8 ( input en, input [3:0] in, output reg [15:0] out);
always @ (en or in) begin
out = en ? 1 << in: 0;
end
endmodule Testbench
module tb;
reg en; reg [3:0] in; wire [15:0] out; integer i;
dec_3x8 u0 ( .en(en), .in(in), .out(out));
initial begin
en <= 0; in <= 0;
$monitor("en=%0b in=0x%0h out=0x%0h", en, in, out);
for (i = 0; i < 32; i = i + 1) begin {en, in} = i; #10; end
end
endmodule Simulation Log ncsim> run en=0 in=0x0 out=0x0 en=0 in=0x1 out=0x0 ... en=1 in=0xf out=0x8000 ncsim: *W,RNQUIE: Simulation is complete.
Verilog
- Accurate temperatuurbewaking in een serverkast met Raspberry Pi
- Circuit met een schakelaar: Praktische gids voor basis elektrische circuits
- Geïntegreerde Schakelingen: Fundamenten van Digitale Logische Poorten
- Programmeerbare Logische Controllers (PLC's): Moderne Digitale Besturing voor Industriële Systemen
- Booleaanse Algebra Begrijpen: Van Logica Fundamenten tot Digitale Schakelingen
- Vermenigvuldigen en delen met wetenschappelijke notatie beheersen
- Industry 4.0 Inzichten: Q&A met Verena Majuntke van Bosch.IO over IoT, Automatisering en de Toekomstfabriek
- Perfecte afwerkingen behalen met Okamoto's precisieslijpmachines
- VHDL & Verilog Meesteren: Schone Combinatoire en Sequentiele Logica Schrijven
- Praktisch Verilog Gate-Level Ontwerp: AND, OR, NOT Gates & 2x1 Multiplexer
- Verilog $timeformat beheersen: Precisie en Weergave-controle