Lecture 08 – Full and Parallel Case

Ryan Robucci

1. References

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

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:

pikchr-QLTFD.svg

decision tree for output u:

pikchr-1QhBk.svg

Multi-output decision tree:

pikchr-2aTd6.svg

Flattened, multi-output decision tree:

pikchr-kUnGm.svg

  • 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

      pikchr-AiJjA.svg

  • 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;
    

pikchr-corB2.svg

  • 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

Priority Example

Assume you are standing outside your door considering an afternoon in the park or a return inside

Decision Algorithm:

  1. If see lightning go inside
  2. Else if see rain walk to park with umbrella
  3. 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

    pikchr-XSGKs.svg

pikchr-Bufc6.svg

6. Overlapping Cases

  • overlapping cases described by a flattened case have special meaning

    pikchr-bSq4o.svg

is really a cascade of binary decisions

pikchr-3BDbr.svg

not the same as

pikchr-ExKxX.svg

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

Warning: default case does not ensure *complete assignment*

  • 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

Important: Output 'x

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;

restrict use of casex

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 for run and bring_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

lightning-rain-sun.svg

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

lightning-rain-sun-wrong.svg

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)

23. Implementing Don't care inputs (wild-card match)

  • casex and casez implement symmetric wildcard matching
    • casex treats x and z as wildcard in caseexpression (sel) and caseitem
    • casez treats z as wildcard in caseexpression (sel) and caseitem
  • 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

danger: restrict use of casex

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

D

casez (sel)
  2'b00   : case_item_statementA;
  2'b10   : case_item_statementB;
  2'b01   : case_item_statementC;
  default : case_item_statementD;
endcase

D

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

C


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

D

casez (sel)
  2'b00   : case_item_statementA;
  2'b10   : case_item_statementB;
  2'b01   : case_item_statementC;
  default : case_item_statementD;
endcase

C

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

C


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

D

casez (sel)
  2'b00   : case_item_statementA;
  2'b1x  : case_item_statementB;
  2'b01   : case_item_statementC;
  default : case_item_statementD;
endcase

D

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

B


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

B

casez (sel)
  2'b00   : case_item_statementA;
  2'b1x   : case_item_statementB;
  2'b01   : case_item_statementC;
  default : case_item_statementD;
endcase

B

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

B


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

D

casez (sel)
  2'b00   : case_item_statementA;
  2'b1x   : case_item_statementB;
  2'b01   : case_item_statementC;
  default : case_item_statementD;
endcase

B

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

B


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

D

casez (sel)
  2'b00   : case_item_statementA;
  2'b1z   : case_item_statementB;
  2'b01   : case_item_statementC;
  default : case_item_statementD;
endcase

B

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

B

25. Specifying a Default Don't Care

  • Two Options
  • Second option may be preferred for reablity and isolated analysis of case statement
default preset
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
default case
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;


don't use case when casez is needed

(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


Synthesis vs Simulation of assignment to 'x

  • In simulation see x output
  • In synthesis, x is treated as don't care and no extra logic is created

Danger Inferred Latches

missing cases can lead to inferred latches

  • Not full case 'b11 produces latch behavior in synthesis and simulation

tip: use always_comb

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 and parallel_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

f a b c

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
1 0 1 0 1 0 1 0 1 0 1 0 int2 int1 int0 irq 0 1 2 0 1 int2 int1 int0 irq 1 2 0

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
1 0 1 0 1 0 int2 int1 int0 irq 0 1 0 2 1 int2 int1 int0 irq 2 1 0


Style: parallel-case

  • 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
1 0 1 0 1 0 int2 int1 int0 irq 0 1 0 1 0 1 0 1 1 0 2 $_DLATCH_P_ D E Q $_DLATCH_P_ D E Q $_DLATCH_P_ D E Q int2 int1 int0 irq 0 1 2

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

Quote from IEEE SystemVerilog 2017 section 12.4.2

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:

Author: Dr. Ryan Robucci

Created: 2025-03-12 Wed 08:52

Validate