Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

SPI on FPGA - output 1 byte from one port?

Status
Not open for further replies.

kidi3

Full Member level 1
Joined
Mar 31, 2013
Messages
98
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Activity points
2,350
Hi guys..
I am at the moment trying to implement an SPI interface onto my FPGA, but i am bit confused on how i TX and RX a byte onto my MISO and MOSI which consist of being one port.

Should i just send one bit every time the CLK has a rising_edge, or is on the falling_edge or on a full clock.. How do transfer/receive a byte (actually more than a byte when receiving) using one MISO and MOSI pin ??
 

Hi guys..
I am at the moment trying to implement an SPI interface onto my FPGA, but i am bit confused on how i TX and RX a byte onto my MISO and MOSI which consist of being one port.

Should i just send one bit every time the CLK has a rising_edge, or is on the falling_edge or on a full clock.. How do transfer/receive a byte (actually more than a byte when receiving) using one MISO and MOSI pin ??

You haven't stated if you are creating a slave or a master in the FPGA.

MISO - Master In Slave Out, this is the RX side of the interface when looking at it from the master device.
MOSI - Master Out Slave In, this is the TX side of the interface when looking at it from the master device.

TX is done on the falling edge of the clock
RX is done on the rising edge of the clock

Maybe this tutorial will help you.

Also I don't get why you are talking about one pin for both MISO and MOSI that isn't SPI?
 

TX is done on the falling edge of the clock
RX is done on the rising edge of the clock
*Most commonly.
 

thanks for your reply..
I trying to interact a FPGA with an ADC (MCP3008) which uses SPI..
I creating the master => fpga, which has the inter act with the slave being the ADC..

I thought of using state machine to perform the TX and RX sequence.. I am for some reason though getting a bad synchronous error.

Basically my TX/RX transmit 1 bit every time a rising_edge occurs, for which i would for each state/bit change the bit which had to be sent/receive.

And ex.

Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
receive: process(newClock, MISO, init_rx, state_rx, RX)
begin 
    if init_rx = '1' then 
        case state_rx is 
            when bit9 =>
                if rising_edge(newClock) then 
                    RX(9) <= MISO;
                    state_rx <= bit8; 
                end if;
            when bit8 =>
                if rising_edge(newClock) then 
                    RX(8) <= MISO;
                    state_rx <= bit7;
                end if;
            when bit7 =>
                if rising_edge(newClock) then 
                    RX(7) <= MISO;
                    state_rx <= bit6;
                end if;
            when bit6 =>
                if rising_edge(newClock) then 
                    RX(6) <= MISO;
                    state_rx <= bit5;           
                end if;  
            when bit5 =>
                if rising_edge(newClock) then 
                    RX(5) <= MISO;
                    state_rx <= bit4;
                 end if;
            when bit4 =>
                if rising_edge(newClock) then 
                    RX(4) <= MISO;
                    state_rx <= bit3;
                end if;
            when bit3 =>
                if rising_edge(newClock) then 
                    RX(3) <= MISO;
                    state_rx <= bit2;
                end if;
            when bit2 =>
                if rising_edge(newClock) then 
                    RX(2) <= MISO;
                    state_rx <= bit1;
                end if;      
            when bit1 =>
                if rising_edge(newClock) then 
                    RX(1) <= MISO;
                    state_rx <= bit0;
                end if;      
            when bit0 =>
                if rising_edge(newClock) then 
                    RX(0) <= MISO;
                    state_rx <= bit9;
                    init_rx <= '0';
                end if;
        end case;           
    end if;
end process;

 

This code wont work at all because of all the rising_edge statements (it might simulate, but it wont synthesise). You need to wrap the clock around all of the synchronous code.

Also, what is "newClock" - it makes me think you've made a clock somewhere. Usually for an SPI intereface you run your logic at a much higher frequency than the SPI clock. Then SPI clock can be created/sampled safely in logic.
 

how should i then transmit and receive 1 byte from one pin.

newClock is just a down scaled version of the clock.. that shouldn't be an issue - already tested.
 

Generating clocks in logic (as opposed to PLLs/clock_managers) is not a good design practice.

Namely because clock generator placement can vary from build to build. Therefore timing skew between it and other logic will change depending on placement and routing, which could result in issues.

The typical method of doing serial transfers is by using a shift register and a counter controlled by an FSM.
 

Hi,

I see 10 bits of data. SPI usually is byte wide, so multiple of 8 bits.

Check if 10 bits is the right count of bits.

Klaus
 

It because the ADC (MCP3008) outputs 10 bit value.

I have come to the conclusion that i might need to redesign my SPI at the moment, because the way i am doing it now doesn't work, and seem to hard to fix.

the way i was thinking of doing was to have 3 FSM.
One controlling the states of SPI connection between FPGA and ADC.
One controlling the data transmission between FPGA and ADC
One controlling receive data from ADC to FPGA.


To accurately interface with the ADC converter, I have to send 3 bytes of information which shall occur while CC is low (CC = low (ADC work) , CC= high (ADC break)). These 3 bytes has to be send at a rising edge.
So to know when i can send the next byte, i need the FSM controlling the transmission to signal my FSM controlling my SPI on when the transmission is done, so it can change the state of the SPI. My idea was to use a signal which is high while the transmission is performed, and low when none is performed, or one has been performed. But cannot seem to do it without multi sourcing things. Does it make sense?
 
Last edited:

Hi,

so your code is only a snippet.

***
has to be send at a rising edge.
Figure 1- 1of the datasheet tells "output data on falling edge" and "input data on rising edge" in both directions.

***
Implementation:
I´d use a single process counting 3 x 8 = 24 bits, not three state machines. Refer to Figure 6-1 of datasheet

To output data (from FPGA) you could use shift regisers or with a MUX.

To input data you (to FPGA) could use a shift register or bitwise selected DFF.

I recommend to use a pullup at the MCP_dout line to get true levels (high) when in tristate, and get a valid low before the MSB. This can be used for interface validation.

Klaus
 

It because the ADC (MCP3008) outputs 10 bit value.

Obviously, you didn't yet read the interface description in the datsheet. MCP3008 uses at least 17 clock cycles for input selection and data aquisition, with a standard SPI master, 24 clock cycles are used.
 

I am not sure if understand whats misunderstood here.. The ADC clocks as output a 10 bit value..
I am not sure how the transmission and receive sequence can be put into one process while keeping the timing.

The only way i see it possible within one process. is by having a state for each clock action... (too messed up. )
 

A counter and some extra logic to detect what count you are on and enable/disable input shifting and output shifting and loading of stuff based on the count. The FSM just controls the counter and stuff like the clock and the CS. Easy to do, I've done many designs with this type of FSM control interface.
 

So.. I tried to make an example.. But cannot seem to get an accurate 10 bit value.

I seem to get odd values, which makes no sense.

Some comment on the code would be appreciated..


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 10/07/2015 10:57:05 PM
-- Design Name:
-- Module Name: main - Behavioral
-- Project Name:
-- Target Devices:
-- Tool Versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_logic_unsigned.all;
use ieee.numeric_std.all;
 
 
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
 
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
 
entity main is
    Port ( MISO : in STD_LOGIC;
           MOSI : out STD_LOGIC;
           CS : out STD_LOGIC;
           SCLK : out STD_LOGIC;
           CLK : in STD_LOGIC);
end main;
 
architecture Behavioral of main is
--signal prescaler : integer range 0 to 3500000 := 3500000 ;
signal prescaler_counter : integer range 0 to 50000000 := 0;
signal newClock : std_logic := '0';
signal TX :std_logic_vector(7 downto 0)  := "00000000";
signal RX : std_logic_vector(9 downto 0) := "0000000000";
type state_type is (start,state2,state3,state4,state5,state6);  --type of state machine.
type TXRX  is (waiting, working);
signal state : state_type := start;
signal shift_counter: integer range 0 to 10:= 0;
signal init_tx: TXRX := waiting;
signal init_rx: TXRX := waiting;
begin
 
prescaler01: process(clk, newClock)
begin
    if rising_edge(clk) then
        if prescaler_counter <= 6 then
            prescaler_counter <= prescaler_counter + 1;
        else
            newClock <= not newClock;
            prescaler_counter <= 0;
       end if;
    end if;            
end process;
 
SCLK <= newClock;
 
-- FPGA transmit data on falling_edge
-- FPGA reads data on rising_edge
 
 
SPI_state: process(newClock)
begin
    if rising_edge(newClock) then
        case state is
            when start =>
                CS <= '1';  
            state <= state2;
            when state2 =>
                case init_tx is  
                    when waiting =>
                        TX <= "00000001";
                        init_tx <= working;
                    when working =>
                        shift_counter <= shift_counter + 1;
                        MOSI <= TX(shift_counter); -- For at få optimal speed bør denne sætte ved falling_edge(newClock og ikke rising_edge)
                        if shift_counter = 8 then
                            MOSI <= '0';
                            init_tx <= waiting;
                            shift_counter<= 0;
                            state <= state3;
                        end if;
                end case;
            when state3 =>
                case init_tx is  
                     when waiting =>
                          TX <= "10000000";
                          init_tx <= working;
                     when working =>
                           shift_counter <= shift_counter + 1;
                           MOSI <= TX(shift_counter);
                           if shift_counter = 8 then
                               init_tx <= waiting;
                               shift_counter<= 0;
                               state <= state4;
                               MOSI <= '0';
                          end if;
                end case;                  
            when state4 =>
                case init_tx is
                    when waiting =>
                        TX <= "00000000";
                        init_tx <= working;
                    when working =>
                        shift_counter <= shift_counter+1;
                        MOSI <= TX(shift_counter);
                        if shift_counter = 8 then  
                            init_tx <= waiting;
                            shift_counter <= 0;
                            state <= state5;
                            MOSI <= '0';
                        end if;
                 end case;
            when state5 =>
                state <= state6;
            when state6 =>
                case init_rx is
                    when waiting =>
                        init_rx <= working
                        shift_counter <= 10;
                    when working =>
                        shift_counter <= shift_counter-1;
                        RX(shift_counter) <= MISO;
                        if shift_counter = 0 then  
                           init_rx <= waiting;
                           shift_counter <= 0;
                           state <= start;
                        end if;
                end case;  
            when others => state <= start;          
        end case;
    end if;              
end process;
 
 
end Behavioral;

 

Rule 1. If you have to nest statements this much then you are probably doing something wrong.

Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
begin
    if rising_edge(newClock) then
        case state is
            when start =>
                -- assignments
            when state2 =>
                case init_tx is  
                    when waiting =>
                        -- assignments
                    when working =>
                        -- assignments
                        if shift_counter = 8 then
                            -- assginments
                        end if;


the way you use the case seems like you think like a software programmer and aren't thinking like a hardware engineer designing a circuit.


Code VHDL - [expand]
1
prescaler01: process(clk, newClock)


newClock is unnecessary and may cause problems as the process will be activated again when the newClock changes state. Besides that generating clocks using the FPGA fabric is not a good design practice.


Code VHDL - [expand]
1
2
RX(shift_counter) <= MISO;
MOSI <= TX(shift_counter);


Once again software concepts applied to hardware.
This results in multiplexing and demultiplexing on the signals MOSI and MISO respectively. This is a bad way to perform shift operations.

Code VHDL - [expand]
1
shift_reg <= shift_reg(shift_reg'left -1 downto 0) & '0';


is a shift register in VHDL (if my syntax is correct). This shift register minimizes the amount of logic required to implement as it's simply a string of FFs with Qs connected to the Ds.

I also recommend setting the state in every branch, letting the tool decide that the next state is the same as the current state (IMO) leaves the synthesis tool in charge of what your FSM transitions are supposed to be. I'd rather tell it what they are explicitly. Or to put it another way, I've never had problems with FSM synthesis, but co-workers who don't follow the same rules I do have had problems in the past.
 

Could you elaborate on the CS.. The ADC only operates when the CS is pulled low.. and "standby" when it is high.
 

omg... that might describe the missing the incorrect output. but yeah.. i think the code has to be redesigned, due to the nested switch cases.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top