【问题标题】:Combinational Logic Timing组合逻辑时序
【发布时间】:2013-11-24 17:43:59
【问题描述】:

我目前正在尝试实现一个数据路径,它在一个时钟周期内计算以下内容。

  • 获取输入 A 和 B 并将它们相加。
  • 将加法结果右移一位。 (除以 2)
  • 从另一个输入 C 中减去移位后的结果。

实体的行为架构如下图所示。

signal sum_out : std_logic_vector (7 downto 0);
signal shift_out : std_logic_vector (7 downto 0);

process (clock, data_in_a, data_in_b, data_in_c)
begin
    if clock'event and clock = '1' then
        sum_out <= std_logic_vector(unsigned(data_in_a) + unsigned(data_in_b));
        shift_out <= '0' & sum_out(7 downto 1);
        data_out <= std_logic_vector(unsigned(data_in_c) - unsigned(shift_out));
    end if;
end process;

当我模拟上面的代码时,我确实得到了我期望得到的结果。但是,在 3 个时钟周期后,我得到了结果,而不是我希望的 1 个。仿真波形如下图。

我还不熟悉实现具有时序问题的设计。我想知道,是否有办法在一个时钟周期内实现上述计算。如果有,我该如何实现它们?

【问题讨论】:

    标签: hardware signal-processing vhdl


    【解决方案1】:

    用信号来做这件事,只需注册链中的最后一个元素(data_out)。这样分析,我没有写测试台来验证模拟。

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    
    entity signal_single_clock is
        port ( 
            signal clock:       in  std_logic;
            signal data_in_a:   in  std_logic_vector(7 downto 0);
            signal data_in_b:   in  std_logic_vector(7 downto 0);
            signal data_in_c:   in  std_logic_vector(7 downto 0);
            signal data_out:    out std_logic_vector(7 downto 0)
        );
    end entity;
    
    architecture behave of signal_single_clock is
        signal sum_out : std_logic_vector (7 downto 0);
        signal shift_out : std_logic_vector (7 downto 0);
    begin
        sum_out <= std_logic_vector(unsigned(data_in_a) + unsigned(data_in_b));
        shift_out <= '0' & sum_out(7 downto 1);
    single_reg:
        process (clock)
        begin
            if clock'event and clock = '1' then
                data_out <= std_logic_vector(unsigned(data_in_c) - unsigned(shift_out));
            end if;
        end process;
    end architecture;
    

    【讨论】:

    • 谢谢大卫。您提供的代码也以正确的结果实现了计时目标。这是否意味着进程块之外的分配信号以串行方式发生? (Sum_out 在 shift_out 之前评估。然后,过程发生。)?
    • shift_out 不应该出现在进程敏感度列表中,我将其删除(多任务处理、自动驾驶错误)。它作为链工作的原因是并发信号分配语句对右侧的信号很敏感。在零时间模型中,进程将等待时钟并且 shift_out 已经准备好。 sum_out、shift_out 和 data_out 的分配是顺序的,但不是串行的,按事件排序。综合设计描述将取决于加起来少于一个目标时钟周期的门延迟。
    • 我必须阅读很多次才能理解它,但我想我现在明白了。 Shift_out 将等待 sum_out 被评估,因为 shift_out 赋值的 sum_out 在其右侧。这将确保 sum_out 和 shift_out 将按顺序工作。这是一个很大的帮助。非常感谢你,大卫!
    • 每个并发语句都有一个等效进程语句,这就是它们被模拟为进程语句的方式。在这种情况下,sum_out 和 shift_out 没有敏感度列表,但以最后一个顺序语句结尾:wait on data_in_a,data_in_b;wait on sum_out;。如您所料,唯一的其他语句与并发信号分配语句相同。
    【解决方案2】:

    当您为进程内的信号分配新值时,该新值仅在进程完成执行后才可用。因此,无论何时您读取信号的值,您都将使用进程开始执行时的原始值。

    另一方面,对变量的赋值会立即发生,如果您愿意,可以在后续语句中使用新值。

    所以,要解决您的问题,只需使用变量而不是信号来实现 sum_outshift_outdata_out。然后简单地将data_out 的值复制到实体的输出端口。

    【讨论】:

    • 谢谢你,瑞克。代码现在显示下一个时钟周期的结果。如果可以,请问是否有一种方法可以仅使用信号而不使用变量来实现相同的目标?
    • 是的,在这种情况下,Brian 的建议正是您所需要的,如果您想使用信号,我建议您使用它。
    • 感谢您的热情回复 :)
    【解决方案3】:

    不使用变量:

    sum <= in_a + in_b;
    process (clock)
    begin
        if rising_edge(clock) then
            data_out <= in_c - ('0' & sum(7 downto 1));
        end if;
    end process;
    

    除了时钟之外的所有声明都是unsigned(7 downto 0); 为什么让它比这更复杂?

    原始的流水线化到 3 个周期,可能会在更高的时钟频率下工作。

    编辑以下评论:

    我想证明 VHDL 并不一定是 冗长的。

    然而,似乎有很多人“教”VHDL,他们只关注琐碎的元素而完全忽略了大局,所以我会稍微说一下。

    VHDL 是一种强类型语言,可防止在类型相互混淆以及(例如)将两个大数相加并得到否定结果时出现错误。

    这并不意味着您需要到处进行类型转换。 确实,如果您需要大量类型转换,这表明您的设计可能是错误的,是时候重新考虑这一点,而不是在错误的道路上继续前进。

    任何语言的代码都应该尽可能简洁。

    否则很难阅读,而且可能有bug。

    类 C 语言和 VHDL 的最大区别在于:

    在 C 中,使用正确的数据类型可以编写 sum = in_a + in_b; 它会起作用。使用错误的数据类型你也可以写sum = in_a + in_b; 它会编译得很好;它实际上做了什么是另一回事!错误是隐藏的:由你来确定正确的类型,如果你弄错了,除了继续测试你无能为力。

    在 VHDL 中,使用正确的类型,您可以编写 sum &lt;= in_a + in_b; 并且使用错误的类型,编译器会强制您编写类似 sum &lt;= std_logic_vector(unsigned(in_a) + unsigned(in_b)); 的东西,这该死的丑陋,但(可能:见注 1)仍然可以正常工作。

    那么回答这个问题:我如何决定使用unsignedstd_logic_vector

    我发现我需要三个输入和一个输出。我可以只是让他们std_logic_vector但我停下来问:他们代表什么?

    数字。

    它们可以是负数吗?不是根据我对规范的阅读(你的问题)。

    所以,无符号数...(注 1)

    我需要对它们进行非算术运算吗?是的,有一个转变。(注2)

    所以,numeric_std.unsignedstd_logic_vector 相关,而不是 natural,它只是一个整数。

    现在您无法完全避免类型转换。编码标准可能会施加限制,例如“所有顶级端口必须是std_logic_vector”,并且您必须实现您被要求执行的外部实体规范;类型转换的中间信号有时比替代信号更干净,例如in_a &lt;= unsigned(data_in_a);
    或者,如果您从同一内存中获取指令、字符和上面的数字,例如,您可能决定内存内容必须是std_logic_vector,因为它只包含数字。但是选择正确的位置来转换类型,你会发现可能 90% 的类型转换消失了。以此作为设计指南。

    (注 1 :但是如果 C

    正确的答案取决于未知数,包括 data_out 的用途:如果它真的应该是无符号的,例如内存地址,您可能希望标记错误而不是对其进行签名)

    (注 2:没有一个合成工具不会翻译 signal a : natural; ... x &lt;= a/2 右移,所以natural 也可以工作,除非有其他原因选择无符号。很多人似乎仍然被教导整数不可合成,这是错误。)

    【讨论】:

    • 亲爱的布莱恩。我喜欢你的建议。它非常压缩。您将如何决定使用 unsigned 与 std_logic_vector 相对?分开使用它们可能是有原因的,但我不知道为什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-28
    相关资源
    最近更新 更多