Continuous assignment
Combinational logic with assign
A continuous assignment in Verilog (assign wire = expression) describes a piece of combinational logic that is always active, the same standing gate network the expression denotes, and it can be derived directly from a truth table as a sum of products using & for AND, | for OR, and ~ for NOT.
Build it in the lab →You have a Verilog module with a port list. This lesson covers the workhorse of combinational Verilog: the continuous assignment, the
assign F = ...; line. It is Verilog's version of VHDL's concurrent assignment, a standing gate network, and because a truth table completely specifies a combinational function, you can write it straight from the table.assign means always live
A statement like
assign F = A & B; is continuous: it is not executed once, it describes a gate that drives F from A and B forever. Write several assign lines in a module and they all run at the same time, in parallel, in any order, because each is its own piece of hardware. The operators are C-like but mean gates: & is AND, | is OR, ~ is NOT, and ^ is XOR. (Watch the doubles: && and || are *logical* operators for if conditions, not gates, so bitwise logic uses the single forms.)Because continuous assignments are parallel hardware, their order in the file does not matter.
assign F = t & C; then assign t = A & B; describes the same circuit as the reverse: two gates wired together, and wires do not care which line you typed first. If reordering assign lines could change behavior, you are thinking in software, not hardware.From truth table to sum of products
Any combinational output can be written as a sum of products: OR together one product (an AND) for each input row where the output is
1, with each input that is 0 in that row negated. "Sum" is OR, "product" is AND. Take XOR, whose output is 1 exactly when the inputs differ:| A | B | F |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Read off the two rows where
F is 1. The row A=0, B=1 gives the product ~A & B. The row A=1, B=0 gives A & ~B. OR them with | and you have the equation, which is also the Verilog, line for line:module xor_gate( input A, input B, output F ); assign F = (~A & B) | (A & ~B); endmodule
That single expression is a complete description of XOR: two AND gates (the products), each fed by an inverter on one input, ORed together. It is the same structure you would draw by hand. The synthesis tool is free to optimise it (perhaps into NANDs), but the behavior is fixed by the equation.
F is 1 only when A and B differ, matching (~A & B) | (A & ~B).Verilog has a built-in XOR operator, so the whole gate is also just
assign F = A ^ B;. That is the shortcut you would use in practice. Writing it as a full sum of products here is the point: it shows that *any* combinational function, not only the ones with a handy operator, comes straight from its truth table.A frequent mistake is forgetting to negate the inputs that are
0 in a row. The row A=0, B=1 is not A & B, it is ~A & B, the term must be true for that exact row and no other. Drop the ~ and your equation lights up for the wrong input combination. Always negate every input that reads 0 in the row you are encoding. (And use &/|, the bitwise gates, not &&/||, which are logical operators for conditions.)Structural style: name the gates and wire them
Every line so far describes logic by its equation and lets the tool choose the gates: this is dataflow style. Verilog has a second, lower-level way to say the same thing, structural style, where you name each gate and wire them together yourself, exactly as you would draw the schematic. Verilog ships primitive gates you can instantiate directly:
and, or, not, nand, nor, xor, xnor. You write the gate keyword, an optional instance name, then the ports in parentheses with the output first and the inputs after. Any signal that carries a value between gates but is not a module port is declared as a [wire](#wires-nets) net. Naming each instance (g1, g2, and so on) is optional but makes simulation waveforms and error messages much easier to follow.module xor_gate( input A, input B, output F ); wire nA, nB, t1, t2; // internal nets between the gates not g1(nA, A); // nA = ~A (output nA first, then input A) not g2(nB, B); // nB = ~B and g3(t1, nA, B); // t1 = ~A & B and g4(t2, A, nB); // t2 = A & ~B or g5(F, t1, t2); // F = t1 | t2 endmodule
wire nets. It is the sum-of-products equation drawn out gate by gate, with the output listed first in every gate's port list.You can attach a propagation delay to a primitive with
#: and #3 g3(t1, nA, B); makes t1 settle 3 simulation time units after its inputs change, modeling the real gate delay a signal takes to propagate. It affects simulation timing only: a synthesis tool ignores the number, since the actual delay comes from the target technology, not from you.Hierarchy: modules inside modules
A module can also contain other modules, the same way a canvas circuit can drop in a saved custom part. You instantiate a child by writing its name, an instance name, and a port map. Connect the ports by name:
.A(sig) wires the child's port A to your signal sig, so the listing order does not matter and every connection is explicit. Here is a **half adder** (SUM is A ^ B, CARRY is A & B) built by instantiating the structural xor_gate above together with the and_gate from the Verilog intro:module half_adder( input A, input B, output SUM, output CARRY ); xor_gate u1(.A(A), .B(B), .F(SUM)); // SUM = A ^ B and_gate u2(.A(A), .B(B), .F(CARRY)); // CARRY = A & B endmodule
u1's output port F drives SUM; u2's F drives CARRY. This is Verilog hierarchy: the same composition you do on the canvas when you drop a saved part into a bigger circuit.| A | B | SUM | CARRY |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
Named ports (
.A(A)) make each connection explicit, so their order is free. Verilog also allows positional connection (xor_gate u1(A, B, SUM);), which matches signals to ports purely by position: reorder the child's port list later and every positional instance silently miswires. Prefer named ports. And in a primitive gate, remember the first port is the output (and g3(t1, nA, B) means t1 = nA & B), the reverse of how the equation reads.Why this matters: sum of products is the bridge from the truth tables you have been filling in to real Verilog. Any combinational block you can describe with a table, a decoder, a comparator, one bit of an adder, you can now write as a continuous assignment. A Karnaugh map then trims that equation to fewer gates. Logic that *remembers* needs one more tool, the clocked
always block, which is next.Try it
Write the continuous assignment for a two-input function
F that is 1 only when A=1 and B=0 (and 0 for the three other rows). How many product terms does it need?Answer
Only one row has output
1, so it is a single product term: assign F = A & ~B;. With just one 1 in the table there is nothing to OR together. (If two rows were 1, you would OR two products with |; the count of product terms equals the count of 1 rows.)Frequently asked
What is a continuous assignment in Verilog?
A continuous (or 'assign') statement, assign wire = expression, describes combinational logic that is always active: a standing gate network that continuously drives the wire from its inputs. It is not executed once like software; multiple assign statements all run in parallel. It is Verilog's equivalent of a VHDL concurrent assignment.
What are the bitwise operators in Verilog?
& is AND, | is OR, ~ is NOT (invert), and ^ is XOR. These build gates. The doubled forms && and || are logical operators used in if conditions, not gates, so combinational logic uses the single (bitwise) forms.
How do you write a Verilog equation from a truth table?
Use sum of products: OR together one product (AND) term for each row where the output is 1, negating every input that is 0 in that row. For XOR, the rows (A=0,B=1) and (A=1,B=0) give assign F = (~A & B) | (A & ~B); (or simply assign F = A ^ B;).
What is the difference between assign in Verilog and a concurrent assignment in VHDL?
There is none in meaning: both describe a standing piece of combinational logic that is always live and runs in parallel with the others. Only the syntax differs, Verilog writes assign F = ... with & | ~, VHDL writes F <= ... with and or not.
What is the difference between dataflow and structural Verilog?
Dataflow style uses continuous assignments (assign F = (~A & B) | (A & ~B);) to describe logic by its equation and lets the synthesis tool choose the gates. Structural style names each primitive gate and wires them by hand (not, and, or instances joined by wire nets), describing the schematic literally. Both compile to the same hardware: dataflow is more concise, structural gives you gate-by-gate control.
What is a wire in Verilog?
A wire is a net that carries a value from its driver to wherever it is read, the Verilog equivalent of a physical connection. You declare internal signals that pass between gates as wire (for example wire t1;), while a module's inputs and outputs are ports. A wire holds no memory: it simply reflects whatever is currently driving it.
How do you instantiate a module in Verilog?
Write the child module's name, an instance name, and a port map: half_adder u1(.A(x), .B(y), .SUM(s), .CARRY(c));. Connecting ports by name (.port(signal)) makes the wiring explicit and order-independent, which is safer than positional connection. Instantiating modules inside modules is how Verilog builds hierarchy, the text equivalent of dropping a saved part into a bigger circuit.
You've got the theory. Now build it from scratch and watch it work.
Build it in the lab →