|Verilog 语法 & FPGA 开发速查表

Verilog 语法 & FPGA 开发速查表

作者: 石志超更新于: 2026/2/27

适用于 XS7050 板卡 + Vivado 2021.2

1. 模块结构

module my_module (
    input        clk,        // 时钟
    input        rst_n,      // 复位(低有效)
    input  [3:0] data_in,    // 4bit 输入
    output [3:0] data_out,   // 4bit 输出(组合)
    output reg   led         // 1bit 输出(寄存器)
);

// ... 逻辑 ...

endmodule

要点:

  1. input 默认是 wire

  2. output 若在 always 块中赋值,必须声明为 output reg

  3. 位宽用 [MSB:LSB],如 [7:0] 表示 8 位


2. 数据类型

类型

用途

典型场景

wire

连线,不能存储

assign 赋值、模块连接

reg

寄存器,可存储

always 块中赋值

注意reg 不一定综合成寄存器!在 always @(*) 中用 reg 仍是组合逻辑。


3. 常量表示

4'b1010      // 4位二进制
8'hFF        // 8位十六进制
32'd100      // 32位十进制
4'b0         // 4位,值为 0000

格式:位宽'进制 值,进制:b(二进制) h(十六进制) d(十进制) o(八进制)


4. 赋值方式

连续赋值(组合逻辑)

assign data_out = data_in & 4'b1111;

阻塞赋值 =(组合逻辑块)

always @(*) begin
    if (sel)
        y = a;
    else
        y = b;    // ← 必须有 else,否则产生锁存器!
end

非阻塞赋值 <=(时序逻辑块)

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;
end

阻塞 vs 非阻塞 铁律

场景

原因

always @(posedge clk)

<=

并行更新,模拟真实 FF 行为

always @(*)

=

顺序求值,模拟组合电路

assign

=

连续赋值,只能用 =

绝对不要在时序块里混用 =<=


5. 常用运算符

运算符

含义

示例

& | ^ ~

按位 与/或/异或/取反

a & b

&& || !

逻辑 与/或/非

if (a && b)

+ - *

算术

cnt + 1

== !=

相等/不等

if (state == IDLE)

> < >= <=

比较

if (cnt >= MAX)

<< >>

左移/右移

led << 1

? :

三目

assign y = sel ? a : b;

{}

拼接

{a, b} 拼接两个信号

{ {n{x}} }

重复

{4{1'b0}} = 4'b0000


6. 组合逻辑 vs 时序逻辑

组合逻辑

// 方式1:assign
assign sum = a + b;

// 方式2:always(必须覆盖所有分支)
always @(*) begin
    case (sel)
        2'b00: y = a;
        2'b01: y = b;
        2'b10: y = c;
        default: y = 4'b0;   // ← 必须有 default!
    endcase
end

时序逻辑(带异步复位)

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        cnt <= 32'd0;        // 异步复位
    else if (cnt >= MAX - 1)
        cnt <= 32'd0;        // 溢出归零
    else
        cnt <= cnt + 1;      // 正常计数
end

7. 常用模板

计数器 + 分频

// 50MHz → 1Hz LED 闪烁
parameter CNT_MAX = 25_000_000 - 1;  // 0.5s

reg [24:0] cnt;
reg        led;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt <= 0;
        led <= 1'b1;         // LED 低有效,复位时灭
    end else if (cnt >= CNT_MAX) begin
        cnt <= 0;
        led <= ~led;
    end else begin
        cnt <= cnt + 1;
    end
end

两级同步器(打两拍)

reg key_r1, key_r2;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        key_r1 <= 1'b1;     // 上拉默认高
        key_r2 <= 1'b1;
    end else begin
        key_r1 <= key_n;    // 第一拍
        key_r2 <= key_r1;   // 第二拍(安全使用此信号)
    end
end

按键消抖

parameter DEBOUNCE_MAX = 1_000_000;   // 20ms @50MHz

reg [19:0] db_cnt;
reg        key_stable;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        db_cnt     <= 0;
        key_stable <= 1'b1;
    end else if (key_r2 != key_stable) begin
        if (db_cnt >= DEBOUNCE_MAX - 1) begin
            db_cnt     <= 0;
            key_stable <= key_r2;     // 稳定了,更新
        end else begin
            db_cnt <= db_cnt + 1;
        end
    end else begin
        db_cnt <= 0;                  // 一致则清零
    end
end

异步复位同步释放

reg rst_n_r1, rst_n_r2;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rst_n_r1 <= 1'b0;   // 异步复位立即生效
        rst_n_r2 <= 1'b0;
    end else begin
        rst_n_r1 <= 1'b1;   // 复位释放时同步到时钟域
        rst_n_r2 <= rst_n_r1;
    end
end
// 后续逻辑使用 rst_n_r2 作为复位信号

简单状态机(三段式)

localparam IDLE = 2'd0,
           S1   = 2'd1,
           S2   = 2'd2;

reg [1:0] state, next_state;

// 第一段:状态寄存器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state <= IDLE;
    else
        state <= next_state;
end

// 第二段:次态逻辑(组合)
always @(*) begin
    next_state = state;           // 默认保持(防锁存器)
    case (state)
        IDLE: if (start) next_state = S1;
        S1:   if (done)  next_state = S2;
        S2:              next_state = IDLE;
        default:         next_state = IDLE;
    endcase
end

// 第三段:输出逻辑(组合)
always @(*) begin
    led = 1'b1;                   // 默认值(防锁存器)
    case (state)
        S1:  led = 1'b0;         // S1 状态亮灯
        default: led = 1'b1;
    endcase
end

8. Testbench 模板

`timescale 1ns / 1ps

module tb_my_module;

reg        clk;
reg        rst_n;
reg  [3:0] key_n;
wire [3:0] led;

// 实例化被测模块
my_module u_dut (
    .clk    (clk),
    .rst_n  (rst_n),
    .key_n  (key_n),
    .led    (led)
);

// 时钟生成:50MHz = 20ns 周期
initial clk = 0;
always #10 clk = ~clk;

// 激励
initial begin
    // 初始化
    rst_n = 0;
    key_n = 4'b1111;            // 按键默认高(未按)
    #200;                       // 复位保持 200ns

    // 释放复位
    rst_n = 1;
    #1000;

    // 按下 KEY0
    key_n[0] = 0;
    #100_000;                   // 按住 100us
    key_n[0] = 1;
    #100_000;

    $display("Simulation finished.");
    $finish;
end

endmodule

Vivado 仿真:右键 TB 文件 → Set as Top → Run Behavioral Simulation


9. Vivado 关键操作流程

  1. 新建工程:File → New Project → RTL → 选器件(XS7050 对应 xc7s50 系列)

  2. 添加源文件:Add Sources → Add or Create Design Sources

  3. 添加约束:Add Sources → Add or Create Constraints → 导入 .xdc

  4. 综合:Run Synthesis

  5. 实现:Run Implementation

  6. 生成 Bitstream:Generate Bitstream

  7. 下载:Open Hardware Manager → Auto Connect → Program Device

  8. 仿真:Run Behavioral Simulation(先设 TB 为顶层)

当修改了某个源代码或者约束,可以直接点击 Generate Bitstream 自动完成 综合、实现、生成 Bitstream。


10. 坑点速查

#

问题

症状

解决

1

时序块用 =

仿真对、板子错

改用 <=

2

端口未约束

报错或随机管脚

.xdc 写全所有端口

3

端口名不匹配

约束无效

Verilog 与 XDC 名一致

4

按键不同步

偶发逻辑错乱

两级 FF 同步

5

无消抖

按一次触发多次

计数器消抖

6

casedefault

产生锁存器

default

7

组合 ifelse

产生锁存器

覆盖所有分支

8

异步复位直接释放

释放时亚稳态

异步复位同步释放