SV14. Unique and Priority Identifiers in SystemVerilog
Overview
SystemVerilog provides three special keywords (unique
, unique0
, and priority
) that can be used with case
and if
statements to provide synthesis tools with optimization hints and enable runtime checking during simulation.
Video Tutorial
Watch this comprehensive video explanation of Unique and Priority Identifiersing:
Unique and Priority Identifiers in Synthesizable SystemVerilog
Table of Contents
- Introduction
- The
unique
Keyword - The
unique0
Keyword - The
priority
Keyword - Practical Examples
- Common Pitfalls
- Best Practices
- Summary
Introduction
SystemVerilog provides three special keywords (unique
, unique0
, and priority
) that can be used with case
and if
statements to provide synthesis tools with optimization hints and enable runtime checking during simulation.
Purpose and Benefits
These keywords serve two primary purposes:
- Synthesis Optimization: Inform synthesis tools about the nature of case items, enabling better logic optimization
- Simulation Checking: Provide runtime violation checking to catch design errors during verification
The main benefit is helping synthesis tools distinguish between scenarios that require:
- Parallel evaluation (when cases are mutually exclusive)
- Priority encoding (when order matters or cases may overlap)
The unique
Keyword
The unique
keyword indicates that case items are mutually exclusive and fully cover all possible input values.
Simulation Behavior
When using unique
, the simulator performs runtime checking and issues warnings if:
- Multiple case items evaluate to true simultaneously
- No case items evaluate to true
Synthesis Implications
The unique
keyword informs the synthesis tool that:
- Case items are mutually exclusive (only one can be true at a time)
- All possible input values are fully covered by the case items
- The tool can optimize for parallel evaluation instead of priority encoding
- No default case is necessary since all values are covered
Example:
typedef enum logic [2:0] {
RED = 3'b001,
GREEN = 3'b010,
YELLOW = 3'b100
} colors_t;
colors_t current_color, next_color;
always_comb begin
unique case (current_color)
RED: next_color = YELLOW;
GREEN: next_color = RED;
YELLOW: next_color = GREEN;
// No default needed - unique tells tools all cases are covered
endcase
end
In this example, the synthesis tool can generate optimized parallel logic instead of a priority encoder because it knows:
- Only one case can be true at a time (mutually exclusive)
- All possible enum values are handled (fully covered)
Important Caveat for Using unique
Keyword
One caveat for using the unique
keyword without fully covered case
or if
statements (and without default
or else
clauses) is that synthesis tools may generate smaller circuits compared to fully covered case
/if
statements with explicit default
/else
handling. However, the resulting circuit could be less robust to environmental noise, and if the circuit state changes to an undefined state, the circuit behavior becomes unpredictable.
Therefore, it is more robust to always fully cover
case
andif
statements. In today’s technology, removing thedefault
statement may only reduce a small number of transistors, but it does not significantly affect the overall circuit area or power consumption, while potentially compromising reliability.
The unique0
Keyword
The unique0
keyword indicates that at most one case item can be true, but not all possible values need to be covered.
Simulation Behavior
The simulator only issues a warning if:
- Multiple case items evaluate to true simultaneously
- No warning if no case items are true
Synthesis Implications
The unique0
keyword tells the synthesis tool:
- Case items are mutually exclusive
- Not all possible values are covered by case items
- May infer latches for uncovered input values
- Can still optimize for parallel evaluation of covered cases
Important Warning
Do NOT use
unique0
for RTL modeling. The ability for no cases to match can lead to unintentional latch inference, which is generally undesirable in synchronous digital design.
The priority
Keyword
The priority
keyword specifies that the order of case items is important and they should be evaluated sequentially in the order written.
Simulation Behavior
The simulator issues a warning if:
- No case items evaluate to true
- No warning for multiple matches (first match wins)
Synthesis Implications
The priority
keyword informs the synthesis tool:
- Case items should be evaluated in order
- First matching condition wins
- Should implement a priority encoder
- Optimizes for sequential rather than parallel evaluation
Practical Examples
Example 1: Optimized State Machine with unique
typedef enum logic [1:0] {
IDLE = 2'b00,
WORK = 2'b01,
DONE = 2'b10
} state_t;
state_t current_state, next_state;
always_comb begin
unique case (current_state)
IDLE: next_state = start ? WORK : IDLE;
WORK: next_state = finished ? DONE : WORK;
DONE: next_state = reset ? IDLE : DONE;
// All enum values covered - synthesis optimizes for parallel evaluation
endcase
end
Example 2: Priority Interrupt Controller with priority
module priority_interrupt_decoder(
input logic [3:0] interrupt_req, // 4 interrupt request inputs
output logic [1:0] interrupt_id, // Encoded interrupt ID
output logic interrupt_valid // Valid interrupt present
);
always_comb begin
priority case (interrupt_req) inside
4'b???1: begin // Interrupt 0 (highest priority)
interrupt_id = 2'b00;
interrupt_valid = 1'b1;
end
4'b??10: begin // Interrupt 1
interrupt_id = 2'b01;
interrupt_valid = 1'b1;
end
4'b?100: begin // Interrupt 2
interrupt_id = 2'b10;
interrupt_valid = 1'b1;
end
4'b1000: begin // Interrupt 3 (lowest priority)
interrupt_id = 2'b11;
interrupt_valid = 1'b1;
end
default: begin // No interrupts
interrupt_id = 2'b00;
interrupt_valid = 1'b0;
end
endcase
end
endmodule
This example demonstrates proper use of priority
where:
- Multiple interrupts can be active simultaneously
- Higher-numbered bit positions have higher priority
- The priority encoder ensures only the highest priority interrupt is serviced
Common Pitfalls
Misusing unique
with Incomplete Coverage
Incorrect Usage:
module address_decode (
input logic [1:0] select,
output logic data
);
always_comb begin
data = 1'b0; // Default assignment
unique case (select)
2'b10: data = 1'b1;
// ERROR: Missing cases for 2'b00, 2'b01, 2'b11
endcase
end
endmodule
Generated library-independent circuit
Problems with this approach:
unique
tells the synthesis tool that all possible values are covered, but they’re not- The tool optimize away the default assignment
data = 1'b0
- Undefined behavior for uncovered input values (2’b00, 2’b01, 2’b11)
Correct Approach:
module address_decode (
input logic [1:0] select,
output logic data
);
always_comb begin
data = 1'b0; // Default assignment
case (select) // Remove 'unique' - not all cases covered
2'b10: data = 1'b1;
// Default assignment handles all other cases
endcase
end
endmodule
Generated library-independent circuit
Best Practices
When to Use Each Keyword
Keyword | Use Case | Synthesis Result | Best For |
---|---|---|---|
unique |
Mutually exclusive, fully covered cases | Parallel evaluation logic | State machines, complete decoders |
priority |
Order-dependent evaluation | Priority encoder | Interrupt controllers, nested conditions |
unique0 |
Mutually exclusive, partially covered | Parallel + potential latches | Avoid in RTL |
Design Guidelines
- Use
unique
when you have complete, mutually exclusive coverage- All possible input combinations are explicitly handled
- Cases cannot overlap
- Want optimized parallel logic
- Use
priority
when order matters- Multiple conditions might be true simultaneously
- Want to service the first (highest priority) match
- Need priority encoder behavior
- Avoid
unique0
in synthesizable RTL- Can lead to unintentional latch inference
- Better to use regular
case
with explicit defaults
- Let simulation catch violations
- Use these keywords during verification to catch design errors
- Address any warnings that appear during simulation
Summary
The unique
, unique0
, and priority
keywords are powerful SystemVerilog features that:
- Improve synthesis results by providing optimization hints to tools
- Enable error checking during simulation to catch design mistakes
- Make design intent clearer to both tools and other engineers
Key takeaways:
- Use
unique
for complete, mutually exclusive case coverage to get optimized parallel logic - Use
priority
when evaluation order matters to get optimized priority encoders - Avoid
unique0
in RTL design due to potential latch inference issues - Always verify your assumptions during simulation - let the tools catch violations
- These keywords are about optimization and verification, not changing fundamental logic behavior
When used correctly, these keywords help create more efficient hardware implementations while catching potential design errors during the verification process.