跳转到内容

可编程逻辑/VHDL 进程

来自维基教科书,开放的书籍,开放的世界

并发 vs. 顺序评估

[编辑 | 编辑源代码]

在 VHDL 中有两种方式进行信号赋值。信号赋值可以通过并发语句或顺序语句完成。并发语句中的赋值立即生效。在以下示例代码中,可以认为当 sFlag = '1' 时,sQ 将始终具有 sD 的值。否则它将为零。这将在 sD 或 sFlag 改变后的一个仿真“周期”内生效。

architecture rtl of some_entity
    signal sFlag: std_logic;
    signal sD: std_logic;
    signal sQ: std_logic;
begin
    with sFlag select
	  sQ <= sD  when '1',
		'0' when others;
end architecture some_entity;

使用进程允许我们编写功能等效的代码,只是使用更自然的顺序语句。在进程中,语句按顺序执行,并且只有进程中所有赋值的最终结果才会生效。可以将进程视为执行的迷你“程序”,并在完成后应用其信号赋值的结果。下面显示了功能等效的顺序代码

architecture rtl of some_entity
    signal sFlag: std_logic;
    signal sD: std_logic;
    signal sQ: std_logic;
begin
    process(sFlag, sD)
    begin
        sQ <= '0';
        if(sFlag = '1') then
            sQ <= sD;
        end if;
    end process;
end architecture some_entity;

当跟踪此代码时,sQ 最初被赋值为零。然后,如果 sFlag = '1',sQ 被赋值为 sD。sQ 不会更新,直到整个进程执行完毕。由于整个进程在一个仿真“周期”内执行,因此最终结果与使用并发赋值的代码相同。

使用“wait”的进程

[编辑 | 编辑源代码]

进程可以构建的一种方式是使用 wait 语句。wait 语句是一个 VHDL 结构,它告诉模拟器延迟执行多长时间或在什么条件下继续执行。以下是 wait 语句的示例

-- Tells the simulator to delay evaluation of the rest of the process for 10 ns;
wait for 10 ns;
-- Tells the simulator to delay evaluation of the rest of the process until a_flag contains the value of '1'
wait until a_flag = '1';

通过使用 wait 语句,模拟器知道何时停止执行进程并让时间在模拟中前进。wait 语句在进程中使用,如以下示例所示

signal sClk: std_logic := '0';
constant cClkPeriod: time := 10 ns;
...

pClockSim: process
begin
  sClk <= not sClk;
  wait for cClkPeriod/2;
end process pClockSim;

在此示例中,信号 sClk 被赋值为其当前值的相反值。然后,进程将等待由常量 cClkPeriod 指定时间的一半,之后将继续执行。当进程结束时,评估将立即从进程开头继续。

使用 wait 语句的进程通常不可综合,通常用于测试平台。但是,它们通常直观易用,允许快速构建测试平台模块和生成用于检查 VHDL 模块功能的其他测试信号。

使用敏感度列表的进程

[编辑 | 编辑源代码]

使用进程的另一种方式是使用敏感度列表

...
architecture rtl of some_entity is
  signal a_flag:       std_logic;
  signal another_flag: std_logic;
  ...
begin
  ...
  process(another_flag)
  begin
    a_flag <= another_flag;
  end process;
  ...
end architecture rtl;

敏感度列表 (another_flag) 是模拟器监视更改的信号的逗号分隔列表。当这些信号发生变化时,模拟器将评估进程并更新分配的信号。在上面的示例中,进程将在信号“another_flag”发生变化时执行。

并发语句作为进程

[编辑 | 编辑源代码]

体系结构中的并发语句可以解释为具有单个语句的进程。

...
architecture rtl of some_entity is
  signal a_flag:       std_logic;
  signal another_flag: std_logic;
  ...
begin
  ...
  process(another_flag)
  begin
    a_flag <= another_flag;
  end process;
  ...
end architecture rtl;

此代码可以缩写为

...
architecture rtl of some_entity is
  signal a_flag:       std_logic;
  signal another_flag: std_logic;
  ...
begin
  ...
  a_flag <= another_flag;
  ...
end architecture rtl;

体系结构中的并发语句简化了模块的语法,并允许使用更少的 VHDL 结构进行简单的赋值。

进程的综合

[编辑 | 编辑源代码]

由于进程调度评估的概念无法直接转换为物理逻辑,因此进程被解释为表示组合逻辑块或寄存器。综合工具通常会从具有敏感度列表的进程中解释组合逻辑块。

architecture rtl of some_entity
    signal sFlag: std_logic;
    signal sD: std_logic;
    signal sQ: std_logic;
begin
    process(sFlag, sD)
    begin
        sQ <= '0';
        if(sFlag = '1') then
            sQ <= sD;
        end if;
    end process;
end architecture some_entity;

以下示例将被综合为下面的多路复用器

从顺序“if”语句综合的多路复用器

除了组合逻辑之外,为了创建有用的数字逻辑电路,我们需要一种在硬件中创建寄存器的方法。可以使用类似于以下的进程模板创建寄存器(触发器)

process(clk, reset)
begin
  if(reset = '1') then
    sQ <= '0';
  elsif rising_edge(clk) then
    sQ <= sQ_next;
  end if;
end process

综合后,此代码将创建一个具有异步高电平复位的寄存器,其“D”输入为 sQ_next,输出为 sQ。

用于描述组合逻辑的具有敏感度列表的进程和“寄存器模板”进程可以一起使用来描述 RTL 数字电路

signal clk: std_logic;
signal reset: std_logic;
signal sQ: std_logic;
signal sQ_next: std_logic;
...
-- Combinatorial logic that assigns a value to sQ_next
process(sQ)
begin
  sQ_next <= not sQ;
end process;

-- This process will create a register that will store the value of sQ_next for one
-- clock cycle
process(clk, reset)
begin
  if(reset = '1') then
    sQ <= '0';
  elsif rising_edge(clk) then
    sQ <= sQ_next;
  end if;
end process

同样,RTL 电路的组合部分使用具有敏感度列表的进程来表达。然后使用上面的标准寄存器模板创建寄存器。

进程编码技巧

[编辑 | 编辑源代码]
  • 使用进程描述组合逻辑时,始终在进程顶部将所有分配的信号“初始化”为默认值。
  • 尽管更短,但一位作者的经验表明,将 RTL 逻辑分离成两个进程(一个用于组合逻辑,一个用于寄存器)更不容易出错。使用信号如“sQ”用于寄存器的当前输出和“sQ_next”用于组合逻辑的输出和寄存器的下一个时钟输出有助于实现这一点。
华夏公益教科书