为什么这不可综合? (在 NOT(clock-edge) 下不保持其值)

Why is this not synthesizable? (does not hold its value under NOT(clock-edge))

我是一名软件工程师 (JAVA/C++) 而不是电气工程师,所以你可以想象 VHDL 对我来说非常混乱,因为我不知道合成器在背后试图做什么场景。它告诉我它无法合成我认为非常简单的架构。 (事实上​​它是为我拥有的几个实体做的,所以我怀疑我误解了一些基本概念并在多个地方重复了架构错误。)

为什么这个不能合成... (错误 - controller.vhd(63):语句不可综合,因为它不 在 NOT(时钟边沿)条件下保持其值。 VHDL-1242 完成:错误代码 2)

LIBRARY ieee;
USE ieee.std_logic_1164.all;

ENTITY controller IS
    PORT (
        ack: out STD_LOGIC := '0';
        data_request: in STD_LOGIC
    );
END controller;

ARCHITECTURE logic OF controller IS
BEGIN
    PROCESS (data_request)
    BEGIN
        if (rising_edge(data_request)) then
            -- other logic will be added here
            ack <= '1';
        elsif (falling_edge(data_request)) then
            -- other logic will be added here too
            ack<='0';
        end if;
    END PROCESS;
END logic;

(是的,我完全知道进程的 "logic" 可以替换为 确认 <= data_request;但我的 vhdl 源实际上比这复杂得多 但我已将其提炼为引发错误的最简单子集。请不要建议用不同的 structure/concurrent 语句替换该过程。)

基本上,当 data_request 线转换为高电平时,ack 输出应被驱动为高电平;在下降沿,它应该变为低电平。 (在每种情况下,我还希望更改一大堆其他内容,因此我需要流程而不是并发语句,ack更改它只是为了向顶级实体发出请求已完成的信号。)

什么是"not holding its value"?当它说 "NOT(clock-edge)" 时,它在说什么 "clock"?

我很想解释如何解决这个问题(不改变结构),并解释我试图要求合成器做什么以及为什么合成器无法实现目标。

与软件编译器相反,硬件综合通常不是逐字进行的。相反,分析整个代码块以找到常见的结构,例如有限状态机。在您的情况下,大多数综合工具都会识别诸如

之类的过程
process (...)
begin
    if rising_edge(clk) then
        -- code here
    end if;
end process;

也就是说,只用一个检查上升沿。您的代码可能会出现问题,因为您使用相同的 if 语句检查上升沿和下降沿。

要使您的代码正常工作,只需使用两个 if 语句即可。然而,正如 指出的那样,在两个边沿上触发可能不适用于所有目标 (FPGA)。模拟没有这个限制。

您也可以尝试添加一个单独的时钟信号,它会在上升沿触发一段特定的代码。此代码然后检查 data_request 信号。

或者(但这取决于您拥有的其他代码)您可以检查 data_request 的水平而不是边缘,例如

if data_request = '1' then
    -- rising edge code
else
    -- falling edge code
end if;

VHDL只是一种编程语言,其实可以有很多结构。但是,您想为设备(FPGA?)编写 VHDL。这意味着您必须根据特定规则对其进行编程。

为了能够检测边缘,您必须及时观察两个时刻的值:之前和之后。因此,您必须在代码中引入时间的概念。 "signal x was '0' at t-1 and is '1' at t."

时钟时间中的时间概念在大多数可编程设备中并不为人所知。作为程序员的你必须介绍它。在大多数数字电路中,您会为此使用具有特定频率的振荡器,通常被称为 'clock'.

数字逻辑还有一个问题,那就是延迟。电力不会以无限的速度通过逻辑。因此,您必须考虑通过不同行进路径的延迟。这使得设计异步逻辑变得非常困难。或者,您可以将值存储在特定位置的内存元素中以解决时序问题,这称为同步逻辑。这些元素通常是寄存器。

为什么我解释这个,是为了激发在您的设计中为您的特定要求引入时钟的原因。您需要记住 data_request 的先前状态才能观察到变化。

    signal data_request_old : std_logic;
begin
    data_request_old <= data_request when rising_edge(clk);
    if data_request = '1' and data_request_old = '0' then
        -- rising edge
    end if;
    if data_request = '0' and data_request_old = '1' then
        -- falling edge
    end if;

x_old <= x when rising_edge(clk)会推断一个寄存器,延迟输入一个时钟周期。

p.s。我假设 data_request 已经与 clk 同步。否则你首先需要同步它。

p.s.2 我将 when rising_edge(clk); 编写为最小结构,大多数 FPGA 综合工具都可以接受。如果你的没有,你需要写一个完整的过程。

问题是您的信号 ack 仅设置在上升沿和下降沿。综合将尝试使您的组件在具有物理约束的现实世界中工作。换句话说,您必须管理没有事件的情况。 在您的情况下,您只需在 if 中添加 else 语句,如下所示:

architecture logic of controller is

signal ack_s : std_logic := '0';

begin
    process(data_request)
    begin
        if (rising_edge(data_request)) then
            -- other logic will be added here
            ack_s <= '1';
        elsif (falling_edge(data_request)) then
            -- other logic will be added here too
            ack_s <= '0';
        else then
            ack_s <= ack_s;
        end if;
    end process;

    ack <= ack_s;    -- You map the output on the internal signal

end architecture logic;

这样你的信号总是映射到一个值。如果您尝试绘制原理图,您就会明白这一点。此外,您必须使用 signal,因为当 ack 是输出时,您不能使用 ack <= ack;。 作为提示,我建议您每次执行 if 块时始终使用 else 语句,因为在现实世界中的电路中,您始终需要默认行为。