如何生成脉冲序列以通用方式提供输出?

时间:2014-09-11 09:04:14

标签: if-statement vhdl fpga pulse

我正致力于生成40位长的脉冲序列。我也必须能够调整频率。我尝试制作一个新的低频时钟,并制作一个新的计数器,它依靠它的上升沿并提供高输出并在40位后终止。它不起作用。我尝试了其他一些方法。它们也不是。

例如;

    library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;

entity con40 is port(clk:in std_ulogic; q:out std_ulogic); 
end entity con40; 

architecture Behaviour of con40 is 
    constant s:std_ulogic_vector:="11111111111111111111111111111111"; 
    signal i:unsigned(4 downto 0):="00000"; 
     signal en:std_logic:='1';
     signal reset:std_logic:='0';
begin 
    q<=s(to_integer(i)); 

    process(reset,clk) is begin 
        if reset='1' then 
          i<=(others=>'0'); 
        elsif rising_edge(clk) then 
            if en='1' then 
                i<=i+1; 
                end if; 
        end if; 
    end process; 
end architecture Behaviour;

此代码中有32位长度,但我想要制作40位,但无论如何,这也不起作用。我认为这种脉冲序列的方法必须是常见的,并且它们被广泛使用。但是嘿!不幸的是,我找不到任何有用的东西。

3 个答案:

答案 0 :(得分:0)

以下代码可以是生成脉冲序列的简单实现。该模块需要一个启动脉冲(StartSequence)并使用&#39; SequenceCompleted&#39;确认生成的序列。

我使用带有set = StartSequencerst = SequenceCompleted_i的基本RS触发器代替状态机。我还把这个过程分成了两个过程:

  1. 状态控制 - 如果需要,可以扩展到完整的FSM
  2. 用于柜台
  3. 最初,模块默认情况下会发出PULSE_TRAIN(0),也会在每次序列生成后发出。因此,如果你想发出40个,否则设置为PULSE_TRAIN := (0 => '0', 1 to 40 => '1')

    此模块在PULSE_TRAIN的位数中是可变的,因此我需要包含一个名为log2ceil的函数,该函数根据PULSE_TRAIN的长度属性计算2s对数,即所需位。 因此,如果'length = 41Counter_us的范围为(5 downto 0)。

    entity PulseTrain is
      generic (
        PULSE_TRAIN       : STD_LOGIC_VECTOR
      );
      port (
        Clock             : in  STD_LOGIC;
        StartSequence     : in  STD_LOGIC;
        SequenceCompleted : out STD_LOGIC;
        Output            : out STD_LOGIC
      ); 
    end entity; 
    
    architecture rtl of PulseTrain is
      function log2ceil(arg : POSITIVE) return NATURAL is
        variable tmp : POSITIVE := 1;
        variable log : NATURAL  := 0;
      begin
        if arg = 1 then  return 0; end if;
        while arg > tmp loop
          tmp := tmp * 2;
          log := log + 1;
        end loop;
        return log;
      end function;
    
      signal State               : STD_LOGIC                                           := '0';
      signal Counter_us          : UNSIGNED(log2ceil(PULSE_TRAIN'length) - 1 downto 0) := (others => '0');
      signal SequenceCompleted_i : STD_LOGIC;
    
    begin 
      process(Clock) is
      begin 
        if rising_edge(Clock) then
          if (StartSequence = '1') then
            State <= '1';
          elsif (SequenceCompleted_i = '1') then
            State <= '0';
          end if;
        end if;
      end process;
    
     SequenceCompleted_i <= '1' when (Counter_us = (PULSE_TRAIN'length - 1)) else '0';
     SequenceCompleted   <= SequenceCompleted_i;
    
      process(Clock)
      begin
        if rising_edge(Clock) then
          if (State = '0') then
            Counter_us <= (others => '0');
          else
            Counter_us <= Counter_us + 1;
          end if;
        end if;
      end process;
    
      Output <= PULSE_TRAIN(to_integer(Counter_us));
    end;
    

答案 1 :(得分:0)

我冒昧地将enreset移动到端口信号,也将常量更改为可识别的40位值,并指定范围使其成为局部静态常量。

你的计数器的问题是它不足以解决40位。您将i指定为5位值,而40位需要6位计数器。

我还在这里添加了第二个架构,其中i为整数类型信号。如果i为无符号值或整数类型,则当第一个位置为0(i)时,您可能需要在39("100111")处翻转"000000"计数器

library ieee;
use ieee.std_logic_1164.all;

entity con40 is 
    port(
        reset:  in  std_ulogic;
        clk:    in  std_ulogic;
        en:     in  std_ulogic;
        q:      out std_ulogic
    ); 
end entity con40; 

architecture foo of con40 is 
    constant s: std_ulogic_vector (0 to 39) := x"feedfacedb"; 
    signal i:   natural range 0 to 39;
begin 
    q <= s(i); 

    process (reset, clk) 
    begin     
        if reset = '1' then 
          i <= 0; 
        elsif rising_edge(clk) and en = '1' then 
            if i = 39 then 
                i <= 0;
            else
                i <= i + 1; 
            end if; 
        end if; 
    end process; 
end architecture;

library ieee;
use ieee.numeric_std.all;

architecture behave of con40 is 
    constant s: std_ulogic_vector (0 to 39) := x"feedfacedb"; 
    signal i:   unsigned (5 downto 0);
begin 
    q <= s(to_integer(i)); 

    process (reset, clk) 
    begin     
        if reset = '1' then 
          i <= "000000"; 
        elsif rising_edge(clk) and en = '1' then 
            if i = "100111" then 
                i <= "000000";
            else
                i <= i + 1; 
            end if; 
        end if; 
    end process; 
end architecture;

我也做了一个快速而肮脏的测试台:

library ieee;
use ieee.std_logic_1164.all;

entity tb_con40 is
end entity;

architecture foo of tb_con40 is
    signal clk:     std_ulogic := '0';
    signal reset:   std_ulogic := '1';
    signal en:      std_ulogic := '0';
    signal q:       std_ulogic;
begin

DUT:
    entity work.con40
        port map  (
            reset => reset,
            clk => clk,
            en => en,
            q => q
        );

CLOCK:
process
begin
    for i in 0 to 46 loop
        wait for 20 ns;
        clk <= not clk;
        wait for 20 ns;
        clk <= not clk;
    end loop;
    wait;
end process;

STIMULUS1:
    reset <= '0' after 40 ns;

STIMULUS2:
    en <= '1' after 60 ns;

end architecture;

可以证明正确的输出:

enter image description here

回复评论问题的附录

模式X"FEEDFACEDB"长度为40位,替代了constant s的32个全1'值,以证明您实际上正在处理s数组值的各个元素。 / p>

停止脉冲序列重复出现:

对于架构foo(使用i的整数类型):

    elsif rising_edge(clk) and en = '1' then 
        -- if i = 39 then
        --     i <= 0;
        -- else
        if i /= 39 then  -- added
            i <= i + 1; 
        end if; 

当计数器达到39时,计数器停止运行。

对于体系结构行为(使用i的无符号类型):

    elsif rising_edge(clk) and en = '1' then 
        -- if i = "100111" then
        --     i <= "000000";
        -- else
        if i /= "100111" then  -- added
            i <= i + 1; 
        end if; 
    end if; 

这两种体系结构在39(i)处的"100111"计数器的行为完全相同。

通过模拟可以显示计数器已停止:

enter image description here

如果不添加额外的控制输入,第二次发出脉冲流的唯一方法就是调用重置。

答案 2 :(得分:0)

正如@ fru1tbat所提到的,它并不是很清楚什么是&#34;没有工作&#34;以及你真正打算做什么。如果你真的只想生成一个脉冲序列,人们会认为你想要生成一系列交替的“脉冲序列”。和&#39; 0&#39;,并非所有&#39; 1都像你发布的代码一样。

此外,i计数器只会计数,并且只能重置为&#39; 0&#39;通过使用reset信号,只要你按照这样的方式就可以了。

如果您想要生成一系列的“1&0”和“0”,您需要这样的事情(未经测试,但应该沿着这些方向):

architecture behaviour of con40 is
    constant trainLength:positive:=80;
    signal i:unsigned(6 downto 0):=(others=>'0');
    ...
begin
    process(reset,clk) is begin 
        if reset then 
            i<=(others=>'0');
            q<='0';
        elsif rising_edge(clk) then
            q<='0';    -- default assignment.
                       -- Defaults to '0' when if-statement fails.

            if i<trainLength then
                i<=i+1;
                q<=not q;
            end if;
        end if;
    end process;
end architecture behaviour;

这为您提供了单次脉冲序列,这意味着除非再次声明reset信号,否则无法重复生成脉冲序列。如果这是你想要的,那就好了,否则,你需要更多的信号来满足你想要重新生成脉冲序列而不重置的情况。

在这里,我假设您想要40个高脉冲,这实际上使列车长度为80个时钟周期,而不是40个。此外,我假设你想要50%的占空比,即HIGH和LOW时间相等。根据您的要求,您可能需要更长或更短的脉冲宽度。

考虑到这些假设,您至少需要一个7位计数器来计算80个时钟。您可能会想到其他更好的方法来做到这一点,但这只是我的头脑,并且可能是一个很好的起点。

如果您的工具尚不支持VHDL-2008的增强端口模式(例如,能够从out - 模式端口读取),那么您可以将q声明为具有buffer模式而不是out。如果您的工具不支持buffer端口模式,那么您可以声明一个内部信号并将其用于您的逻辑。 E.g:

signal i_q: std_ulogic;
...
i_q<=not i_q;  -- use internal signal for logic instead.
q<=i_q;        -- drive output from internal signal.

要调整频率,只需在clk输入中提供更高或更低的频率。这可以从另一个PLL,分频器或您可用的任何其他振荡电路生成。只需将其输出提供给clk

希望这有帮助。