Unpredictable output of a simple program

Guest
Hello everyone!

I'm trying to design an SPI slave component in VHDL for Spartan-6.

I don't have problems with receiving a byte, but some strange things happen when I try to send a byte to a master device.

Since I don't have too much experience with VHDL, can someone please take a look at this simple program and tell me what did I do wrong.

I'm trying to send a byte 0xA6 from the slave to the master. However, for some reason, my SPI slave sends 0xA6, but also 0xB4 and 0xD3 occasionally.

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;

entity spi_slave is
port(
clk : in std_logic; -- 50 MHz clock
rst_n : in std_logic; -- asynchronous reset (active low)
ss_n : in std_logic; -- SPI slave select (active low)
sck : in std_logic; -- SPI clock
--mosi : in std_logic; -- master out, slave in (not in use)
miso : out std_logic -- master in, slave out
);
end entity spi_slave;

architecture RTL of spi_slave is

-- registers
signal ss_n_q : std_logic;
signal sck_q, sck0_q : std_logic;
--signal mosi_q : std_logic;
signal miso_q : std_logic;
signal cnt_q : natural range 0 to 7; -- counter that counts how many bits have been received

constant din : std_logic_vector(7 downto 0) := "10100110"; -- 0xA6

begin

miso <= miso_q;

spi_slave_logic : process(clk, rst_n, ss_n_q, sck_q, sck0_q, cnt_q, miso_q) is

-- wires
variable miso_d : std_logic;
variable cnt_d : natural range 0 to 7;

begin

miso_d := miso_q;
cnt_d := cnt_q;

if ss_n_q='1' then -- slave is not selected
cnt_d := 0; -- reset counter
miso_d := din(7); -- set the MSB at the output
else
if (sck0_q='0' and sck_q='1') then -- SPI clock rising edge
cnt_d := (cnt_q+1) rem 8;
elsif (sck0_q='1' and sck_q='0') then -- SPI clock falling edge
miso_d := din(7-cnt_q);
end if;
end if;


-- latch wires to registers
if rst_n='0' then
null; -- to be implemented
elsif rising_edge(clk) then
ss_n_q <= ss_n;
sck_q <= sck; -- present clock
sck0_q <= sck_q; -- past clock
--mosi_q <= mosi;
miso_q <= miso_d;
cnt_q <= cnt_d;
end if;

end process spi_slave_logic;

end architecture RTL;
 
Yeah, your program shows you are very new to VHDL. You are making some very
basic errors mixing combinatorial and sequential in the same process as well
as just not coding things right.

Your logic appears to be using a counter and a mux to select the bit to
send. That's not an error, but a simpler way is to load the data into a
shift register and shift it out rather than trying to track a count, etc.

The real problem comes from the fact that the code controlling the counter
and mux is coded as if it were combinatorial logic when the counter at least
needs to be sequential.

I can't see any particular reason to code the counter as a variable... or to
use it at all... Here is some pseudo code.

To make code easier read here, you should replace tabs with a couple of
spaces and your lines won't wrap so badly.

process (clk, rst)
begin
if(rst) then
OutShift <= "whatever"; -- redundant really since this will be
-- overwritten as soon as reset is
-- released and the next clock comes along
-- with the select unasserted. But it
-- helps simulation.
elsif (rising_edge(clk)) then
sck_d <= sck; -- I use _d to indicate a register delay in this
sck_dd <= sck_d; -- case for sync to clock and edge detect
ss_n_d <= ss_n;
if (ss_n_d) then -- select not asserted
OutShift <= "data to go out";
elsif (not sck_d and sck_dd) then -- falling edge of sck
OutShift <= Outshift(Outshift'high-1 downto 0) & "0";
end if;

miso_q <= OutShift (OutShift'high);

end process;


See how simple that is? Mostly because the only logic you need is a
loadable register and the edge detection AND function. The counter and mux
are much more complicated.

Rick C


marko.gulin2@gmail.com wrote on 11/15/2017 5:42 PM:
Hello everyone!

I'm trying to design an SPI slave component in VHDL for Spartan-6.

I don't have problems with receiving a byte, but some strange things happen when I try to send a byte to a master device.

Since I don't have too much experience with VHDL, can someone please take a look at this simple program and tell me what did I do wrong.

I'm trying to send a byte 0xA6 from the slave to the master. However, for some reason, my SPI slave sends 0xA6, but also 0xB4 and 0xD3 occasionally.

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;

entity spi_slave is
port(
clk : in std_logic; -- 50 MHz clock
rst_n : in std_logic; -- asynchronous reset (active low)
ss_n : in std_logic; -- SPI slave select (active low)
sck : in std_logic; -- SPI clock
--mosi : in std_logic; -- master out, slave in (not in use)
miso : out std_logic -- master in, slave out
);
end entity spi_slave;

architecture RTL of spi_slave is

-- registers
signal ss_n_q : std_logic;
signal sck_q, sck0_q : std_logic;
--signal mosi_q : std_logic;
signal miso_q : std_logic;
signal cnt_q : natural range 0 to 7; -- counter that counts how many bits have been received

constant din : std_logic_vector(7 downto 0) := "10100110"; -- 0xA6

begin

miso <= miso_q;

spi_slave_logic : process(clk, rst_n, ss_n_q, sck_q, sck0_q, cnt_q, miso_q) is

-- wires
variable miso_d : std_logic;
variable cnt_d : natural range 0 to 7;

begin

miso_d := miso_q;
cnt_d := cnt_q;

if ss_n_q='1' then -- slave is not selected
cnt_d := 0; -- reset counter
miso_d := din(7); -- set the MSB at the output
else
if (sck0_q='0' and sck_q='1') then -- SPI clock rising edge
cnt_d := (cnt_q+1) rem 8;
elsif (sck0_q='1' and sck_q='0') then -- SPI clock falling edge
miso_d := din(7-cnt_q);
end if;
end if;


-- latch wires to registers
if rst_n='0' then
null; -- to be implemented
elsif rising_edge(clk) then
ss_n_q <= ss_n;
sck_q <= sck; -- present clock
sck0_q <= sck_q; -- past clock
--mosi_q <= mosi;
miso_q <= miso_d;
cnt_q <= cnt_d;
end if;

end process spi_slave_logic;

end architecture RTL;


--

Rick C

Viewed the eclipse at Wintercrest Farms,
on the centerline of totality since 1998
 

Welcome to EDABoard.com

Sponsor

Back
Top