Skip to content

ECE 2300 Coding Conventions

Any significant hardware design project will usually require developers to use a standardized set of coding conventions. These conventions may be set by company leaders, owners of an open-source project, or simply through historical precedent. Standardized coding conventions allows the code we write to be consistent with others using the same convention and improves readability, maintainability, and extensibility. We have developed a simple set of coding conventions for ECE 2300 which students are required to use in all lab assignments. Keep in mind that these are just guidelines, and there may be situations where it is appropriate to defy a convention if this ultimately improves the overall code quality. These guidelines cover Verilog hardware designs implemented using gate-level (GL) modeling and register-transfer-level (RTL) modeling as well as Verilog test benches and Verilog interactive simulators.

1. Directories and Files

This section discusses the physical structure of how files should be organized in a project. In ECE 2300, we will provide you all of the files you need and they will already be organized into the appropriate directories. However, it can still be useful to understand the overall organization.

1.1. Directories

A project is comprised of multiple subprojects (e.g., each lab will be a separate subproject). Each subproject is a subdirectory with two additional subdirectories for test benches and interactive simulators. If we have a subproject named fb then it would be organized as follows.

 /fb
   FooBar_GL.v
   /test
     FooBar_GL-test.v
   /sim
     foo-bar-sim.v

Verilog files with hardware modules are in the fb subdirectory. Verilog files with test benches are in the fb/test subdirectory. Verilog files with interactive simulators are in the fb/sim subdirectory.

1.2. File Names

All Verilog files should use the .v filename extension.

In general, each Verilog hardware module should be in a separate file, and the name of the file should match the name of the Verilog hardware module. If a Verilog file contains a Verilog hardware module named FooBar_GL, then the name of the Verilog file should be FooBar_GL.v.

In general, each Verilog hardware module should have its own Verilog test bench file with the same name as the Verilog hardware module and a -test suffix. So the Verilog test bench file for FooBar_GL would be named FooBar_GL-test.v. Sometimes we will factor out test cases so we can share them across multiple test benches in which case the corresponding Verilog file will have a -test-cases suffix.

Verilog files for interactive simulators usually use all lowercase, a dash (-) as a separator, and a -sim suffix. So the interactive simulator for the FooBar hardware module might be named foo-bar-sim.v.

1.3. Includes

To instantiate a Verilog module in a different Verilog module you must explicitly include the appropriate Verilog hardware module file using the Verilog include preprocessor directive. You should use the complete path starting at the root of the project. So for example, if we wanted to instantiate the FooBar_GL module in a different module we will use the following:

`include "fb/FooBar_GL.v"

1.4. Include Guards

All Verilog hardware module files should have include guards. These make sure that the contents of a Verilog file are only included once in the overall project, even if they are included multiple times from different files. An include guard uses the Verilog ifndef/endif and define preprocessor directives as follows.

`ifndef FOO_BAR_GL_V
`define FOO_BAR_GL_V

// ... FooBar_GL module definition ...

`endif /* FOO_BAR_GL_V */

We name the guard with the same name as the Verilog file but using all caps, underscore (_) as a separator, and a _V suffix.

2. Formatting

This section discusses general formatting of files across all kinds of files.

2.1. Line Length

In general, you should attempt to keep the length of a line in your file to less than 74 characters. This amount of characters is ideal as it makes code easier to read, enables printing on standard sized paper, and allows the viewing of two files side-by-side on a modern laptop or four files on 24" to 27" inch monitors. Lines longer than 80 characters should be avoided unless there is a compelling reason to use longer lines to increase code quality.

2.2. Indentation

Absolutely no tabs are allowed. Only spaces are allowed for the purposes of indentation. The standard number of spaces per level of indentation is two. Note that in VS Code usually when you press the tab key it will actually insert spaces which is fine. We just want to avoid real tab characters inserted into Verilog files since this means the code formatting requires every reader to use the same tabstop settings.

2.3. Vertical Whitespace

Vertical whitespace can and should be used to separate conceptually distinct portions of your code. A blank line within a block of code serves like a paragraph break in prose: visually separating two thoughts. Vertical whitespace should be limited to a single blank line. Do not use two or more blank lines in a row.

2.4. Horizontal Whitespace

In general, whitespace should be used to separate distinct conceptual "tokens". Do not cram all of the characters in an expression together without any horizontal whitespace. Additional horizontal whitespace can be used to align visual columns when appropriate.

3. Naming

Proper naming is critical to enable the reader to quickly understand your code.

3.1. Port and Wire Names

All port and wire names should use snake case which means we use lower-case letters and an underscore (_) to separate words. Do not use camel case for port or wire names. Single letter port or wire names should be used sparingly. Use port and wire names that clearly indicate the purpose of the signal.

3.2. Module Names

All module names should use camel case which means we use upper-case letters and no other separator to separate words. Module names might also include a suffix separated by an underscore (_) to specify the bitwidth and level of modeling. For example, a 32-bit ripple-carry adder implemented using GL modeling would be named AdderRippleCarry_32b_GL and a 32-bit adder implemented using register-transfer-level modeling would be named Adder_32b_RTL. A bitwidth suffix may not make sense for every hardware module, but all hardware modules must have a suffix indicating the level of modeling.

3.3. Instance Names

Module instance names should use snake case which means we use lower-case letters and an underscore (_) to separate words. Do not use camel case for module instance names. Use module instance names that clearly indicate the purpose of the instantiated module.

Unlike modern programming languages like Python or C++, Verilog does not have a clean way to manage namespaces for module names. This means if you define modules with the same name in two different files, then it could cause a namespace collision which can be difficult to debug. Thus module names must be unique across the entire project.

4. Gate-Level (GL) Modeling

This section focuses on coding conventions suitable for gate-level (GL) modeling.

4.1. GL Allowable Constructs

In GL modeling, students must explicitly declare gates and then use wires to connect these gates to create a gate-level network using the following constructs:

  • wire (single bit and multiple bit)
  • not, and, or, xor, nand, nor, xnor

Other allowable constructs include literals, wire slicing, and assign statements used only for connecting wires together.

  • literals (e.g., 1'b0, 1'b1)
  • wire slicing (e.g., x[0], x[1:0])
  • wire concatenation (e.g., { a, b })
  • assign for connecting wires (e.g., assign x = y;);
  • assign for setting a wire to a constant value (e.g., assign x = 1'b0;)

Module instantiation is allowed as long as the instantiated module is itself adheres to the GL allowable constructs.

4.2. GL Signal Declaration

You may only use wire for GL modeling. Do not use any other signal types such as logic or reg. Wires should be created close to where they will be first used.

Declaring multi-bit wires is allowed using the following syntax.

wire [7:0] x; // 8-bit wire
wire [3:0] y; // 4-bit wire
wire [5:0] z; // 6-bit wire

An unpacked arrays of one-bit wires should not be used in place of a multi-bit wire. So the following is incorrect.

wire x [8]; // unpacked array of 8 1-bit wires (not allowed)
wire y [4]; // unpacked array of 4 1-bit wires (not allowed)
wire z [6]; // unpacked array of 6 1-bit wires (not allowed)

Never use any other kind of indexing. So the following are all incorrect:

wire [0:7] x; // incorrect indexing
wire [4:1] y; // incorrect indexing
wire [7:2] y; // incorrect indexing

Vertically right align the closing square brackets and vertically left align the wire names. The following is correct:

wire        w;
wire  [7:0] x;
wire  [3:0] y;
wire [15:0] z;

The following is incorrect:

wire w;        // incorrect vertical alignment
wire [7:0] x;  // incorrect vertical alignment
wire [3:0] y;  // incorrect vertical alignment
wire [15:0] z; // incorrect vertical alignment

4.3. Primitive Gate Instantiation

There is always exactly one space between the name of the primitive gate and the opening parenthesis. The wires are separated by a comma and a single space, and there is no space after the opening parenthesis or before the closing parenthesis. This is the only allowed formatting for a primitive gate instantiation.

wire x;
and(x, in1, in2);    // incorrect, no space before the parenthesis
and (x,in1,in2);     // incorrect, no horizontal whitespace
and (x, in1,in2 );   // incorrect, inconsistent horizontal whitespace
and ( x, in1, in2 ); // incorrect, space after/before parenthesis
and (x, in1, in2);   // correct

4.4. Literals

In hardware modeling, avoid using literals without specifying the bitwidth. So prefer 1'b0 instead of 0 and 1'b1 instead of 1. In test benches, using 0 and 1 is acceptable. Use underscores (_) to help make long literals more readable as follows

16'b0000_1010_0110_1111

4.5. Assign

Signal assignment using assign is allowed to implement physical connections.

Do not declare and assign to a wire in a single statement. So this is not allowed:

wire foobar = in[3:0];

Instead declare a wire and the assign to that wire in two separate statements.

wire foobar;
assign foobar = in[3:0];

Consider aligning the = sign if it improves readable as below.

assign foobar = in[3:0];
assign baz    = in[7:4];

4.6. GL Module Definition

A GL module definition should specify each port on a separate line (indented by two spaces) and place the opening and closing parenthesis on their own lines. The definition should vertically align the wire keywords, bitwidth declarations, and port names as follows.

module FooBar_GL
(
  input  wire        val,
  input  wire  [3:0] addr,
  input  wire [15:0] data,
  output wire        wait
);

  // ... module implementation here ...

endmodule

4.7. GL Module Instantation

A GL module may only instantiate other GL modules. Module instantation should specify each port connection on a separate line (indented by two spaces) and place the opening and closing parenthesis on their own lines. The definition should vertically align the port connections as follows.

FooBar_GL foo_bar
(
  .val  (foo_val),
  .addr (foo_addr),
  .data (foo_data),
  .wait (foo_wait)
);

Every port connection is written as the port name, a space, and then the signal in parenthesis (i.e., .val (foo_val)). There is no space after the opening parenthesis and no space before the closing parenthesis. This is the only allowed formatting for a port connection. The only extra horizontal whitespace allowed is additional spaces before the opening parenthesis so that the port connections vertically align as in the example above. In the following, only the last port connection is correct.

FooBar_GL foo_bar
(
  .val(foo_val),      // incorrect, no space before the parenthesis
  .addr ( foo_addr ), // incorrect, space after/before parenthesis
  .data( foo_data ),  // incorrect, both of the above
  .wait (foo_wait)    // correct
);

It is fine for ports and wires to have the same name as long as the intent is clear. We recommend using a direct transformation of the module name to create the module instance name (i.e., foo_bar is a direct transformation from FooBar_GL), but this is not required. Using the module instance name as a prefix for the wires used to connect to the module can sometimes be useful.

The following is incorrect formatting since: (1) the opening parenthesis is not on its own line; (2) the port connections are not indented by two spaces; and (3) the port connections are not vertically aligned.

FooBar_GL foo_bar(
.val (foo_val),
.addr (foo_addr),
.data (foo_data),
.wait (foo_wait)
);

5. Register Transfer Level (RTL) Modeling

This section focuses on coding conventions suitable for register-transfer-level (RTL) modeling. This section inherits the coding conventions from the previous section where appropriate.

5.1. RTL Operators

In RTL, students describe hardware behavior using operators and always blocks instead of instantiating primitive gates. Some verilog operators you may be allowed to use, depending on lab instructions, are:

Operation Type Verilog Operator Example Usage
Bitwise AND & assign y = a & b;
Bitwise OR | assign y = a | b;
Bitwise XOR ^ assign y = a ^ b;
Bitwise NOT ~ assign y = ~a;
Logical NOT ! assign y = !a;
Logical AND && assign y = a && b;
Logical OR || assign y = a || b;
Shift Left << assign y = a << b;
Shift Right >> assign y = a >> b;
Addition + assign sum = a + b;
Subtraction - assign diff = a - b;
Multiplication * assign prod = a * b;
Tenrary ?: assign out = (c) ? a : b;

5.2. RTL Signal Declaration

You may only use logic for RTL modeling. Do not use any other types such as wire or reg.

5.3. Combinational Always Blocks

Combinational alwaysblocks use always_comb and are only allowed to be used with a single case or casez statement. No other constructs are allowed in combinational always blocks. You are not allowed to use always @(*) as it adds ambiguity and prevents linters from warning about common mistakes. Combinational always blocks should always use begin/end and increase the level identation.

There are two very important concerns with combinational always blocks:

  • Inferred latches occur when a signal is not fully assigned in all control paths so the signal "remembers" its previous value. Latches are usually unintentional and cause unpredictable issues.

  • X-optimism is when simulation converts unknown X values into known values (i.e., it treats X values "optimistically"). Conditional statements like if and case can cause of X-optimism, because these statements treat X values as if they were false. This can potentially cause the design to pass tests in simulation but fail in real hardware.

To avoid inferred latches and X-optimism, students must include a default case, and the default case must assign all signals to x. The following shows a correct combinational always block with a case statement.

always_comb begin
  case ( sel )
    2'b00: y = a;
    2'b01: y = b;
    2'b10: y = c;
    default: y = 'x;
  endcase
end

Note that blocking assignments or if statements are not allowed in combinational always blocks.

5.4. Sequential Always Blocks

Sequential always blocks describe hardware that has state and updates only on a clock edge. Unlike combinational logic, outputs here depend on both current inputs and the previous state. You must use always_ff. You cannot use always. In this course, only positive edge-triggered flip-flops are allowed. Asynchronous reset is not allowed, only synchronous reset is allowed. Combinational blocks should always use begin/end and increase the level identation.

To avoid X-optimism, students must include a final else clause which must assign all signals to x. The following shows a correct sequential block implementing a reset flip-flop.

always_ff @( posedge clk ) begin

  if ( rst )
    q <= 1'b0;
  else if ( !rst )
    q <= d;
  else
    q <= 'x;

end

Sequential always blocks should be very simple and model just a flip-flop. No real combinational logic should be placed in sequential always block. Do not include arithmetic, nested if statements, or other complex logic.

5.5. RTL Module Definition

We create modules for RTL in a similar way as how we do for GL except that we must use logic instead of wire. An example is as follows:

module FooBar_RTL
(
  input  logic        val,
  input  logic  [3:0] addr,
  input  logic [15:0] data,
  output logic        wait
);

  // ... module implementation here ...

endmodule

5.6. RTL Module Instantation

RTL Module Instantation is done in the same way as GL, including the .val (foo_val) formatting of every port connection described in Section 4.7. As a refresher, it must be formated as follows:

FooBar_RTL foo_bar
(
  .val  (foo_val),
  .addr (foo_addr),
  .data (foo_data),
  .wait (foo_wait)
);

6. Comments

Though challenging to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to wires and module instances is much better than using obscure names that you must then explain through comments. When writing your comments, write in a manner such that in a few years time, you can still look back and be able to understand your code and/or logic.

Do not state the obvious. In particular, don't literally describe what code does, unless the behavior is not obvious to a reader who generally understands Verilog. Instead, provide higher level comments that describe why the code does what it does, or make the code self describing. Over commenting is just as bad as under commenting.

6.1. ASCII Characters

Comments must only use ASCII characters. Do not use any kind of unicode characters in your comments. Including emoji's or foreign characters using unicode is not allowed.

6.2. Comment Style

Use // comments. Do not use /* */ comments. Include a space after // before you start commenting.

//without space, incorrect formatting
// with space, correct formatting

6.3. Trailing Comments

Trailing comments are acceptable as long as they are short. For example, trailing comments to describe ports works well.

module FooBar_GL
(
  input  wire        val,   // valid
  input  wire  [3:0] addr,  // write address
  input  wire [15:0] data,  // write data
  output wire        wait   // module is busy, must wait
);

6.4. Test Case Comments

Every test case should start with a test case title block which at a minimum gives the name of the test case. Often, it can also be helpful to write a small description of what the test case is trying to achieve. Within a directed test case using check tasks always use a comment to label the columns so it is clear what you are checking. An example is shown below.

  //----------------------------------------------------------------------
  // test_case_1_basic
  //----------------------------------------------------------------------
  // This is a basic test case just for smoke testing. Passing this test
  // case in no way guarantees any kind of functionality!

  task test_case_1_basic();
    t.test_case_begin( "test_case_1_basic" );

    //     in        tens     ones
    check( 5'b00000, 4'b0000, 4'b0000 );
    check( 5'b00001, 4'b0000, 4'b0001 );
    check( 5'b01111, 4'b0001, 4'b0101 );
    check( 5'b11111, 4'b0011, 4'b0001 );

    t.test_case_end();
  endtask

The horizontal lines used in the test case title block should extend exactly 74 characters (i.e., two spaces, two '/' characters, and 70 - characters).

6.5. File Comments

All files should include a "title block". This is a comment at the beginning of the file which at minimum must give the name of the file. Often, it can also be helpful to write a small description of the interface in a comment right below the title block. So the title block for Foo_GL.v should look something like:

//=======================================================================
// FooBar_GL
//=======================================================================
// Memory module which enables writing data. If the memory module is busy
// then the wait signal will be one.

The horizontal lines used in the title block should extend exactly 74 characters (i.e., two '/' characters and 72 = characters).

6.6. Instructor Comments

You must remove the lab assignment comments provided in the released code which are purely there to tell you what to do. These comments are no longer applicable once you have followed the instructions and implemented your hardware design. You should also remove any unnecessary ECE2300_UNUSED and/or ECE2300_FLOATING macros that were provided in the released code. So example, these should be removed:

  //''' LAB ASSIGNMENT '''''''''''''''''''''''''''''''''''''''''''''''''''
  // Implement the binary to binary coded decimal converter
  //>'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

  // remove these lines before starting your implementation
  `ECE2300_UNUSED( in );
  `ECE2300_FLOATING( tens );
  `ECE2300_FLOATING( ones );

Do not just blindly remove all instructor comments! Most of the instructor comments are useful and will improve your code quality score!

7. Autoformatter

The sections above describe the formatting we expect to see in every file you submit. Some of that formatting is applied for you by an autoformatter, and the rest is left to you. This section goes into a little more detail on exactly what the autoformatter handles and what it deliberately leaves alone.

The autoformatter is a small wrapper named ece2300-format around a customized version of Verible, an open-source suite of SystemVerilog developer tools. Both are already installed on ecelinux, so there is nothing for you to set up.

Running ece2300-format on a file does two things in sequence. The first pass checks your file for some of the problems the formatter cannot fix for you and stops if it finds any (Section 7.3); the second pass then rewrites the code of the file that made it through (Section 7.1). The file is overwritten in place, so a file that fails the first pass is left essentially as you wrote it.

Please make sure that for the parts the autoformatter cannot handle, you keep to the conventions discussed above so that you don't lose points on your submissions.

7.1. What the Autoformatter Fixes

You do not need to fix any of the following by hand. Write the code, run the autoformatter, and it will do all of this for you.

  • Indentation
  • Runs of two or more blank lines, collapsed down to one
  • Horizontal whitespace between tokens
  • Vertical alignment of wire and logic declarations
  • Vertical alignment of assign statements
  • The shape of a module definition, with each port on its own line and the opening and closing parenthesis on their own lines
  • The shape of a module instantiation, with each port connection on its own line, the parentheses on their own lines, and a single space in .val (foo_val)
  • The single space between a primitive gate and its opening parenthesis, as in and (x, in1, in2)
  • Vertical alignment of port declarations, of port connections, and of the trailing comments on port declarations
  • Indentation of begin/end blocks

Wrapping long lines to 74 characters is mostly handled, but the autoformatter will not break a comment or a single long expression. Lines like the one below are left exactly as they are, and it is up to you to shorten them if you do not like the specific look.

assign count_load = (state == 2'b00) || (state == 2'b10) || (rst && count_done);

It is advised to shorten code lines to 74 characters even before any formatting calls are made as we would hope the code is always relatively readable!!!

7.2. What the Autoformatter Does Not Fix

The autoformatter only rewrites whitespace. It never renames anything, never rewrites the text of a comment, and never changes what your hardware does. You are still responsible for all of the following.

  • Everything in Section 1: which directory a file lives in, what the file is named, how it is included, and its include guards

  • All naming (Section 3): snake case for port, wire, and instance names; camel case for module names; and the bitwidth and modeling-level suffixes on a module name. We will lint and tell you that something doesn't look right, but it is up to you to actually fix the naming.

  • The GL allowable constructs (Section 4.1), and the restriction to wire for GL modeling and logic for RTL modeling

  • Sized literals, and underscores in long literals (Section 4.4)

  • Declaring a wire and assigning to it in two separate statements (Section 4.5)

  • The default case in a case statement, which prevent inferred latches

  • Almost everything in Section 6: what you comment and why, ASCII-only comments, // instead of /* */, the space after //, and the 74-character horizontal rules in title blocks

  • Everything written inside a task ... endtask or a casez ... endcase, which the autoformatter skips on purpose (Section 7.4)

  • Test benches

  • Tab characters and trailing spaces

7.3. What the First Pass Reports

The first pass of the formatter can be important as it reports some formatting issues that the autoformatter currently does not try to handle and stops rather than trying to format the rest of your file. These are things that will need to be manually fixed before the formatter will let you try to format the file. Among the things it will report are:

  • leftover lab assignment instructor comments (Section 6.6)
  • always @(*) used instead of always_comb
  • <= used in an always_comb block, or = used in an always_ff block
  • a case statement with no default case
  • a port or wire name that is not snake case
  • a parameter name that is not snake case, or a localparam name that is neither snake case nor all caps
  • a tab character, or trailing whitespace at the end of a line
  • a packed range that is not declared in decreasing order, such as wire [0:7] x
  • a file whose name does not match the name of the module inside it
  • a positional port connection where a named port connection is required
  • a numeric literal wider than its declared bitwidth

A tab character and a trailing space are both reported rather than fixed. These are yours to clean up before the autoformatter will touch the file at all. We have these in place, partially to also check the non-autoformatted parts, which we hope will help you out.

7.4. What the Autoformatter Leaves Alone

Formatting is disabled inside task ... endtask and inside casez ... endcase. Everything between those keywords is passed through exactly as you wrote it, while the code around them is still formatted normally. Only those two constructs are exempt: a plain case statement and a function ... endfunction are both formatted like any other code.

Test benches are written almost entirely as tasks, and the column labels in a directed test case (Section 6.4) would not survive the autoformatter's alignment. The same is true of the aligned state tables that a casez is usually used for. These regions are yours to format by hand, and the conventions in earlier sections apply to them.

Because these regions are yours, they are also where the horizontal whitespace described in Section 2.4 does the most work. A control signal table is only readable when its columns line up, and the autoformatter will neither create that alignment for you nor repair it if you do not fix it yourself.

The example below, for a vending machine controller, is the shape we expect. Each port of the task is on its own line, the assignments in the task body are aligned, there are comments to tell what each row is for, and every entry in the table is padded so that it sits underneath its label.

  localparam IDLE = 2'd0;
  localparam PAID = 2'd1;
  localparam VEND = 2'd2;

  // Next state combinational logic

  always_comb begin
    casez ( { state, coin_in, item_sel } )

      //          coin  item
      // state    in    sel
      {  IDLE,    1'b0, 1'b? } : state_next = IDLE;
      {  IDLE,    1'b1, 1'b? } : state_next = PAID;
      {  PAID,    1'b?, 1'b0 } : state_next = PAID;
      {  PAID,    1'b?, 1'b1 } : state_next = VEND;
      {  VEND,    1'b?, 1'b? } : state_next = IDLE;
      default                  : state_next = 'x;

    endcase
  end

  // Task for setting the output control signals

  task automatic cs
  (
    input logic coin_rdy_,
    input logic motor_en_,
    input logic disp_en_
  );
    coin_rdy = coin_rdy_;
    motor_en = motor_en_;
    disp_en  = disp_en_;
  endtask

  // Output control signal table

  always_comb begin
    casez ( state )

      //           coin  motor disp
      //           rdy   en    en
      IDLE:    cs( 1'b1, 1'b0, 1'b0 );
      PAID:    cs( 1'b0, 1'b0, 1'b1 );
      VEND:    cs( 1'b0, 1'b1, 1'b0 );
      default: cs(   'x,   'x,   'x );

    endcase
  end

A directed test case works the same way. The task is skipped, so the column labels and the padding between the arguments of each check call are entirely up to you; the example in Section 6.4 is the shape we expect.

7.5. Turning the Autoformatter Off

The autoformatter is a tool. As stated at the very beginning of this document, these conventions are guidelines, and there are situations where defying these guidelines can actually improve the overall code quality. The same is true of the autoformatter. If it rewrites a region into something you find harder to read than what you wrote, you are allowed to keep your version. Wrap the region in a pair of formatting directives and the formatter will pass it through exactly as you wrote it.

// verilog_format: off  // hand-aligned columns read better here
...
// verilog_format: on

Always give a short reason on the off directive, as in the example above, so that a reader can tell this is a deliberate choice and not something you forgot to turn back on. Use // for the directives rather than the /* */ form, which the formatter also accepts but Section 6.2 does not.

Two things are worth keeping in mind. First, turning the formatter off does not turn the conventions off. Everything in Sections 1 through 6 still applies inside a disabled region, and you are now the one responsible for all of it. Second, the directives only disable the second pass. The first pass still reads the whole file, so a tab, a trailing space, or a missing default case inside a disabled region is still reported, and runs of two or more blank lines are still collapsed down to one.

Reach for this when you have a real reason: a hand-aligned table, a column of related assignments, an expression whose line breaks carry meaning. The point of every rule in this document is that your code is readable to you and to everyone else who has to work on it, and where your own layout serves that better than the tool's, keep your layout. What this is not is a way to opt out of the conventions wholesale. A file wrapped end to end in verilog_format: off is not a formatted file, and we will read it as such.