Logic in Systemverilog:
In short: In SystemVerilog, logic is a 4-state data type (0, 1, X, Z) that replaces the old Verilog reg in most cases. It can be driven by one procedural block or one continuous assignment, so you no longer have to choose between reg and wire for single-driver signals. Use logic for almost every signal in RTL and in verification code; keep wire only when a net truly needs more than one driver.
If you have ever paused while typing a testbench and wondered whether a signal should be reg or wire, this post is for you. The logic type was added in SystemVerilog (IEEE 1800) to remove that daily confusion. This guide explains what logic is, why it exists, how it behaves according to the LRM, where you should and should not use it, and it ends with a set of real questions engineers ask about it.
A quick refresher: reg and wire in Verilog
To see why logic is helpful, it helps to remember the two Verilog types it grew out of.
A wire models a physical connection between two elements. It cannot store a value on its own. It only shows the value of whatever is driving it, and it must be driven by a continuous assignment (an assign statement) or by the output of a module. Remove the driver and a wire floats to Z (high impedance).
A reg can hold a value. You assign to it inside procedural blocks such as initial or always, and it keeps that value until the next procedural assignment. The name is misleading: a reg is not always a hardware register. It is simply a variable you write from procedural code.
So what is the logic type?
logic is a 4-state SystemVerilog data type. Every bit can be one of four values: 0, 1, X (unknown), or Z (high impedance). Those two extra values matter for hardware verification, because a real chip has signals that start out unknown and buses that can be tri-stated.
The one rule to remember is about drivers. A logic signal may have a single driver. That single driver can be a procedural block or a continuous assignment, and this is the point Verilog got wrong for years. In Verilog you were forced to pick: a reg for procedural assignments, a wire for continuous ones. With logic you write the signal once and drive it whichever way suits the code, as long as only one thing drives it.
Because of that single-driver rule, you cannot use logic for a net that several drivers share, such as a tri-state bus with multiple talkers. For that case you still use wire, which is a 4-state net type and can resolve many drivers.
A simple way to picture it
Think of a whiteboard in a meeting room. If only one person is allowed to write on it, everything stays clear: you can always tell who wrote what and the board holds the last thing that person wrote. That single-writer whiteboard is logic. It holds a value, and one owner controls it, whether that owner updates it step by step (procedural) or keeps it tied to a formula (continuous assignment).
Now imagine several people are allowed to write on the same board at the same time. You need a rule for what happens when two of them write over each other. That shared, many-writer board is a wire: it needs resolution because more than one driver can talk at once. The lesson is the same one hardware teaches: one owner per signal keeps things clear, and you only reach for the shared board when the design genuinely needs many drivers.
The classic example, corrected and explained
Here is a small module that shows both kinds of assignment on logic signals. It is written to compile cleanly under IEEE 1800.
module sv_logic #(parameter int CYCLE = 10) (input logic xyz);
logic a; // driven procedurally below
logic c; // driven by a continuous assignment below
// Procedural driver: toggles 'a' every half cycle.
initial begin
a = 1'b0;
forever #(CYCLE/2) a = ~a; // note the semicolon
end
// Continuous driver: 'c' follows an expression at all times.
// 'assign c = ~c;' would be a zero-delay feedback loop, so we
// drive 'c' from the input instead to keep the example legal.
assign c = ~xyz;
endmoduleTwo things are worth calling out. First, the signal a is written from an initial block (a procedural driver), while c is written from an assign statement (a continuous driver). Both signals are declared as logic, and each has exactly one driver, so both are legal. In plain Verilog, a would have needed to be a reg and c a wire.
Second, notice the fix compared to the older version of this example. The original wrote assign c = ~c;, which feeds a signal back into itself with no delay and creates a loop that never settles. Driving c from an input instead keeps the intent (a continuous assignment on a logic signal) while staying correct. The original also declared parameter CYCLE; with no value and left off a semicolon; both are corrected above.
Where logic works and where it does not
Use logic for almost everything you used to declare as reg, and for single-driver signals you used to declare as wire. That covers most RTL signals and most signals inside verification code such as drivers, monitors, and checkers that work at the pin level.
Keep wire (or another net type) when a signal has more than one driver at the same time. The common cases are:
- A tri-state bus where several devices can drive the same lines.
- A signal that needs a specific resolution when two drivers disagree.
- Any net that models a shared physical connection with multiple sources.
If you declare such a shared net as logic and connect two drivers to it, the tool will report a multiple-driver error, because logic allows only one.
logic vs reg vs wire, side by side
| Feature | logic / reg / wire |
|---|---|
| Number of states | logic 4-state, reg 4-state, wire 4-state |
| Can hold a value | logic yes, reg yes, wire no (reflects its driver) |
| Procedural assignment | logic yes, reg yes, wire no |
| Continuous assignment | logic yes, reg no, wire yes |
| Multiple drivers | only wire (and other net types); logic and reg no |
Read that table and the takeaway is clear. logic gives you what reg and single-driver wire both offered, in one name, so you make fewer decisions and write fewer bugs.
A verification-style example
In testbench code you constantly declare pin-level signals that one block writes and another reads. logic fits that job well. Here is a tiny driver-like snippet that sets a bus and a valid flag.
interface bus_if (input logic clk);
logic valid;
logic [31:0] data; // single driver from the driver task
endinterface
// Inside a class-based driver, drive the pins once per item.
task automatic drive_item(virtual bus_if vif, logic [31:0] payload);
@(posedge vif.clk);
vif.data <= payload; // one driver owns 'data'
vif.valid <= 1'b1;
@(posedge vif.clk);
vif.valid <= 1'b0;
endtaskBecause only the driver writes data and valid, declaring them as logic is correct and keeps the interface simple. If two components needed to drive the same wire, you would switch that net to wire and add a resolution scheme.
Common mistakes
- Using
logicfor a multi-driver bus and then getting a multiple-driver error. Usewirefor shared nets. - Writing self-feedback such as
assign x = ~x;with no delay, which never settles. Break the loop with a real source or a clocked element. - Assuming
regmeans a hardware register. It does not; it is just a procedurally assigned variable. - Declaring a parameter with no default and no override, which leaves the value undefined. Give parameters a sensible default.
- Mixing procedural and continuous assignments on the same
logicsignal. That is two drivers, which is still illegal.
Key takeaways
logicis a 4-state (0, 1, X, Z) SystemVerilog type that can hold a value.- It allows one driver, either procedural or continuous, which is why it replaces most uses of
regand single-driverwire. - Use
wireonly when a net truly has multiple drivers. - Prefer
logicacross RTL and verification code to reduce type-choice mistakes.
Honesty note: the code here follows a plain reading of the SystemVerilog LRM (IEEE 1800). It is written to illustrate the concept rather than captured from a specific simulator run, so confirm behaviour on your own tool or on EDA Playground before relying on it.
Related reading
- Different array types and queues in SystemVerilog
- Static properties and methods in SystemVerilog
- Inheritance in SystemVerilog OOP
- Advantages of UVM over SystemVerilog
What is the logic data type in SystemVerilog?
It is a 4-state variable type with values 0, 1, X, and Z. It can hold a value and can be driven by a single driver, either a procedural block or a continuous assignment.
What is the difference between logic and reg?
Both are 4-state and both can hold a value and take procedural assignments. The difference is that logic also accepts a continuous assignment on a single-driver signal, while reg does not. In new code, prefer logic.
What is the difference between logic and wire?
A wire is a net that cannot store a value and can have multiple drivers with resolution. A logic signal can store a value but allows only one driver. Use wire for shared nets and logic for single-driver signals.
Can logic have multiple drivers?
No. A logic signal allows exactly one driver. If you connect two drivers, the tool reports a multiple-driver error. Use wire for a net that needs more than one driver.
Can I use a continuous assignment on a logic signal?
Yes, as long as it is the only driver. You can write assign my_logic = expr; on a logic signal, which was not allowed on a reg in Verilog.
Is logic 2-state or 4-state?
logic is 4-state (0, 1, X, Z). If you want a 2-state type for faster simulation and only need 0 and 1, use bit instead.
Should I use logic everywhere in RTL?
Use it for every single-driver signal, which is most of them. Switch to wire only where a net genuinely has multiple drivers, such as a tri-state bus.
Does logic mean a hardware register?
No. Like the old reg, the name does not imply a flip-flop. Whether a signal becomes a register depends on how you assign to it, for example a clocked always_ff block, not on the logic keyword.




