SystemVerilog RTL coding standards for FPGA/ASIC design. Use when generating RTL modules, interfaces, state machines, or reviewing RTL code structure.
SystemVerilog coding standards for production RTL design in the AXIUART_RV32I project.
Every RTL and interface file MUST begin with:
`timescale 1ns / 1ps
All variable declarations MUST be at the beginning of module/block:
✅ Correct:
module Example (/*...*/);
// All declarations first
typedef enum logic [1:0] {IDLE, RUN, DONE} state_t;
state_t state, state_next;
logic [7:0] counter;
logic valid;
// Logic follows declarations
always_comb begin
// ...
end
endmodule
❌ Wrong:
module Example (/*...*/);
logic [7:0] counter;
always_comb begin
logic temp; // ❌ Declaration inside block
temp = counter + 1;
end
endmodule
Default: Synchronous, active-high reset
✅ Standard:
always_ff @(posedge clk) begin
if (rst) begin
counter <= '0;
end else begin
counter <= counter + 1;
end
end
For active-low reset, invert explicitly:
module My_Module (input logic clk, input logic rst_n);
logic rst;
assign rst = ~rst_n; // Explicit inversion
always_ff @(posedge clk) begin
if (rst) begin // Use internal active-high
// ...
end
end
endmodule
always_ff for sequential logicalways_comb for combinational logicalways @* in new code// Combinational logic
always_comb begin
data_next = data;
if (increment) data_next = data + 1;
end
// Sequential logic
always_ff @(posedge clk) begin
if (rst) begin
data <= '0;
end else begin
data <= data_next;
end
end
Never generate:
// TODO| Element | Convention | Example |
|---|---|---|
| Modules | Capitalized_With_Underscores | Frame_Parser, Uart_Tx |
| Signals | lowercase_with_underscores | rx_fifo_data, frame_valid |
| Parameters | ALL_CAPS_WITH_UNDERSCORES | FIFO_DEPTH, CLK_FREQ_HZ |
| Localparams | ALL_CAPS_WITH_UNDERSCORES | SOF_BYTE, STATE_WIDTH |
| Enums | lowercase_t suffix | parser_state_t, axi_state_t |
| Enum Values | ALL_CAPS | IDLE, PROCESSING, ERROR |
| Interfaces | lowercase_if suffix | axi4_lite_if, uart_if |
| Functions | lowercase_with_underscores | calculate_crc(), state_to_string() |
| Tasks | lowercase_with_underscores | send_byte(), wait_for_ack() |
| File Type | Pattern | Example |
|---|---|---|
| RTL Module | Module_Name.sv | Frame_Parser.sv |
| Interface | interface_name_if.sv | axi4_lite_if.sv |
| Package | package_name_pkg.sv | axiuart_reg_pkg.sv |
Critical: Module name MUST match file name exactly (case-sensitive).
Use three-process state machine with typed enums:
// 1. State type with encoding hint
(* fsm_encoding = "one_hot" *)
typedef enum logic [3:0] {
IDLE = 4'b0001,
PROCESSING = 4'b0010,
WAITING = 4'b0100,
ERROR = 4'b1000
} state_t;
state_t state, state_next;
// 2. Helper function for debug (recommended)
function automatic string state_to_string(state_t st);
case (st)
IDLE: return "IDLE";
PROCESSING: return "PROCESSING";
WAITING: return "WAITING";
ERROR: return "ERROR";
default: return "UNKNOWN";
endcase
endfunction
// 3. Combinational next-state logic
always_comb begin
state_next = state; // Default: hold state
case (state)
IDLE: begin
if (start) state_next = PROCESSING;
end
PROCESSING: begin
if (done) state_next = IDLE;
else if (error) state_next = ERROR;
end
// ... other states
endcase
end
// 4. Sequential state register
always_ff @(posedge clk) begin
if (rst) begin
state <= IDLE;
end else begin
state <= state_next;
end
end
// 5. Output logic
always_comb begin
busy = (state == PROCESSING);
error_flag = (state == ERROR);
end
Use $clog2() for width calculations:
module Fifo #(
parameter int DEPTH = 64,
parameter int DATA_WIDTH = 8
)(/*...*/);
// Address width: log2(DEPTH)
localparam int ADDR_WIDTH = $clog2(DEPTH); // 6 bits for 64 entries
// Count width: log2(DEPTH) + 1 for full detection
localparam int COUNT_WIDTH = $clog2(DEPTH) + 1; // 7 bits
logic [ADDR_WIDTH-1:0] wr_ptr, rd_ptr;
logic [COUNT_WIDTH-1:0] count; // 0 to DEPTH (inclusive)
endmodule
Critical: 64-entry FIFO requires 6-bit addresses (0-63) and 7-bit counter (0-64).
`timescale 1ns / 1ps
// Brief module description (1-2 lines)
// Key features or notes
module Module_Name #(
// Parameters grouped by category
parameter int WIDTH = 32,
parameter int DEPTH = 64
)(
// Clock and reset first
input logic clk,
input logic rst,
// Input signals grouped logically
input logic [WIDTH-1:0] data_in,
input logic valid_in,
// Output signals grouped logically
output logic [WIDTH-1:0] data_out,
output logic valid_out,
// Debug signals last (if any)
output logic [7:0] debug_state
);
// Module body
endmodule
`timescale 1ns / 1ps
interface axi4_lite_if #(
parameter int ADDR_WIDTH = 32,
parameter int DATA_WIDTH = 32
)(
input logic clk,
input logic rst
);
// Signal declarations
logic [ADDR_WIDTH-1:0] awaddr;
logic awvalid;
logic awready;
// ... other signals
// Modport for master
modport master (
output awaddr, awvalid,
input awready
// ... other directions
);
// Modport for slave
modport slave (
input awaddr, awvalid,
output awready
// ... other directions
);
endinterface
Register definitions come from register_map/axiuart_registers.json. Never hardcode addresses.
import axiuart_reg_pkg::*;
module Register_Block #(
parameter logic [31:0] BASE_ADDR = 32'h4000_0000
)(/*...*/);
// Compute relative offsets
localparam logic [11:0] REG_CONTROL_OFFSET =
(axiuart_reg_pkg::REG_CONTROL - BASE_ADDR);
always_comb begin
case (addr_offset)
REG_CONTROL_OFFSET: rdata = control_reg;
REG_STATUS_OFFSET: rdata = status_reg;
default: rdata = '0;
endcase
end
endmodule
Prefix debug signals with debug_:
module Frame_Parser (
// Functional ports
input logic clk, rst,
output logic frame_valid,
// Debug ports (at end)
output logic [7:0] debug_rx_data,
output logic [3:0] debug_state,
output logic debug_crc_error
);
// Implementation
endmodule
Before committing RTL code:
`timescale 1ns / 1ps present in every file$clog2())always_ff for sequential, always_comb for combinationalAll code must compile with 0 errors, 0 warnings:
dsim -genimage image.so -sv -timescale 1ns/1ps -f dsim_config.f
Core RTL coding principles:
`timescale 1ns / 1ps at top of every fileCapitalized_With_Underscores, signals: lowercase_with_underscoresalways_ff for sequential, always_comb for combinational