1

我正在写一个 VHDL 作业,它会产生一个奇怪的行为,我不明白。

概念如下。应该有一个 LFSR 用于生成随机数。LFSR 可以由 I_CLK 或 I_NEXT 输入驱动。如果 LFSR 由 I_CLK 驱动,它应该在其输出上自动生成随机数,但如果它由 I_NEXT 输入驱动,它应该通过手动将 I_NEXT 值从 0 更改为 1 来生成数字。我有以下问题代码。如果我注释掉其中一个进程,LFSR 可以正常工作,但如果启用了所有进程,它就根本不起作用。你能帮我找出问题所在吗?我认为这应该是设计错误,但我不知道我的设计有什么问题。

entity LFSR_v2 is

Generic (
            width       :   positive    :=  31;
            tap_1       :   positive    :=  30;
            tap_2       :   positive    :=  27                
        );

Port ( 
           i_enable     :   in    std_logic;
           i_reset      :   in    std_logic;
           i_clk        :   in    std_logic;

           i_next       :   in    std_logic;           
           i_free_run   :   in    std_logic;
           i_load       :   in    std_logic;
           i_direction  :   in    std_logic;

           o_number     :   out   std_logic_vector (width -1 downto 0);
           i_seed       :   in    std_logic_vector (width -1 downto 0)      

       );

end LFSR_v2;



architecture Behavioral of LFSR_v2 is

signal internal_number  :   std_logic_vector(width -1 downto 0);


begin

-------------------------------------------------------------------------------------------
-- FREE RUNNING PROCESS
--
-- In Free Running mode the LFSR switches its state on every rising edge of the i_clk input.
-------------------------------------------------------------------------------------------
next_number_free_run : process(i_clk, i_reset)
--variable fileline : line;
--variable gen_num  : integer;

    begin 

        if rising_edge(i_clk) then      

            --------------------------------------
            -- NORMAL MODE
            -- enable   =   1
            -- reset    =   0
            --------------------------------------
            if (i_enable = '1' and i_free_run = '1') then


                -- Internal number to the output
                o_number <= internal_number;

                -----------------------------
                -- RESET
                -----------------------------
                if(i_reset = '1') then
                    if(i_direction = '1') then
                        internal_number <= (OTHERS => '1');
                    else
                        internal_number <= (OTHERS => '0');
                    end if;  
                else
                    ------------------------------
                    -- LOAD SEED
                    -- load = 1
                    ------------------------------
                    if(i_load = '1') then
                        internal_number <= i_seed;
                    else     
                        --------------------------------------
                        -- GENERATE NEXT NUMBER - FREE RUNNING
                        -- load = 0
                        -- free_run = 1
                        -------------------------------------                       
                        if(i_direction = '1') then
                            internal_number <= internal_number(width - 2 downto 0) & (internal_number(tap_1) xnor internal_number(tap_2));
                        else
                            internal_number <= internal_number(width - 2 downto 0) & (internal_number(tap_1) xor internal_number(tap_2));
                        end if;

                        ----------------------------------------
                        -- FILE LOGGING
                        ----------------------------------------
                        --gen_num := to_integer(internal_number);
                        --write(fileline, gen_num);
                        --writeline(MyFile, fileline); 

                    end if;

                end if;                       

            end if;

        end if;

    end process next_number_free_run;


    ---------------------------------------------------------------------------------
    -- MANUAL RUNNING PROCESS
    -- 
    -- In this mode the LFSR does not use the input clock to generate the next number.  
    -- Number can be generated by creating a 0 -> 1 signal change on the i_next input.
    ---------------------------------------------------------------------------------
    next_number_man_run : process(i_next, i_reset)
        --variable fileline : line;
        --variable gen_num  : integer;

            begin 

               if rising_edge(i_next) then      

                    --------------------------------------
                    -- NORMAL MODE
                    -- enable   =   1
                    -- reset    =   0
                    --------------------------------------
                    if (i_enable = '1' and i_free_run = '0') then

                        -- Internal number to the output
                        o_number <= internal_number;

                        -----------------------------
                        -- RESET
                        -----------------------------
                        if(i_reset = '1') then
                            if(i_direction = '1') then
                                internal_number <= (OTHERS => '1');
                            else
                                internal_number <= (OTHERS => '0');
                            end if;
                        else
                            ------------------------------
                            -- LOAD SEED
                            -- load = 1
                            ------------------------------
                            if(i_load = '1') then
                                internal_number <= i_seed;
                            else     
                                --------------------------------------
                                -- GENERATE NEXT NUMBER - FREE RUNNING
                                -- load = 0
                                -- free_run = 1
                                -------------------------------------                       
                                if(i_direction = '1') then
                                    internal_number <= internal_number(width - 2 downto 0) & (internal_number(tap_1) xnor internal_number(tap_2));
                                else
                                    internal_number <= internal_number(width - 2 downto 0) & (internal_number(tap_1) xor internal_number(tap_2));
                                end if;

                                ----------------------------------------
                                -- FILE LOGGING
                                ----------------------------------------
                                --gen_num := to_integer(internal_number);
                                --write(fileline, gen_num);
                                --writeline(MyFile, fileline); 

                            end if;



                        end if;                       

                    end if;

                end if;

            end process next_number_man_run;

end Behavioral;

代码测试台:

    ----------------------------
    -- TEST SEED INIT
    ----------------------------

        -- ENABLE OFF -> SEED SHOULD NOT BE INITIALIZED
        s_enable    <=  '0';
        s_reset     <=  '0';
        s_free_run  <=  '0';
        s_load      <=  '1';
        s_next      <=  '0';
        s_direction <=  '0';

        s_seed   <=  (OTHERS => '1');
        wait for 20 ns;

        -- ENABLE ON -> SEED SHOULD BE INITIALIZED
        s_enable    <=  '1';
        s_reset     <=  '0';

        s_next      <=  '0';
        s_free_run  <=  '0';
        s_load      <=  '1';
        s_direction <=  '0';

        s_seed   <=  (OTHERS => '1');
        wait for 20 ns;

        -- DRIVE MANUAL
        s_next      <=  '1';
        wait for clk_period /2;
        s_next      <=  '0';
        wait for clk_period /2;
        s_next      <=  '1';
        wait for clk_period /2;
        s_next      <=  '0';
        wait for clk_period /2;
4

2 回答 2

2

而不是使用时钟源多路复用器,您应该使用同步时钟使能,正如 Brian 所建议的那样。

当时钟使能为高时,LFSR 在自由运行时钟的上升沿向上/向下计数一步i_clk。定义是:

  • 如果i_free_run为高,则时钟使能也为高,即始终计数。
  • 如果i_free_run为低电平,则时钟使能仅在一个时钟周期内为高电平,i_clk每次i_next由低电平变为高电平,即单步i_next

i_next按钮驱动,您必须:

  • 用 采样按钮值i_clk,即使其与时钟同步,
  • 去抖动采样的按钮值。i_next然后是去抖动器的输出。

我已将此方法应用于您的代码。为了限制代码大小,我将实现缩短为一个方向,并且没有使用种子进行初始化。您必须按照指示进行完整的实施。请注意,当使用 XNOR 进行计数时,您必须将 LFSR 初始化为全零。

library ieee;
use ieee.std_logic_1164.all;

entity LFSR_v2 is
    Generic (
        width       :   positive    :=  31;
        tap_1       :   positive    :=  30;
        tap_2       :   positive    :=  27                
        );
    Port ( 
        i_enable     :   in    std_logic;
        i_reset      :   in    std_logic;
        i_clk        :   in    std_logic;
        i_next       :   in    std_logic;           
        i_free_run   :   in    std_logic;
--        i_load       :   in    std_logic;
--        i_direction  :   in    std_logic;
--        i_seed       :   in    std_logic_vector (width -1 downto 0)
        o_number     :   out   std_logic_vector (width -1 downto 0)
        );
end LFSR_v2;

architecture Behavioral of LFSR_v2 is
    signal internal_number : std_logic_vector(width -1 downto 0);
    signal clock_enable    : std_logic;
    signal next_old        : std_logic := '0';  -- old value of "i_next"
begin

    -- calculate clock enable
    clock_enable <= '1' when i_free_run = '1' else
                    i_next and not next_old;

    process(i_clk)                      -- no i_reset here!
    begin 
        if rising_edge(i_clk) then
            next_old <= i_next;         -- save old value for edge detection

            -- This should be outside of the clock-enable block or even a concurrent statement
            o_number <= internal_number;

            if (clock_enable = '1' and i_enable = '1') then    -- "i_enable" as in original code

                ---------------------------------------------------------------
                -- Replace the following short implementation with your full
                -- implementation
                ---------------------------------------------------------------
                if(i_reset = '1') then
                    internal_number <= (OTHERS => '0');  -- must be all zero for XNOR below!
                else
                    internal_number <= internal_number(width - 2 downto 0) &
                                       (internal_number(tap_1) xnor internal_number(tap_2));
                end if;                       
            end if;
        end if;
    end process;
end Behavioral;

这是我的测试台:

library ieee;
use ieee.std_logic_1164.all;

entity LFSR_v2_tb is
end LFSR_v2_tb;

architecture sim of LFSR_v2_tb is
    component LFSR_v2
        generic (
            width : positive;
            tap_1 : positive;
            tap_2 : positive);
        port (
            i_enable    : in  std_logic;
            i_reset     : in  std_logic;
            i_clk       : in  std_logic;
            i_next      : in  std_logic;
            i_free_run  : in  std_logic;
            o_number    : out std_logic_vector (width -1 downto 0));
    end component;

    -- component generics
    constant width : positive := 31;
    constant tap_1 : positive := 30;
    constant tap_2 : positive := 27;

    -- component ports
    signal i_enable    : std_logic;
    signal i_reset     : std_logic;
    signal i_clk       : std_logic := '1';
    signal i_next      : std_logic;
    signal i_free_run  : std_logic;
    signal o_number    : std_logic_vector (width -1 downto 0);

begin  -- sim
    DUT: LFSR_v2
        generic map (
            width => width,
            tap_1 => tap_1,
            tap_2 => tap_2)
        port map (
            i_enable    => i_enable,
            i_reset     => i_reset,
            i_clk       => i_clk,
            i_next      => i_next,
            i_free_run  => i_free_run,
            o_number    => o_number);

  -- clock generation
  i_clk <= not i_clk after 10 ns;

  -- waveform generation
  WaveGen_Proc : process
  begin
      i_free_run <= '1';                -- start with a free-running clock
      i_reset    <= '1';
      i_enable   <= '1';                -- must be high even for reset
      i_next     <= '0';
      wait until rising_edge(i_clk);

      i_reset    <= '0';                -- now let the LFSR toogle on i_clk
      wait until rising_edge(i_clk);
      wait until rising_edge(i_clk);
      wait until rising_edge(i_clk);

      i_free_run <= '0';                -- change to single step mode
      wait until rising_edge(i_clk);
      wait until rising_edge(i_clk);
      wait until rising_edge(i_clk);

      for i in 1 to 3 loop              -- 3 single steps
          i_next <= '1';                    -- do single step
          wait until rising_edge(i_clk);
          wait until rising_edge(i_clk);
          wait until rising_edge(i_clk);
          i_next <= '0';
          wait until rising_edge(i_clk);
          wait until rising_edge(i_clk);
          wait until rising_edge(i_clk);
      end loop;  -- i

      i_free_run <= '1';                -- change back to free-running clock
      wait until rising_edge(i_clk);
      wait;
  end process WaveGen_Proc;
end sim;

这就是模拟结果。请注意,输出信号在“...”框中会迅速变化。

模拟输出

于 2016-01-06T08:29:25.800 回答
1

您不能在一个实体中实现两种不同的设计。

使用任一:

  1. 两个实体或
  2. 同一实体的两种不同架构或
  3. 两个if..generate语句和一个通用参数来切换实现。

解决方案 2 和 3 在您的情况下不太好,因为一个使用时钟,另一个使用下一个信号。一个信号始终未使用 -> 实体的端口列表充满了虚拟信号。

于 2016-01-05T19:22:54.527 回答