Lecture 08 – Full and Parallel Case
Ryan Robucci
1. References
- (Cummings 1999) Some examples and discussion are cited from Clifford E. Cummings' "fullcase parallelcase", the Evil Twins of Verilog Synthesis http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase.pdf
2. Hardware Synthesis vs. Simulation
- Simulators don't interpret HDL code the same as synthesis tools
- Simulated behavior of unsynthesized, behavioral code is sometimes grossly mismatched to the hardware synthesized
- Many such mismatches can be avoided by following the prescribed coding guidelines
- Sometimes simulating post-synthesis HDL code is good idea.
- post-synthesis HDL code is the code output from the process of mapping all HDL code to structural code using the modules representing the building blocks of the target hardware technology (e.g. FPGA, standard-cell ASIC)
- The modules for those building blocks have modules provided which have already been coded specifically to stimulate closely the behavior of the final physical hardware
- Lets take a quick look at a couple examples where simulation and hardware synthesis lead to a different interpretation of HDL code
Example: Ambiguous Parallel Blocks
always @(posedge clk) begin y1 = M; end always @(posedge clk) begin y1 = b; end always @(posedge clk) begin y2 = b; end always @(posedge clk) begin y2 = a; end
Synthesis Report:
Essentially, identifies four registers, two of them are the same, and the remaining two drive the same output
Register <y2> equivalent to <y1> has been removed Register <y2> equivalent to <y1> has been removed Found 1-bit register for signal <y1>. Summary: inferred 2 D-type flip-flop(s). Unit <ambigous_parallel> synthesized. ========================================================================= HDL Synthesis Report Macro Statistics * Registers : 2 1-bit register : 2 Advanced HDL Synthesis Report Macro Statistics # Registers : 2 Flip-Flops : 2 ========================================================================= - Low Level Synthesis* ========================================================================= ERROR:Xst:528 - Multi-source in Unit <ambigous_parallel> on signal <y2>; this signal is connected to multiple drivers. Drivers are: Output signal of FD instance <y1> Output signal of FD instance <y1_ren>
Example: Non-conflicting parallel blocks
next two may be OK for simulation, but why?
always @(posedge clk) begin if (a == 1) y3 = 1; end always @(posedge clk) begin if (a == 0) y3 = 0; end
- Though this may simulate, the compiler will likely not be able to synthesize it, though again this is synthesizer dependent
- Why not? Because a common approach is initially synthesizeeach procedural block independently and then infer connections.
- Remember the Guideline: Avoid assigning to a variable from more than one always block
- Hardware-based vs software-based mindset: think of each block as describing a driver in hardware, and there shouldn't be more than one driver (unless tristate is involved)
3. The Verilog Case Statement
- Case statements play a central role in HDLs
- Allows description of
- basic Look Up Tables for combinational logic
- control and assignment decisions
- more compact than if-else statements
- all logic can be described as some order of decisions on each bit — logic can be represented by a mapped to a Binary Decision Diagram (binary decision tree)
- for compactness, organization, interperability, and managability decision algorithms are
- described with more than one option per decision
- are shared amoung multiple outputs (assign multiple variables) than having a unique algorithm for each bit of output
- Allows description of
truth table for output u and output v :
in | in | out | out |
---|---|---|---|
a | b | u | v |
0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 0 | 1 |
1 | 1 | 1 | 0 |
decision tree for output v:
decision tree for output u:
Multi-output decision tree:
Flattened, multi-output decision tree:
- The structure of a case statements might suggest a parallel and independent evaluation of cases
when thinking of a case statement like the decision tree, it may seem that the order does not matter This is natural when previous experience with case statement involve simple conditional matches to one condition
However, case statements are equivalent to if-else decision trees Logic is implied by a priority that is embedded in the order of the statements, which comes into play when using when not checking exactly only one bit-vector possibilty of the case select
case (case_select) case_item1 : case_item_statement1; case_item2 : case_item_statement2; case_item3 : case_item_statement3; case_item4 : case_item_statement4; default : case_item_statement5; endcase
is equivelent to
if (case_select is matched by case_item1) case_item_statement1; else if (case_select is matched by case_item2) case_item_statement2; else if (case_select is matched by case_item3) case_item_statement3; else if (case_select is matched by case_item4) case_item_statement4; else case_item_statement5;
- important for more complex conditional matches, such as match ranges and wildcards
?
care must be taken to combine output decisions into one construct
a b u v 0 0 0 0 0 1 0 0 1 0 1 0 1 1 1 1 u = 0; v = 0; if ({a,b}>=2) begin u = 1; if (b==1) begin v = 1; end end
↕️ ↕️ ↕️ ↕️
u = 0; v = 0; case ({a,b}) 2'b11: begin u = 1; v = 1; end 2'b1?: begin u = 1; v = 0; end endcase
u = 0; v = 0; if (b==1) begin u = 1; v = 0; end else if ({a,b}>=2) begin //never evaluated as true u = 1; v = 1; end
↕️ ↕️ ↕️ ↕️
u = 0; v = 0; case ({a,b}) 2'b1?: begin u = 1; v = 0; end 2'b11: begin u = 1; v = 1; end endcase
4. Case Priority
- Order of caseitems sets priority (first match wins), therefore the order encodes meaning/logic
- carefully consider the order of the cases in a case statement when overlapping matches exist
Assume you are standing outside your door considering an afternoon in the park or a return inside
Decision Algorithm:
- If see lightning go inside
- Else if see rain walk to park with umbrella
- Else run to park
- Taking in isolation the condition in the second line, we could conclude an intent to “walk to the park" if "we see rain”
- The algorithm does not intend this. It does tells us to “walk to the park" iff we "see rain" AND we "do not see lighting"
- Conditions presented as case items in case statements must be interpreted in the same way, with attention to the order and precedence
5. Multiple output assignments
Code is often more expressive and multiple outputs are assigned in the same body of code
6. Overlapping Cases
overlapping cases described by a flattened case have special meaning
is really a cascade of binary decisions
not the same as
7. Critical Design Considerations for procedural code (always begin…end blocks)
Case statements serve as a foundation for learning to design complex procedural code. An aquired deciplined approach towards case statements will can applied to procedural code in general.
- For signals that are used externally to the block in which they are assigned, ensure correct intent to generate combinational vs sequential logic
- Our coding rule is that if we are coding an clocked (edge-selecive) always block, then any signal assigned using a blocking assign (=) should not be used outside the block so that the registered version of the signal is trimmed and only the combinatorial version remains. We enforce this by using named blocks and local variables for the output of combinatorial circuits.
- For signals that are used only interially in the block in which they are assigned, ensure that no combinational signal is read before it is assigned a new post-trigger value lest a memory might be inferred from a previous trigger
- For sequential signals, consider that values read are the pre-trigger values are not newly assigned values to be updated at the end of the current time
- What happens when there are conditions within states in which some outputs or variables are not assigned?
8. Complete Assignment for Combinatorial Functions
- The definition of a combinatorial function must have an assignment to every variable for all possible cases. Does your code cover all the cases?
- This must be assessed for each and every bit of each and every output variable.
- Draw a decision tree, label conditions for branching carefully including branches for default decisions.
- Mark where variables are assigned. If a tree can be traversed to a leaf without assigning every output variable then it is not complete.
1: always @(a,b) begin 2: z =0 ; 3: if (a>b) begin 4: z = 1; 5: x = 1; 6: end else begin // (a <= b) 7: x = 2; 8: if (a<(b-100)) begin 9: x=3; 10: y=3; 11: end else if ((b-3)>=a) begin // && ((a>= (b-100))) 12: if ((b-a) == 3) begin 13: x = 4; 14: y = 4; 15: end else if ((b-a) == 4) begin 16: x = 5; 17: y = 5; 18: end else if ((b-a) == 5) begin 19: x = 6; 20: y = 6; 21: end else begin 22: y = 7; 23: end 24: end else if ((b-3)<a) begin 25: x=8; 26: end 27: 28: end 29: 30: end 31:
- Tip: either
- flattened decision hierarchy can avoid mistakes (minimize embedded if…else and case constructs), but cases to uniquely identify can be numerous
- or strictly use binary decisions (if…else) (Binary Decision Diagram), though the depth can be great
- see the problem with the else body at line 25
- a default case helps cover every input state, but it does not guarantee complete assignment of all variables
- a complete asignment is garunteed by setting every output bit in every case statement
9. Bad things that can happen
Unintentional and Intentional Latches
In a combinatorial block any input condition in which any output is not assigned may result in an inferred latch.
Latches
always @(x,en) begin if (en==1) begin y = x; end end
always @(a) begin if (a>1) begin y=a; end end
Combinational
always @(x,en) begin if (en==1) begin y = x; end else begin y = 0; end
always @(a) begin if (a>1) begin y=a; end else begin y=0; end end
Unintentional Register Enables
- Inside a sequential-trigger block, not specifying an output may generate an enable on that output register
- unless the finite state machine extractor and synthesizer figures out how to avoid it by generating logic to repeat values in certain states (FSM discussed later)
- Not specifying an output assignment under some condition in a state means the previous value should be held when the block is triggered
Potential Enable on Register
always @(posedge clk) begin if (en==1) y<=x; end
always @(posedge clk) begin if (a<10) y<=a; else if (a>20) y<=20; end
No Enable
always @(posedge clk) begin if (a<10) y<=a; else if (a>20) y<=20; else y<=10; end
10. "Output Don't Care" using 'x
- What happens if output assignment is not provided for every input condition?
- If edge-selective, an enable may be added to registers
- If combinatorially triggered, latches created
- Can assign outputs to be `'x` to avoid extra enables or latches
- To a synthesizer, assignment to `'x` represents a don't care condition for an output
Assignment to 'x is treated as Don't Care for synthesis
Register with Enable
always @(posedge clk) if (a<10) y<=a; else if (a>20) y<=20;
No Enable
always @(posedge clk) if (a<10) y<=a; else if (a>20) y<=20; else y<=8'bx;
- latch vs combinatorial:
Latches
always @(x,en) if (en==1) y = a;
always @(a) if (a>1) y=a;
Combinatorial Blocks
always @(x,en) if (en==1) y = a; else y = 8'bx;
always @(a) if (a>1) y=a; else y=8'bx;
11. Unintended Registered Combinational Logic
- Implementing combinatorial logic signals in a edge triggered block may create registered logic if the signal is used outside the block (if not used outside the block, Xilinx will report “trimming” of the register)
- Combining combinatorial logic in an edge-triggered block limits you to only implement registered logic outputs. You can't have a combinatorial output from an edge-triggered block, though you can have internal combinatorial logic
- In the example below, if c is used outside the block a register will be created for it, though it's combinatorial version survives internally to immediately propagated through the remaining calculations. If c is not used elsewhere, the register is trimmed. Some caution against this practice.
logic [7:0] c,d; always @(posedge clk) begin c=a+b; d=c+3; q<=d; end assign g=c+1; //use of registered c
We'll discuss this more later, but for now our coding guidelines are that we not create registers using blocking statements. If it is trimmed, we are still following this rule. So, signals assigned in sequentially triggered block using a blocking statement should not be used outside the block they are assigned
always @(posedge clk) begin :blk automatic logic [7:0] _p,_d; _p=a+b; _d=_p+3; q<=_d; end
12. Case statement with Inferred Latch
input lighting; //see lightning input rain; // feel rain input sun; // sun shining
output logic cmd_go; // flag decision goto park output logic cmd_run; //flag decision run output logic cmd_umbrella; //flag decision to use umbrella
always @ (*) begin case ({lighting,rain,sun}) {3'b110} : begin cmd_go =0; cmd_run = 0; cmd_umbrella=0; end {3'b010} : begin cmd_go =1; cmd_run = 0; cmd_umbrella=1; end {3'b001} : begin cmd_go =1; cmd_run = 1; cmd_umbrella=0; end endcase end
- Many “physical” cases of {lighting,rain,sun} are not covered above and thus a physical latch may be inferred for synthesis
- Even if all physical cases are covered, in simulation lighting, rain and sun can take on values x and z
Meaning a latch is inferred for simulation under metalogic cases
i.e. if lighting, rain, or sun is equal to
'x
or'z
then do nothing (hold the previous value)
13. Adding Default Don't Care Case (skip)
Setting values to 'x is treated as Don't Care for synthesis
module mux3c (y, a, b, c, sel); output logic y; input [1:0] sel; input a, b, c; always @(a, b, c, sel) case (sel) 2'b00: y = a; 2'b01: y = b; 2'b10: y = c; default: y = 1'bx; endcase endmodule
- default covers `sel=="11"` setting to DONT-CARE
- For Synthesis: allows further logic optimization in synthesis
- For Simulation: it also covers any condition where sel has 'x or 'z in it and assigns output to metalogic value x
14. Consider Safe Defaults (skip)
Is this safe?
case ({lighting,rain,sun}) {3'b110} : begin go =1'b0; run = 1'b0; umbrella = 1'b0; end {3'b010} : begin go =1'b1; run = 1'b0; umbrella = 1'b1; end {3'b001} : begin go =1'b1; run = 1'b1; umbrella = 1'b0; end default : begin go = 1'bx; run = 1'bx; umbrella = 1'bx; end endcase
- default case item covers intentionally and accidentally omitted cases
- what if I see the sun and there is lighting? Use default cases and don't care with caution
- Give thought to incomplete specifications provided to you and raise concerns when appropriate
Consider a safer default, while minimizing impact of additional logic:
case ({lighting,rain,sun}) {3'b110} : begin go =1'b0; run = 1'b0; umbrella=1'b0; end {3'b010} : begin go =1'b1; run = 1'b0; umbrella=1'b1; end {3'b001} : begin go =1'b1; run = 1'b1; umbrella=1'b0; end default : begin go = 1'b0; run = 1'bx; umbrella = 1'bx; $display('Default Condition Encountered in Simulation'); end endcase
- Some designers rely on 'x in simulation to identify neglected cases, which this new version avoids
- useful for viewing waveform: easy to visually find red _│¯¯¯│___░░░¯¯¯¯¯│__
- systematically better to use
$display
or in SystemVerilog use$warning('Default Condition Encountered in Simulation');
or can use SystemVerilog assertions or assumptions (which we may learn about a later in the course)
15. Consider Impact of Defaults (skip)
Remeber that replacing Don't-Cares for outputs with a new value tends to increase the amount of logic required. This was studied in your introduction to digital logic design course. Remember K-Maps and grouping.
16. Alternate Default (skip)
go = 1'b0; run = 1'bx; umbrella = 1'bx; case ({lighting,rain,sun}) {3'b110} : begin go =1'b0; run = 1'b0; umbrella=1'b0; end {3'b010} : begin go =1'b1; run = 1'b0; umbrella=1'b1; end {3'b001} : begin go =1'b1; run = 1'b1; umbrella=1'b0; end endcase
17. Implementing Wild-Card Matches on inputs
- casex treats 'x and 'z as wild-card
- casez treats 'z as wildcard
- Note: in Verilog, ? is an alias for z in numerical literals
- So, when coding a case statement with "don't cares," use a casez
statement and use "?" characters instead of "z" characters in the case items to indicate wild-card bits.
casez (sel) 3'b1??: int2 = 1'b1; 3'b?1?: int1 = 1'b1; 3'b??1: int0 = 1'b1;
casex shouldn't be used for synthesis code, and only used in simulation if needed
18. Full Case and Parallel Case
- Full case: Does your logic cover all the cases for each and variable achieving complete assignment.Avoids inferred latches.
- Parallel Case: Are all the cases mutually exclusive?
- Overlapping cases can mean complex logic is involved to determine correct action.
- Control logic for Full, Parallel case statements can clearly be implemented as sum of products and easily optimized.
- Concepts apply to if-else constructs as well.
19. Non-parallel case
- This is a non-parallel case, order matters here since the match cases are overlapping
- Priority is given to the first case, then second, and so on
- Also, the first match blocks other statements
- Based on order, this implements a priority encoder
- Encoding logic in “the order” is not preferred style by some guidelines
20. Non-parallel case
casez ({lighting,rain,sun}) 3'b1?? : go =1'b0; run = 1'b0; bring_umbrella=1'b0; 3'b?1? : go =1'b1; run = 1'b0; bring_umbrella=1'b1; 3'b??1 : go =1'b1; run = 1'b1; bring_umbrella=1'b0; default : go = 1'b0; run = 1'bx; bring_umbrella = 1'bx; endcase
- Note the second case is used only if
rain==1 && lighting != 1
- This logic happens to be intended
- Note that in this example
default:
covers clear night 000 and any undetermined state: x inputs in simulation. Allows synthesizer to optimize logic forrun
andbring_umbrella
Though encoding intent explicitly can be better:
casez ({lighting,rain,sun}) 3'b1?? : go =1'b0; run = 1'b0; bring_umbrella=1'b0; //all cases with lightning 3'b?1? : go =1'b1; run = 1'b0; bring_umbrella=1'b1; // remaiing cases with rain 3'b??1 : go =1'b1; run = 1'b1; bring_umbrella=1'b0; // remaining cases with sun 3'b000 : go =1'b0; run = 1'bx; bring_umbrella=1'bx; // remaining case 000 default: go =1'bx; run = 1'bx; bring_umbrella = 1'bx; //only encountered in 4-state simulation endcase
What happens when the first two items are switched?
casez ({lighting,rain,sun}) 3'b?1? : go =1'b1; run = 1'b0; bring_umbrella=1'b1; //** //all cases with rain 3'b1?? : go =1'b0; run = 1'b0; bring_umbrella=1'b0; //** // remaining caes with sun 3'b??1 : go =1'b1; run = 1'b1; bring_umbrella=1'b0; // remaining cases with sun 3'b000 : go =1'b0; run = 1'bx; bring_umbrella=1'bx; // remaining case 000 default: go =1'bx; run = 1'bx; bring_umbrella = 1'bx; //only encountered in 4-state simulation endcase
function changed, therefore presentation was not "parallel case"
21. How to "Parallel Case"
module decoder (sel, a, b, c); input [2:0] sel; output a, b, c; reg a, b, c; always @(sel) begin {a, b, c} = 3'b0; casez (sel) 3'b1??: a = 1'b1; 3'b?1?: b = 1'b1; 3'b??1: c = 1'b1; endcase end endmodule
- Did designer intend that `b=1` whenever `sel[1]==1` ?
- Hopefully not because that is not what this does.
- `b=1` whenever `sel[1]==1 && sel[2]! = 1`
PARALLEL CASE IMPLEMENTS NON-OVERLAPPING MATCHES:
- parallel case: This code implements a priority encoder regardless of case order
- Statement preceding casez provides default case, so this block is also “full-case”.
Example
casez ({lighting,rain,sun}) 3'b1?? : go =1'b0; run = 1'b0; bring_umbrella=1'b0; //all cases with lightning 3'b01? : go =1'b1; run = 1'b0; bring_umbrella=1'b1; //cases with rain and no lighting 3'b001 : go =1'b1; run = 1'b1; bring_umbrella=1'b0; //cases with sun and no rain and no lighting 3'b000 : go =1'b0; run = 1'bx; bring_umbrella=1'bx; //no rain,no sun,no lighting default: go =1'bx; run = 1'bx; bring_umbrella = 1'bx; endcase
- the zero in the leftmost position of the second item makes it clear that this case does not apply when there is lighting
- It also creates a
case_item
with matches that are mutually exclusive to the first. - When all caseitems and any included default have mutually exclusive match sets, we have a parallel cases
- If any input to the
case_expression
can match more than one caseitem or a caseitem and default, the we do not have a parallel cases - As an academic exercise, consider effect of moving first case.
- Non-parallel Case <-> item order does matter
- parallel Case <-> case item order not does matter
casez ({lighting,rain,sun}) 3'b01? : go =1'b1; run = 1'b0; bring_umbrella=1'b1; //**//cases with rain and no lighting 3'b1?? : go =1'b0; run = 1'b0; bring_umbrella=1'b0; //**//all cases with lightning 3'b001 : go =1'b1; run = 1'b1; bring_umbrella=1'b0; //cases with sun and no rain and no lighting 3'b000 : go =1'b0; run = 1'bx; bring_umbrella=1'bx; //no rain,no sun,no lighting default: go =1'bx; run = 1'bx; bring_umbrella = 1'bx; endcase
- Note: Walking in park with umbrella during a lighting storm can be risky
22. casex,casez
- internal details of Verilog:
- In literal numbers ? is an alternative for z
- (It is not a shorthand for 0,1,x,z as with UDPs (User Defined Primitives) which are not encountered in this course)
- In literal numbers ? is an alternative for z
23. Implementing Don't care inputs (wild-card match)
- casex and casez implement symmetric wildcard matching
- casex treats
x
andz
as wildcard in caseexpression (sel) and caseitem - casez treats
z
as wildcard in caseexpression (sel) and caseitem
- casex treats
- When coding a case statement with "don't cares," use a `casez` statement and use "?" characters instead of "z" characters in the case items to indicate "don't care" bits.
casez (sel) 3'b1??: int2 = 1'b1; 3'b?1?: int1 = 1'b1; 3'b??1: int0 = 1'b1;
- symmetric wildcard matching means that a value of 'z in sel during simulation is considered a wildcard
casex should NOT be used for synthesis, only simulation if special need exists
24. Which Case Clarifying Study Examples
- These examples may be reviewed to test understanding of matching syntax/rules
- key point: wildcard matching is symmetric between case expressions and caseitems
sel = 2'bx1
case (sel) 2'b00 : case_item_statementA; 2'b10 : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casez (sel) 2'b00 : case_item_statementA; 2'b10 : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casex (sel) //(not useful for synth.) 2'b00 : case_item_statementA; 2'b10 : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
sel = 2'bz1
case (sel) 2'b00 : case_item_statementA; 2'b10 : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casez (sel) 2'b00 : case_item_statementA; 2'b10 : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casex (sel) //(not useful for synth.) 2'b00 : case_item_statementA; 2'b10 : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
sel = 2'b11
case (sel) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casez (sel) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casex (sel) //(not useful for synth.) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
sel = 2'b1x
case (sel) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casez (sel) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casex (sel) //(not useful for synth.) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
sel = 2'b1z
case (sel) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casez (sel) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casex (sel) //(not useful for synth.) 2'b00 : case_item_statementA; 2'b1x : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
sel = 2'b1x
case (sel) 2'b00 : case_item_statementA; 2'b1z : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casez (sel) 2'b00 : case_item_statementA; 2'b1z : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
casex (sel) //(not useful for synth.) 2'b00 : case_item_statementA; 2'b1z : case_item_statementB; 2'b01 : case_item_statementC; default : case_item_statementD; endcase
25. Specifying a Default Don't Care
- Two Options
- Second option may be preferred for reablity and isolated analysis of case statement
module mux3c (y, a, b, c, sel); output y; input [1:0] sel; input a, b, c; reg y; always @(a , b , c , sel) y=1'bx; //← case (sel) 2'b00: y = a; 2'b01: y = b; 2'b10: y = c; endcase endmodule
module mux3c (y, a, b, c, sel); output y; input [1:0] sel; input a, b, c; reg y; always @(a , b , c , sel) case (sel) 2'b00: y = a; 2'b01: y = b; 2'b10: y = c; default: y = 1'bx; //← endcase endmodule
26. Allowed Use of x,z,? for Synthesis
- casex: allows `x` in the literal for the
case_expression
and in the case-item literal expression - Synthesizer may only consider x and z/? in case-item expression
- casez: allows `z` in the literal for the
case_expression
and in the literal case-item expression- Synthesizer may only consider z/? in case-item expression
- A reasonable practice is to not use x or z/? in the
case_expression
for code intended for synthesis, though some synthesis tool may allow it
module my_parallel_case (sel, a, b, c); input [2:0] sel; output a, b, c; reg a, b, c; always @(sel) begin {a, b, c} = 3'b0; casez (sel) 3'b1??: a = 1'b1; 3'b?1?: b = 1'b1; 3'b??1: c = 1'b1; endcase end endmodule
Same as
casez (sel) 3'b1??: a = 1'b1; 3'b01?: b = 1'b1; 3'b001: c = 1'b1;
(caveat: later we'll see `case () inside`)
module my_parallel_case (sel, a, b, c); input [2:0] sel; output a, b, c; reg a, b, c; always @(sel) begin {a, b, c} = 3'b0; case (sel) 3'b1??: a = 1'b1; 3'b?1?: b = 1'b1; 3'b??1: c = 1'b1; endcase end endmodule
Remember,? Is same as z in literal expressions
- In simulation see x output
- In synthesis, x is treated as don't care and no extra logic is created
missing cases can lead to inferred latches
module mux3a (output reg y, input [1:0] sel, input a, b, c); always @* case (sel) 2'b00: y = a; 2'b01: y = b; 2'b10: y = c; endcase endmodule
- Not full case 'b11 produces latch behavior in synthesis and simulation
tip use always_comb
- for newer synthesizers, with
always_comb
this issue should be detected and reported
module mux3a (output reg y, input [1:0] sel, input a, b, c); always_comb case (sel) 2'b00: y = a; 2'b01: y = b; 2'b10: y = c; endcase endmodule
27. Use of Compiler/Synthesizer Directives (quick note)
- Avoid
full_case
andparallel_case
synthesizer directors
Note: some tools support a directive / pragma to complete the assignments by assigning don't cares
case (S): // synthesis full_case
This may lead to inconsistent interpretation between pre-synthesis simulation and post-synthesis behavior, or behaver between tools. Suggest to avoid unless adopted in conventions and standards of your work organization.
Reference: "full_case parallel_case", the Evil Twins of Verilog Synthesis
http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase.pdf
- newer SystemVerilog syntax faciliates universal understanding amount Synthesizers and Simulators
28. Case Items with multiple case expressions (language details)
some languages rely on case fall-through to group matches
0: 2: 4: print("value is even"); break;
verilog allows groupig matching expressions in one case item and does not have fall-through
0,2,4: $display("value is even");
29. Coding with a constant select (interesting option)
It is possible to use a constant select and use variable case items:
reg [2:0] encode ; case (1) encode[2] : $display(“Select Line 2”) ; encode[1] : $display(“Select Line 1”) ; encode[0] : $display(“Select Line 0”) ; default $display(“Error: One of the bits expected ON”); endcase
Do you need this? Not usually. But you may come accross such a construct.
30. System Verilog `priority`, `unique`, `unique0`
Implicitly embedded Sequential State (implied memories from imcomplete assignment)
a.k.a. inferred latch
Incomplete Assignment specification
case(x) // a 2-bit variable 2'b00: Q = 1'b1; 2'b11: Q = 1'b0; endcase
complete:
case (X) // a 2-bit variable 2'b00: Q = 1'b1; 2'b11: Q = 1'b0; default: Q = 1'b0; endcase
Let synthesizer choose (optimize) by assigning don't care to remaining assignments
case (X) // a 2-bit variable 2'b00: Q = 1'b1; 2'b11: Q = 1'b0; default: Q = 1'bx; endcase
Overlapping Conditional Cases
by convention, case statements with wild cards should be encoded uniquely
Consider the following:
casez ({a,b,c}) 3'b1??: f = 1'b0; 3'b??1: f = 1'b1; endcase
the second case provided does not imply that the output is high whenever c is high.
The following provides parallel case statments: no two match
casez ({a,b,c}) 3'b1??: f = 1'b0; 3'b0?1: f = 1'b1; endcase
module dut(f,a,b,c); output logic f; input a; input b; input c; always_comb begin casez ({a,b,c}) 3'b1??: f = 1'b0; 3'b10?: f = 1'b1; 3'b??1: f = 1'b1; default: f = 1'bx; endcase end endmodule
resulting design
SystemVerilog provides `unique case` and `unique0 case` to verify that a design using parallel cases `unique case` enforces matches in all cases while `unique0 case` only enforces uniqueness
a simulator will run through futher cases, but in no particular order, even after the first match to verify unique matches…second match will invoke an warning
casez ({a,b,c}) 3'b1??: f = 1'b0; 3'b0?1: f = 1'b1; endcase
Unique Case
- In the code below.
- Line 7: provides full case for every output
- Line 8: with unique/unique0, for each output the decision tree for each output is built with cases considered independently
- Line 12: the default case would not provide a full case, and actually has no effect here although it may be required by linter/coding standards
module dut(int2,int1,int0,irq); output reg int2, int1, int0; input [2:0] irq; always @(irq) begin {int2, int1, int0} = 3'b000; casez (irq) 3'b1?? : int2 = 1'b1; 3'b?1? : int1 = 1'b1; 3'b??1 : int0 = 1'b1; default: {int2, int1, int0} = 3'b000; endcase end endmodule // dut
❌
module dut(int2,int1,int0,irq); output reg int2, int1, int0; input [2:0] irq; always @(irq) begin {int2, int1, int0} = 3'b000; unique casez (irq) 3'b1?? : int2 = 1'b1; 3'b?1? : int1 = 1'b1; 3'b??1 : int0 = 1'b1; default: {int2, int1, int0} = 3'b000; endcase end endmodule // dut
- Should only use unique-case when properly coding parallel case
- Style recommendation is to always code parallel cases and use `unqiue` to allow tool to identify identify errors
✔️
module dut(int2,int1,int0,irq); output reg int2, int1, int0; input [2:0] irq; always @(irq) begin {int2, int1, int0} = 3'b000; //default pre-assigned /*v-----1 :::enables parallel case synthesis\nsome tools check for overlapping case:::green:::-0.5:::0.5*/ unique casez (irq) /*v-----3 :::must implement proper parallel case:::green:::-0.5:::0.5*/ 3'b1?? : int2 = 1'b1; 3'b01? : int1 = 1'b1; 3'b001 : int0 = 1'b1; default: // {int2, int1, int0} = 3'b000; preassigned /*^------4 :::must provide full case in case block:::green:::1:::0.5*/ endcase end endmodule // dut
- clean and explicit full and parallel case
- any variables assigned in one case item must be assigned in all case items, including the default.
module dut(int2,int1,int0,irq); output reg int2, int1, int0; input [2:0] irq; always @(irq) begin unique casez (irq) 3'b1?? : begin int0= 1'b0; int1= 1'b0; int2 = 1'b1; end 3'b01? : begin int0= 1'b0; int1= 1'b1; int2 = 1'b0; end 3'b001 : begin int0= 1'b1; int1= 1'b0; int2 = 1'b0; end default: {int2, int1, int0} = 3'b000; endcase end endmodule // dut
- In the next example, latches are inferred even with the default case provided on line 12.
- using alwayscomb on line 6 should cause the compiler to successfully identify implied memory, but you still need to understand how to identify the cause of implied memory in the code and correct it.
❌
module dut(int2,int1,int0,irq); output reg int2, int1, int0; input [2:0] irq; always @(irq) begin //{int2, int1, int0} = 3'b000; unique casez (irq) 3'b1?? : int2 = 1'b1; 3'b?1? : int1 = 1'b1; 3'b??1 : int0 = 1'b1; default: {int2, int1, int0} = 3'b000; endcase end endmodule // dut
31. SystemVerilog case-unique and case-priority
Violations in 2017 Standard (not implemented in every tool)
no-match with no provided default overlapping-match affect on synthesis for each case condition affect on simulation additional affect on simulation with no provided default unique violation violation functionality of each case-item determined independently after 1st match continue to look for second match, stopping at violation until violation or match, check all conditions for no-match unique0 violation functionality of each case-item determined independently after 1st match continue to look for second match, stopping at violation priority violation classic cascade condition logic until match, check all conditions for no-match
32. Use of unique0
Important to consider when assigning some outputs and not others in every case
The following is from from: https://dvcon-proceedings.org/wp-content/uploads/systemverilog-2009-enhancements-priority-unique-unique.pdf last few examples
❌ Incorrect Example 1:
unique assertion not satisfied
module dec2_4b ( output logic [3:0] y, input logic [1:0] a, input logic en); always_comb begin y = '0; // provids full case unique case ({en,a}) // unique assertion 3'b100 : y[a]='1; 3'b101 : y[a]='1; 3'b110 : y[a]='1; 3'b111 : y[a]='1; endcase // unique assertion not satisfied because of missing cases end endmodule
Example 2:
default satisfies unique assertion's requirement of full case, but is awkward
module dec2_4c ( output logic [3:0] y, input logic [1:0] a, input logic en); always_comb begin y = '0; unique case ({en,a}) 3'b100 : y[a]='1; 3'b101 : y[a]='1; 3'b110 : y[a]='1; 3'b111 : y[a]='1; default: ; // empty default endcase end endmodule
Example 3:
unique0 provides coder's intent to not cover all input cases
module dec2_4d ( output logic [3:0] y, input logic [1:0] a, input logic en); always_comb begin y = '0; unique0 case ({en,a}) 3'b100 : y[a]='1; 3'b101 : y[a]='1; 3'b110 : y[a]='1; 3'b111 : y[a]='1; endcase end endmodule
❌ Incorrect Example 4:
- Incorrect attempt to use default to achive complete assignment.
- in example multiple output bits are assigned from the case but not every bit is assigned for every input vector
- provide complete assignment with
always_comb
module dec2_4e ( output logic [3:0] y, input logic [1:0] a, input logic en); always_comb begin unique case ({en,a}) 3'b100 : y[a]='1; 3'b101 : y[a]='1; 3'b110 : y[a]='1; 3'b111 : y[a]='1; default: y = '0; endcase end endmodule
33. SystemVerilog Set membership case construct: case inside
inside keyword to the right of the paranthesis modifies the matching behavior of case to accept wild-card and ranges
For example:
logic [2:0] status; always @(posedge clock) priority case (status) inside 1, 3 : task1; // matches 'b001 and 'b011 3'b0?0, [4:7]: task2; // matches 'b000 'b010 'b0x0 'b0z0 // 'b100 'b101 'b110 'b111 endcase // priority case fails all other values including // 'b00x 'b01x 'bxxx
- supports wild-cards in caseitems (literals)
- can replace casez in practical use case
- differs from casez in that asymmetric wild-card matching is used, only z in the caseitems are considered, not the input/caseexpression
- supports ranges like [4:7] for 4,5,6,7
module top (q,dA,dB, sel, clk); output logic q; input logic [1:0] sel; input logic dA,dB, clk; always_ff @(posedge clk) begin unique case (sel) inside 3 : q<=dB; [1:2] : q<=dA; 0 : q<=q; endcase end endmodule
34. priority-if unique-if
- priority keyword provides checks on set of conditions provided in if-else tree for which an else statement is provided
- may use priority when intending to ensure that all cases are covered
- may use unique when intending to code non-overlapping conditions
- may use unique0 when intending to code non-overlapping conditions and ensure all cases are covered
Violations in 2017 Standard (not implemented in every tool)
no-match with no else overlapping-match affect on simulation additional affect on simulation with no else unique violation violation after 1st match continue to look for second match, stopping at violation until violation or match, check all conditions for no-match unique0 violation after 1st match continue to look for second match, stopping at violation priority violation until match, check all conditions for no-match
- from IEEE SystemVerilog section 12.4.2 unique-if, unique0-if, and priority-if https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8299595 :
The keywords unique, unique0, and priority can be used before an if to perform certain violation checks.
If the keywords unique or priority are used, a violation report shall be issued if no condition matches unless there is an explicit else.
Example to invoke violation:
unique if ((a==0) || (a==1)) $display("0 or 1"); else if (a == 2) $display("2"); else if (a == 4) $display("4"); // values 3,5,6,7 cause a violation report
Example Violation-free:
priority if (a[2:1]==0) $display("0 or 1"); else if (a[2] == 0) $display("2 or 3"); else $display("4 to 7"); // covers all other possible values, // so no violation report
If the keyword `unique0` is used, there shall be no violation even when there is no matched. For example:
unique0 if ((a==0) || (a==1)) $display("0 or 1"); else if (a == 2) $display("2"); else if (a == 4) $display("4"); // values 3,5,6,7 // cause no violation report
Unique-if and unique0-if assert that there is no overlap in a series of if–else–if conditions, i.e., they are mutually exclusive and hence it is safe for the conditions to be evaluated in parallel.
In unique-if and unique0-if, the conditions may be evaluated and compared in any order. The implementation shall continue the evaluations and comparisons after finding a true condition. A unique-if or unique0-if is violated if more than one condition is found true. The implementation shall issue a violation report and execute the statement associated with the true condition that appears first in the if statement, but not the statements associated with other true conditions.
After finding a uniqueness violation, the implementation is not required to continue evaluating and comparing additional conditions. The implementation is not required to try more than one order of evaluations and comparisons of conditions. The presence of side effects in conditions may cause nondeterministic results.
A priority-if indicates that a series of if–else–if conditions shall be evaluated in the order listed. In the preceding example, if the variable a had a value of 0, it would satisfy both the first and second conditions, requiring priority logic.
35. Appendix
Synthesizer Directives
Some synthesis tools have special directives that can be provided in comments One such directive is to provide full-case not coded explicitly in the standard HDL
Full Case Directive Example:
- Full case directive is redundant if default case is given
- Without the directive, all unmatched cases become latches for SYNTHESIS, and cause holding of values in SIM.
- With the directive, the output defaults to don't care when not covered by the coded cases for the purpose of synthesis avoiding latches.
- This causes a discrepancy between simulation and synthesis unless post-synthesis code is used for simulation
- Using
full_case
directive out of habit when missing a case accidentally in design is poor practice
Parallel Case Directive Example:
This is basically a priority encoder
i.e. case_items
1??,01?,001 for sim
But in synthesis case 2 is handled irrespective of case1 and generates the following
Synthesis Result: