Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
11 years ago

State Machine for SPI Master/Slave Interface

http://www.alteraforum.com/forum/attachment.php?attachmentid=10118&stc=1

Dear all,

I am trying to write a state machine that checks to see if correct data is being received on the SPI link by lighting an LED.

I have written a code but I am not sure if I am writing the state machine correctly, it definitely does not compile.

Here is the code that I have written in relation to the test bench, please find the file attached.

library IEEE; -- Reference for VHDL source code    
use IEEE.STD_LOGIC_1164.all; -- Package defined in the IEEE (Found in library IEEE)
-- Entity declaration
entity spi_statemachine is
   generic (n: positive := 16; -- Number of bits
    port (-- Master --                                                
            di_m: in std_logic_vector(15 downto 0);
            wren_m: in std_logic;
            -- Slave --
            do_s: out std_logic_vector(15 downto 0);
            do_valid_s: out std_logic;
            -- Clock operation --
            rst_i: in std_logic;
            clk_i: in std_logic;
            -- Output detection --
            correct: out std_logic);
end spi_statemachine;
-- Architecture behaviour
architecture detect of spi_statemachine is
type state_type is (createData, writeData, delay, writeEnable,
                          checkValid, receivedData, checkFinished, correctIndication);    -- Enumeration type
signal state: state_type;
begin
    P1: process (clk_i, rst_i) -- Clock and reset
    variable dataLength: integer := n; -- Length of data
    variable count: integer := 1;
    begin
        if rst_i = '0' then -- Reset operation used initialize all signals to predetermined state
            state <= createData; 
        elsif clk_i'event and clk_i = '1' then    -- Signal attribute used to test for a change on a signal
            case state is
                when createData =>
                        state <= writeData;
                    else
                        state <= createData;
                    end if;
                when writeData =>
                        di_m <= std_logic_vector(to_unsigned(dataLength,n)); -- Write data
                        state <= delay;
                    
                when delay =>
                        count := count + 1;
                    if (count > 1) then
                        state <= writeEnable;
                    count := 0;
                    else
                        state <= delay;
                    end if;
                
                when writeEnable =>
                    wren_m <= '1';
                    state <= checkValid;
                    
                when checkValid =>
                    wren_m <= '0';
                    state <= receivedData;
                
                when receivedData =>
                    if do_s = di_m then
                        state <= checkFinished;
                    end if;
                
                when checkFinished =>
                    correct <= '1';
                when others => null;
            end case;
        end if;
    end process;
end detect;

Any kind of help will be much appreciated.

Kind regards,

Deadman

10 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    1. you forgot to use the numeric_std library in your code (you're converting from integer to slv via an unsigned type). So put at the top

    use ieee.numeric_std.all;

    2. di_m abd wren_m are inputs , you cannot assign a value to input. (line 41, 54, 58). You can only assign values to outputs.

    3. The count variable is useless. The first time you're in the delay state it's value will be 2, and you'll go strait to write_enable (learn how signals work vs variables0

    4. You cannot read values from outputs (assuming you're trying to compile with VHDL 1993 code).

    I highly suggest you read the errors. They are usually very descriptive.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    1. you forgot to use the numeric_std library in your code (you're converting from integer to slv via an unsigned type). So put at the top

    use ieee.numeric_std.all;

    2. di_m abd wren_m are inputs , you cannot assign a value to input. (line 41, 54, 58). You can only assign values to outputs.

    3. The count variable is useless. The first time you're in the delay state it's value will be 2, and you'll go strait to write_enable (learn how signals work vs variables0

    4. You cannot read values from outputs (assuming you're trying to compile with VHDL 1993 code).

    I highly suggest you read the errors. They are usually very descriptive.

    --- Quote End ---

    Thank you for your reply.

    I have read the errors and corrected them, the code is compiling.

    Do you know how to generate random numbers to inputs and outputs instead if fixed values?

    Kind regards,

    Deadman
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    Thank you for your reply.

    I have read the errors and corrected them, the code is compiling.

    Do you know how to generate random numbers to inputs and outputs instead if fixed values?

    Kind regards,

    Deadman

    --- Quote End ---

    There are two ways.

    1. If you need synthesisable pseudo random number, then google linear feedback shift register (LFSR).

    2. For simulation only, the ieee.math_real package has a procedure called "uniform" that generates pseudo random real values between 0.0 and 1.0. You can use these to scale any value you want. For example, a rand_int procedure in my personal testbench tools package:

    
    procedure rand_int( variable seed1, seed2 : inout positive; min, max : in integer; result : out integer) is
            variable rand      : real;
            variable val_range : real;
        begin
            assert (max >= min) report "Rand_int: Range Error" severity Failure;
            
            uniform(seed1, seed2, rand);
            val_range := real(Max - Min + 1);
            result := integer( trunc(rand * val_range )) + min;
        end procedure;
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I am still trying to write a state machine that checks to see if correct data is being received between the input 'di_i' and the output 'do_o' but I get the following error message:

    Error (10409): VHDL Type Conversion error at spi_statemachine.vhd(89): converted type of object near text or symbol "std_logic_vector" must match integer type of target object
    

    I have highlighted the error in red below. I do not know why I am getting this error message, I have set 'std_logic_vector' and 'data_out' to 16 bits.

    library IEEE; -- Reference for VHDL source code	
    use IEEE.STD_LOGIC_1164.all; -- Package defined in the IEEE (Found in library IEEE)
    use IEEE.numeric_std.all; -- Used to covert integer to std_logic_vector via unsigned type. 
    -- Unsigned types represent postive values including zero. The compiler interprets each
    -- unsigned type as a binary number, with the digit of the left as the MSB (Most Significant Bit)
    -- Entity declaration
    entity spi_statemachine is -- Entity name
    	-- Generics --
       generic (n: positive := 16); -- Number of bits. Generic is used to specify parameters, for example, the
    										  -- length of data.
    	port (-- Inputs --
    			di_i: in std_logic_vector(n-1 downto 0); -- Data in
    			do_valid_o: in std_logic;	-- Check if data is valid (No data setup glitches on the data transfer),
    												-- valid during one clk_i rising edge
    			-- Outputs --
    			do_o: out std_logic_vector(n-1 downto 0); -- Data out
    			wren_i: out std_logic; -- Write port, valid during one clk_i rising edge
    			-- Start operation --
    			start: in std_logic;	-- Start state machine
    			-- Clock operation --
    			rst_i: in std_logic; 
    			clk_i: in std_logic; -- Clock synchronicity with data
    			-- Output indication --
    			correct: out std_logic); -- Check if data transmitted is correct
    end spi_statemachine; -- End entity
    -- Architecture behaviour
    architecture detect of spi_statemachine is -- Identifying the architecture
    type state_type is (createData, writeData, delay, writeEnable,
    						  checkValid, compareData, checkFinished, correctData); -- Enumeration types
    						  
    -- Signals
    signal state: state_type; -- State of the machine
    begin
    	P1: process (clk_i, rst_i, start) -- Sensitivity list. The process wakes up when an event occurs
    												 -- on one of the signals in the sensitivity list.
    	-- Variables --
    	-- Variables change immediately therefore 'rst_i' is used to stop this from happening.
    	variable data: integer := 15; -- Data length. Integer is a data type that represents positive and negative 
    											-- whole numbers.
    	variable error_rate: natural := 0; -- Incrementing error. Natural is a data type is used to represent natural 
    												 -- (nonnegative) numbers.
    	variable data_in: integer := 15;
    	variable data_out: integer := 15;
    	begin
    		if rst_i = '1' then -- Reset operation used initialize all signals to predetermined state
    			do_o <= (others => '0'); 
    			wren_i <= '0';
    			correct <= '0';
    			data := 15;
    			error_rate := 0;
    			state <= createData;
    			data_in := 15;
    			data_out := 15;
    		elsif clk_i'event and clk_i = '1' then	-- Signal attribute used to test for a change on a signal
    			case state is
    				when createData =>
    					if (start = '1') then -- Start state machine
    						state <= writeData;
    					else
    						state <= createData;
    					end if;
    					
    				when writeData =>
                                            data_out := std_logic_vector(to_unsigned(data, n)); -- Write data
    					state <= delay;
    					
    				when delay =>
    					state <= writeEnable;
    				
    				when writeEnable =>
    					wren_i <= '1';
    					state <= checkValid;
    					
    				when checkValid =>
    					if (do_valid_o = '1') then
    						data_in := di_i;
    						wren_i <= '0';
    						state <= compareData;
    					else
    						state <= writeEnable;
    					end if;
    				when compareData =>
    					if data_in = data_out then
    						error_rate := error_rate-1;
    					else
    						error_rate := error_rate+1;
    						state <= checkFinished;
    					end if;
    						
    				when checkFinished =>
    					if (error_rate > 0) then
    						correct <= '0';
    					else
    						correct <= '1';
    					end if;
    			end case;
    		end if;
    	end process;
    end detect;

    Kind regards,

    Deadman
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    data_out is an integer variable. So is the data variable. So no need to convert to std_logic_vector.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    ps. data_out and data are always 15. What are you trying to do with these variables?

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    ps. data_out and data are always 15. What are you trying to do with these variables?

    --- Quote End ---

    I am trying to assign them to input 'di_i' and the output 'do_o' so that I can compare them, please see the code highlighted in red below.

    library IEEE; -- Reference for VHDL source code    
    use IEEE.STD_LOGIC_1164.all; -- Package defined in the IEEE (Found in library IEEE)
    use IEEE.numeric_std.all; -- Used to covert integer to std_logic_vector via unsigned type. 
    -- Unsigned types represent postive values including zero. The compiler interprets each
    -- unsigned type as a binary number, with the digit of the left as the MSB (Most Significant Bit)
    -- Entity declaration
    entity spi_statemachine is -- Entity name
        -- Generics --
       generic (n: positive := 16); -- Number of bits. Generic is used to specify parameters, for example, the
                                              -- length of data.
        port (-- Inputs --
                di_i: in std_logic_vector(n-1 downto 0); -- Data in
                do_valid_o: in std_logic;    -- Check if data is valid (No data setup glitches on the data transfer),
                                                    -- valid during one clk_i rising edge
                -- Outputs --
                do_o: out std_logic_vector(n-1 downto 0); -- Data out
                wren_i: out std_logic; -- Write port, valid during one clk_i rising edge
                -- Start operation --
                start: in std_logic;    -- Start state machine
                -- Clock operation --
                rst_i: in std_logic; 
                clk_i: in std_logic; -- Clock synchronicity with data
                -- Output indication --
                correct: out std_logic); -- Check if data transmitted is correct
    end spi_statemachine; -- End entity
    -- Architecture behaviour
    architecture detect of spi_statemachine is -- Identifying the architecture
    type state_type is (createData, writeData, delay, writeEnable,
                              checkValid, compareData, checkFinished, correctData); -- Enumeration types
                              
    -- Signals
    signal state: state_type; -- State of the machine
    begin
        P1: process (clk_i, rst_i, start) -- Sensitivity list. The process wakes up when an event occurs
                                                     -- on one of the signals in the sensitivity list.
        -- Variables --
        -- Variables change immediately therefore 'rst_i' is used to stop this from happening.
        variable data: integer := 15; -- Data length. Integer is a data type that represents positive and negative 
                                                -- whole numbers.
        variable error_rate: natural := 0; -- Incrementing error. Natural is a data type is used to represent natural 
                                                     -- (nonnegative) numbers.
        variable data_in: integer := 15;
        variable data_out: integer := 15;
        begin
            if rst_i = '1' then -- Reset operation used initialize all signals to predetermined state
                do_o <= (others => '0'); 
                wren_i <= '0';
                correct <= '0';
                data := 15;
                error_rate := 0;
                state <= createData;
                data_in := 15;
                data_out := 15;
            elsif clk_i'event and clk_i = '1' then    -- Signal attribute used to test for a change on a signal
                case state is
                    when createData =>
                        if (start = '1') then -- Start state machine
                            state <= writeData;
                        else
                            state <= createData;
                        end if;
                        
                    when writeData =>
                        data_out := std_logic_vector(to_unsigned(data, n)); -- Write data
                        state <= delay;
                        
                    when delay =>
                        state <= writeEnable;
                    
                    when writeEnable =>
                        wren_i <= '1';
                        state <= checkValid;
                        
                    when checkValid =>
                        if (do_valid_o = '1') then
                            data_in := di_i;
                            wren_i <= '0';
                            state <= compareData;
                        else
                            state <= writeEnable;
                        end if;
                    when compareData =>
                        if data_in = data_out then
                            error_rate := error_rate-1;
                        else
                            error_rate := error_rate+1;
                            state <= checkFinished;
                        end if;
                            
                    when checkFinished =>
                        if (error_rate > 0) then
                            correct <= '0';
                        else
                            correct <= '1';
                        end if;
                end case;
            end if;
        end process;
    end detect;

    Kind regards,

    Deadman
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    di_i is a std_logic_vector, so to assign it to the data in variable you need to type convert it:

    data_in <= to_integer( unsigned( di_i) );

    And you still have the same problem that you're doing an unnecessary (and illegal) type conversion for the data_out assignment from the data variable.

    There are other type conversion problems. I suggest fix the syntax errors first before worry about the functionality.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I have fixed the syntax errors and my state machine is compiling.

    My state machine is the following code:

    library IEEE; -- Reference for VHDL source code    
    use IEEE.STD_LOGIC_1164.all; -- Package defined in the IEEE (Found in library IEEE)
    use IEEE.numeric_std.all; -- Used to covert integer to std_logic_vector via unsigned type. Unsigned types represent postive 
    								  -- values including zero. The compiler interprets each unsigned type as a binary number, with the 
    								  -- digit of the left as the MSB (Most Significant Bit).
    -- Entity declaration
    entity spi_statemachine is -- Entity name.
    	-- Generics --
    	generic (n: positive := 16); -- Number of bits. Generic is used to specify parameters, for example, the length of data.
    	port (-- Inputs --
             do_o: in std_logic_vector(n-1 downto 0); -- Data in.
             do_valid_o: in std_logic; -- Check if data is valid (No data setup glitches on the data transfer), valid during one 
    											  -- clk_i rising edge.
             -- Outputs --
             di_i: out std_logic_vector(n-1 downto 0); -- Data out.
             wren_i: out std_logic; -- Write port, valid during one clk_i rising edge.
             -- Start operation --
             start: in std_logic; -- Start state machine.
             -- Clock operation --
             rst_i: in std_logic; 
             clk_i: in std_logic; -- Clock synchronicity with data.
             -- Output indication --
             correct: out std_logic); -- Check if data transmitted is correct.
    end spi_statemachine; -- End entity
    -- Architecture behaviour
    architecture detect of spi_statemachine is -- Identifying the architecture.
    type state_type is (createData, writeData, delay, stopwriteEnable,
                        checkValid, compareData, checkFinished); -- Enumeration types.
                              
    -- Signals and types
    signal state: state_type; -- State of the machine.
    type int_array is array (integer range <>) of integer;
    begin
    	P1: process (clk_i, rst_i, start) -- Sensitivity list. The process wakes up when an event occurs on one of the signals 
    												 -- in the sensitivity list.
       -- Variables -- (Variables change immediately therefore 'rst_i' is used to stop this from happening)
       variable error_rate: natural := 0; -- Incrementing error. Natural is a data type is used to represent natural
    												  -- (nonnegative) numbers.
       variable data_in: std_logic_vector(n-1 downto 0); -- Data length. Integer is a data type that represents positive and 
    																	  -- negative whole numbers.
       variable data_out: std_logic_vector(n-1 downto 0);
    	variable random_data: int_array(0 to 15);
    	variable count: integer := 0;
    	variable i: integer := 0;
       begin
    		if rst_i = '1' then -- Reset operation used initialize all signals to predetermined state.
    			-- Initial values for all outputs, variables and signals --
    			di_i <= (others => '0'); 
             wren_i <= '0';
             correct <= '0';
             error_rate := 0;
             data_in := (others => '0');
             data_out := (others => '0');
    			count := 0;
    			i := 0;
    			random_data(0) := 14492;
    			random_data(1) := 9335;
    			random_data(2) := 10648;
    			random_data(3) := 21153;
    			random_data(4) := 14657;
    			random_data(5) := 10583;
    			random_data(6) := 10651;
    			random_data(7) := 18398;
    			random_data(8) := 12598;
    			random_data(9) := 30086;
    			random_data(10) := 6444;
    			random_data(11) := 25436;
    			random_data(12) := 25025;
    			random_data(13) := 27533;
    			random_data(14) := 3525;
    			random_data(15) := 31968;
    			state <= createData;
    			elsif clk_i'event and clk_i = '1' then -- Signal attribute used to test for a change on a signal.
    				case state is
    					-- When state 'createData' and if 'start' equals '1', then state is 'writeData' and 'i' increments count from
    					-- 0 to 15. If 'i' increments count above 15 then 'i' equals 0 and otherwise state is 'createData'.
    					when createData =>
    						if (start = '1') then
    							state <= writeData;
    								i := i + 1;
    							if (i > 15) then
    								i := 0;
    							end if;
    							else
    								state <= createData;
    							end if;
                        
    					-- 'random_data' is converted into 'std_logic_vector' and is then is written in to 'data_out' and 'di_i'.
    					-- 'random_data' will be counted (i) up to 15 (n).
                   when writeData =>
    						data_out := std_logic_vector(to_unsigned(random_data(i), n));
    						di_i <= std_logic_vector(to_unsigned(random_data(i), n));
    						state <= delay;
    					
                   when delay =>
    						count := count + 1;
    						if	(count > 1) then
    							state <= stopwriteEnable;
    							wren_i <= '1'; -- Starts transmission.
    							count := 0;
    						else
    							state <= delay;
    						end if;
    					 
                   when stopwriteEnable =>
    						state <= checkValid;
                        
    					-- If 'do_valid_o' equals '1' then 'do_o' gets the value of 'data_in'. 'wren_i' equals '0' as it only as
    					-- only equals '1' for one clock cycle.
                   when checkValid =>
    						if (do_valid_o = '1') then
    							data_in := do_o;
    							wren_i <= '0';
    							state <= compareData;
                      else
    							state <= checkValid;
                      end if;
    					-- If 'data_in' equals 'data_out' and the error rate is below '0' then there is no error, if the error 
    					-- rate is above '0' then there is an error.
                   when compareData =>
    						if data_in = data_out then
    							error_rate := error_rate - 1;
    						else
    							error_rate := error_rate + 1;
    						end if;
    							state <= checkFinished;
                            
    					-- If the error rate is greater than '0' then correct equals '0', otherwise correct equals '1'.
                   when checkFinished =>
    						if (error_rate > 0) then
    							correct <= '0';
    						else
    							correct <= '1';
                      end if;
                end case;
            end if;
        end process;
    end detect;

    Kind regards,

    Deadman
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I am trying to write a test bench for my state machine but I don't seem to be getting the correct results.

    The test bench for my state machine is the following code:

    library IEEE;
    use IEEE.STD_LOGIC_1164.all;
    -- Entity --
    entity spi_statemachine_test is
       generic (n: positive := 16);
    end spi_statemachine_test;
    -- Component declaration --
    architecture behaviour of spi_statemachine_test is
    component spi_statemachine
    	port (do_o: in std_logic_vector(n-1 downto 0);
             do_valid_o: in std_logic;
             di_i: out std_logic_vector(n-1 downto 0);
             wren_i: out std_logic;
             start: in std_logic;
             rst_i: in std_logic; 
             clk_i: in std_logic;
             correct: out std_logic);
    end component;
    		
    -- Signals --
    signal clk_i: std_logic;
    signal start: std_logic;
    signal rst_i: std_logic;
    signal wren_i: std_logic;						        					                      
    signal di_i: std_logic_vector(n-1 downto 0);		
    signal do_valid_o: std_logic;                     										 
    signal do_o: std_logic_vector(n-1 downto 0);
    signal correct: std_logic;
    -- Clock period --
    constant clk_period: time := 10 ns;
      	
    -- Compomemt instantiation --
    begin
    	C: spi_statemachine
    		port map(di_i => di_i,
    					wren_i => wren_i,
    					do_o => do_o,
    					do_valid_o => do_valid_o,
    					start => start,
    					rst_i => rst_i,
    					clk_i => clk_i,
    					correct => correct);
    				
    -- Clock --
    clk_process_slave: process
    	begin
    		clk_i <= '0';
    		wait for clk_period/2;
          clk_i <= '1'; 
          wait for clk_period/2;
    	end process clk_process_slave;
    	
    -- Stimulus -- 
    stimulus: process is
    	variable data_v: std_logic_vector(n-1 downto 0);
    		begin
    			start <= '1';
    			rst_i <= '1';
    			do_valid_o <= '0';
    			do_o <= (others => '0');
    			wait for 15ns;
    			rst_i <= '0';
    			wait until wren_i'event and wren_i = '1';
    			data_v := di_i;
    			wait until clk_i'event and clk_i = '1';
    			do_o <= data_v;
    			wait until clk_i'event and clk_i = '1';
    			wait until clk_i'event and clk_i = '1';
    			do_valid_o <= '1';
    			wait until clk_i'event and clk_i = '1';
    			do_valid_o <= '0';
    			wait until clk_i'event and clk_i = '1';
    			assert correct <= '0'
    			report "There in an incorrect value on the output LED)"
    			severity error;
    			wait for 5ns;
    			wait;
    end process stimulus;
    end behaviour;

    The following screenshot is of my simulation, 'correct' should be high because the correct data is being received between 'di_i' and 'do_o'.

    https://www.alteraforum.com/forum/attachment.php?attachmentid=10201

    Kind regards,

    Deadman