verilog语法1
1.可综合/不可综合
可综合的代码最终会被映射为组合逻辑或时序逻辑。
不可综合这些关键字和结构在真实的硅片或 FPGA 里是没有对应的物理电路的,综合工具看到它们会直接报错或忽略。
初始化与时间控制 (initial块, $display, $finish 等系统任务):
循环语句(部分不可综合):
延时控制 (#):
2.模块/例化/参数化模块
module adder8 (
input wire [7:0] a,
input wire [7:0] b,
input wire cin,
output wire [7:0] sum,
output wire cout
);
assign {cout, sum} = a + b + cin;
Endmodule
module counter #(
parameter WIDTH = 8, // 位宽,可被覆盖
parameter MAX = 199 // 计数上限,可被覆盖
)(
input wire clk,
input wire rst_n, // 低电平复位
input wire en,
output reg [WIDTH-1:0] cnt,
output wire tick
);
localparam ZERO = {WIDTH{1'b0}}; // 内部常量,不可覆盖,parameter 与 localparam,这两个是常量,不是信号,不占硬件资源,只在编译期起作用。
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= ZERO;
else if (en) begin
if (cnt >= MAX)
cnt <= ZERO;
else
cnt <= cnt + 1'b1;
end
end
assign tick = (cnt == MAX); // 计满时输出一个周期脉冲
Endmodule
counter #( //模块名
.WIDTH(16), //修改
.MAX(49999)
) u_cnt_big (//实例名,必须注意可以多次例化,但是实例名是唯一的,
.clk (clk), //左侧是模块里面的信号(子),右侧是连接的信号(本)
.rst_n(rst_n),
.en (en),
.cnt (),
.tick (tick_big)
);
模块名 #(参数覆盖) 实例名 (
.端口名(连接的信号),
.端口名(连接的信号)
);
//========== 第 1 次例化 ==========
adder8 u_add1 (
.a (a1),
.b (b1),
.y (y1)
);
//========== 第 2 次例化(同一个模块) ==========
adder8 u_add2 (
.a (a2),
.b (b2),
.y (y2)
);
3.数据类型
Wire表示一根导线,它自己不存储任何值,必须由外部持续驱动:
用 assign 驱动
或者由模块的输出端口驱动
或者由实例的输出端口驱动
x是不定态,z是高阻
reg 是变量,可以在 always 块里被赋值,能保存值。但注意:
reg ≠ 寄存器! 它只是"能在 always 里被赋值的变量",综合出来可能是组合逻辑,也可能是时序逻辑,取决于 always 块的写法。
有符号数 signed
Verilog 默认所有信号都是无符号。要做有符号运算(比如负数加法)要显式声明:
verilog
wire signed [7:0] a = -8'sd5; // 有符号
wire signed [7:0] b = 8'sd3;
wire signed [8:0] s = a + b; // 需要多一位防溢出
<位宽>'<进制><数值>
verilog
8'd255 // 8 位十进制 255
// 省略位宽时默认 32 位
'hFF // 等价于 32'h000000FF
255 // 等价于 32'd255
integer(整数型)
verilog
integer i; // 默认 32 位有符号整数
// 最典型用法:for 循环变量(仿真、generate 里)
integer j;
initial begin
for (j = 0; j < 8; j = j + 1)
mem[j] = 0;
End
Verilog 没有真正的"二维数组",但支持"多位宽的寄存器数组",就是存储器:
verilog
// 256 个 8 位宽的存储单元,就是一块 256×8 的 RAM
reg [7:0] mem [0:255];
不能整体赋值,只能逐元素赋值
四运算符
算术
wire [7:0] a = 8'd10, b = 8'd3;
wire [7:0] sum = a + b; // 13
wire [7:0] diff = a - b; // 7
wire [7:0] prod = a * b; // 30
wire [7:0] quot = a / b; // 3(整数除法,截断)
wire [7:0] rem = a % b; // 1(取模)
wire [7:0] pow = a ** 2; // 100(仅部分工具支持常量指数)
但是要注意这个位宽,
// 加法:结果位宽 = max(位宽) + 1
wire [8:0] sum9 = {1'b0,a} + {1'b0,b};
// 乘法:结果位宽 = 两个操作数位宽之和
wire [15:0] prod16 = a * b; // 8×8 → 16 位
// 无符号和有符号混用,整个表达式按无符号处理,负数会变成大正数!
wire signed [7:0] sa = -8'sd5;
wire [7:0] ub = 8'd3;
wire signed [8:0] r = sa + ub; // ❌ 结果错误,sa 被当成无符号 2,只要有负数,就全链路都用 signed,不要混用。
关系运算符
关系运算结果是 1 位,即使比较的是 8 位数
== 和 === 的区别(常考)
==:只要有一边是 x 或 z,结果就是 x(不定态)。
===:把 x、z 当成具体值比较,结果一定是 0 或 1。只在仿真/建模时用,不可综合。
逻辑运算符和位运算符
wire [3:0] a = 4'b1010;
wire [3:0] b = 4'b1100;
// 位运算:逐位操作,结果 4 位
wire [3:0] and_bit = a & b; // 4'b1000
wire [3:0] or_bit = a | b; // 4'b1110
wire [3:0] xor_bit = a ^ b; // 4'b0110
wire [3:0] not_bit = ~a; // 4'b0101
// 逻辑运算:整数值是否非零,结果 1 位
wire and_log = a && b; // 1(a、b 都非零)
wire or_log = a || b; // 1(至少一个非零)
wire not_log = !a; // 0(a 非零)
归约运算符(一元位运算)
把向量的所有位"归约"成 1 位,是位运算的一元形式:
运算符 | 含义 | a = 4'b1010 时结果 |
|---|---|---|
&a | 所有位相与 | 0(有 0 就是 0) |
~&a | 与非归约 | 1 |
|a | 所有位相或 | 1(有 1 就是 1) |
~|a | 或非归约 | 0 |
^a | 所有位异或(奇偶校验) | 0(偶数个 1) |
~^a | 同或归约 | 1 |
移位运算符
运算符 | 名称 | 空位补什么 |
|---|---|---|
<< | 逻辑左移 | 补 0 |
>> | 逻辑右移 | 补 0 |
<<< | 算术左移 | 补 0(和 << 相同) |
>>> | 算术右移 | 补符号位(无符号时补 0) |
同步和异步
异步:@(posedge clk or negedge rst_n) —— rst_n 变化立即触发 always 块。
同步:@(posedge clk) —— rst_n 变化不触发,只有时钟沿才检查它
异步复位最大的坑在"释放"时刻,不在"拉低"时刻。
拉低(assert):随时拉低都安全,立刻复位,没问题。
释放(deassert,rst_n 从 0 变 1):如果这个跳变恰好发生在时钟上升沿附近,触发器的复位撤除和时钟采样"撞车",可能进入亚稳态(输出既不是 0 也不是 1,而是中间电平,持续一段时间才稳定)
复位拉低用异步(快),释放用同步(安全)。做法是把复位信号先经过两级触发器同步到时钟域,再用它的输出做复位。

时钟/CDC 相关
多 bit 信号:多个 bit 到达时间不同,采到"半新半旧",必须用异步 FIFO 或格雷码。
单 bit 脉冲直接打两拍:脉冲可能比目的时钟周期还窄,被完全漏掉,要先展宽再同步。
If/case
If有优先级,case是并行,没有优先级
If得写else,case得写default,防止生成锁存器,最后前面写上默认值
always @(*) begin
y = 8'd0; // 默认值兜底
if (en) y = a;
End
case (表达式)
值1: 语句;
值2: 语句;
值3, 值4: 语句; // 多个值共享一个分支,逗号分隔
default: 语句; // 兜底
endcase
Case每个值必须不用,但值的位宽必须相同

浙公网安备 33010602011771号