ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VHDL并发条件与选择信号赋值:硬件描述核心语法与工程实践

VHDL并发条件与选择信号赋值:硬件描述核心语法与工程实践

1. 项目概述:理解VHDL中的并发信号赋值

在数字电路设计领域,VHDL(VHSIC Hardware Description Language)是描述硬件行为的基石。对于许多初学者甚至有一定经验的工程师来说,VHDL中“并发(Concurrent)”这一概念,尤其是与条件(Conditional)和选择(Selected)信号赋值结合时,常常是理解上的一个分水岭。这不仅仅是语法问题,更关乎你能否写出真正符合硬件思维、可综合且高效的代码。很多人从软件编程转过来,很容易把“if-else”和“case”语句的思维直接套用,结果写出来的代码要么仿真通过但综合后时序灾难,要么根本就是错误的理解了硬件并行工作的本质。

今天,我们就来彻底拆解“并发条件与选择信号赋值”这个核心主题。这不仅仅是VHDL语言的两个语法糖,它们是连接行为描述与底层硬件结构的关键桥梁。掌握了它们,你就能更清晰地表达多路选择器、优先级编码器、状态机输出逻辑等常见电路,并且让你的代码意图对综合工具更加友好。无论是你正在学习VHDL基础,还是工作中需要优化一段RTL代码,理解并发赋值与顺序赋值的区别,以及如何正确使用条件与选择赋值,都是无法绕开的必修课。接下来,我会结合具体的代码示例、综合后的电路图思维以及实际工程中的踩坑经验,带你从“会用”到“懂为什么这么用”。

2. 核心概念辨析:并发执行与顺序执行

在深入语法细节之前,我们必须先建立正确的硬件描述世界观。VHDL代码最终描述的是硬件电路,而硬件电路的核心特性是并发性:所有门电路、触发器、连线在通电后是同时工作的。为了描述这种并发行为,VHDL引入了“并发语句(Concurrent Statements)”的概念。它们直接对应到硬件的某个部分,并且这些语句在模拟(仿真)时被认为是同时执行的,没有先后顺序。

2.1 并发语句的本质

并发语句直接出现在VHDL的架构体(Architecture Body)中,与进程(Process)语句并列。最常见的并发语句就是并发信号赋值语句。当你写下output_signal <= input_a and input_b;这样一行代码在进程外部时,它描述的就是一个永久的、持续工作的与门。仿真器在每一个仿真周期(Delta Cycle)都会评估等式右边表达式的值,并安排更新左边的信号。这与软件中“执行完一条语句再执行下一条”的顺序思维截然不同。

理解这一点至关重要:在架构体中,多个并发信号赋值语句的书写顺序不影响其功能。它们描述的是电路中多个独立的、同时存在的部件。例如,描述一个全加器,你可能会用三个并发赋值语句分别计算“和(Sum)”、“进位生成(Carry Generate)”和“进位传播(Carry Propagate)”,这三个计算在硬件上是并行发生的。

2.2 顺序语句的舞台:进程(Process)

与并发语句相对的是顺序语句(Sequential Statements),它们只能出现在进程(Process)、函数(Function)或过程(Procedure)内部。if,case,loop,wait等都是典型的顺序语句。在进程内部,代码像软件一样按书写顺序执行。进程本身作为一个整体,是并发执行的。你可以有多个进程,它们之间是并发关系;但每个进程内部的语句,是顺序关系。

这里最大的混淆点来了:ifcase作为顺序语句,如何与“并发赋值”产生联系?答案就在于VHDL提供了两种特殊的、功能强大的并发信号赋值形式when-else(条件信号赋值)和with-select-when(选择信号赋值)。它们虽然语法上类似于ifcase,但本质是并发语句,直接写在架构体中,用于描述多路选择逻辑。

注意:一个常见的误解是认为在进程外用了whenwith就是顺序执行,这是错误的。when-elsewith-select-when是并发语句,它们没有“执行顺序”,只描述了一个时刻输入与输出的映射关系。

2.3 关键区别总结

为了更清晰,我们用一个表格来对比:

特性并发条件/选择赋值 (when-else/with-select)进程内的顺序if/case赋值
位置架构体(Architecture)内,进程外。进程(Process)、函数或过程内部。
执行模型并发执行。描述一个持续的组合逻辑电路。顺序执行(在进程内部)。
推断电路通常直接推断为多路选择器(MUX)。结构非常明确。可能推断出锁存器(Latch)或寄存器,取决于是否条件完备且是否在时钟边沿触发。
完整性要求必须完备。所有可能的输入情况都必须被覆盖,否则会隐含地保持原值,这通常会导致不希望出现的锁存器。在组合逻辑进程中,也必须完备,否则同样会产生锁存器。在时序逻辑进程中(时钟触发),不要求条件完备。
可读性对于描述简单的、纯粹的组合逻辑多路选择,非常直观简洁。更灵活,可以混合复杂的逻辑计算、变量操作等,适合描述复杂的控制流。
优先级when-else具有隐含的优先级,从上到下评估。with-select是并行的,所有选择分支平等。if-elsif-else具有优先级。case是并行的(但通常综合工具会优化为并行MUX)。

3. 并发条件信号赋值详解

并发条件信号赋值使用when-else语法,它是描述带有优先级的多路选择器最直接的方式。

3.1 基本语法与硬件映射

其基本语法结构如下:

target_signal <= value1 when condition1 else value2 when condition2 else ... valueN when conditionN else default_value;

这条语句是并发的,意味着它独立于架构体中的其他语句。仿真器会持续监测所有condition和表达式valueX。它从上到下依次判断条件,第一个为“真”的条件所对应的值,会被赋予目标信号。如果所有条件都不为真,则使用default_value

从硬件角度看,这直接映射为一个**优先级编码器(Priority Encoder)**后接一个多路选择器。条件1的优先级最高,条件N的优先级最低。例如,一个简单的三模式选择器:

architecture rtl of example is signal mode : std_logic_vector(1 downto 0); signal data_a, data_b, data_c, output : integer; begin -- 优先级: mode="11" > mode="10" > mode="01" > 其他 output <= data_a when mode = "11" else data_b when mode = "10" else data_c when mode = "01" else 0; -- 默认值 end architecture;

这段代码综合出的电路,会先判断mode是否等于“11”,如果是则选择data_a;如果不是,再判断是否等于“10”,以此类推。这是一个典型的带优先级的链式结构。

3.2 必须覆盖所有情况:避免锁存器陷阱

这是使用并发条件赋值时最容易出错的地方。在组合逻辑中(即不是由时钟触发的时序逻辑),输出必须在所有可能的输入条件下都有明确的定义。在上面的例子中,mode是一个2位信号,有4种可能(“00”, “01”, “10”, “11”)。我们的语句显式覆盖了“11”、“10”、“01”三种情况,并通过最后的else 0覆盖了剩下的“00”情况。这样是完备的,会综合出纯粹的组合逻辑(多路选择器)。

如果我们遗漏了最后的else子句:

output <= data_a when mode = "11" else data_b when mode = "10" else data_c when mode = “01”; -- 缺少 else!

mode为“00”时,output应该等于什么?VHDL语义规定,在这种情况下,output保持其之前的值不变。对于硬件来说,“保持之前的值”意味着需要记忆单元,也就是一个锁存器(Latch)。在大多数同步数字电路设计中,无意中生成的锁存器是灾难性的,它们会导致时序难以分析、测试困难、并可能引入毛刺和亚稳态问题。

实操心得:养成条件赋值必写else,选择赋值必写when others的习惯。对于std_logicstd_logic_vector类型,使用else ‘X’when others并不是好主意(’X’在仿真中有用,但综合工具可能无法处理)。对于数值类型,设置一个合理的默认值(如0);对于控制信号,设置一个安全的状态。这是写出可综合、稳健的RTL代码的基本素养。

3.3 复杂表达式与嵌套使用

when-else的表达式部分(valueX)可以是任何复杂的表达式,并且条件本身也可以包含逻辑运算。这使得它可以描述相当复杂的组合逻辑。

-- 使用复杂的条件表达式 interrupt_enabled <= ‘1’ when (cpu_status = “IDLE” and interrupt_mask = ‘0’) or (debug_mode = ‘1’ and soft_int = ‘1’) else ‘0’; -- 目标信号本身可以出现在条件中,用于描述带反馈的组合逻辑(需谨慎,避免组合环路) next_state <= STATE_A when (current_state = STATE_IDLE and start = ‘1’) else STATE_B when (current_state = STATE_A and count > 100) else STATE_IDLE when (current_state = STATE_B and done = ‘1’) else current_state; -- 保持当前状态,注意:这可能会在特定条件下推断出锁存器!

最后一行else current_state需要特别注意。在状态机描述中,我们通常希望在未触发转移条件时状态保持不变。在组合逻辑的进程或并发赋值中,这种“保持”语义必然导致锁存器。因此,正确的做法是将状态转移逻辑放在时钟触发的同步进程(时序逻辑)中描述,这样“保持”的行为由寄存器(Flip-Flop)实现,而不是危险的锁存器。并发赋值通常更适合描述状态机的输出逻辑(摩尔或米利输出),而不是状态寄存器本身。

4. 并发选择信号赋值详解

并发选择信号赋值使用with-select-when语法,它描述的是一个并行的多路选择器,所有选择分支在逻辑上是平等的,没有优先级之分。

4.1 基本语法与硬件映射

其基本语法结构如下:

with selection_expression select target_signal <= value1 when choice1, value2 when choice2, ... valueN when choiceN, default_value when others;

selection_expression是一个表达式,其值用于与各个choice进行匹配。choice必须是静态的或离散的值,并且所有可能的值必须被覆盖,这通常通过强制性的when others子句来保证。

硬件上,这直接映射为一个多路选择器(MUX)。选择表达式作为MUX的选择线,各个choice值作为选择线的编码,所有输入通道(value1valueN)是并行的。例如,一个4选1的MUX:

architecture rtl of mux4 is signal sel : std_logic_vector(1 downto 0); signal in0, in1, in2, in3, output : std_logic_vector(7 downto 0); begin with sel select output <= in0 when “00”, in1 when “01”, in2 when “10”, in3 when “11”, (others => ‘0’) when others; -- 对于std_logic_vector,others是必须的 end architecture;

综合工具会生成一个4输入、8位宽的多路选择器。选择信号sel的每一位都平等地参与选择逻辑,没有先后顺序。

4.2when others的强制性及其重要性

when others子句在with-select语句中几乎是强制性的(除非选择表达式是枚举类型,且你列出了所有枚举值)。这是因为选择表达式(如std_logic_vector)可能的值空间非常大(例如,一个8位向量有256种可能),你不可能也不应该列出所有情况。when others捕获所有未明确列出的情况,保证了逻辑的完备性,避免了锁存器的推断。

对于std_logicstd_logic_vector类型,others覆盖了除了你明确列出的‘0’和‘1’之外的所有其他值(如‘Z’, ‘L’, ‘H’, ‘-’, ‘U’, ‘X’, ‘W’)。在可综合代码中,我们通常只关心‘0’和‘1’,但为了代码健壮性和避免仿真与综合不匹配,必须处理others

-- 处理 std_logic 类型的选择 with command select valid <= ‘1’ when ‘0’, ‘0’ when ‘1’, ‘0’ when others; -- 处理 ‘Z’, ‘X’, ‘U’ 等值 -- 为 others 选择一个安全的默认值,通常是复位状态或无效状态 with error_code(3 downto 0) select alarm_level <= “00” when “0000”, -- 无错误 “01” when “0001” | “0010”, -- 低级错误,使用“|”表示或 “10” when “0100” to “0111”, -- 中级错误,使用“to”表示范围 “11” when others; -- 捕获所有其他编码,视为严重错误

4.3 选择表达式的灵活性

选择表达式可以是任何得出离散值的表达式,不仅限于简单的信号名。这增加了描述的灵活性。

-- 选择表达式可以是拼接操作 with (mode & enable) select operation <= OP_IDLE when “00”, OP_READ when “01”, OP_WRITE when “10”, OP_ERROR when “11”, OP_IDLE when others; -- 选择表达式可以是函数调用(函数必须返回离散类型) with get_priority_level(int_vector) select grant <= ‘1’ when 0, ‘0’ when others;

需要注意的是,选择表达式必须是“静态”或可在综合时确定的,或者其所有可能值是可枚举的。过于复杂的表达式可能会让代码可读性变差,此时使用进程内的case语句可能更合适。

5. 条件赋值与选择赋值的对比与选型

理解了两种赋词的语法和硬件映射后,一个很自然的问题是:我该用哪一个?选择取决于你想要描述的硬件行为。

5.1 优先级 vs. 并行性

这是最根本的区别。

  • when-else(条件赋值)隐含优先级。条件按书写顺序评估,第一个为真的条件胜出。这完美描述了诸如中断优先级仲裁、带优先级的请求处理等场景。其综合出的电路是链式结构,速度可能较慢(关键路径长),但面积可能较小(如果某些条件互斥或可优化)。
  • with-select(选择赋值)无优先级,并行匹配。所有选择分支地位平等,选择表达式直接解码后控制一个多路选择器。这描述了诸如指令解码、基于状态码的纯查找表等场景。其综合出的电路通常是并行的多路选择器,速度较快,但面积可能随选择位宽指数增长。

5.2 代码可读性与维护性

  • when-else:当条件逻辑复杂、相互重叠、且有明确的优先次序时,使用when-else更符合人类的思维习惯,代码也更清晰。例如,一个报警系统:火警优先级最高,然后是入侵警报,最后是设备故障警报。
  • with-select:当选择是基于一个值的精确匹配,并且所有选项是互斥且完备的集合时,使用with-select更直观。它看起来就像一个真值表或查找表,一目了然。例如,根据一个2位操作码选择执行哪种运算。

5.3 综合工具优化差异

现代综合工具非常智能,它们可能会对你的代码进行重写和优化。

  • 一个写得好的when-else链,如果条件互斥,工具可能会将其优化成与with-select类似的并行MUX结构。
  • 一个with-select语句,如果choice不是简单的常量,工具内部可能会引入优先级逻辑。 但作为设计者,我们不应该依赖工具的优化来纠正我们的设计意图。我们应该用最贴近目标硬件结构的语法来描述逻辑。这样代码更可读、意图更明确,也减少了综合工具误解我们意图的风险。

5.4 选型决策指南

我们可以用一个简单的决策流程来帮助选择:

  1. 你的逻辑本质上是否有优先级?
    • -> 使用when-else
    • -> 进入第2步。
  2. 你的选择标准是否是一个值的精确匹配(像一个查找表)?
    • -> 使用with-select
    • -> 可能使用进程内的ifcase语句更灵活。
  3. 条件是否复杂,且when-else会导致冗长的链式结构?
    • -> 考虑将部分逻辑提取成中间信号,或者评估是否真的需要这么复杂的优先级。有时用with-select配合精心编码的选择表达式可能更简洁。
  4. 是否需要描述“保持之前值”的行为(在组合逻辑中应避免)?
    • 需要-> 停下来!你很可能需要用时序逻辑(时钟触发的进程)来实现,而不是组合逻辑的并发赋值。

6. 高级应用与混合使用模式

在实际工程中,我们 rarely 只使用单一的赋值语句。混合使用并发赋值、进程以及合理的代码组织,是写出高质量VHDL的关键。

6.1 在生成语句中使用并发赋值

生成语句(generate)用于描述重复结构或条件化结构,其内部通常包含并发语句。结合并发赋值非常强大。

-- 例:一个位宽可参数化的奇偶校验生成器,使用生成语句和并发赋值 architecture rtl of parity_gen is signal parity_chain : std_logic_vector(DATA_WIDTH downto 0); begin parity_chain(0) <= ‘0’; -- 初始值 -- 生成一个链式的异或逻辑 gen_xor_chain: for i in 0 to DATA_WIDTH-1 generate parity_chain(i+1) <= parity_chain(i) xor data_in(i); end generate gen_xor_chain; parity_out <= parity_chain(DATA_WIDTH); end architecture;

在这个例子中,parity_chain(i+1) <= ...是一个在生成语句内部的并发信号赋值。它描述了多个并发的异或门。综合后会产生一个异或门链。

6.2 组合进程与并发赋值的等价转换

一个只包含组合逻辑(没有时钟,没有wait)的进程,其功能通常可以用一组并发赋值语句(包括when-elsewith-select)来等价描述。选择哪种方式,取决于可读性和个人/团队习惯。

-- 方式1:使用并发条件赋值 output_signal <= a when sel = “00” else b when sel = “01” else c when sel = “10” else d; -- 方式2:使用并发选择赋值 with sel select output_signal <= a when “00”, b when “01”, c when “10”, d when others; -- 方式3:使用组合进程 process(sel, a, b, c, d) -- 敏感列表必须包含所有输入信号 begin if sel = “00” then output_signal <= a; elsif sel = “01” then output_signal <= b; elsif sel = “10” then output_signal <= c; else output_signal <= d; end if; end process;

上面三种描述在逻辑上是等价的,都会综合成一个4选1的MUX。方式1和2更简洁,意图更直接。方式3的进程写法更传统,对于从软件转来的人可能更熟悉,并且在需要描述更复杂内部变量或顺序操作时是必须的。

注意事项:对于组合进程,敏感列表必须包含所有在进程内部读取的信号a, b, c, d, sel),否则会导致仿真与综合结果不一致,这是一个非常常见的错误源。而并发赋值语句没有敏感列表,这是它的一个优点。

6.3 描述复杂组合逻辑块

对于复杂的组合逻辑,可以分层描述。先用并发赋值描述一些中间信号,再用另一个并发赋值或进程描述最终输出。

architecture rtl of alu is signal arith_result, logic_result, shift_result : std_logic_vector(31 downto 0); signal cmp_flag : std_logic; begin -- 第一层:并行计算各个功能单元的结果 arith_result <= a + b when opcode(0) = ‘0’ else a - b; logic_result <= a and b when opcode(1 downto 0) = “00” else a or b when opcode(1 downto 0) = “01” else a xor b when opcode(1 downto 0) = “10” else not a; shift_result <= a sll to_integer(unsigned(b(4 downto 0))); -- 假设b的低5位是移位位数 -- 第二层:根据主操作码选择最终输出 with opcode(3 downto 2) select data_out <= arith_result when “00”, logic_result when “01”, shift_result when “10”, (others => cmp_flag) when “11”, -- 比较结果扩展为32位 (others => ‘0’) when others; -- 另一个并发的比较逻辑 cmp_flag <= ‘1’ when a = b else ‘0’; end architecture;

这种分层描述的方式使得代码结构清晰,每个并发语句块功能单一,易于调试和修改。

7. 常见问题、陷阱与调试技巧

即使理解了原理,在实际编码中依然会遇到各种问题。这里记录了一些典型的坑和解决方法。

7.1 问题1:产生了意想不到的锁存器

现象:综合报告(Synthesis Report)中出现了“Latch”或“Latches”的警告或推断信息。原因:在组合逻辑(并发赋值或组合进程)中,输出信号没有在所有可能的输入条件下被赋值。排查与解决

  1. 检查when-else:确保有最终的else子句。
  2. 检查with-select:确保有when others子句,并且它覆盖了选择表达式的所有剩余值。
  3. 检查选择条件的完备性:对于std_logic_vector信号,考虑所有位组合。对于枚举类型,确认是否列出了所有枚举值。
  4. 如果确实需要“保持”行为:问自己,这真的是组合逻辑吗?大多数情况下,你需要的是一个时钟触发的时序进程,用寄存器来保持值。

7.2 问题2:仿真与综合结果不一致

现象:代码在仿真(如ModelSim)中功能正确,但综合后下载到FPGA或ASIC中行为异常。原因

  1. 敏感列表不完整(针对进程):这是最常见的原因。组合进程的敏感列表必须包含所有读取的信号。
  2. 未初始化的信号std_logic类型默认值是’U’(未初始化)。在并发赋值中,如果依赖其初始值做判断,综合后可能不是你想要的行为。最好有明确的复位逻辑。
  3. when others处理不当:在仿真中,std_logic可能有‘X’, ‘Z’等值,你的when others可能将其映射到某个默认值。但在综合后,实际电路只有‘0’和‘1’,这可能导致选择路径不同。确保你的设计在只有‘0’和‘1’时也能正确工作。解决
  • 对于进程,使用VHDL-2008的process(all)自动生成敏感列表,或者极其小心地手动维护。
  • 对所有寄存器(在时钟进程中的信号)和重要的内部状态信号,实现明确的复位或初始化。
  • 在测试中,不仅要测试正常‘0’/‘1’序列,也要在仿真初期测试复位和未知状态下的行为。

7.3 问题3:优先级逻辑导致时序违例

现象:综合或布局布线后的时序报告显示,某条路径的延迟太大,无法满足时钟频率要求。这条路径往往是一个很长的when-else链。分析:一个长的when-else链综合成多级逻辑,形成了很长的组合逻辑路径(关键路径)。优化策略

  1. 流水线化:在关键路径上插入寄存器,将组合逻辑拆分成多个时钟周期完成。
  2. 重写逻辑:评估优先级是否必要。有时可以通过逻辑变换(如使用选择器with-select或平衡树结构)来缩短路径。
  3. 使用中间寄存器:将when-else链中某些条件的计算结果提前一个周期算好,存到寄存器中,下一周期再使用。
  4. 综合工具指令:有些工具支持属性(Attribute)或编译指令来指导综合器对特定逻辑进行优化(如parallel_case类似的功能,但需谨慎使用)。

7.4 问题4:代码冗长难以维护

现象:一个when-elsewith-select语句有几十行,条件或选择支非常多。解决

  1. 使用函数或过程封装:将复杂的条件判断或值计算封装到函数中,使顶层赋值语句简洁。
    function get_alarm_level(err : error_vector_type) return std_logic_vector is begin if err(CRITICAL) = ‘1’ then return “11”; elsif err(MAJOR) = ‘1’ then return “10”; elsif err(MINOR) = ‘1’ then return “01”; else return “00”; end if; end function; ... alarm_level <= get_alarm_level(error_reg);
  2. 使用查找表常数:对于纯查找表逻辑,可以定义一个常数数组。
    type opcode_lut_type is array (0 to 7) of std_logic_vector(3 downto 0); constant OPCODE_LUT : opcode_lut_type := (x“1”, x“3”, x“0”, x“F”, x“A”, x“5”, x“C”, x“8”); ... microcode <= OPCODE_LUT(to_integer(unsigned(instruction(2 downto 0))));
  3. 分模块设计:将过于复杂的逻辑拆分到不同的实体(Entity)或模块中,通过层次化来管理复杂度。

8. 实战案例:一个简单的仲裁器设计

让我们通过一个完整的简单例子来融会贯通。设计一个带有固定优先级的4路请求仲裁器。

需求:有4个设备(req0, req1, req2, req3)请求总线访问。仲裁器输出授权信号(grant)和授权的设备编号(grant_id)。优先级固定为 req0 > req1 > req2 > req3。当无请求时,grant输出为低,grant_id输出为一个无效值(如“11”)。

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- 用于无符号数运算 entity priority_arbiter is port ( req : in std_logic_vector(3 downto 0); -- 请求信号,req(0)优先级最高 grant : out std_logic; -- 授权有效 grant_id : out std_logic_vector(1 downto 0) -- 授权设备ID ); end entity priority_arbiter; architecture rtl of priority_arbiter is -- 内部信号,用于更清晰的逻辑描述 signal internal_grant : std_logic; signal internal_id : std_logic_vector(1 downto 0); begin -- 核心仲裁逻辑:使用并发条件赋值描述优先级 -- 优先级逻辑:req(0) > req(1) > req(2) > req(3) (internal_grant, internal_id) <= (‘1’, “00”) when req(0) = ‘1’ else (‘1’, “01”) when req(1) = ‘1’ else (‘1’, “10”) when req(2) = ‘1’ else (‘1’, “11”) when req(3) = ‘1’ else (‘0’, “11”); -- 无请求时,grant=0, id=“11”(无效) -- 将内部信号连接到输出端口 grant <= internal_grant; grant_id <= internal_id; -- 另一种等效的描述方式,使用两个独立的并发赋值语句: -- grant <= req(0) or req(1) or req(2) or req(3); -- 只要有任一请求,grant有效 -- grant_id <= “00” when req(0) = ‘1’ else -- “01” when req(1) = ‘1’ else -- “10” when req(2) = ‘1’ else -- “11”; -- 当req(3)=‘1’或无请求时,id都是“11” -- 注意:这种方式下,无请求时grant_id也是“11”,可能与设计意图不符。 -- 需要根据具体需求调整。第一种聚合赋值的方式意图更明确。 end architecture rtl;

代码解析与思考

  1. 聚合赋值(internal_grant, internal_id) <= ...是VHDL的聚合赋值,可以同时给多个信号赋值,使相关的逻辑绑定在一起,提高了可读性。
  2. 完备性when-else链以else (‘0’, “11”)结束,覆盖了无任何请求的情况,避免了锁存器。
  3. 优先级体现:条件从上到下检查,req(0)的优先级最高。
  4. 输出寄存器:这是一个纯组合逻辑设计。在实际高速系统中,可能会将grantgrant_id用寄存器打一拍再输出,以改善时序。那将是一个时序逻辑设计,需要在时钟进程中对这些信号进行赋值。

这个例子展示了如何用简洁的并发条件赋值清晰地描述一个带有优先级的硬件仲裁逻辑。它直接对应了硬件中的优先级编码器和选择器,综合工具可以毫无歧义地将其映射为相应的门级电路。通过这个练习,你应该能更深刻地体会到,并发信号赋值不仅仅是语法,更是一种硬件描述思维。

返回列表