Module 8: RTL Coding for Quality & Synthesis · 8.1 RTL Coding Guidelines & Style

Naming Conventions and File Organization

Module 7 was about what the RTL does. This module starts with how it reads.

Every convention in this article has one thing in common: it costs nothing functionally and saves someone, often you six months later, real time working out what a piece of code does without reading all of it first. That is the whole case for style discipline, and it is worth taking seriously precisely because it is cheap.

Signal naming: let the name carry information

A signal’s name is free documentation if you let it be. Compare:

// Uninformative: every reader has to trace usage to know what these mean
wire a, b, c;
reg  x, y;
// Self-documenting: a reader knows the role without tracing anything
wire req_valid, req_ready, req_data;
reg  fifo_full, fifo_empty;

A few conventions appear in nearly every real style guide, each for a specific reason:

  • Active-low signals get an _n suffix: rst_n, cs_n, we_n. You met the active-low reset convention in Section 5.5, for the CMOS cell reason discussed there. The suffix is what lets a reader tell, at a glance and without a schematic, which polarity a signal asserts on.
  • Clocks are named clk, or clk_<domain> when there is more than one: clk_100mhz, clk_usb. A CDC lint tool (Section 7.5) often relies on naming like this, alongside an explicit clock definition file, to work out which signals belong to which domain.
  • Registered signals are sometimes suffixed _reg or _q, distinguishing them from the combinational logic that feeds them: sum_reg versus the combinational sum. This echoes the register-versus-combinational-cloud picture from the pipeline article in Section 5.5.
  • FSM next-state signals are named next_state or state_next, matching every two-block and three-block FSM from Section 5.6. A reviewer who has seen one of your FSMs then recognises the pattern in the next.

Module and instance naming

// Module names: lowercase, underscore-separated, describing WHAT it is
module uart_transmitter (...);

// Instance names: prefixed to distinguish them from the module type,
// and named for the instance's ROLE, not just a repeat of the module name
uart_transmitter u_tx_channel_a (...);
uart_transmitter u_tx_channel_b (...);

The u_ prefix (some teams use i_ for instance) solves a recurring confusion. In a hierarchical path like top.u_tx_channel_a.baud_gen, it is immediately clear which segments are instances and which are signals. And naming an instance by its role (u_tx_channel_a) rather than repeating the module name (uart_transmitter_0) means a waveform viewer’s hierarchy pane or a synthesis report reads as documentation of what each instance does in this design, not merely which module type it is.

File organization: mirror the hierarchy

A common, effective convention: one module per file, file name matching the module name, directories mirroring the design hierarchy:

rtl/
├── top/
│   └── soc_top.v
├── cpu/
│   ├── alu.v
│   ├── register_file.v
│   └── control_unit.v
├── peripherals/
│   ├── uart_transmitter.v
│   ├── uart_receiver.v
│   └── spi_master.v
└── common/
    ├── sync_2ff.v          # from Section 7.5, reused across the whole project
    └── async_fifo.v        # from Section 7.4, reused across the whole project

The common/ directory matters more than it looks. Modules like sync_2ff and async_fifo, built once with care in Module 7, belong in a shared place that every other part of the project references, rather than being copied (and eventually, inevitably, diverging) into each place that needs one. It is the same discipline that motivated parameterization in Section 7.1: one verified source reused everywhere instead of N slowly diverging copies.

Why this matters beyond aesthetics

Consider a review. A reviewer opens a file they have never seen, following a bug report that says “the rst_n handling looks wrong in the SPI block”. If every module in the project spells reset differently, rst_n here, reset there, rst somewhere else, some active-high and some active-low with no naming cue, the reviewer’s first task is not finding the bug but reverse-engineering the local dialect of this file. Multiply that across every file in a project and inconsistent naming is not a cosmetic issue. It is a tax on every review, every onboarding and every debugging session for the life of the codebase.

Semiconductor companies maintain written RTL style guides, often dozens of pages, covering naming, file organisation, commenting and structure, and enforce them with lint tools that flag violations before a change reaches review. That is not bureaucracy for its own sake. It is the measured response to the friction described above at a scale where small per-file costs compound across thousands of files.

Practice Exercise

You have inherited a file with these signal names: Reset, CLOCK, data_out_reg_n, enable. Identify which violate the conventions in this article, and propose corrected names.

Solution
  • Reset: mixed case is inconsistent with the lowercase, underscore-separated convention, and the name does not indicate polarity. If active-low (the common case), rename to rst_n.
  • CLOCK: all-caps is unusual for a signal (all-caps is usually reserved for `define macros and parameters) and needlessly long. Rename to clk.
  • data_out_reg_n: the _n suffix conventionally means active-low, but this is a registered data signal, not an active-low control. The suffix is misleading. If the intent is “registered”, use _reg or _q: data_out_reg.
  • enable: not wrong, but vague on its own. If the design has several enables, a more specific name like wr_enable or fifo_enable avoids ambiguity once the file has more than one thing that could be “the” enable.

Naming and file organisation cost nothing functionally, the synthesized gates are identical either way, but they are the cheapest improvement available to a codebase’s usability by anyone other than its author. In the next article we look at reset strategy consistency, the case of “consistency” that goes beyond naming into functional risk if it is not enforced project-wide.

Reset Strategy Consistency →