一、Verilog 语言概述
- 用途:硬件描述语言(HDL),用于数字电路设计、仿真和综合。
- 设计层次:从门级(Gates)到行为级(Behavioral),支持自顶向下(Top-Down)和自底向上(Bottom-Up)设计。
- 核心思想:通过模块(Module)描述电路功能,模块间通过端口(Port)连接。
二、基本语法
1. 模块结构(Module)
module 模块名(端口列表);
// 端口声明(输入、输出、双向)
input [位宽-1:0] 输入端口; // 例:input [3:0] a;
output [位宽-1:0] 输出端口;
inout [位宽-1:0] 双向端口; // 需配合三态门(tri)
// 内部信号声明(reg/wire)
reg [位宽-1:0] 寄存器型; // 用于时序逻辑(always块内赋值)
wire [位宽-1:0] 线网型; // 用于组合逻辑(assign赋值或模块间连接)
// 逻辑描述(组合/时序逻辑)
// ...
endmodule
2. 数据类型
-
线网型(Wire):
- 表示物理连线,无存储功能,需持续驱动(
assign或模块输出)。 - 例:
wire [7:0] data;
- 表示物理连线,无存储功能,需持续驱动(
-
寄存器型(Reg):
- 表示存储单元(如触发器),在
always块内赋值,默认初始值为x(未知)。 - 例:
reg clk;
- 表示存储单元(如触发器),在
-
其他类型:
-
integer:32 位有符号整数(仿真用,不推荐综合)。 -
parameter:参数(常量),用于定义位宽、延迟等。parameter WIDTH = 8; // 定义参数 reg [WIDTH-1:0] data;
-
3. 运算符
| 类型 | 运算符示例 | ||
|---|---|---|---|
| 算术运算 | + - * / %(取模) |
||
| 逻辑运算 | &&(与) ` |
(或) !`(非) |
|
| 位运算 | & ` |
~(非) ^`(异或) |
|
| 关系运算 | == != < > <= >= |
||
| 移位运算 | <<(左移) >>(右移) |
||
| 拼接运算 | {a, b[2:0], 1'b1}(组合信号) |
4. 组合逻辑描述
-
assign语句(连续赋值,用于 wire 型):assign out = a & b; // 与门 assign {c_out, sum} = a + b + c_in; // 全加器(拼接结果) -
always @(*)块(敏感于所有输入,用于 reg 型):reg [1:0] out; always @(*) begin // * 表示所有输入信号变化时触发 case (sel) 2'b00: out = a; 2'b01: out = b; 2'b10: out = c; default: out = 2'bxx; // 避免 latch endcase end注意 :组合逻辑
always块需覆盖所有输入情况,否则会生成锁存器(Latch),不推荐。
5. 时序逻辑描述
-
边沿触发(触发器) :
reg [7:0] data_reg; always @(posedge clk or negedge rst_n) begin // 上升沿触发,低电平复位 if (!rst_n) begin data_reg <= 8'd0; // 复位状态 end else begin data_reg <= data_in; // 时钟沿更新 end end注意 :时序逻辑用非阻塞赋值(
<=),避免竞争冒险。
6. 条件与循环语句
-
if-else:always @(*) begin if (en) out = a; else out = b; end -
case:always @(*) begin casez (addr) // casez 忽略z位,casex忽略x/z位 3'b1??: out = 8'hff; 3'b01?: out = 8'h00; default: out = 8'hxx; endcase end -
循环语句(主要用于仿真,综合支持有限):
// 生成块(generate,用于重复实例化) generate genvar i; for (i=0; i<4; i=i+1) begin: reg_array reg dff; always @(posedge clk) dff <= data[i]; end endgenerate
7. 模块实例化
-
位置关联:按端口顺序连接。
adder u_adder (a, b, c_in, sum, c_out); // 实例名 u_adder -
名称关联(推荐,可读性好):
adder u_adder ( .a(a), .b(b), .c_in(carry_in), .sum(result), .c_out(carry_out) );
三、常用电路示例
1. 2 选 1 多路选择器
module mux2_1(
input a, b, sel,
output out
);
assign out = sel ? b : a; // 组合逻辑
endmodule
2. 4 位加法器(带进位)
module adder_4bit(
input [3:0] a, b,
input c_in,
output [3:0] sum,
output c_out
);
assign {c_out, sum} = a + b + c_in; // 拼接运算
endmodule
3. D 触发器(带异步复位)
module dff(
input clk, rst_n, d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= 1'b0;
else q <= d;
end
endmodule
4. 计数器(模 16)
module counter_16(
input clk, rst_n, en,
output reg [3:0] cnt,
output reg overflow
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 4'd0;
overflow <= 1'b0;
end else if (en) begin
if (cnt == 4'd15) begin
cnt <= 4'd0;
overflow <= 1'b1;
end else begin
cnt <= cnt + 1'b1;
overflow <= 1'b0;
end
end
end
endmodule
四、仿真相关(Testbench)
-
用于验证设计的正确性,生成激励信号并观察输出。
module tb_counter; // 测试模块(无端口)
reg clk, rst_n, en;
wire [3:0] cnt;
wire overflow;// 实例化被测试模块 counter_16 u_counter( .clk(clk), .rst_n(rst_n), .en(en), .cnt(cnt), .overflow(overflow) ); // 生成时钟(50MHz,周期20ns) initial begin clk = 1'b0; forever #10 clk = ~clk; end // 生成激励 initial begin rst_n = 1'b0; // 复位 en = 1'b0; #20 rst_n = 1'b1; // 20ns后释放复位 #10 en = 1'b1; // 使能计数 #300 en = 1'b0; // 300ns后停止 #50 $finish; // 结束仿真 end // 打印输出 initial begin $monitor("Time: %0t, cnt: %d, overflow: %b", $time, cnt, overflow); endendmodule
五、关键注意事项
-
组合逻辑 vs 时序逻辑:
- 组合逻辑:无时钟,用
assign或always @(*),阻塞赋值(=)。 - 时序逻辑:有时钟,用
always @(posedge clk),非阻塞赋值(<=)。
- 组合逻辑:无时钟,用
-
避免 Latch :组合逻辑需覆盖所有输入情况(
if配else,case配default)。 -
可综合性 :仿真语句(如
$display、#延迟)不能被综合,仅用于 Testbench。 -
参数化设计 :用
parameter提高代码复用性(如位宽可配置)。