# SystemVerilog RTL Design

> Provides RTL design patterns, synthesis guidelines, and coding templates. Use when the user mentions 'module design', 'FSM', 'state machine', 'pipeline', 'synthesizable', 'parameterize', 'parameter', 'localparam', 'clock domain', 'CDC', 'FIFO', 'register file', 'always_ff', 'always_comb', 'generate', or asks about synthesis-related coding.

- Skill: `codejunkie99/systemverilog-rtl-design` (Agent Skill)
- Install (CLI): `npx skillmds@latest add codejunkie99/systemverilog-rtl-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/codejunkie99/systemverilog-rtl-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: codejunkie99 (https://skillmd.com/u/codejunkie99)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/codejunkie99/systemverilog-rtl-design

---


# SystemVerilog RTL Design Reference

## Coding Standards

### Signal Declarations

```systemverilog
// Preferred: logic for all signals
logic [7:0] data;
logic       valid;

// Avoid: reg/wire (older style)
// reg [7:0] data;   // Don't use
// wire valid;        // Don't use
```

### Always Blocks

| Block Type | Use Case | Assignment |
|------------|----------|------------|
| `always_ff` | Sequential logic (flip-flops) | Non-blocking `<=` |
| `always_comb` | Combinational logic | Blocking `=` |
| `always_latch` | Latches (avoid!) | Blocking `=` |

```systemverilog
// Sequential - non-blocking
always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= '0;
    else
        q <= d;
end

// Combinational - blocking
always_comb begin
    y = a & b;
    z = y | c;  // Uses updated y
end
```

## Design Patterns

### Parameterized Module

```systemverilog
module fifo #(
    parameter int WIDTH = 8,
    parameter int DEPTH = 16,
    parameter bit FWFT  = 1'b0  // First-word-fall-through
) (
    input  logic             clk,
    input  logic             rst_n,
    // Write interface
    input  logic [WIDTH-1:0] wr_data,
    input  logic             wr_en,
    output logic             full,
    // Read interface
    output logic [WIDTH-1:0] rd_data,
    input  logic             rd_en,
    output logic             empty
);
    localparam int ADDR_WIDTH = $clog2(DEPTH);

    // Memory array
    logic [WIDTH-1:0] mem [DEPTH];

    // Pointers
    logic [ADDR_WIDTH:0] wr_ptr, rd_ptr;  // Extra bit for full/empty

    // ... implementation
endmodule
```

### FSM with typedef enum

```systemverilog
typedef enum logic [2:0] {
    IDLE    = 3'b001,
    RUNNING = 3'b010,
    DONE    = 3'b100
} state_t;

state_t state, next_state;

// State register (sequential)
always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state <= IDLE;
    else
        state <= next_state;
end

// Next state logic (combinational)
always_comb begin
    next_state = state;  // Default: hold
    unique case (state)
        IDLE:    if (start)    next_state = RUNNING;
        RUNNING: if (complete) next_state = DONE;
        DONE:    next_state = IDLE;
        default: next_state = IDLE;  // Safe default
    endcase
end

// Output logic (combinational)
always_comb begin
    busy = (state == RUNNING);
    done = (state == DONE);
end
```

### Pipeline Stage

```systemverilog
module pipeline_stage #(
    parameter int WIDTH = 32
) (
    input  logic             clk,
    input  logic             rst_n,
    // Upstream
    input  logic [WIDTH-1:0] data_in,
    input  logic             valid_in,
    output logic             ready_out,
    // Downstream
    output logic [WIDTH-1:0] data_out,
    output logic             valid_out,
    input  logic             ready_in
);

    // Handshake: transfer when valid && ready
    wire transfer_in  = valid_in  && ready_out;
    wire transfer_out = valid_out && ready_in;

    // Pipeline register
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out  <= '0;
            valid_out <= 1'b0;
        end else if (transfer_in || transfer_out) begin
            data_out  <= data_in;
            valid_out <= valid_in;
        end
    end

    // Ready when empty or downstream accepts
    assign ready_out = !valid_out || ready_in;

endmodule
```

### Clock Domain Crossing (2FF Synchronizer)

```systemverilog
module sync_2ff #(
    parameter int WIDTH = 1,
    parameter int STAGES = 2
) (
    input  logic             clk_dst,
    input  logic             rst_n,
    input  logic [WIDTH-1:0] data_src,
    output logic [WIDTH-1:0] data_dst
);
    logic [WIDTH-1:0] sync_reg [STAGES];

    always_ff @(posedge clk_dst or negedge rst_n) begin
        if (!rst_n) begin
            for (int i = 0; i < STAGES; i++)
                sync_reg[i] <= '0;
        end else begin
            sync_reg[0] <= data_src;
            for (int i = 1; i < STAGES; i++)
                sync_reg[i] <= sync_reg[i-1];
        end
    end

    assign data_dst = sync_reg[STAGES-1];

endmodule
```

### Generate Blocks

```systemverilog
// Conditional generate
generate
    if (USE_BRAM) begin : gen_bram
        bram_module u_mem (...);
    end else begin : gen_lutram
        lutram_module u_mem (...);
    end
endgenerate

// Iterative generate
generate
    for (genvar i = 0; i < N; i++) begin : gen_stages
        pipeline_stage #(.WIDTH(WIDTH)) u_stage (
            .data_in (stage_data[i]),
            .data_out(stage_data[i+1]),
            ...
        );
    end
endgenerate
```

## Synthesis Guidelines

### Do's

| Pattern | Why |
|---------|-----|
| `always_ff` / `always_comb` | Clear intent to synthesis tools |
| `unique case` / `priority case` | Helps optimization, catches errors |
| `'0` / `'1` for reset values | Flexible width |
| Synchronous reset (FPGAs) | Uses flip-flop reset input |
| Named port connections | `.port(signal)` - readable, catches errors |

### Don'ts

| Anti-Pattern | Problem |
|--------------|---------|
| `initial` blocks | Not synthesizable (simulation only) |
| `#delays` | Not synthesizable |
| `force`/`release` | Not synthesizable |
| `fork`/`join` | Not synthesizable |
| Incomplete `case` | Infers latch |
| Missing `else` | May infer latch |
| Reading before writing in `always_comb` | Infers latch |

### Avoiding Latches

```systemverilog
// BAD - infers latch (missing else)
always_comb begin
    if (sel)
        y = a;
    // y not assigned when sel=0 -> LATCH
end

// GOOD - complete assignment
always_comb begin
    if (sel)
        y = a;
    else
        y = b;
end

// GOOD - default value first
always_comb begin
    y = '0;  // Default
    if (sel)
        y = a;
end
```

## Timing Patterns

### Registered Outputs

```systemverilog
// Register outputs to improve timing
always_ff @(posedge clk) begin
    data_out_reg <= internal_result;
end
assign data_out = data_out_reg;
```

### Pipelining for Timing Closure

```systemverilog
// Break long combinational paths with registers
// Before: a -> [long_logic] -> y

// After:
always_ff @(posedge clk) begin
    stage1 <= a;                    // Input register
    stage2 <= long_logic_part1;     // Pipeline register
    y      <= long_logic_part2;     // Output register
end
```

## Common Modules

| Module | Key Considerations |
|--------|-------------------|
| FIFO | Gray code pointers for CDC, full/empty flags |
| Arbiter | Fixed priority, round-robin, or weighted |
| Register File | Read-during-write behavior (bypass?) |
| Counter | Overflow handling, enable, load |
| Shift Register | Parallel load, serial in/out |
| Mux | One-hot select vs binary select |
| Encoder/Decoder | Priority vs non-priority |

