本节摘要:std_logic 用九个值精确刻画真实电路的电平状态,是 VHDL 区别于软件语言的标志性设计。本节逐一讲清九值的来历与用途、综合器只认其中四个的事实、向量的方向声明与初始化写法,并解释仿真里最常见的"满屏 U"现象。
VHDL 自带一个二值类型 bit,只有 '0' 和 '1',看起来够用,实际不够。真实电路里存在:没初始化之前的未知态、两根线打架的冲突态、总线释放后的高阻态、弱上拉弱下拉。bit 对这些状态全部无能为力,于是 IEEE 标准逻辑包定义了 std_logic,一口气给出九个值:
| 值 | 含义 | 仿真中何时出现 | 综合器态度 |
|---|---|---|---|
| 'U' | 未初始化 | 信号从未被驱动过 | 报错或警告 |
| 'X' | 强制未知 | 多驱动冲突、无效运算 | 报错 |
| '0' | 强低电平 | 正常驱动 | 实现 |
| '1' | 强高电平 | 正常驱动 | 实现 |
| 'Z' | 高阻 | 总线释放 | 实现(三态门) |
| 'W' | 弱未知 | 弱驱动冲突 | 报错 |
| 'L' | 弱低 | 上拉电阻场景 | 部分实现 |
| 'H' | 弱高 | 上拉电阻场景 | 部分实现 |
| '-' | 无关项 | 显式标注不用管 | 可优化 |
关键事实:综合器只认真实现 '0'、'1'、'Z' 和 '-' 四个值,其余五个是仿真专属。这不代表它们没用——恰恰相反,'U' 和 'X' 是仿真器替你抓 bug 的两个哨兵。波形里一大片 'U',说明你的信号没初始化、或驱动它的进程根本没跑;出现 'X',说明发生了冲突或把 'U' 拿去参与了运算。软件里"未初始化变量"最多是个随机数,硬件里它意味着你上电后电路行为不可预测。

单个 std_logic 是一根线,std_logic_vector 是一排线。声明必须给范围和方向:
signal addr : std_logic_vector(7 downto 0); -- 高位在左,行业惯例 signal data : std_logic_vector(0 to 7); -- to 方向,少见但有
downto 0 意味着最左边是最高位(下标 7),to 7 意味着最左边是最低位(下标 0)。两种都合法,但行业压倒性使用 downto,理由很实际:它与二进制数的书写习惯一致,"10000000" 在 downto 向量里就是一百二十八,人脑零转换。混用两种方向在做拼接与比较时会踩坑,建议全工程统一 downto。
向量的字面量是双引号字符串,单个位用单引号。几个高频操作集中看:
signal v : std_logic_vector(7 downto 0) := "1010_0011"; -- 下划线分组,纯为人眼 signal lo : std_logic_vector(3 downto 0); signal sel : std_logic; ... lo <= v(3 downto 0); -- 切片:取低四位 v(7) <= sel; -- 单bit赋值:改最高位 v <= lo & "0000"; -- 拼接:低四位拼四个零 v <= (others => '0'); -- 全零;'1'、'Z' 同理 v <= (7 => '1', others => '0'); -- 只把第 7 位置一
(others => '0') 是最值得形成肌肉记忆的写法——它按向量实际长度自动展开,位宽改了也不用跟着改代码。
理解九值最快的办法是亲手把它们造出来。写一个只有并发赋值的小架构,故意制造冲突:
architecture demo of nine_val is signal a, b : std_logic := '0'; signal bus_y, u_y : std_logic; begin a <= '1' after 20 ns; bus_y <= '0' when a = '0' else 'Z'; -- 一号驱动 bus_y <= '1' when b = '0' else 'Z'; -- 二号驱动,与一号冲突 u_y <= u_y and a; -- 自我参与运算 end architecture demo;
跑仿真看波形:bus_y 在两路驱动同时有效时变 'X';u_y 从头到尾是 'U',因为它从未被有效驱动过。改一下 a 的延迟再跑,观察冲突窗口移动。做完这个实验,九值就不再是表格里的字符,而是你在波形里见过、能主动制造的现象。
⚠️ 常见坑:复位只复位了数据路径、忘了复位状态寄存器,上板后行为随机——仿真里这个错误的化身就是复位释放前波形里的 'U'。凡是"仿真里先红一片(部分工具用红色标 X)"的设计,别急着看后面的波形,先解决前面的 U/X。
💡 关键直觉:把 'U' 当"这根线还没通电",把 'X' 当"两根线在打架",把 'Z' 当"手从总线上拿开"。九值是物理世界的诚实词汇表,不是理论装饰。
std_logic_vector 是逻辑向量,不是数字。要做算术运算必须先 cast 到 unsigned 或 signed:
signal a, b : std_logic_vector(7 downto 0); signal sum : unsigned(7 downto 0); sum <= unsigned(a) + unsigned(b);
unsigned 和 signed 是 numeric_std 包定义的数组类型,与 std_logic_vector 同构,所以可以无缝转换。signed 用 2 的补码表示负数。
直接把 std_logic_vector 喂给 + 运算符——+ 只对 numeric 类型定义,对 std_logic_vector 不重载,编译会报"no overloaded operator"。
另一个常见错误:混淆 unsigned 和 signed 做比较。unsigned('0') 是 0,signed('0') 也是 0,但 unsigned(-1) 是 wrap-around 到 65535(对于 16 位)。工程上务必明确每个信号的"有符号/无符号"语义,混用是 bug 重灾区。