0

I learned VHDL 5 years back, and never used after that as I was working on different domain. Now I'm working in a project that required some work in VHDL. I have to implement SPI to program a ADF4158 device. I opened a book for syntax and tried to program. I need to develop the below module, I coded as per my understanding and also a test bench but it doesnot work in the simulation like I need. Below is the module I want to develop and the over all block diagram.

Block diagram of process and vhdl module (clickable): dcdc

The following is the SPI timing diagram as follows (clickable):

The below is the VHDL code I wrote to acheive the above SPI communication:

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

entity sender is
  Generic( DATA_WIDTH  : INTEGER := 32); --32 bit registers to pogram (total 8 registers to program)
  Port ( sys_clk : in  STD_LOGIC;   --clock from microblaze (32 MHz)
         enable : in  STD_LOGIC;  --from microblaze to trigger start
         reset : in  STD_LOGIC;   --reset spi clk generation
         start_data_read : out STD_LOGIC;   --to microblaze as flag so that microblaze starts sending register_select codes (pgogram data for target register) and the data to pogram - to buffer
         start_data_write : out STD_LOGIC;  --to microblaze as flag so that microblaze starts sending register_select codes (pgogram data for target register) and the data to pogram - to mosi port
         data_in : in  STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --program data (32bit data in 8 phases depending on register_select)
         process_done : out  STD_LOGIC; --flag bit to microblaze when complete process is complete
         register_select : in  STD_LOGIC_VECTOR(2 downto 0); --select code to identify which register to grogram, theorder can be manupulated from microblaze
         sclk : buffer  STD_LOGIC; --spi clock for ADC (3.2 MHz)
         ss : out  STD_LOGIC := '1'; --select line for ADF (slave)
         mosi : out  STD_LOGIC); --program data for ADF
end sender;

architecture Behavioral of sender is

   type machine is(store, sending); --states of state-machine
   signal state : machine := store;
   signal clk_divide: STD_LOGIC_VECTOR(5 downto 0); --clock cycle ratio between system clock from microblaze and spi clock for ADF
   signal tx_buffer_0   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 0)
   signal tx_buffer_1   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 1)
   signal tx_buffer_2   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 2)
   signal tx_buffer_3   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 3)
   signal tx_buffer_4   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 4)
   signal tx_buffer_5   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 5)
   signal tx_buffer_6   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 6)
   signal tx_buffer_7   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 7)
begin

   ClK_GEN: process(sys_clk)
   begin --spi sclk 20:1 cycles of sys_clk(system runs at 32 MHz and SPI CLK required is 1.6 MHz)
   if rising_edge(sys_clk) then
      if reset = '1' then
         clk_divide <= (others => '0');
         mosi <= 'Z'; --send high impedence
      else
         if clk_divide < "001010" then --10
            sclk <= '0';
            clk_divide <= clk_divide + 1;
         else
            if clk_divide < "010100" then --20
               sclk <= '1';
            else
               clk_divide <= (others => '0');
            end if;
        end if;
      end if;
   end if;
end process ClK_GEN;

SEND: process(sclk,enable,register_select)
begin
   if rising_edge(sclk) then
      if enable = '1' then
         case state is
            when store =>
               start_data_read <= '1'; --ask microblaze to send register_selectcodes and data
               case register_select is
                  when "000" =>
                      tx_buffer_7 <= data_in; --copy data to buffer
                  when "001" =>
                      tx_buffer_6 <= data_in;
                  when "010" =>
                      tx_buffer_5 <= data_in;
                  when "011" =>
                      tx_buffer_4 <= data_in;
                  when "100" =>
                      tx_buffer_3 <= data_in;
                  when "101" =>
                      tx_buffer_2 <= data_in;
                  when "110" =>
                      tx_buffer_1 <= data_in;
                  when "111" =>
                      tx_buffer_0 <= data_in;
                  when others =>
                      tx_buffer_1 <= (OTHERS => '0');
               end case;
               state <= sending; --change state to next
            when sending =>
               start_data_write <= '1'; --ask microblaze to send register_select codes to pgrogram a register
               case register_select is
                  when "000" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_7 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "001" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_6 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "010" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     ss <= '1';
                  when "011" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_4 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "100" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_3 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "101" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_2 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "110" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_1 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "111" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_0 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when others =>
                    mosi <= '0';
               end case;
            end case;
         end if;
      end if;
   end process SEND;

end Behavioral;

I also wrote a test bench code to send the above module, belowis the code:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL; 

ENTITY test_sender IS
END test_sender;

ARCHITECTURE behavior OF test_sender IS 
 -- Component Declaration for the Unit Under Test (UUT)

COMPONENT sender
PORT(
    sys_clk : IN  std_logic;
    enable : IN  std_logic;
    reset : IN  std_logic;
    start_data_read : OUT  std_logic;
    start_data_write : OUT  std_logic;
    data_in : IN  std_logic_vector(31 downto 0);
    process_done : OUT  std_logic;
    register_select : IN  std_logic_vector(2 downto 0);
    sclk : buffer  std_logic;
    ss : OUT  std_logic;
    mosi : OUT  std_logic
);
END COMPONENT;
--Inputs
signal sys_clk : std_logic := '0';
signal enable : std_logic := '0';
signal reset : std_logic := '0';
signal data_in : std_logic_vector(31 downto 0) := (others => '0');
signal register_select : std_logic_vector(2 downto 0) := (others => '0');

--Outputs
signal start_data_read : std_logic;
signal start_data_write : std_logic;
signal process_done : std_logic;
signal sclk : std_logic;
signal ss : std_logic;
signal mosi : std_logic;

-- Clock period definitions
constant clk_sys_period : time := 31.25 ns;

BEGIN

-- Instantiate the Unit Under Test (UUT)
uut: sender PORT MAP (
   sys_clk => sys_clk,
   enable => enable,
   reset => reset,
   start_data_read => start_data_read,
   start_data_write => start_data_write,
   data_in => data_in,
   process_done => process_done,
   register_select => register_select,
   sclk => sclk,
   ss => ss,
   mosi => mosi
);

-- Clock process definitions
clk_sys_process :process
begin
    sys_clk <= '0';
    wait for clk_sys_period/2;
    sys_clk <= '1';
    wait for clk_sys_period/2;
end process;

-- Stimulus process
stim_proc: process
begin       
-- hold reset state for 100 ns.
    wait for 1ns;
    enable <= '1';
    if start_data_read = '1' then
        wait for 1ns;
        register_select <= "000";
        wait for 1 ns;
        data_in <= "01001111001101010101010111100001";
        wait for 5 ns;
        register_select <= "001";
        wait for 1 ns;
        data_in <= "11001111001101010111011111100101";
        wait for 5 ns;
        register_select <= "010";
        wait for 1 ns;
        data_in <= "00000011001101010101011101100101";
        wait for 5 ns;
        register_select <= "011";
        wait for 1 ns;
        data_in <= "00011111001101010101010011100101";
        wait for 5 ns;
        register_select <= "100";
        wait for 1 ns;
        data_in <= "10001111001101010101011111100001";
        wait for 5 ns;
        register_select <= "101";
        wait for 1 ns;
        data_in <= "11001111001101010101011110000101";
        wait for 5 ns;
        register_select <= "110";
        wait for 1 ns;
        data_in <= "00101000001101010101011111100101";
        wait for 5 ns;
        register_select <= "111";
        wait for 1 ns;
        data_in <= "11111111001101010101011110100101";
        wait for 5 ns;
    end if;

    if start_data_write = '1' then
        wait for 1ns;
        register_select <= "000";
        wait for 5 ns;
        register_select <= "001";
        wait for 5 ns;
        register_select <= "010";
        wait for 5 ns;
        register_select <= "011";
        wait for 5 ns;
        register_select <= "100";
        wait for 5 ns;
        register_select <= "101";
        wait for 5 ns;
        register_select <= "110";
        wait for 5 ns;
        register_select <= "111";
        wait for 5 ns;
    end if;

-- insert stimulus here 
    wait;
  end process;
END;

The simulation is not working properly, and so the code is wrong, but I can't find out where is the mistake, it would be great if some one can help me and make me understand my mistake.

dDebug
  • 57
  • 1
  • 1
  • 8
  • Can you please indent your code? – Paebbels Jan 27 '15 at 14:35
  • Thanks paebbeles for the advice. The code was not readable after the 4 space editing, sorry for that. Its edited now! – dDebug Jan 27 '15 at 15:01
  • Several problems with your code, including dead state "sending". Here is a report with problems: http://www.sigasi.com/vhdl-code-check?ID=28172344 – Philippe Jan 27 '15 at 15:56
  • Make TX_Buffer an array, and you'll clean up a lot of case statements. You are also driving MOSI in two separate processes : move its reset action into the process that has to drive it. These won't fix the problems but they'll help clean up the code. –  Jan 27 '15 at 22:22
  • I tried to make the test bech better, but the results are still not understandable. So I cahnge the question a little bit to solve step by step: Is it OK to generate the SCLK clock like counters in a seperate process? Or should I be using a IP core clock wizard to provide the SPI clock. – dDebug Jan 30 '15 at 09:07

1 Answers1

2

Do you mean to do a data_read and data_write in this simulation? In RTL we can do a check like the following because we look many times (probably on an edge of clock):

if start_data_read = '1' then

However your testbench process does not loop. So instead of doing an "IF" the way we do in RTL code, you need to do something to cause your test to stop until that condition exists. So your test would be the following:

stim_proc: process
begin       
-- hold reset state for 100 ns.
    wait for 1ns;
    enable <= '1';
    if start_data_read /= '1' then
        wait until start_data_read = '1' ;
    end if ;
    -- Do a read sequence
    . . . 
    if start_data_write /= '1' then
        wait until start_data_write = '1' ;
    end if ;
    -- Do a write sequence

Within your read and write sequence, you have "wait for 5 ns". Can your device really accept operations this quickly? This is faster than a clock cycle. Are the operations in any way aligned with clock? You can wait for clock cycles by doing either of the following:

wait until rising_edge(Clk) ; 
wait until Clk = '1' ; -- assumes Clk is only '0' or '1' (safe for clocks)

Once you get the basics working, you might further consider encapsulating a single write or read operation into a subprogram.

Jim Lewis
  • 3,601
  • 10
  • 20
  • Thanks Jim, the suggestion are really appreciated. I will rewrite the test bench as you adviced. The timing are really fast and I will calculate to make sence mathematically. Do you think, the actual VHDL code is correct - the way I am sending data using left shift? – dDebug Jan 27 '15 at 15:49
  • I did not review your RTL code. There are issues there as well. Keep in mind that VHDL only does capture. You still have to do the detailed design. I would expect that once the MicroBlaze has written to the register file, the hardware could autonomously send the values over to the SPI. You might want to do a detailed block diagram of what that would look like and then write the code. Use only numeric_std and not std_logic_arith. – Jim Lewis Jan 27 '15 at 22:53