Chapter 4 of 16
Data types: the difference between logic, reg, and wire
Understand how SystemVerilog's logic type unifies and simplifies Verilog's reg/wire split, across both combinational and sequential logic, plus where each type's boundaries still apply.
In Verilog, signals must be strictly split into two data types:
wire: represents a connection driven by a continuous assignment (assign) or a module port; it cannot be assigned inside analwaysblock.reg: represents a variable assigned inside a procedural block (always/initial). Despite the name "reg", it does not necessarily synthesize to a register — combinationalalwaysblocks useregtoo.
This distinction often confuses beginners: reg doesn't mean a hardware register, it just means "a variable assigned by a procedural block."
Where this split comes from: continuous vs. procedural assignment
The wire/reg split is really a split by assignment style, not by whether a signal is a register:
- Continuous assignment (an
assignstatement) describes a wire that continuously tracks the expression on its right-hand side; its target must be declaredwire. - Procedural assignment (an assignment inside an
initial/alwaysblock) describes "store this value into a variable when some event happens"; its target must be declaredreg.
The two can't be mixed — the compiler will flag it directly:
wire w;
initial begin
w = 1'b1; // ✗ compile error: wire cannot be assigned inside a procedural block
end
reg r;
assign r = 1'b1; // ✗ compile error: reg cannot be the target of a continuous assignmentIn other words, plain Verilog forces you to decide upfront whether you'll describe a signal with assign or with a procedural block, and declare it wire or reg accordingly — a purely syntactic burden that has nothing to do with whether the signal ends up as a real register.
SystemVerilog's logic type
SystemVerilog introduces the logic type to simplify this. logic can be assigned inside a procedural block just like reg, and can also be connected to a port just like wire, as long as it isn't driven by multiple drivers at once (it can't be used where multiple drivers are needed, such as a tri-state bus — that still requires wire). Like wire and reg, logic is also a 4-state type (it can represent 0, 1, x, and z) — the next chapter goes deeper into the difference between 2-state and 4-state data types.
module mux2 (
input logic sel,
input logic a,
input logic b,
output logic y
);
always_comb begin
y = sel ? b : a;
end
endmoduleIn the example above, y is of type logic — it's both a module output port and is assigned inside an always_comb block. That combination isn't allowed in plain Verilog (ports can only be declared wire, unless explicitly overridden with reg, and combinational outputs need extra care).
logic works for sequential logic too
The example above is combinational logic (always_comb). logic works just as well in sequential logic (always_ff), playing the role reg used to — here's a simple D flip-flop with an asynchronous reset:
module dff_async_rst (
input logic clk,
input logic rst_n,
input logic d,
output logic q
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
endmoduleHere q is logic too: it's an output port, and it's also assigned inside always_ff using <= (a non-blocking assignment) — a later chapter on procedural blocks goes into the difference between blocking (=) and non-blocking (<=) assignment in detail; for now, just remember sequential logic conventionally uses <=. Together with mux2 above, this shows that whether you're writing combinational or sequential logic, declaring things logic covers both — no more picking between wire and reg.
The short version, if you're curious now:
<=schedules its update to happen only after every block has finished evaluating with this time step's starting values, so it doesn't matter whichalways_ffblock the simulator happens to run first — everyone reads the same old values, matching how real hardware updates all its registers "at once" on a clock edge. Using=(blocking) here could make the result depend on simulator execution order, a mismatch between simulation and real hardware. Chapter 8 covers exactly how that scheduling works.
When you still need wire
When a signal needs to be driven by multiple drivers (for example a tri-state bus driven by several modules), you must use wire (or a more explicit wire variant such as tri), because logic only allows a single driver — multiple drivers will cause a compile or simulation error. A typical case is several devices sharing the same bus lines, where at most one device is "enabled" to drive them at any given time and the rest output high-impedance z:
module tristate_buf (
input wire data_in,
input wire enable,
output wire data_out
);
assign data_out = enable ? data_in : 1'bz;
endmoduleIf several instances of tristate_buf have their data_out tied to the same wire, as long as the surrounding logic/protocol guarantees at most one enable is high at a time, the shared wire never gets driven to conflicting values at once — exactly the scenario wire allows and logic doesn't.
Comparing the three types
| Property | wire | reg (plain Verilog) | logic (SystemVerilog) |
|---|---|---|---|
Assignable inside a procedural block (always/initial)? | No | Yes | Yes |
Can be the target of a continuous assignment (assign)? | Yes | No | Yes (single driver only) |
| Can be used directly as a module output port type? | Yes | Not in plain Verilog | Yes |
| Supports multiple drivers (e.g. a tri-state bus)? | Yes | N/A | No — falls back to wire |
4-state (0/1/x/z)? | Yes | Yes | Yes |
Summary
- The
wire/regsplit is really "continuous vs. procedural assignment," unrelated to whether a signal is a real register. logiccan replaceregand, in most cases,wire— for both combinational (always_comb) and sequential (always_ff) logic.- You only need
wirewhen you genuinely need multiple drivers (such as a tri-state bus). - Using
always_combandalways_ffinstead of plainalwayslets tools catch common mistakes like unintended latches at compile time.
Which statement accurately describes the logic type in SystemVerilog?
In plain Verilog, which of the following causes a compile error?
Which data type should you use when multiple modules need to drive the same tri-state bus? (lowercase English word)