02. 四态逻辑、位宽、进制与有符号数
Verilog 的一个数不只有“数值”,还至少有 位宽、进制、每一位的逻辑状态、是否有符号。忽略任何一项,都可能得到表面能编译、硬件却错误的代码。

四态逻辑
跳转到“四态逻辑”| 值 | 含义 | 常见场景 |
|---|---|---|
0 | 确定的逻辑低电平 | GND、条件为假 |
1 | 确定的逻辑高电平 | VCC、条件为真 |
x | 未知,当前无法判断是 0 还是 1 | 未复位寄存器、驱动冲突、未知传播 |
z | 高阻,当前驱动器近似断开 | 三态总线、未驱动线网 |
x 不是硬件中的“第三种稳定电平”。它主要是仿真器告诉你:信息不足或存在冲突。看到 x 时,不要急着用条件把它隐藏,而应追查未初始化、复位和多驱动。
z 表示该驱动端不驱动总线:
assign data_bus = output_enable ? data_out : 8'bz;现代 FPGA 内部通常不会保留任意三态网络;三态多用于芯片 I/O。综合工具可能把内部三态转换为多路选择逻辑。
常量的完整写法
跳转到“常量的完整写法”格式是:
<位宽>'<进制><数值>常见进制:
b:二进制,例如8'b1010_0101;o:八进制,例如6'o52;d:十进制,例如8'd165;h:十六进制,例如8'hA5。
下划线只为阅读分组,不改变数值:
16'b1010_0101_1100_001132'h1234_ABCD25_000_000 // 未指定位宽的十进制常量位宽必须算清楚
跳转到“位宽必须算清楚”wire [3:0] a = 4'b1010; // 4 位,a[3] 是最高位wire [7:0] b = 8'hA5; // 8 位wire c = 1'b1; // 1 位[7:0] 一共有 7 - 0 + 1 = 8 位。常见选取方式:
wire [7:0] data;wire msb = data[7];wire [3:0] high = data[7:4];wire [3:0] low = data[3:0];目标更窄:高位被截断
跳转到“目标更窄:高位被截断”wire [3:0] low = 8'b1011_0110;// low 得到 4'b0110目标更宽:发生扩展
跳转到“目标更宽:发生扩展”无符号值通常补 0;有符号值在满足表达式规则时会进行符号扩展。不要依赖自己没推导过的隐式规则,关键位置明确转换或拼接:
wire [3:0] small = 4'b1010;wire [7:0] zero_extended = {4'b0000, small};加法为什么可能溢出
跳转到“加法为什么可能溢出”两个 8 位无符号数的最大值都是 255,和最大为 510,需要 9 位表示:
wire [7:0] a;wire [7:0] b;wire [8:0] sum;
assign sum = {1'b0, a} + {1'b0, b};显式在输入前补一位,比假设表达式会自动保留进位更安全、更容易审查。
无位宽常量的风险
跳转到“无位宽常量的风险”教材为了入门把 100 解释为常见实现中的 32 位十进制数。工程中还要注意:无位宽十进制常量通常参与有符号运算,其精确宽度规则会影响表达式扩展。推荐:
- 寄存器值、掩码、比较终值尽量写明确位宽;
- 大数字可用下划线分组;
- 不要把负数直接塞进没有想清楚符号属性的向量。
localparam [7:0] LIMIT = 8'd99;assign done = (counter == LIMIT);有符号数
跳转到“有符号数”wire signed [7:0] temperature;wire signed [7:0] offset;wire signed [8:0] adjusted;
assign adjusted = temperature + offset;8 位二进制补码 8'b1111_1111:
- 按无符号解释是 255;
- 按有符号解释是 -1。
位模式相同,解释不同。混合 signed 与 unsigned 时,结果可能转为你没预期的无符号比较。建议让同一表达式中的操作数位宽和符号属性保持一致,并为结果保留足够位数。
相等比较:== 与 ===
跳转到“相等比较:== 与 ===”a == b // 逻辑相等;遇到 X/Z,结果可能为 Xa === b // 全等;X/Z 也逐位参与比较RTL 设计的正常数据判断通常使用 ==。=== 更常用于 testbench 检查,例如确认某信号确实为 x。不要用 === 掩盖设计中的未知状态。
标识符规则与命名
跳转到“标识符规则与命名”普通标识符可由字母、数字、下划线和 $ 组成,但首字符必须是字母或下划线,并区分大小写:count 与 Count 是两个名字。
推荐约定:
- 普通信号小写加下划线:
cpu_addr; - 参数大写:
DATA_WIDTH; - 低有效信号带
_n:rst_n; - 时钟名包含
clk:sys_clk; - 实例名以
u_开头:u_uart_rx; - 名字表达角色,不用
a1、temp2等模糊名称。
常见错误
跳转到“常见错误”wire [7:0] data = 4'hF; // 合法但会补宽;最好明确意图wire [3:0] data2 = 8'hAB; // 高 4 位被截断wire [7:0] sum = a + b; // 可能丢失进位wire result = signed_a < b; // signed/unsigned 混合比较要警惕练习
跳转到“练习”- 把十进制 45 写成 8 位二进制和 8 位十六进制常量。
4'b1111 + 4'b0001至少需要多少位才能无损保存?8'hF2[7:4]的概念值是什么?实际代码中应先把常量赋给向量再做位选。- 解释
x与z的区别,并各写出一个可能出现它们的场景。
参考答案:8'b0010_1101、8'h2D;加法结果需要 5 位;高半字节是 4'hF;x 表示未知,z 表示不驱动。
上一篇:硬件思维 · 返回目录 · 下一篇:信号、端口与模块