1

I am writing a VHDL homework, which produces a strange behavior, which I do not understand.

The concept is the following. There should be an LFSR which is used to generate random numbers. The LFSR could be driven by I_CLK or by I_NEXT input. If the LFSR is driven by the I_CLK, it should automatically generate random numbers on its output, but if its driven by the I_NEXT input, it should generate number by changing the I_NEXT value manually from 0 to 1. I have a problem with the following code. If I comment out one of the processes, the LFSR works fine but if all the processes are enabled, it just do not work at all. Could you help me figure out the problem? I think it should be a design error, but I do not know what is wrong with my design.

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 bench for the code:

    ----------------------------
    -- 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;
ToMmY_hun
  • 77
  • 11
  • 1
    It's probably worth adding the testbench to the question. –  Jan 05 '16 at 18:13
  • @BrianDrummond Related tests have been added, thanks for the answer. :) – ToMmY_hun Jan 05 '16 at 18:14
  • 2
    OK. Check for the obvious things first, like multiple processes driving the same signals without proper arbitration. If that's the problem, do you need multiple processes, or can you adapt one process to deliver both functionalities under different "if" conditions? –  Jan 05 '16 at 18:19
  • @BrianDrummond I think I cant do that. In the first process, the LFSR should be driven by the I_CLK (normal clock) signal, but in the second one, it should be driven by a manually generated I_NEXT signal. Therefore I dont think it is possible to create only one process. – ToMmY_hun Jan 05 '16 at 18:25
  • 1
    Two options : (1) resynch I_Next to I_Clk and use the clocked process if you can (i.e. waiting a clock cycle is tolerable) and (2) ... no, if they have to share the same internal_number, number (2) is too ugly to contemplate. –  Jan 05 '16 at 18:32
  • @BrianDrummond I thought about it and managed to remove the other process and keeping the functionality. I did it by moving the section from "if rising_edge(i_next) then " to the corresponding "end if;" into the first process, and added the I_NEXT to the sensitivity list of the first process. It's working now, but I have to run a few tests to be sure. – ToMmY_hun Jan 05 '16 at 18:39
  • This will fail at synthesis time, unfortunately, as it requires registers with 2 independent clock inputs. Which don't exist. Possibly the least ugly answer is to select either "clk" or "next" onto a new clock signal, depending on "free_run". Xilinx make a clock mux for this purpose, since switching clocks without glitches is tricky. But a better answer is still. IMO, to rethink the basic approach so you can use option (1). –  Jan 05 '16 at 18:46
  • @BrianDrummond The clock input selection is a great idea! :) – ToMmY_hun Jan 05 '16 at 19:15
  • But, the clock source multiplexing requires another control input which steers the clock multiplexer. Your question is unclear: What should happen if `I_CLK` toggles and `I_NEXT` is low (or high) all the time? – Martin Zabel Jan 05 '16 at 20:56
  • @MartinZabel It should count up/downwards. My solution was the following: I have made a new signal named ghost_clock and created another process, which maps the I_CLK or I_NEXT to the ghost_clock based on the I_FREE_RUN input. The process is sensitive for the I_CLK rising_edge event, which is always present but not used to drive the design in every case. – ToMmY_hun Jan 05 '16 at 22:15

2 Answers2

2

Instead of using a clock source multiplexer, you should use a synchronous clock-enable as also suggested by Brian.

When the clock enable is high, the LFSR counts up/down one step at the rising edge of the free-running clock i_clk. The definition is:

  • If i_free_run is high, then the clock enable is also high, i.e. counting always.
  • If i_free_run is low, then the clock enable is only high for one clock cycle of i_clk every time i_next has changed from low to high, i.e., single step with i_next.

As i_next is driven by a button, you must:

  • sample the button value with i_clk, i.e., make it synchronous to clock,
  • debounce the sampled button value. i_next is then the output of the debouncer.

I have applied this method to your code. To limit the code size, I have shortened the implementation to just one direction and no initialization with a seed. You have to put in your full implementation as indicated. Please note, that you have to initialize the LFSR with all zero when counting up with XNOR.

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;

This is my testbench:

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;

And this is the simulation result. Please note, that the output signal changes rapidly at the "..." boxes.

simulation output

Martin Zabel
  • 3,589
  • 3
  • 19
  • 34
1

You can not implement two different designs in one entity.

Use either:

  1. two entities or
  2. two different architectures of the same entity or
  3. two if..generate statements and a generic parameter to switch the implementations.

Solutions 2 and 3 are not so good in your case, because one uses a clock and the other a next signal. One signal is always unused -> the port list of the entity is filled with dummy signals.

Paebbels
  • 15,573
  • 13
  • 70
  • 139
  • I think the clock source multiplexing could solve my problem. The LFSR design does not differ in anything, but the clock source. – ToMmY_hun Jan 05 '16 at 19:43
  • Is `i_next` a real clock? For example a pressed toggle button on a board is not a clock source. Switching clocks is no trivial circuit. You should use predefined macros or hard IPs. – Paebbels Jan 05 '16 at 19:52
  • It is not a real clock, just a button like thing. – ToMmY_hun Jan 05 '16 at 20:00
  • 2
    Then re-synch it to clk, debounce it, detect its rising edge (it is high, but a version delayed 1 more clock cycle is still low), use that as a clock enable in "manual" mode, and make the whole thing synchronous as I suggested in the first place. –  Jan 05 '16 at 22:29
  • I havn't seen it in the first place. But, the signal `i_free_run` actually selects between both implementations as now pointed out in the comments. – Martin Zabel Jan 05 '16 at 22:45
  • Yes, it does. I'll post the solution if its completed to help others. – ToMmY_hun Jan 06 '16 at 09:05
  • @BrianDrummond Okay, I will do that. Thank you! :) – ToMmY_hun Jan 06 '16 at 09:16