ECE552 Fall 2026
Project 1: RISC-V Assembly Language Project 2: Digital Design and Debugging Project 3: Single-Cycle Processor Project 3: Extra Credit - FPGA Demo Project 4: Basic Pipelined Processor Project 5: Forwarding and Branch Prediction Project Schedule Verilog Rules Reference Materials
ECE 552 / Verilog Rules
Back to Course Home

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 *.v

This 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 when x and y are 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.v is exempt from this ban -- behavioral if/else is 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.

Table of Contents
  • Operator Restrictions
  • Constant Operations
  • Builtin Tasks and Functions
  • Behavioral Logic & Control Flow
  • Timing & Event Control
  • Literals & Types

2026 ECE 552 Course Staff, University of Wisconsin-Madison