Verilog Rules
UW—Madison ECE 552: Fall 2026
This document outlines the specific Verilog subset permitted for course projects. The primary goal is to ensure all code is synthesizable and reflects actual hardware components rather than abstract simulation models. Additionally, SystemVerilog and advanced constructs are intentionally excluded to encourage you think about the underlying hardware, and improve compatibility with some tools.
A Verilog code checker is used to verify that submissions follow these rules. False positives are possible - if the code appears correct but the checker is complaining, please post on Piazza or contact the teaching staff by email.
Run it yourself before submitting. Starting with Project 2, the exact same checker is included at the root of the riscv32i-arch-course-starter-fa26 repository (
python-ece552/) -- see its README for install/usage. Once installed:ece552 validate -w *.vThis lets you catch a rule violation locally instead of finding out from a failed Gradescope submission.
Note: These rules only apply to the synthesizable processor. You can use any features you want for your testbench and verification, but do not upload this code to Gradescope.
Operator Restrictions
The following operators are allowed (and encouraged when appropriate):
| Operator | Example | Operator | Example |
|---|---|---|---|
| Bitwise AND | x & y |
AND Reduction | &x |
| Bitwise OR | x | y |
OR Reduction | |x |
| Bitwise XOR | x ^ y |
XOR Reduction | ^x |
| Bitwise XNOR | x ~^ y |
XNOR Reduction | ~^x |
| Bitwise Invert | ~x |
NAND Reduction | ~&x |
| Ternary | sel ? x : y |
NOR Reduction | ~|x |
| Equality | x == y |
Constant Shift Left | x << const |
| Inequality | x != y |
Constant Shift Right | x >> const |
| Concatenation | {x, y} |
Replication | {const{x}} |
| Binary Addition | x + y |
Binary Subtraction | x - y |
| Unary Negation | -x |
Greater Than | x > y |
| Less Than | x < y |
Greater Than or Equal | x >= y |
| Less Than or Equal | x <= y |
Logical AND | x && y |
| Logical OR | x || y |
Note:
&&/\|\|are only safe to use whenxandyare already 1-bit signals. On wider buses, the checker treats the implicit reduction as a compile error -- your submission will not build, and you'll receive 0 pts with no partial credit. Use an explicit comparison (x != 0) or reduction operator instead if you need to combine multi-bit signals.
The following operators are not allowed as they synthesize poorly or do not map to any hardware.
| Operator | Example | Operator | Example |
|---|---|---|---|
| Strict Equality | x === y |
Strict Inequality | x !== y |
| Multiplication | x * y |
Division | x / y |
| Modulo | x % y |
Power | x ** y |
| Shift Left (Variable) | x << y |
Wildcard Equality | x ==? y |
| Shift Right (Variable) | x >> y |
Wildcard Inequality | x !=? y |
| Arithmetic Shift Right | x >>> y |
In addition, do not use signed logic (including the $signed() function) as
it leads to weird behavior and is a common source of bugs.
Hint: If you need an "unsupported" operation like multiplication or a variable shift, you must build that component structurally using the allowed bitwise and boolean operators.
Constant Operations
Some operations are only allowed when operated on constant expressions. For example, shifts are allowed by constant amounts:
wire [7:0] x = 8'hAA;
wire [7:0] y = x << 2;
But not by variable amounts:
input wire [2:0] amount;
// ...
wire [7:0] x = 8'hAA;
wire [7:0] y = x << amount;
Builtin Tasks and Functions
In general, builtin functions like $display() and $assert() are not
synthesizable and disallowed. An exception is the $clog2() function when a
constant expression is passed as input - this can be used to calculate the
number of bits needed.
Behavioral Logic & Control Flow
Many control flow constructs synthesize poorly or are not allowed: for,
while, do...while, repeat, forever, join, wait, disable.
This applies to procedural loops -- inside an always/initial block.
Structural, synthesizable loop generation with generate/genvar (for
instantiating repeated hardware, e.g. a ripple-carry chain or a barrel
shifter's mux stages) is always fine and is not restricted by this rule.
Case statements are allowed as an alternative to nested ternary expressions. However, they must be fully specified and have a default case to avoid inferring a latch.
Conditional statements (if/else) are only allowed within clocked blocks to
infer registers:
reg [7:0] x;
always @(posedge i_clk) begin
if (i_rst)
x <= 8'h00;
else
x <= x + 1;
end
They are not allowed in combinational logic (use a case statement or ternary instead):
reg [7:0] x;
always @(*) begin
if (i_sel)
x = 8'hAA;
else
x = 8'hBB;
end
Note:
rf.vis exempt from this ban -- behavioralif/elseis permitted there (see the register file problem on the Project 2 page for details, once it's released).
Timing & Event Control
Explicit timing controls are not allowed in synthesizable hardware, as timing is governed by the system clock.
Simulation Timing
Statements involving delays (#), cycle delays (##), or repeated events are not allowed.
Signal Events
Sensitivity lists may only use posedge or implicit @(*) signals.
The negedge trigger and iff conditions on events are not allowed.
Literals & Types
The following types and expressions are not allowed: real numbers, string literals, time literals, and null.
Class-based expressions (e.g., new, copy) are strictly for simulation and cannot be used.