Two Verilog features placed on a deprecation list by the SystemVerilog committee — defparam and procedural assign/deassign — why each is problematic, what problems each causes for tools and designers, and the clean alternatives that replace them.
The SystemVerilog committee identified Verilog language features that share three characteristics: they are simulation inefficient, they are easily abused, and they are the source of design problems. Crucially, each deprecated feature has a clean, unambiguous alternative that provides the same capability without the problems.
Deprecation does not mean removal. Tools are still required to support both features in this standard. Deprecation means a future revision of the standard might remove them. Engineers are strongly encouraged to migrate away from these features now.
The two deprecated features are: defparam statements and procedural assign/deassign statements.
A defparam statement overrides the value of a module parameter from outside the module and its instantiation hierarchy — potentially from a completely separate file, compiled separately, at any point in the design.
// Old practice — defparam overrides parameter from a separate scope module mem_ctrl; adder #() u_add(...); // instantiated with defaults defparam u_add.WIDTH = 16; // overrides WIDTH inside u_add endmodule // Or from a completely separate file — even worse: // defparam top.cpu.alu.WIDTH = 32; // buried in a separate .v file
The core problem is that a defparam can appear almost anywhere — before, after, or in a completely different file from the instance it modifies. This creates a class of bugs that are very hard to find and a class of tool complexity that is unavoidable:
defparam can appear before the instance it modifies — the compiler must scan ahead.defparam can appear after the instance — the compiler must scan backward.defparam can be in a completely separate file — the compiler cannot know final parameter values until all design files are compiled.defparam can override a parameter hierarchically — that override may in turn depend on further defparam statements elsewhere.defparam statements can override the same parameter — the result is undefined.Both alternatives provide all the information at the instantiation site itself — no hunting through other files required.
// Parameter override buried elsewhere module top; adder u1(...); defparam u1.WIDTH = 16; // separate statement endmodule
// All information at the instantiation site module top; adder #(.WIDTH(16)) u1(...); // Verilog-2001 named override // or positional: adder #(16) u1(...); endmodule
| Method | Syntax | Issues |
|---|---|---|
| defparam (deprecated) | defparam inst.PARAM = val; | Can be in any file, any order — unverifiable until all files compiled |
| Implicit positional override | #(val1, val2) | Order-dependent; all parameters up to target must be listed |
| Explicit named override (preferred) | #(.PARAM(val)) | None — self-contained, order-independent, type-checked at instantiation |
Verilog has two forms of the assign statement. The distinction matters:
assign inside an always or initial block) — becomes active when the assign statement executes and can be deactivated by a deassign statement. This form is deprecated.// ✓ Continuous assignment — NOT deprecated (standard RTL practice) assign y = a & b; // ✗ Procedural continuous assignment — DEPRECATED always @(posedge clk) begin if (hold) assign q = d; // activates a continuous driver on q from inside a procedure else deassign q; // removes the continuous driver end
assign can appear both inside and outside procedures creates confusion — designers often do not understand which form they are using or what behaviour to expect.deassign — easy to forget.assign and regular procedural assignments to the same variable is difficult to predict and tool-specific.assign/deassign can be handled more clearly with force/release — which is always explicit and clearly distinct from normal assignments.always @(posedge clk) if (set) assign out = data; else deassign out;
// Explicit force/release (testbench use) always @(posedge clk) if (set) force out = data; else release out; // Or plain procedural assignment (RTL use) always @(posedge clk) out <= (set) ? data : out;
assign/deassign, which can be confused with continuous assignment, force and release have no other meaning — they always indicate a temporary override of a signal’s value, most commonly used in testbenches. Any code reader immediately understands the intent.| Feature | Why deprecated | Preferred alternative |
|---|---|---|
defparam | Can appear in any file, any order; final parameter values unknown until all files compiled; two defparams on same parameter → undefined | Explicit named override: #(.PARAM(val)) at the instantiation site |
Procedural assign/deassign | Creates hidden persistent driver inside a procedure; confused with continuous assignment; interaction with normal procedural assignments is unpredictable | force/release for testbench overrides; plain procedural <= or = for RTL |
defparam or procedural assign/deassign will continue to work. But new code should never use them, and existing code should be migrated at the next available opportunity.