You are here: Home / Past Courses / Spring 2011 - ECPE 293B / Resources / Hardware Style Guide

Hardware Style Guide

This style guide is courtesy of the NetFPGA project team, including John Lockwood and Greg Watson.

This is a GUIDE for writing Verilog for synthesis. As such it is a list of suggestions and recommendations, not rules. Some suggestions are very subjective while others are almost mandatory - i.e. you should have a good reason for not following them.

Naming

Module/instance naming

You are free to name your modules as you please, provided that the following conditions are met:

  • The final top level modules should be the chip name with _top appended, e.g. unet_top.v.
  • When instantiating a module the instance name should be either:
    • identical to the module name if the module name adequately describes the purpose. e.g. sram_output_fifo sram_output_fifo (...);
    • the module name followed by a descriptive subscript. e.g. fifo fifo_sram_output (...);

 

Signal naming

  • All signals are lowercase alpha, numeric and underscore only. (Exceptions permitted for external pins and auto-generated code.)
  • Avoid active low signals in the core. If you must have them then append _b to the signal name. e.g. active_low_b
(Active low signals at the pins are fine but invert them in the core before using them.)
  • Buses are numbered high order to low order: bus[31:0]
  • Array locations are numbered low to high. e.g. 2048x32 bit RAM:
reg [31:0] mem_array [0:2047];
  • Don't try to indicate the direction of the signal in the name: e.g. my_signal_in or my_signal_i is bad. Just use my_signal. This makes it much easier to hook up blocks.
  • Do use the same name for the signal at all levels of hierarchy.
  • Do indicate the direction of a signal as a comment in the signal list:
my_module (
    my_signal,  //I: Input from other_module
);

 

Constant naming

  • Use capitals to identify constants: e.g. DEBUGIDLE

 

Indenting

  • Use three spaces per indentation level. Expand tabs to spaces to prevent indentation changing based upon editor settings.
Vim users can place the following modeline near the top or bottom of each file:
// vim:set shiftwidth=3 softtabstop=3 expandtab:
Emacs users
  • Indent begin/end statements as follows:
if (this) begin
    do something;
end
else begin
    do something else;
end

 

Commenting

  • Comment your code. Someone will be coming back to this code in 6 months time, and that person will probably be you.
  • When entering multi-line comments, try to use the "/*..*/" commenting rather than putting "//" in front of every line. It makes editing much easier.

 

Module declaration/instantiation

  • Each module should be defined in a separate file.
  • Use Verilog 2001 ANSI-C style port declarations:
my_module (
   input signal_A,
   input signal_B,

   output reg signal_C,

   input clk
);
  • Declare inputs and outputs one per line. It makes grep'ing so much easier. If I want to find who drives a signal in code written this way, I can grep for "output.*signal". It also gives you a handy place to put a comment.
  • Group signals logically by their function (e.g. keep DDR signals together). Place the 'miscellaneous' signals at the end of the signal list. This will include clk,reset and anything else that generally has a high fanout and goes between many modules.
my_module (
    signals_to_from_block_A, // description

    signals_to_from_block_B, // description

    input reset,
    input clk
);
  • Instantiate signals one per line whether as inputs or when instantiating a block—it makes it easier on the scripts!
  • Don't instantiate modules using positional arguments. Always use the dot form:
my_module my_module(
    .signal (signal),
    .a_bus  (a_bus),
    ...
);
(There are some utilities for building instantiations of modules that work if the signal names are consistent between levels.)
  • Use explicit in-line parameter redefinition when overriding parameters:
my_module my_module #(.WIDTH(32)) (
    ...
);

 

Clocking

  • Core clock signal for a module is 'clk'.
  • All other clocks should have a description of the clock and the frequency embedded—it makes life easier for the synthesis person. e.g. clk_ddr_400
  • Keep to one clock per module if possible. (It's easier to understand and it will synthesize much faster.)

 

Synchronization/clock domain crossing

  • If you have signals that need to be synchronized (cross asynchronous clock boundaries) then name the synch flops as synch_<something>. e.g.
reg synch_stage_1, synch_stage_2;
These can be given special treatment for simulation, and you can apply automatic checks to ensure all synch domains have synch flops.
Corollary: Don't use 'synch' in any other flop names.
  • Avoid mixing synchronization/domain crossing code with general logic. Clearly delineate the code in a large module with other logic or place in a separate module.
  • The statements "input" and "output" will default their arguments to wires, so there is no need to put a wire statement in the code for anything that is an input or an output. Other wires (hookup wires) need to be declared only if they are multi-bit, although some people like to declare the single-bit ones as well.

 

Reset

Reset signal is:

  • called 'reset'
  • active high within the core
  • synchronous

 

Assignment statments

  • Sequential elements MUST only have non-blocking assignments (<=)
  • Combinatorial should only have blocking assignments (=)
  • Don't mix blocking and non-blocking assignments within a code block.

 

Parameters, defines and constants

  • Do parameterize modules if appropriate and if readability is not degraded.
  • Propagate parameters through the hierarchy:
#(parameter ADDR_WIDTH = 10,
  parameter DATA_WIDTH = 32)
( input [DATA_WIDTH-1:0] data,
  input [ADDR_WIDTH-1:0] addr,
  ...
);
...
/* instance using the same parameters */
b_module b_module_0
#(.ADDR_WIDTH(ADDR_WIDTH),
  .DATA_WIDTH(DATA_WIDTH))
( .data(data)...);
  • Place all global `defines in external defines files that are included in the project.
  • Do not declare `define statements in individual modules.
  • Use localparam for constants that should not be redefined from outside of the module. E.g. the states for a state machine:
localparam IDLE    = 3'd0; // Oven idle
localparam RAMP_UP = 3'd1; // Oven temperature ramping up
localparam HOLD    = 3'd2; // Hold oven at bake temperature
...

 

Debugging & assertions

  • Group your assertions at the end of the module, surrounded by synthesis translate_off and synthesis translate_on:
... // User code here

// synthesis translate_off
always @(posedge clk)
   // Verify that the length never exceeds 10
   if (len > 10)
      $display($time, " ERROR: Length exceeds 10 in %m");
// synthesis translate_on

endmodule

 

State machines

  • Simple state machines: feel free to combine flops with next-state combinational logic
  • Long/complex state machines: separate flops from combinational/next state logic. e.g.
always @(posedge clk)
begin
    state <= state_nxt;
    addr <= addr_nxt;
end

always @*
begin
    // Default to current state
    state_nxt = state;
    addr_nxt = addr;

    if (reset)
    begin
        state_nxt = START_STATE;
        addr_nxt = 'h0;
    end
    else
    begin
        case (state)
            START_STATE: begin
                if (ready)
                    state_nxt = GO_STATE;
            end
            ...
        endcase
    end
end
  • Assign default values at the start of conditional statements: this tends to shorten the code (more readable) and helps to ensure that everything always gets a value.
always @* begin
   a = default_a;
   b = default_b;

   case (expr)
      state_1 : ... // if a should be default_a then we dont put it here.
      state_2 : ...
   endcase

 

General Verilog coding

  • Synthesizable logic goes in leaf modules.
    • As much as possible, try to keep all synthesizable code in leaf modules, and use hierarchical modules only to hook things up. A single "!" at core can wreak havoc.
  • Use @* for the sensitivity list for combinational logic always blocks:
always @*
   a = b + c;
  • Don't try to write ultra-slick Verilog—it makes it harder to read.
  • Avoid for loops—they are usually difficult to understand. Sometimes they are really useful: e.g. unrolling bit expressions that involve other bits.
  • generate blocks are encouraged for generating instances/blocks of code:
generate
   genvar i;
   for (i = 0; i < 4; i = i + 1) begin: par_gen
      parity8 parity(
         .data(wr_data[(i * 8) +: 8]),
         .parity(wr_par[i])
      );
   end
endgenerate
  • Don't use the `include directive in synthesizable code. This is never necessary and introduces synthesis dependencies that are difficult to track.
    • If an include absolutely cannot be avoided then surround included file with `ifdef`define and `endif directives.
  • No tri-states in the core! Only tri-state I/Os!
  • Use small-ish modules—try to keep modules small and readable.

 

Synthesis

  • NO LATCHES! Check the results of synthesis and ensure you do not have any inferred latches—it usually means you missed something in your coding.
  • No asynchronous logic/combinatorial feedback loops/self timed logic.
  • Try to flop all inputs and outputs to a block—it makes it much easier to set timing constraints. This means you will have to think about how modules are grouped for synthesis. It's fine not to have flops in a module if that module is going to be synthesized with the modules that do have the relevant source/sink flops.
  • All non data-path flops must have a reset.
  • All data-path flops must reset within 5 clocks. Usually this means that you only need to reset the input pipeline flops, and subsequent flops will reset within the specified number of clocks. If you're not sure then reset them explicitly.
  • If you do have some flops with reset and some without, then don't put them both in the same block of code:
BAD:
always @(posedge clk) 
    if (reset) 
        stage_1 <= 'h0;
    else begin
        stage_1 <= input;
        stage_2 <= stage_1;
    end
This causes reset to be used as an enable on stage_2. Move stage_2 to a separate block or take it out of the if-then-else.
GOOD:
always @(posedge clk) 
   if (reset) 
      stage_1 <= 'h0;
   else
      stage_1 <= input;

always @(posedge clk)
   stage_2 <= stage_1;
  • Specify ALL cases in a case statement (use default if you don't need them all).
Bear in mind that XST will prioritize in top-to-bottom order.
  • Don't use casex and casey
  • Don't use pragmas for case: parallel_case full_case
  • Keep it simple. If you cannot envisage the final synthesized logic for your code then XST will probably have a hard time.
  • Don't mix hand-instantiated circuitry with RTL

 

Acknowledgements, further reading, and more

Many of these recommendations are courtesy of Paul Zimmer.

For further reading on Verilog styles, synthesis techniques, useful scripts, etc I recommend the following papers:

Many of the above papers appeared at SNUG: http://www.snug-universal.org/papers/papers.htm