Coding for Reuse Course - Module 1 Lesson 1
Coding Rules and File Control

In this lesson we will study some basic rules associated with coding for reuse. We will also learn about version (file) control. The rules will be frequently cross referenced throughout the balance of this course.

Rules and Recommendations  

The Virtual Socket Interconnect Alliance (or VSIA for short) existed until 2008. It consisted, primarily, of a group of device manufacturers and synthesis vendors. The VSIA devised different sets of rules and recommendations to facilitate and evaluate the reuse quality of designs. Two important contributions from this group were OpenMore and QIP. OpenMore and QIP were spreadsheets that assigned various points to each rule and recommendation, and ranges of total points for reuse quality.

Both OpenMore and QIP were based upon the "Reuse Methodology Manual" (RMM) by Michael Keating and Pierre Bricaud. OpenMore was based upon version 2, first published in 1998. QIP focused more upon system-on-a-chip (SoC) designs and was based upon RMM version 3, published in 2002.

The author of this page gratefully acknowledges the contributions of VSIA documents and of Ms. Keating and Bricaud in the creation of these lessons. Although none of these rules are intentionally verbatim, some of these rules originate from a subset extracted from OpenMore; with further interpretation and extrapolation from the author. Additional rules were developed by the author himself, drawing upon years of coding designs for reuse. There are no points associated with these rules, they are to be used simply as guidance in the creation of reusable code.

The words 'will' or 'shall' denote a rule. The words 'would' or 'could' denote a recommendation.

These rules and recommendations will be frequently cross referenced throughout subsequent lessons. Cross references shall be by rule/recommendation number. Links to the cross reference shall be to the subsection rather than to a specific number.

1. Naming Conventions
    

1.1 Parameter and definition names will be expressed with UPPERCASE letters.

    

1.2 Module, function, gate, instance, and signal names will be expressed with lowercase letters.

    

1.3 Parameter, definition, module, function, UDP, and signal names will be reflective of their purpose or characteristics (example: mux2x1 rather than abc for a 2 to 1 mux module). This rule does not preclude the use of abbreviations for names that can be readily interpreted by knowledgeable observers.

    

1.4 Parameter and signal names will be used consistently throughout the project hierarchy; except in the case of signals or parameters used when instantiating a primitive, gate, library construct, parameterized, or otherwise reused module or primitive within the project hierarchy.

    

1.5 Module names for modules that are coded in RTL shall have the suffix '_r', and those coded at the gate level shall have the suffix '_g'. Function names should have the suffix '_f'. The top level module used exclusively for a test bench shall have the suffix '_t'. Parameter files shall use the suffix '_p'.

    

1.6 Instance names shall have the prefix u. Instance names may use either a numerical or functional system; however, the systems may not be mixed and shall be used consistently throughout the project hierarchy. In a numerical system, the scope of the base number is module rather than project level. In other words, a module instantiated as u2 at a calling module level may use u1, u2_1, or u3 as an instance name for the first subsequent module. In a functional system, instance names must be in the form '<u><module name>', or in the case of multiply defined modules '<u><module name><_> <primary output name or instance number>'.

    

1.7 Instance names shall have a suffix that reflects the coding of the called module or function; '_g' gate level, '_r' RTL level, '_f' function level, or '_t' test level. For example, when calling a 'bufif1' gate construct, the instance name could be 'u1_g'.

    

1.8 Output or inout signals that connect directly outside of the project scope shall have the suffix '_pad'. Active low signals shall have the suffix '_n_pad'.

    

1.9 Internal, active low signals shall have the suffix '_n'.

    

1.10 Clock signals shall either be named 'clk' or have the prefix 'clk_'. If the prefix 'clk_' is used, the subsequent name shall reflect the approximate period or frequency of that clock, or the function or external name of that clock.

    

1.11 Reset signals shall either be named 'rst' ('rst_n' if the signal is active low) or have the prefix 'rst_'. If the prefix 'rst_' is used, the subsequent name shall describe the target of that reset.

    

1.13 Names shall not differ from keywords or other names within the same scope (i.e. project of module) only in case. For example, the names foobar and fooBar may not be used within the same module. If both signals have project scope (i.e. inputs, definitions, modules, or outputs) they may not be used in the same project.

2. Clocks
    

2.1 Gated clocks should not be used. If a gated clock is required, it shall propagate to no more than one project element.

    

2.2 The least number of clock domains possible shall be used in any project.

    

2.3 All clock domains shall be documented in terms of frequency and duty cycle. When appropriate clock domains shall also document I/O standards.

    

2.4 No more than one clock domain may be used within the scope of a single module except when instantiating subsequent modules.

    

2.5 Source and derived (subrate) clocks should not be mixed in the same project. If a derived clock is required, it should function as a clock enable for the source clock.

    

2.6 The project should not use both clock edges of a single clock. If both clock edges are required, they shall be isolated to separate modules (i.e. both clock edges shall not be used within the scope of a single module except for instantiation of subsequent modules).

    

2.7 Signals crossing clock domain boundaries must be clocked multiple times within the target domain before being used or propagated.

    

2.8 All clock domain crossings shall occur in single, re-synchronization modules only. Re-synchronization strategies will be well documented.

3. Architecture
    

3.1 Projects shall be coded in a 'top down' manner (i.e. starting with the project's inputs/outputs and working in a hierarchical fashion, downward).

    

3.2 Glue logic should not appear in the top-level module.

    

3.3 Designs shall have a single top-level with all other modules being subordinate.

    

3.4 Inputs and outputs which connect directly to devices outside of the project scope (i.e. I/O pads) shall reside in the top-level module only.

    

3.5 Signals may only be propagated to modules directly above or below the originating or connecting module in the project hierarchy.

    

3.6 Distinct hierarchical branches may only connect via signals originating or propagated through modules above the joined modules within the project hierarchy.

    

3.7 Internal tristates are a vendor-specific construct that is not supported by, and sometimes causes un-traceable errors in many synthesis tools; including tools supplied by many vendors whose device architecture supports internal tristates. Internal tristates shall be replaced by multiplexers. Tristate conditions are acceptable at an I/O design level only. If internal tristates are required for a specific application, the code for the internal tristate must be coded with conditional statements that target a specific device, and is considered non-reusable. An alternate, multiplexer-based version, shall be supplied.

    

3.8 Vendor-specific constructs, such as DLLs, Multipliers, etc.; should be avoided where possible. If a vendor specific construct is required or desired for a particular application, it shall be coded with conditional statements, with a generic, alternate version supplied. Vendor specific constructs are considered non-reusable.

    

3.9 Lowest common denominator rule. Many of the older programmable devices use a 'sum of products' algorithm that will support a fan-in of up to 32. Many have multiple output capabilities (i.e. pterm expansion, cascade chains, carry term propagation, etc.). Modern FPGAs and some CPLDs use a 4 input, single output, lookup table structure that may or may not register the result. Re-usable designs should target the latter, assuming up to four inputs and a single output for any design element, and optimizing the design for that scenario. Reusable code should never assume the availability of RAM or ROM unless devices that do not support ROM or RAM are never considered for a design target; in which case this requirement shall be documented.

    

3.10 Clocks which propagate to more than one design element (i.e. global usage) may not be used in any logic equation. Many devices have dedicated, low-skew clock lines. When a clock is used in a logic equation, the routers for many devices will not use these dedicated resources, causing skew and timing errors.

    

3.11 Each module should perform only function or a group of closely related functions (for example: design block). I have seen many designs in the past where this is a "junk" module (sometimes even named junk) where a group of unrelated signals are generated. This type of project is difficult to debug, and even harder to sustain.

    

3.12 Specify blocks are usually associated with ASICs and are not usually supported by FPGA synthesis tools. They can be used by advanced simulation tools. When used, they should be coded using conditional statements. >/P>

    

3.13 UDPs and procedure blocks are not uniformly supported by FPGA synthesis tools. Conditional compile is recommended.

4. Instantiations
    

4.1 Built-in primitives (gates) shall be instantiated using ordered instantiation. Many synthesizers use vendor libraries for built-in primitives. While instantiation order is ensured by the language, actual signal names are not; and can vary from vendor to vendor.

    

4.2 UDPs, procedure blocks, functions, and modules shall use named instantiation, with one instantiation per line.

    

4.3 Instantiations of parameterized modules will employ 'defparam' statements or named parameter instantiation (preferred method) to set all parameterized attributes, even when the default definition of a particular parameter is used.

5. Testability
    

5.1 A simulation test bench or group of test benches must be supplied with reusable code. The test bench or benches must cover all corner cases.

    

5.2 A means of initializing the design to a known state (i.e. global set/reset) must be provided.

    

5.3 'INITIAL' statements normally do not model actual hardware and cannot be substituted for a global reset. However, they may be used in test benches, or when the 'power-up' state of the hardware is known.

    

5.5 A means of placing all design output pads into a high-impedance state (i.e. global tristate) must be provided.

    

5.6 IEEE 1149.1 (JTAG) boundary scan (EXTEST) capabilities are preferred for manufacturing test. JTAG BIST, RAMBIST, and INTEST and IEEE 1149.5 (MTM) bus capabilities can be provided, however, the expanded JTAG capabilities are generally only used during device manufacture and advanced diagnostic testing.

6. Style

This section describes rules that can prevent common coding errors; that render a design completely unusable. It also details a set of rules and recommendations that promote the understanding and readability of your code, decreases debug time, and improves reuse quality.

Verilog code is written as simple text files and can be easily maintained by such tools as MicroSoft™ EDIT or NOTEPAD or UNIX&trade and LINUX™ xwindows™ nedit and emacs, or, for the masochists out there, UNIX&trade and LINUX™ VI. Jedit from SourceForge is highly recommended, since it features syntax highlighting for several languages, including Verilog.

The rules and recommendations of this section may seem onerous at the front-end of the development process, but can yield exponential benefits at the back end.

    

6.1 LOGICAL OPERATORS AND CONSTRUCTS SHALL NEVER BE SUBTITUTED WHERE ARITHMETIC OPERATIONS ARE INTENDED!!!!!!!

This is by far the most frequent error I have encountered when debugging devices from other developers. In particular, the '~' (arithmetic ones complement) and the '!' (logical 'FALSE') operators are thought by many inexperienced developers to be equivalent. They are not. When the '!' operator is encountered the operand or expression is expanded to sixteen bits and then evaluated as 'FALSE'. This can lead to excess logic generation and subsequent timing errors or difficult to trace defects. Example: the statements "if (foobar)" (logical) and "if (foobar == 1'b1)" (arithmetic) are not equivalent.

    

6.2 A conditional statement which uses a constant shall explicitly declare the value of that constant. Whenever possible it should also declare the width and radix of that constant.

    

6.3 The value 'x' may only be used for comparison and not for assignment. 'x' cannot be synthesized.

    

6.4 Signal definitions shall appear as one per line, with a note describing the purpose or characteristics of that signal.

    

6.5 Signal definitions should be grouped by type (example: input, output, inout, wire, reg, tri0, etc.). Best practice also groups signals by width in ascending order. Additional grouping by function or relevant design partition, especially in larger projects, is also recommended.

    

6.6 Frequently reused parameters (example: instruction mnemonics for our microprocessor instruction set) should be kept in separate files and incorporated into the project via the use of compiler "`include" directives.

    

6.7 Modules that differ from one another only in some easily defined attribute, such as width or depth, should use only one, parameterized version of that module. Instantiations of that module will then use the 'defparam' statement or parameter overriding to set that attribute for each specific instantiation (see rule 4.3).

    

6.8 Uniform, hierarchical indentation should be used within all procedure and conditional blocks.

    

6.9 The conditional form:

     <Test condition>? <True result> : <False result>

shall test no more than one condition (i.e. shall not be nested).

Although technically correct, for best readability this conditional form should be avoided altogether.

Example: I once saw a section of code from a developer for simple tristate inverter coded as:

     always @(sel or a) out = (sel)? ((a)? 1'b0 : 1'b1) : 1'bz;

rather than:

    bufif1 utrinot_g(out_pad,~a,sel);

    

6.10 Non-blocking assignment shall be used when modeling sequential logic. Blocking assignment shall be used when modeling combinatorial logic.

    

6.11 Complete sensitivity lists shall be used in all procedural statements and blocks.

    

6.12 Hard coded constants other that '1' or '0' should be replaced with parameters.

    

6.13 Notes to delineate logic blocks, or describe function or purpose of code sections, should be frequently used.

    

6.14 When defining parameters for a state machine whose bits or bit fields connect directly to signals, the form:

    parameter <PARAMETER NAME> = <WIDTH>'b<VALUE>;

shall be used.

A vertical group of notes above the parameter definitions should then be used to associate the corresponding bits or bit fields with the relevant signals.

Example

    

6.15 A header should be provided with each module. At a minimum, it should provide descriptions of the modules purpose and design logic, declare the design level (i.e., RTL, gate, etc.), note any parameters and clock domains used and modules called, and detail any deviations from coding for reuse rules. Your employer or module supplier may also require a boilerplate. The header should also contain a change history noting any significant changes, when they were applied, who performed those changes, and why the changes were performed. More extensive headers are sometimes used for the top level module to provide additional information such as author and date, design dependencies, libraries used, design for test strategy, related documents, and initial implementation (i.e. device used, resources consumed, synthesis and simulation software packages and revisions employed, etc.).

The sample headers provided also contain a 'regular expression' in the form:

    Last Update  <yymmdd:hhmm>

for a script that can automatically "timestamp" the file whenever it is edited.

    

6.16 OpenMore specifies a maximum line length of 132 characters. However, for best readability and compatibility with various editors, maximum line length should be limited to 79 characters.

File Control  

File control allows a project to be easily imported to other tools and architectures, and ensures that the proper versions of all project elements are used.

In general, a Verilog project should use the fewest number of files required to implement that particular project. I have seen some projects that exceeded 250 individual files, making it virtually impossible to re-synthesize. Unless a particular module is a library or otherwise version-controlled module used in multiple projects, it should be copied intact into the larger project structure. If the exceptions are in some time-critical path, they too should be considered for intact inclusion into larger project files.

Version control is extremely important. Without it, aside from the obvious inordinate use of disk space, errors that were solved long ago may "magically" re-appear. I had a colleague once who kept every version of files, working and non-working, that he ever created. Even he could not keep track; and on multiple occasions edited the wrong file. Do you really want customers, end-users, and subsequent reuse or sustainment engineers to see all of your failures? A nightmare scenario for debug, reuse, and subsequent sustainment is the 'Ala carte' approach; that is, the designer selects the proper versions of one file from directory A, another from directory B, and so on and so forth. At most, no more than two or three versions of any particular file should exist at any one time. Get rid of the rest; and never deliver more than one version with a released project.

One culprit of multiple file versions can be traced to certain synthesis and simulation tools. Some tools will create additional directories and copy versions of files into those directories as a default mode, unless the tool options are explicitly set by the user. I, myself, have been caught by this arrangement; and have spent hours trying to resolve a simple error by editing the original file that the tool had copied, at inception, into another directory.

Exercise  
  1. The expression "always @(sel or a) out = (sel)? ((a)? 1'b0 : 1'b1) : 1'bz;" buried in a module violates several rules. Can you name them?


  2. How may instances be named?


  3. What are the key elements a module header should contain? What additional elements may appear in an extended header?


  4. True or false? "Each module shall be coded in a separate file to allow updates to occur in that module only."


  5. How must a signal, that originates in one clock domain, be re-synchronized in another clock domain?


  6. What elements should be added to your code for testability?


  7. When, if ever, should ordered instantiation be used?


  8. True or false? "The project should not use both clock edges of a single clock. If both clock edges are required, they must be isolated to separate modules."


  9. How are derived (subrate) clocks incorporated?


  10. When should you use a parameterized module? When instantiating parameterized modules, when should you use a 'defparam' statement or parameter overrides?