1
votes

I am attempting to implement an SPI interface. I have 2 questions about this, this is the first. (I decided to ask each question individually to simplify things.

Nothing seems to be working, so I have stripped my design right down to just a shift register, where the data which is clocked in should be returned to the SPI master device.

Here is what I have at the moment.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity interface is

port
(
     -- SPI
    SPI_MOSI: in std_logic;
    SPI_MISO: out std_logic;
    SPI_CLK: in std_logic;
);

end interface;

architecture Behavioral of interface is

-- SPI
signal SPI_REG: std_logic_vector(7 downto 0) := (others => '0');

-- SPI interface
process(SPI_CLK)

begin

    if rising_edge(SPI_CLK) then

        SPI_MISO <= SPI_REG(7);
        SPI_REG <= SPI_REG(6 downto 0) & SPI_MOSI; -- Concatenate bits

    end if;

end process;

end Behavioral;

I also have a main (cpp) program running on a Raspberry Pi.

It's pretty basic again, this is what it does.

#include <wiringPiSPI.h>
#include <iostream>

int main()
{
    wiringPiSPISetup(0, 500000); // Device 0, slowest speed available

    // Create array of 64 bytes (unsigned char)
    // Print this array

    wiringPiSPIDataRW(0, data, 64);

    // Print array again and compare data by eye
}

Currently I have set up the array creation so that it contains the numbers 0 to 63 in sequential order. They are printed out, after each one is converted to an int. (I have omitted this code for simplicity.)

This is a typical input:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...

And here is the typical output:

31 128 0 129 1 130 2 131 3 132 4 135 5 134 6 ...

So the first 2 bytes are junk. I would have expected 1 junk byte to be returned... One which was set to contain zero! (As in the VHDL code.)

Then following this, every other byte is junk, I have no clue why... And the actual data which is sent appears to be returned, although it comes back with junk bytes in between...

I am unsure if this is because I have misunderstood the SPI hardware definition or whether my VHDL code is not correct.

1
Without understanding Raspbian it appears you're getting 16 bits for every array value. Device 0 sounds like it's expected to be some particular device with a two byte transfer sequence, you appear to be mixing metaphors between physical layer transfer and protocol layer talking to a device. (Instruction/address in one byte, data in the the second). The first two bytes configuration for the device (assuming these are the very first two). There's an implication your VHDL model is performing correctly. - user1155120
See ST SPI protocol 2 SPI communication flow , PDF pages 8 - 10, See Figure 3. - user1155120
Your VHDL looks fine. I agree with David that there's some misunderstanding with the transmitter. Can you put a scope probe on to MOSI and MISO? - Russell
@DavidKoontz What should I be looking at here? - FreelanceConsultant
@Russell I can but I wont see anything at 500 kHz... - FreelanceConsultant

1 Answers

1
votes

Quick Answer

It takes 9 clock cycles for the first clocked in bit to appear at the output.

Method of verification

I altered the code to the following and noticed that the output was delayed by 1 bit compared to a straight wire connecting MOSI to MISO. (Short these 2 pins and there is no delay, as expected, however the following code produced a 1 bit delay.)

Note that this should be really obvious to someone who is familiar with VHDL.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity interface is

port
(
     -- SPI
    SPI_MOSI: in std_logic;
    SPI_MISO: out std_logic;
    SPI_CLK: in std_logic;
);
    
end interface;

architecture Behavioral of interface is

-- SPI
signal SPI_REG: std_logic_vector(7 downto 0) := (others => '0');

-- SPI interface
process(SPI_CLK)
        
begin

    if rising_edge(SPI_CLK) then
        
        SPI_MISO <= SPI_MOSI;
            
    end if;
    
end process;

end Behavioral;

If this produces a 1 bit delay, then it should be obvious to you why, if you have done some elementary digital electronics. (It is due to the way in which the clock edge transfers data through the system, of course.)

Full Solution

So to fix this you can just change the code so that it uses an asynchronous data transfer. (Not sure what the correct name for this is in the VHDL world.)

Here is the change:

process(SPI_CLK)
        
begin

    SPI_MISO <= SPI_MOSI;

    if rising_edge(SPI_CLK) then
        
    end if;
    
end process;

This transfer of data happens immediately (or pretty fast at least, nano seconds or less) and not just on the clock edge.

So the full solution for 8 bits is just this:

entity interface is

port
(
     -- SPI
    SPI_MOSI: in std_logic;
    SPI_MISO: out std_logic;
    SPI_CLK: in std_logic;
);
    
end interface;

architecture Behavioral of interface is

-- SPI
signal SPI_REG: std_logic_vector(7 downto 0) := (others => '0');

-- SPI interface
process(SPI_CLK)
        
begin

    SPI_MISO <= SPI_REG(7);

    if rising_edge(SPI_CLK) then
        
        SPI_REG <= SPI_REG(6 downto 0) & SPI_MOSI; -- Concatenate bits
            
    end if;
    
end process;

end Behavioral;

That takes 8 clock cycles for the input to appear at the output, rather than 9.