Вы находитесь на странице: 1из 6

04/04/2015

Home

Serial Peripheral Interface (SPI) Tutorial

Tutorials

Community
Poll
Tutorials

Mojo - FPGA Tutorials

Serial Peripheral Interface (SPI) Tutorial

Serial Peripheral Interface (SPI)


Serial Peripheral Interface, or SPI, is a very common communication protocol used
for two-way communication between two devices. A standard SPI bus consists of 4
signals, Master Out Slave In (MOSI), Master In Slave Out (MISO), the clock (SCK),
and Slave Select (SS). Unlike an asynchronous serial interface, SPI is not
symmetric. An SPI bus has one master and one or more slaves. The master can
talk to any slave on the bus, but each slave can only talk to the master. Each slave
on the bus must have it's own unique slave select signal. The master uses the
slave select signals to select which slave it will be talking to. Since SPI also
includes a clock signal, both devices don't need to agree on a data rate before
hand. The only requirement is that the clock is lower than the maximum frequency
for all devices involved.

Example SPI Transfer


When the master of the SPI bus wants to initiate a transfer, it must first pull the SS
signal low for the slave it wants to communicate with. Once the SS signal is low,
that slave will be listening on the bus. The master is then free to start sending data.
There are 4 different SPI bus standards that all have to do with the SCK signal. The
4 modes are broken down into two parameters, CPOL and CPHA. CPOL stands for
Clock POLarity and designates the default value (high/low) of the SCK signal when
the bus is idle. CPHA stands for Clock PHAse and determines which edge of the
clock data is sampled (rising/falling). The data sheet for any device will specify
these parameters so you can adjust accordingly. The most common settings are
CPOL=0 (idle low) and CPHA=0 (sample rising edge).
Here is an example transfer with CPOL=0 and CPHA=0.

https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi

1/6

04/04/2015

Serial Peripheral Interface (SPI) Tutorial

The bits in a SPI transmission are sent LSB first.


Any SPI transmission is controlled solely by the master. The master generates the
clock and controls the slave select signal(s). This means that the slave has no way
of sending data to the master on its own!
Each SPI transfer is full-duplex, meaning that data is sent from the master to the
slave and from the slave to the master at the same time. There is no way for a
slave to opt-out of sending data when the master makes a transfer, however,
devices will send dummy bytes (usually all 1's or all 0's) when communication
should be one way. If the master is reading data in for a slave, the slave will know
to ignore the data being sent by the master.
Devices that use SPI typically will send/receive multiple bytes each time the SS
signal goes low. This way the SS signal acts as a way to frame a transmission. For
example, if you had a flash memory that had an SPI bus and you want to read
some data, the SS signal would go low, the master would send the command to
read memory at a certain address, and as long as the master kept SS low and
toggling SCK the flash memory would keep sending out data. Once SS returned
high the flash memory knows to end the read command.
Since the MISO signal can be connected to multiple devices, each device will only
drive the line when its SS signal is low. This is shown by the grey area.

SPI Slave
In the Mojo Base Project, the file spi_slave.v contains the SPI module used to
interface with the AVR. The AVR, in this case, is the master and the FPGA is the
slave. The reason the AVR is the master is because the SPI bus is used to transfer
data from the analog pins. Since the FPGA has no way of knowing when the data
would be available, the FPGA would have to keep asking the AVR if it had any data.
By making the AVR the master, it allows it to send the data right when it's ready.
https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi

2/6

04/04/2015

Serial Peripheral Interface (SPI) Tutorial

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

module spi_slave(

inputclk,

inputrst,

inputss,

inputmosi,

outputmiso,

inputsck,

outputdone,

input[7:0]din,

output[7:0]dout

)
;

r
e
gmosi_d,mosi_q;

r
e
gss_d,ss_q;

r
e
gsck_d,sck_q;

r
e
gsck_old_d,sck_old_q;

r
e
g[7:0]data_d,data_q;

r
e
gdone_d,done_q;

r
e
g[2:0]bit_ct_d,bit_ct_q;

r
e
g[7:0]dout_d,dout_q;

r
e
gmiso_d,miso_q;

assign miso=miso_q;

assign done=done_q;

assign dout=dout_q;

always @(*)begin

ss_d=ss;

mosi_d=mosi;

miso_d=miso_q;

sck_d=sck;

sck_old_d=sck_q;

data_d=data_q;

done_d=1'b0;

bit_ct_d=bit_ct_q;

dout_d=dout_q;

if (ss_q)begin

bit_ct_d=3'b0;

data_d=din;

miso_d=data_q[7];

end else begin

if (!sck_old_q&&sck_q)begin //risingedge

data_d={data_q[6:0],mosi_q};

bit_ct_d=bit_ct_q+1'b1;

if (bit_ct_q==3'b111)begin

dout_d={data_q[6:0],mosi_q};

done_d=1'b1;

data_d=din;

end

end else if (sck_old_q&&!sck_q)begin //falling

miso_d=data_q[7];

end

end

end

always @(posedge clk)begin

https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi

3/6

04/04/2015

Serial Peripheral Interface (SPI) Tutorial

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

if (rst)begin

done_q<=1'b0;

bit_ct_q<=3'b0;

dout_q<=8'b0;

miso_q<=1'b1;

end else begin

done_q<=done_d;

bit_ct_q<=bit_ct_d;

dout_q<=dout_d;

miso_q<=miso_d;

end

sck_q<=sck_d;

mosi_q<=mosi_d;

ss_q<=ss_d;

data_q<=data_d;

sck_old_q<=sck_old_d;

end

endmodule

This is module assumes CPOL = 0 and CPHA = 0.


It waits for SS to go low. Once SS is low, it starts shifting data into the data_d/_q
register. Once eight bits have been shifted in it signals that it has new data on
dout. On the falling edges of the clock, it shifts out the data that was provided by din
at the beginning of the transmission.

SPI Master
Our Clock/Visualizer Shield, uses a Real-Time Clock (RTC) device that provides the
Mojo with the current time. The RTC is connected to the Mojo through an SPI bus.
In this case, the FPGA on the Mojo is the master and the RTC is the slave.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

module spi#(parameterCLK_DIV=2)(

inputclk,

inputrst,

inputmiso,

outputmosi,

outputsck,

inputstart,

input[7:0]data_in,

output[7:0]data_out,

outputbusy,

outputnew_data

)
;

l
o
calparamSTATE_SIZE=2;

l
o
calparamIDLE=2'd0,

WAIT_HALF=2'd1,

TRANSFER=2'd2;

r
e
g[STATE_SIZE-1:0]state_d,state_q;

r
e
g[7:0]data_d,data_q;

https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi

4/6

04/04/2015

Serial Peripheral Interface (SPI) Tutorial

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

r
e
g[CLK_DIV-1:0]sck_d,sck_q;

r
e
gmosi_d,mosi_q;

r
e
g[2:0]ctr_d,ctr_q;

r
e
gnew_data_d,new_data_q;

r
e
g[7:0]data_out_d,data_out_q;

assign mosi=mosi_q;

assign sck=(~sck_q[CLK_DIV-1])&p;(state_q==

assign busy=state_q!=IDLE;

assign data_out=data_out_q;

assign new_data=new_data_q;

always @(*)begin

sck_d=sck_q;

data_d=data_q;

mosi_d=mosi_q;

ctr_d=ctr_q;

new_data_d=1'b0;

data_out_d=data_out_q;

state_d=state_q;

case (state_q)

IDLE:begin

sck_d=4'b0;

ctr_d=3'b0;

if (start==1'b1)begin

data_d=data_in;

state_d=WAIT_HALF;

end

end

WAIT_HALF:begin

sck_d=sck_q+1'b1;

if (sck_q=={CLK_DIV-1{1'b1}})begin

sck_d=1'b0;

state_d=TRANSFER;

end

end

TRANSFER:begin

sck_d=sck_q+1'b1;

if (sck_q==4'b0000)begin

mosi_d=data_q[7];

end else if (sck_q=={CLK_DIV-1{1'b1}})

data_d={data_q[6:0],miso};

end else if (sck_q=={CLK_DIV{1'b1}})begin

ctr_d=ctr_q+1'b1;

if (ctr_q==3'b111)begin

state_d=IDLE;

data_out_d=data_q;

new_data_d=1'b1;

end

end

end

endcase

end

always @(posedge clk)begin

if (rst)begin

https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi

5/6

04/04/2015

Serial Peripheral Interface (SPI) Tutorial

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

ctr_q<=3'b0;

data_q<=8'b0;

sck_q<=4'b0;

mosi_q<=1'b0;

state_q<=IDLE;

data_out_q<=8'b0;

new_data_q<=1'b0;

end else begin

ctr_q<=ctr_d;

data_q<=data_d;

sck_q<=sck_d;

mosi_q<=mosi_d;

state_q<=state_d;

data_out_q<=data_out_d;

new_data_q<=new_data_d;

end

end

endmodule

In this case CPOL = 0 and CPHA = 1.


The overall idea is the same, however, the FPGA now needs to generate the SCK
signal. The parameter CLK_DIV is used to specify how much the FPGA's clock
should be divided. The default value is 2, which means that the frequency of SCK
will be 1/4th (2^2 = 4 clock cycles) of that of the FPGA. If CLK_DIV was set to 3, SCK
would be 1/8th (2^3 = 8 clock cycles) the frequency of the FPGA's clock.

https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi

6/6