How to write synthesizable RTL

  • RTL is a description of a sequential design that possesses data between registers, possibly applying some logic to the data on the way.
  • RTL is the synthesizable subset of an HDL (Verilog, VHDL, System Verilog).
    • But not everything can be implemented in the hardware (i.e. with logic gates). Therefore, only a subset of the HDL is considered "Synthesizable".
The Unforgivable Rules-

  • Never put logic on "reset" or the "clock".
    • Never mix reset types.
    • Never create clock domain crossings.
  • Do not infer latches
    • Every "if" has an "Else".
    • Full case settlement
    • LHS for every signal for each condition
  • Assignment (always blocks)
    • In Combinational (always @ *) use blocking (=) assignment
    • In Sequential (always @ posedge) use non-blocking (<=) assignment
    • Always separate the sequential and combinational logic
    • Never assign a signal (LHS) from more than one always blocks

1. No logic on reset (or clock):-

  • The reset and clock signals are not just any signal and they needed to be handled with the core.
    • clock signals should be only used to clock registers.
    • reset signals should be only used to reset registers.
  • Logic glitches are a [characteristic, not a design error.
Any glitch on the clock or reset signal is catastrophic!

-    A glitch on the clock causes an unwanted data sampling.

-    A glitch on the reset causes fops to reset accidentally.

Therefore, do not put the logic on reset and clock signals.

Ex: 1. assign something == a&&reset;                    ......(wrong)

      2. always@*

            case (state)                                         .....(wrong)

                1'b1011 : if (b || reset)

                next_state=idle;

Note: Let's assume you have an input that you want to sample at the beginning of a process. The wrong way to do it would be a something like this-

input in;

reg in_sampled;

always @ (posedge clk or negedge rst)

        if (!rst) in_sampled <= in;

        else...........

But remember we need to map a synchronous block to a flip-flop. A flip-flop can be reset/set to '0' or '1' and "in" is a non-constant signal. Therefore, the synthesizer would need to decide to create a set or reset signal during reset, depending on the value of "n". This is logic on reset.

- Alternate way is by making an "initialize" state and a "start"state.

Ex: always@*

                case state:

                  INIT: begin 

                                    next_in = in;

                                    next_STATE = start;

......

always @ (posedge clk or negedge rst)

if(!rst)

        state <= INIT;

        in_sample <= 0;

    else

        state <= next_state;

         in_sampled <= next_in;


2. No clock domain crossings:-

  • Some (or most) designs have more than one clock.
      • These clocks may have a different source.
      • They may run at different frequencies.
      • If we cannot know the phase between them- they are "asynchronous".
  • If you cannot have a path between asynchronous clocks, this is known as a "Clock Domain Crossing".
3. No Latch Interference:-

  • The only way to "remember" a value, is with a register (i.e. flip-flop or latch).
    • If we accidentally ask to remember a value, a latch with inferred.
  • This is due to not assigning LHS for all conditions.
    • A missing case option (and no default)
    • A combinational 'if' but without an 'else'.
    • An if/else/case without assignment to all LHS signals.
Ex: always@*
        case (state)
        START: begin
            next_state=FINISH;
        end
        FINISH: begin
            next_state=START;
               finished=1'b1;
            end
        endcase
    • For all of the above, we have to keep the value for the non-defined condition.
  • A similar problem is a missing signal in the sensitivity list.
    • Essentially don't change the output when this signal changes.
    • The Synthesizer may ignore this, but the simulator will adhere to it!
    • Just use always@* and this will not happen!
  • To ensure no latch interference, just assign default values for all combinational assignments:
    • At the beginning of an always@* block assign all LHS to a default value.
    • Overwrite the default value, as necessary within following if/else/case conditions.
Ex: always@*
        begin
                next_a=1'b0;
                next_b=1'b1;
                next_c=1'b0;
            case (state)
                       STATE1:next_a=1'b1;
                       STATE2:next_b=1'b0;
                       STATE3:next_c=1'b1;
            ----
            ------
Also, it's a good practice to provide flip-flop with a reset value.
always@ (posedge clk or negedge rst)
    if (~rst)
        state <= START;
    else
    ----
    -------
4. And Finally, Seq/Comb separation !
  • "Sequential logic" is defined within simple "always@ posedge" blocks that map directly to std cells from the library.
  • Everything else (i.e., combinational logic) is defined using "assign" and "always@*" blocks.
  • In other words, there is a clear separation between sequential and combinational logic.
5. No multi-driven nets:-
  • Never assign a signal from two always blocks (or "assign's" )
    • This results in two logic blocks driving the same net.
    • CMOS cannot tolerate multi-driven nets
  • Each register have its own always@ posedge block.
  • Signals can appear in the LHS of only one always@8 block.
  • Signals can appear in the RHS all over the phases.
  • A combination signal cannot be assigned (LHS) by itself.
  • In other words, it cannot appears on both LHS and RHS in the same logic path even upstream several stages.

Comments

Popular posts from this blog

TCL Scripts

Logic Synthesis

ASIC Block level PD flow