【问题标题】:Why is my VHDL counter not outputting pulses as desired?为什么我的 VHDL 计数器没有按需要输出脉冲?
【发布时间】:2013-08-07 02:38:56
【问题描述】:

我正在创建一个可变频率脉冲序列来控制电机,并使用以下代码使用计数器生成脉冲,该计数器按某个输入增量值inc_i 进行计数。然后,我将计数器输出的 MSB dout_o[N-1] 通过管道传输到我的 FPGA 上的输出引脚。这应该给我一个所需频率的 50% 占空比方波,但我只是看到一个信号开始变低,变高,然后再也不会关闭。我的代码有明显的问题吗?

--****************************************************************************
-- Load required libraries
--****************************************************************************
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

--****************************************************************************
-- Define the inputs, outputs, and parameters
--****************************************************************************
entity var_count is

    generic(N: integer :=32);               -- for generic counter size
    port(
            inc_i       : in    std_logic_vector(N-1 downto 0);
            load_i      : in    std_logic;
            clk_i       : in    std_logic;
            clear_i     : in    std_logic;
            clk_en_i    : in    std_logic;
            count_en_i  : in    std_logic;
            msb_o       : out   std_logic
        );

end var_count;

--****************************************************************************
-- Define the behavior of the counter
--****************************************************************************
architecture behavior of var_count is

    -- Define our count variable. No need to initialize in VHDL.
    signal count : unsigned(N-1 downto 0) := to_unsigned(0, N);
    signal incr  : unsigned(N-1 downto 0) := to_unsigned(0, N);

begin   
    -- Define our clock process
    clk_proc : process(clk_i, clear_i, load_i)
    begin
        -- Asynchronous clear
        if clear_i = '1' then
            count <= to_unsigned(0, N);
        end if;

        -- Asynchronous load
        if load_i = '1' then
            incr <= unsigned(inc_i);
        end if;

        -- Define processes synch'd with clock.
        if rising_edge(clk_i) and clk_en_i = '1' then
            if count_en_i = '1' then            -- increment the counter
                count <= count + incr;
            end if;
        end if;     
    end process clk_proc;

    -- Output the MSB for the sake of generating a nice easy square wave.
    msb_o <= count(count'left);

end behavior;

对于 N 位计数器,方波频率应由公式 pulse_freq = clock_freq * inc_i / 2^N 给出。

我还试图通过将我的计数器输出 (msb_o(k)) 的 MSB 通过一位 D-Q 触发器来生成单个时钟周期脉冲以获取 msb_o(k-1),然后执行:

pulse = ~msb_o(k) * msb_o(k-1)

其中~ 表示逻辑NOT* 表示逻辑AND。这应该只在计数器翻转时给我一个时钟周期脉冲。当我使用示波器读取输出引脚时,这和 MSB 本身(50% 占空比方波)都没有显示。如果您在我的代码中发现任何错误,请告诉我。

感谢您的帮助!

可以找到我用来将这些放在一起的参考资料 hereherehere

编辑 1:我已根据用户提出的建议更新了问题中的代码。现在,当我输出计数器的 MSB 时,它会变高并且永远不会再次关闭。我需要在溢出时重置计数器(我在上面尝试过这样做)。关于如何做到这一点的任何建议?

编辑 2: 我意识到,由于我的增量值 inc_i 不一定是 1,因此对于翻转计算,我可能无法达到 2**N - 1 的值。因此,我将翻转条件更改为:

if count > (2**N - unsigned(inc_i) - 1) then
    count <= to_unsigned(0, N);

我现在似乎得到了脉冲,但它们并不完全是 50% 的占空比(我认为这是有道理的),当我更改增量值 inc_i 时,它们似乎没有按预期改变。知道为什么费率没有变化吗?

编辑 3: 我意识到我对我的应用程序所关心的只是计数器的 MSB,因为我将使用它来生成 50% 占空比的方波,或者翻身时脉动。鉴于此,我将实体声明中的dout_o 替换为:

`msb_o : out std_logic`

我将最后的并发赋值语句替换为:

msb_o <= '1' when count > (2**(N-1) - 1) else '0';

我仍然收到非常奇怪的脉冲,它们根本不是 50% 的占空比或不一定是正确的频率。任何进一步的建议将不胜感激。

编辑 4: 更新了我的代码并进行了进一步的更改。还将this free book 添加到引用列表中。

编辑 5:将我的代码更新为(最终)工作版本。

【问题讨论】:

  • 模拟是否按预期工作?
  • @BrianDrummond 我不确定如何理解模拟,因为使用它的整个程序非常庞大。我对所有的逻辑信号都有点迷失。我将尝试在模拟中找出这些信号的行为并返回结果。
  • 执行单元测试是值得的——也许不是在大型项目的每个组件上——但也许是其中的大部分,而且绝对是像这样的关键测试。一个简单的测试台可用于以在顶级测试中难以安排的方式对组件施加压力,并获得答案,通常比等待 StackExchange 帖子更快!

标签: counter vhdl fpga


【解决方案1】:

流程不遵守流程被识别为的一般规则 综合工具的触发器,因为条件的外层确实 不包括过程灵敏度列表中的信号。综合工具有 可能对此给出了一个或多个警告。

一种重写流程以遵守综合规则的方法 人字拖是:

clk_proc : process (CLK)
begin
  if (CLK'event and CLK = '1') then
    if CLEAR = '1' then           -- clear the counter
      COUNT <= COUNT - COUNT;
    elsif CLK_EN = '1' then       -- increment the counter
      COUNT <= COUNT + INC;
    end if;
  end if;
end process clk_proc;

这会将时钟边沿检查置于外层,并假设 CLEAR 是同步的。

有关寄存器的 VHDL 编码风格的更多信息例如 Xilinx XST User Guide.

您对单个时钟周期脉冲的描述看起来不错,所以原因是 它不起作用可能是源于上述。

可以考虑对模块进行更大的重写,以便应用这些 有用的VHDL编码规则:

  • 输入和输出端口用 _i 和 _o 命名,因为这样便于阅读 模块实例化的代码
  • 没有默认值的信号,因为这可能不适用于所有 FPGA 技术
  • 只有大写的常量标识符,因为这使得代码读取 更容易。
  • 不要使用 std_logic_unsigned,因为这是 Synopsys 库而不是 VHDL 标准
  • 使用rising_edge() 进行边缘检测,以提高可读性
  • 使用(others =&gt; '0') 来清除,而不是COUNT - COUNT,因为这样可以 在模拟中即使 COUNT 都是 X 的

以下代码中的示例:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity var_count is
  port(
    inc_i    : in  std_logic_vector(31 downto 0);
    clk_i    : in  std_logic;
    clear_i  : in  std_logic;
    clk_en_i : in  std_logic;
    dout_o   : out std_logic_vector(31 downto 0));
end var_count;

architecture behavior of var_count is

  -- Define our count variable
  signal count : std_logic_vector(31 downto 0);

begin

  -- Define our clock process
  clk_proc : process (clk_i)
  begin
    if rising_edge(clk_i) then
      if clear_i = '1' then           -- clear the counter
        count <= (others => '0');
      elsif clk_en_i = '1' then       -- increment the counter
        count <= std_logic_vector(unsigned(count) + unsigned(inc_i));
      end if;
    end if;
  end process clk_proc;

  -- Concurrent assignment statement
  dout_o <= count;

end behavior;

如果您还没有使用模拟器,那么 ModelSim 可能是一个不错的选择 改进您的工具箱。 Alera 有ModelSim-Altera Starter Edition 这是免费的,可用于小型设计。

【讨论】:

  • 我正在使用 Quartus II v9.0 进行编程。我可以在其中进行模拟,但是整个程序非常复杂,以至于模拟对我来说有点毫无意义。不过,我并没有花太多时间学习模拟器。谢谢您的回答!我将努力应用更改并发布我的结果。
  • 还有一个问题,当计数器达到最大值时,它会自动翻转吗?我需要这种行为。
  • @Engineero Simulation从不毫无意义,对于大型设计更是如此。是的,计数器会翻转。
  • 模拟这个模块是一个简单的任务,你不需要整个项目。如果您不模拟您的设计,那么您会浪费大量时间并使您的问题过于复杂。
  • 太好了,感谢@FarhadA 告诉我模拟很简单,但对如何进行模拟没有任何见解。有人愿意详细说明吗?我不确定如何将其与我的设计分开并对其进行仿真,当我尝试用整个设计对其进行仿真时,我什么也得不到。我可能在设置输入文件时做错了。
【解决方案2】:
  • 初始化一个信号是没有意义的,如果这个信号是一个寄存器,把它复位为零(或任何其他需要的值)并确保你的设计从一个复位状态开始。
  • 您的时钟进程对在进程中读取的CLEAR 不敏感。我假设您需要异步 CLEAR。
  • std_logic_vectors 无法直接使用IEEE.numeric_std.all 添加,建议使用IEEE.numeric_std.all 以实现跨工具的兼容性。
  • 当您可以使用 (others =&gt; '0') 将寄存器全部清零时,使用 COUNT-COUNT 重置寄存器是没有意义的。

请看下面的代码。

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;


entity VAR_COUNT is

port(
        INC     : in    std_logic_vector(31 downto 0);
        CLK     : in    std_logic;
        CLEAR   : in    std_logic;
        CLK_EN  : in    std_logic;
        DOUT    : out   std_logic_vector(31 downto 0)
    );

end VAR_COUNT;

architecture behavior of VAR_COUNT is

-- Initialising a signal has no effect in hardware
signal COUNT:std_logic_vector(31 downto 0) := x"00_00_00_00";

-- Define our clock process, your clocked process was not senstive to CLEAR
begin clk_proc:process(CLK,CLEAR)
    begin
        if CLEAR = '1' then         -- clear the counter
            COUNT <= (others=>'0'); --always use this to reset
        elsif CLK_EN = '1' then     -- increment the counter
            if (CLK'EVENT AND CLK = '1') then
                COUNT <= std_logic_vector(unsigned(COUNT) + unsigned(INC));
            end if;
        end if;     
    end process clk_proc;

-- concurrent assignment statement
DOUT <= COUNT;

end behavior;

【讨论】:

  • 只是文本项目符号的注释: * 如果 CLEAR 用作同步信号,而不是异步清除,则不需要将 CLEAR 包含在过程的敏感度列表中。 * 如果使用 IEEE.std_logic_unsigned.all,则确实可以直接添加 std_logic_vectors,因为这会重载 std_logic_vector 参数的 + 运算符。
猜你喜欢
  • 2015-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-21
  • 2018-04-21
  • 1970-01-01
相关资源
最近更新 更多