Skip to content

Verilog coding guidelines

wnew edited this page Jan 1, 2012 · 32 revisions

Coding Guidelines for development of primitives in Verilog HDL

This document consists of some general guidelines followed by a more technical set of rules that must be kept in mind while coding in Verilog HDL.

####General Guidelines:

  1. The functionality of the primitive or sub-system should be clear, before starting Verilog coding.
  2. Verilog should be written in a manner that helps the synthesis tool infer desired digital logic.
  3. To ensure that the synthesis tool has inferred the desired logic, it is necessary to synthesize the code on the target device’s synthesis tool (in our case XST). (Note: Read carefully IEEE Verilog Language Reference Manual for synthesizable and non-synthesizable constructs in Verilog)
  4. Every tool has its synthesis guidelines and it necessary to understand them before coding.
  5. Do have a look and take appropriate actions for the warnings that the synthesis tool generates (if any) while you compile your design.
  6. To be on the safer side, it is suggested to carry out the functional and gate level (netlist) simulation of the primitives and sub-systems.

####Formatting Guidelines

  1. Only one module per file.
  2. 3 Space indentations, NO Tabs, these make a hash of the code when opened in another application.
  3. All parameter names must be in CAPS CASE.
  4. All port names must be in full lower case.
  5. Include spaces either side of operators.
  6. One Verilog statement per line
  7. Keep the line length to max 80 characters.
  8. One line comments (//) must be used. Do not use multiline (/.../) comments.
  9. Any code that is commented out must be removed before committing to the repository.

####Guidelines for coding in Verilog HDL: These guidelines only form a checklist while coding. They are not exhaustive in nature. It is assumed that the reader is aware of the concepts of digital design and to a certain extent, understands Verilog coding. Throughout these guidelines signals having “_i” suffix are input signals and those with “_o” suffix are output signals. Active low signals have “_ni” and “_no” suffix.

  1. Latches are harmful in any synchronous design and hence they should be avoided.

The following constructs in Verilog lead to an inference of latch by the synthesis tool:

a.Missing ‘else’ statement in a combinational block b.It is not just the case of the missing else statement. Even if the else statement is present, the same variable will have to be assigned in the else section of the code. A latch will still be inferred if the same variable is not assigned in the ELSE section.

always@(select_i or in1_i)
begin
   if(select_i == 1’b1)
   begin
      op_o = in1_i;
   end
end

Note: For those using Verilog-2001 compliant synthesis tool, it is better to use always@(*) for combinational constructs. Otherwise all the variables in the’ if’ and ‘else-if ‘conditions and those getting assigned (i.e. on the right-hand side of the assignment) within the block have to be added.

c. Missing ‘default’ statement in ‘case’ block:

always@(*)
begin
   case(select_i)
      1’b0 : op_i = in1_i;
   endcase
end

d. Using combinational loops:

always@(*)
begin
   if(en_i)
      data_o = in_i;
   else
      data_o = data_o;
end

Such combinational loops would create a latch.

  1. Synchronize a reset signal if using an asynchronous reset In case an asynchronous reset signal is used in the design, it must be synchronously de-asserted. Assertion may not cause problems, but it needs to be de-asserted synchronous to the respective clock. A two-flop synchronizer can be used to do this. If there are multiple-clock domains, the reset needs to be synchronized in all the respective clock domains. Synchronous de-assertion of resets avoids the problem of metastability.

  2. Use non-blocking assignments for sequential logic blocks and blocking for combinational logic blocks To understand the sequence of execution of the blocking and non-blocking statements in Verilog, please refer the Verilog Language Reference Manual. It also provides an event queue according to which the events are scheduled by a simulator.

always@(*)
begin
   if(en_i)
   begin
      op_o = in1_i ;
   end else begin
      op_o = in2_i ;
   end
end

For sequential blocks,

always@(posedge clk_i  or negedge reset_ni)
begin
   if(!reset)
   begin
      data_o <= 8’b0;
   end else begin
      data_o <= data_d;
   end
end

Also, do not mix blocking and non-blocking assignments in a single ‘always’ block. Use $strobe instead of $display to display variables that have been assigned using the non-blocking assignment.

  1. Avoid Race Conditions : Do not assign to the same variable from different ‘always’ blocks

In terms of hardware description, ‘always’ blocks imply concurrency. However, a simulator may randomly choose to sequence these blocks during execution in a given time stamp and this might lead to mismatch in the output due to race condition. The output may turn out to different for different simulators, as it depends on the execution order of the blocks.

Also, synthesis tool infers such conditions as a signal driven by multiple drivers. One must check for same outputs beings assigned from different blocks, when such a warning (or error) is shown by the synthesis tool.

always@(posedge clk_i)
begin
   op_i <= in1_i;
end

always@(posedge clk_i)
begin
   op_i <= 8’b0;
end
  1. State machine coding techniques: Finite State machines (FSM) should have a combinational block in which the next state is assigned using the present state and a sequential block which assigns the next state to the present state.

It is preferable to use separate ‘always’ blocks for assigning the outputs based on the present state. This holds true for both Moore and Mealy type of state machines.

A three always block state-machine can be written as follows:

parameter IDLE    = 2’b00 ;
parameter RD_DATA = 2’b01 ;
parameter STOP    = 2’b10 ;
parameter ACK     = 2’b11 ;

// Sequential Block for state assignment
always@(posedge clk_i or negedge reset_ni)
begin
   if(!reset)
   begin
      present_state <= IDLE;
   end else begin
      present_state <= next_state;
   end
end

//Combinational block for deciding next_state

always@(*)
begin
   case(present_state)
   begin
      IDLE: 
         if(in1_i == 8’h37)
            next_state = RD_DATA;
         else
            next_state = present_state; 
          
      RD_DATA: 
         if(cnt == 8’hFF)
            next_state = STOP;
         else
            next_state = present_state; 

      STOP:
         if(buf_empty && !valid)
            next_state = IDLE;
         else
            next_state = ACK;
        
      ACK: 
         next_state = IDLE;

   endcase
end
        
// assigning the outputs

assign valid_o = (present_state == ACK) ? 1’b1 : 1’b0;

always@(*)
begin
   case(present_state)
   begin
     IDLE:
        buf_empty_o = 1’b0;
     RD_DATA:
        buf_empty_o = 1’b0;
     STOP: 
        if(cnt == 8’hFF)
           buf_empty_o = 1’b1;
        else
           buf_empty_o = 1’b0;
     ACK:
        buf_empty_o = 1’b0;
   endcase
end
  1. Use of ‘timescale directive: ‘timescale’ directive provides the simulator with the details about the real time by specifying reference time unit and the precision within that time unit. The syntax is `timescale reference time step / time precision. Usually, a 1ns/1ps should suffice for most of the cases. If this directive is not specified, the simulator would take default settings, which may vary across different simulators. Also, it should be noted that this directives gets overridden while compilation and hence it is a good practice to declare it in test-bench or test-case to avoid confusion.

  2. Use of ‘casex’ and ‘casez’ statements: Do not use casex for synthesizable logic. If at all, use only casez construct.

  3. Full case and Parallel case directives: Full case and parallel case directives are meant for the synthesis tool and can cause pre and post synthesis simulation mismatches. (In Verilog 2001, an additional construct called attributes has been added to the language. You can make use of this construct to overcome the Synopsys directive and issues with these.)

  4. Synthesizable Verilog: Not all keywords in Verilog HDL are synthesizable. Go though Verilog Language Reference Manual to check the ones which are acceptable for synthesis. Synthesis tools would ignore any non-synthesizable keyword / statement during synthesis. (e.g. # delays are not synthesizable).

  5. It is recommended to code all intentional priority encoders using if-else-if-else constructs.

  6. Do not use internal tri-state signals. This means that you should not use the inout type of signals in the modules that are NOT top level modules.

  7. Multiple clock sources must be used only in the top- most module in the hierarchy. All the modules below the top module must have only one clock source. This helps in timing closure for most of the tools.

####Design Guidelines:

  1. Avoid the use of clock gating as much as possible. Also, use clock only for synthesis of sequential blocks and not for combinational blocks i.e. do not use always@(clk or reset or sel) etc.

  2. Only the registered output (output from flip-flops) should be used while crossing the clock domains. If the output of combinational logic is used, the target domain clock pulse might capture a glitch of the combinational output and cause malfunction of the circuit.

  3. Multiple clock domains designs: During clock domain crossing,

3a. Slower domain to faster clock domain: For single bit signals, a two-flop synchronizer can be used, whereas for multi-bit signals, an asynchronous FIFO needs to be implemented.

3b. Faster clock domain to slower clock domain: For single bit signals, handshaking mechanism can be used, and for multiple bit signals, use of asynchronous FIFO is recommended.

Initial Version developed by Kaushal D. Buch, GMRT-TIFR

For suggestions or queries, please email [email protected] or [email protected]

Clone this wiki locally