- name
- verilog-code
- description
- 编写和审查可综合的 Verilog RTL 代码。当用户要求编写、设计、实现、修复或审查任何 Verilog/RTL 模块、 硬件模块或数字逻辑时自动触发。触发词包括:写Verilog、写RTL、实现模块、设计电路、review代码、 审查Verilog、FSM、状态机、FIFO、pipeline、流水线、arbiter、仲裁器、counter、计数器、 shift register、移位寄存器、handshake、握手协议、AXI、APB、SPI、I2C、UART、 clock divider、分频器、debounce、去抖、edge detector、边沿检测、synchronizer、同步器、 dual-port RAM、双端口RAM。即使用户只说"写一个模块"或"实现这个功能"而没有显式提到"Verilog", 也应使用此 skill。涵盖新代码生成和已有 Verilog 代码的 review/debug。
- user-invocable
- true
# Verilog RTL 编码指南
## 1. 模式检测
根据用户意图判断工作模式:
**WRITE 模式**(用户要求创建/实现/设计模块):
- 遵循下方所有编码规则
- 如果要实现的模块匹配已知设计模式(FSM、FIFO、pipeline 等),先读取 `references/design-patterns.md` 中对应章节作为起点
**REVIEW 模式**(用户要求检查/审查/debug 已有代码):
- 读取 `references/review-checklist.md`,按照结构化流程逐项审查
- 如果发现的问题匹配已知错误类型,参考 `references/common-mistakes.md`
---
## 2. 硬件思维 — Verilog 不是软件
这是写出正确 Verilog 的根基。在写每一行代码前,用以下思维替代软件的顺序执行模型:
**并行执行**:每个 `always` 块是一个独立的硬件电路,所有 `always` 块同时运行。它们不是"按顺序执行的函数",而是"同时工作的独立电路"。
**非阻塞赋值 `<=`(时钟块)**:在 `always @(posedge clk)` 中,所有 RHS 在时钟沿**同时采样**,然后所有 LHS **同时更新**。这意味着:
```verilog
always @(posedge clk) begin
a <= b; // a 拿到的是 b 的旧值
b <= a; // b 拿到的是 a 的旧值 → 实现了交换
end
```
这不是"先执行第一行再执行第二行",而是一个寄存器组在时钟沿同时锁存。
**阻塞赋值 `=`(组合块)**:在 `always @(*)` 中使用,描述组合逻辑的连线关系。虽然有执行顺序,但综合结果是同一个门级电路。
**绝对禁止混用**:同一个 `always` 块内不得同时出现 `<=` 和 `=`。时钟块用 `<=`,组合块用 `=`。
**`wire` 是导线,`reg` 是存储或组合输出**:`reg` 不一定是寄存器——在组合 `always` 块中的 `reg` 综合后是纯组合逻辑。关键在于它被 `always` 块驱动。
---
## 3. 可综合性规则
RTL 代码中**禁止使用**以下仿真专用语法:
| 禁止的语法 | 原因 | 替代方案 |
|---|---|---|
| `initial` 块 | 无硬件对应物 | 使用复位信号初始化 |
| `#10` 等延时 | 不可综合 | 用时钟周期控制时序 |
| `$display`, `$monitor`, `$finish` | 系统任务 | 仅在 TB 中使用 |
| `fork`/`join` | 无硬件对应物 | 用并行 always 块 |
| `force`/`release`/`deassign` | 不可综合 | 用正常赋值逻辑 |
**必须遵守的规则**:
- **完整条件**:所有 `if` 必须有 `else`,所有 `case` 必须有 `default`。不完整的条件会推断出 latch,这几乎从来不是你想要的
- **敏感列表**:组合逻辑用 `always @(*)`,不要手动列出信号。时序逻辑用 `always @(posedge clk or negedge rst_n)`(异步复位)或 `always @(posedge clk)`(同步复位)
- **复位**:每个触发器都必须有复位值。使用 active-low 异步复位 (`rst_n`) 作为项目惯例
- **单驱动**:同一信号只能在一个 `always` 块或一条 `assign` 中驱动,不可多驱动
- **无内部三态**:内部信号不要用三态,用 mux 代替
- **整数除法**:非 2 的幂的除法和取模通常不可综合,用移位代替
---
## 4. 模块结构模板
所有模块遵循以下统一结构,与项目现有风格保持一致:
```verilog
module module_name #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4
) (
input wire clk,
input wire rst_n,
// ---- Input ports ------------------------------------------------
input wire [DATA_WIDTH-1:0] data_in,
input wire valid_in,
// ---- Output ports -----------------------------------------------
output reg [DATA_WIDTH-1:0] data_out,
output reg valid_out
);
// =========================================================================
// Local parameters
// =========================================================================
localparam DEPTH = 1 << ADDR_WIDTH;
// =========================================================================
// Internal signals
// =========================================================================
reg [DATA_WIDTH-1:0] data_reg;
wire some_condition;
// =========================================================================
// Combinational logic
// =========================================================================
assign some_condition = /* ... */;
// =========================================================================
// Sequential logic
// =========================================================================
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_reg <= {DATA_WIDTH{1'b0}};
data_out <= {DATA_WIDTH{1'b0}};
valid_out <= 1'b0;
end else begin
// 功能逻辑
end
end
endmodule
```
**结构约定**:
- `clk` 和 `rst_n` 始终是前两个端口
- 端口用 `// ---- 注释行 ----` 分组
- 模块内部按区域划分:Local parameters → Internal signals → Combinational → Sequential
- 区域之间用 `// ===` 注释分隔(匹配项目现有风格)
- 参数名 `UPPER_SNAKE_CASE`,信号名 `snake_case`,模块名 `snake_case`
- Active-low 信号加 `_n` 后缀(如 `rst_n`, `cs_n`)
- 时钟信号加 `clk` 前缀(如 `clk`, `clk_div2`)
---
## 5. 关键编码规则
### 5.1 计数器边界
计数器 wrap-around 比较 `MAX_VAL - 1`,不是 `MAX_VAL`。这是最常见的 off-by-one 错误:
```verilog
// 正确:0 到 N-1 共 N 个计数
if (count == MAX_COUNT - 1) begin
count <= {WIDTH{1'b0}};
end else begin
count <= count + 1'b1;
end
```
### 5.2 位宽匹配
显式指定所有字面量的位宽,不要使用无宽度字面量:
```verilog
// 错误
count <= count + 1; // 1 是 32 位整数
data <= 0; // 0 是 32 位整数
// 正确
count <= count + 1'b1;
data <= {DATA_WIDTH{1'b0}};
```
使用 `$clog2()` 从深度推导地址宽度:
```verilog
localparam ADDR_WIDTH = $clog2(DEPTH);
```
### 5.3 复位方法论
异步 active-low 复位,复位分支始终放在 if-else 的第一个分支:
```verilog
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// 所有寄存器的复位值 — 不要漏掉任何一个
state <= IDLE;
count <= {WIDTH{1'b0}};
data_out <= {DATA_WIDTH{1'b0}};
valid_out <= 1'b0;
end else begin
// 正常功能逻辑
end
end
```
### 5.4 FSM 编码
使用 `localparam` 定义状态,不要用 `` `define``。始终有 `default` 分支回到安全状态:
```verilog
localparam [1:0] S_IDLE = 2'd0,
S_LOAD = 2'd1,
S_PROC = 2'd2,
S_DONE = 2'd3;
reg [1:0] state, state_next;
```
组合逻辑中的 next-state 信号用 `_next` 后缀,寄存器版本不加后缀。
### 5.5 流水线规则
- 所有数据信号必须经过**相同级数**的流水线寄存器
- `valid` 信号必须与数据**同步流水**
- 添加反压(backpressure)时,stall 信号必须同时冻结数据和 valid
### 5.6 Valid/Ready 握手协议
- `valid` 不能依赖 `ready`(发送端不看接收端是否准备好就可以发起请求)
- `ready` 可以依赖 `valid`
- `valid` 一旦拉高,在 `ready` 为低期间不得撤回
- `valid` 为高期间数据必须保持稳定
### 5.7 内存/数组声明
```verilog
// 正确:使用 [0:DEPTH-1] 显式声明范围
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
// 读写模式
always @(posedge clk) begin
if (wr_en)
mem[wr_addr] <= wr_data;
rd_data <= mem[rd_addr]; // 读优先,1 周期延迟
end
```
---
## 6. 输出自检清单
生成 Verilog 代码后,在呈现给用户之前,逐条对照以下清单验证:
- [ ] 每个 `if` 都有 `else`,每个 `case` 都有 `default`
- [ ] 没有 `initial` 块、`#delay`、系统任务(`$display` 等)
- [ ] 复位分支中列出了所有寄存器的复位值,无遗漏
- [ ] 时钟 `always` 块只用 `<=`,组合 `always` 块只用 `=`,无混用
- [ ] 所有字面量显式指定位宽(无裸 `0`、`1` 等整数字面量)
- [ ] 组合逻辑敏感列表是 `@(*)`,时序逻辑是 `@(posedge clk or negedge rst_n)`
- [ ] 计数器边界条件正确(比较 N-1 而不是 N,注意 off-by-one)
- [ ] 无多驱动信号(同一信号在多个 always/assign 中赋值)
- [ ] `clk`, `rst_n` 是模块的前两个端口
- [ ] 参数名 `UPPER_SNAKE_CASE`,信号名 `snake_case`
- [ ] 组合 always 块中的信号在所有分支中都有赋值(防止 latch 推断)
- [ ] FSM 的 case 语句有 default 分支,且 default 回到安全状态
---
## 7. 参考文件
以下参考文件在需要时读取,不要每次都全部加载:
- **`references/design-patterns.md`** — 当实现的模块匹配以下任何模式时读取:
FSM、FIFO、pipeline、handshake/skid buffer、arbiter、counter、shift register、
edge detector、debouncer、clock divider、dual-port RAM、synchronizer
- **`references/common-mistakes.md`** — 当调试综合错误、仿真不匹配、或需要理解某个
常见 Verilog 陷阱时读取
- **`references/review-checklist.md`** — 在 REVIEW 模式下读取,按照结构化流程审查代码
---
## 8. Icarus Verilog 兼容性
RTL 代码使用 Verilog-2001/2005 语法,确保与 Icarus Verilog (`iverilog -g2012`) 兼容:
- RTL 中使用 `reg`/`wire`,不要用 SystemVerilog 的 `logic`
- 不要在 RTL 中使用 `always_ff`/`always_comb`/`always_latch`(这些是 SV 语法,仅 TB 中使用)
- `$clog2()` 在 `-g2012` 模式下支持
- `generate` 块正常支持
- TB 文件是 SystemVerilog (`.sv`),由 `tb-creator` skill 处理,本 skill 不管 TB
## 9. 项目工作流提示
生成 RTL 文件后,提醒用户:
- RTL 文件放在 `RTL/` 目录下
- 运行 VS Code 的 "compile and run" 任务来编译和仿真
- 如果有新的 RTL 文件,`gen_filelist.py` 会自动扫描 `RTL/` 目录
GitHub에서 보기