Reputation: 77
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;
Upvotes: 1
Views: 596
Reputation: 3659
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:
i_free_run
is high, then the clock enable is also high, i.e. counting always.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:
i_clk
, i.e., make it synchronous to clock,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.
Upvotes: 2
Reputation: 16211
You can not implement two different designs in one entity.
Use either:
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.
Upvotes: 1