Design of Serial Peripheral Interface (SPI) Using Verilog HDL
SPI (Serial Peripheral Interface) Design:-
SPI is a synchronous serial data link between a master and multiple slave devices. Since clock is generated by master, clock requirements of slave devices are reduced unlike asynchronous communication.
An SPI device can either be a master or a slave. Master generates SS (Slave Select), MOSI (Master Output Slave Input), and SCK (Serial Clock) signals. Slave device generates MISO (Master Input Slave Output) signal (Figure 1). SS signal is an active low enable used by master to enable slave.
Figure-1:-SPI Master/Slave configuration.
During each SPI clock cycle, full duplex data transmission occurs:
> Master sends a bit on the MOSI line; slave reads it from the same line.
> Slave sends a bit on the MISO line; master reads it from the same line.
If data is sent on the MOSI line from master to slave, previous data is sent on the MISO line from slave to master (Figure 2).
Figure-2:-Data transmission between SPI Master and Slave.
In this design, data is read from ROM and converted from parallel to serial with shifting operations, in order to send data to the MOSI line bit by bit. MSB (Most Significant Bit) is sent first, and then other bits are sent. Process is completed by sending the LSB (Least Significant Bit). SCK is provided either reading or writing data synchronously. Data is changed at the falling edge of SCK, and sampled by the slave at the rising edge of SCK.
TOP_MODULE:-
Top Module consists of four modules. Two of them are FSM (Finite-State Machine) modules, one of them is a ROM memory module, and the other one is a clock divider.
Figure-3:- SPI TOP Module Structure.
Figure-4:- SPI TOP Module internal architecture.
FSM_SPI Module :-
FSM_SPI module converts 8-bit parallel data to serial data. It also generates SCK for synchronization, and SS to select slave device. The flow chart of FSM_SPI is shown in the Figure 5. SCK and DONE signals are set ‘1’and ‘0’ respectively. Then START input signal is checked. This signal is generated by FSM_CTRL module. It means that data has been read from the ROM, data is valid at FSM_SPI input. If START is ‘0’, SS is kept at high till START signal becomes high. When START is ‘1’, counter is cleared and data sending is allowed by setting SS to ‘0’. Then DATA signal is copied to TEMP register, after that SCK is inverted. In this way, a falling edge of SCK is generated for shifting data. That also explains why the SCK’s value kept high in the beginning. After that, MSB of the TEMP is copied to MOSI line and TEMP register is shifted left by one bit. Then SCK is inverted again, in this way, a rising edge of SCK is generated and data sampled by the slave device. Counter is used to track number of sent bits. When LSB of TEMP is sampled, counter is reached its maximum value and DONE signal becomes high. It means that the 8 bit DATA is sent successfully. At “completed” state, DONE signal is kept high so FSM_CTRL module has enough time to bring START signal low. During that time no data transferring is performed. SCK is used in FSM for generate and copied to SCK output.
Figure-5:- SPI_FSM Module State Machine.
FSM_CTRL Module:-
FSM_CTRL module reads the data from ROM module. It also sends a START signal to FSM_SPI to start data sampling. The flow chart of FSM_CTRL is shown in Figure 6. Initial value of ADDRESS and START signals are set to 0. ADDRESS is an output signal which specifies the ROM’s address. After data on the ROM specified by address is copied to DATA register, DATA is checked whether it is valid or not at “checking” state. When DATA is 0x00, it means DATA is not valid. In this case, START signal is set to ‘0’. If DATA signal has a valid value and DATA isn’t an “ESC” character, then START signal is set ‘1’ and sent to FSM_SPI module. Until DONE signal becomes high at FSM_SPI module, new data isn’t read from ROM. When DONE signal is ‘1’, START signal goes to low, and ADDRESS is increased in order to read the next data from ROM.
Figure-6:- FSM_CONTROL State Machine.
ROM_MEMORY Module:-
ROM is a kind of another interface between user and design. User applies data to ROM; design reads the user’s data from ROM. In this design, ROM_Memory module is written in Verilog language. Creating a ROM block in Verilog has an advantage since Verilog can read data directly from a .txt file.
CLOCK_DIVIDER Module:-
SPI communication needs CLK clock either read or write data synchronously. CLK clock is generated by FSM_SPI. Since frequency of SCK is selected as 97.66 kHz, FSM_SPI must be clocked at twice of this frequency. Clock divider block has an eight bit counter [7:0] which is clocked at 50 MHz frequency. MSB of that counter has a 195.312 kHz frequency. FSM_CTRL and FSM_SPI modules are running with this 195.312 kHz clock.
SIMULATIONS:-
ISIM provided by Xilinx ISE Design Suite is used to simulate and validate the design. The verification method consists of simulation of individual blocks and testing various commands by top level simulations FSM_SPI module, which is responsible for data transfer, is the first designed part. Outputs of this module are SS, MOSI and SCK for generating SPI signals End of operation is indicated by DONE signal. Inputs of this module are an 8-bit DATA signal for reading data and a begin operation signal indicated by START signal. When START signal becomes high, DATA is copied to the TEMP register. Then the value at the TEMP registers is put at every falling edge of SCK on the MOSI output. A counter tracks number of sent bits. Once DATA transferring is completed successfully, DONE signal becomes high. SS signal depends on the START only at the beginning of whole operation. Its transition from low to high occurs when the operation is finished. Data transferring is verified with simulations of FSM_SPI module and the results are shown in Figures.
FSM_CTRL module is responsible for reading data from ROM and sending a start and DATA signals to FSM_SPI module. Outputs of this module are ADDRESS and START signal. ADDRESS specifies data at ROM. Start of operation indicates with START. Inputs of this module are DATA and DONE signal. DONE is used to increase ADDRESS linearly for every successful read from ROM. Top module is also simulated to verify connections between sub-modules.
Figure-7:- Simulation Result of SPI TOP LEVEL Module.
Figure-8:- Simulation Result of SPI_CONTROL Module.
Figure-9:- Simulation Result of SPI_FSM Module.
Figure-10:- Simulation Result of ROM Memory Block.
Figure-11:- Simulation Result of CLOCK_DIVIDER Module.
VERILOG CODE :-
A)TOP_SPI_MODULE:-
module TOP_SPI_MODULE(
input clr,
input clock,
output mosi,
output sck,
output ss
);
wire [2:0] Address;
wire [7:0] data;
CLOCK_DIVIDER clockdivider ( .clr(clr), .clock(clock), .clk(clk));
ROM rommemory ( .Address(Address), .data(data));
SPI_CONTROL spicontrol ( .data(data), .clr(clr), .clk(clk), .done(done),.Address(Address),.start(start));
FSM_SPI fsmspi ( .data(data),.clr(clr),.start(start),.clk(clk),.done(done),.mosi(mosi),.sck(sck),.ss(ss));
endmodule
B)SPI_CONTROL:-
module SPI_CONTROL(
input [7:0] data,
input clr,
input clk,
input done,
output reg [2:0] Address,
output reg start
);
parameter [1:0] initiall=2'b00,
checking=2'b01,
send=2'b10;
reg [1:0] state;
always @ (posedge clr or posedge clk)
begin
if(clr)
begin
state<=initiall;
end
else
begin
case(state)
initiall: begin
Address<=3'b000;
start<=0;
state<=checking;
end
checking:begin
if(data==8'h00)
begin
Address<=3'b000;
start<=0;
state<=checking;
end
else begin
start<=1;
state<=send;
end
end
send: begin
if(done)
begin
// start<=0;
Address<=Address+3'b001;
state<=checking;
end
else begin
state<=send;
end
end
endcase
end
end
endmodule
C) FSM_SPI Module:-
module FSM_SPI(
input [7:0] data,
input clr,
input start,
input clk,
output reg done,
output reg mosi,
output sck,
output reg ss
);
reg sclk;
integer counter;
reg [7:0] temp;
parameter [2:0] initiall=3'b000,
copy=3'b001,
clk_gen=3'b010,
shifting=3'b011,
completed=3'b100;
reg [2:0] state;
always @ ( posedge clr or posedge clk )
begin
if(clr)
begin
sclk<=1;
done<=0;
state<=initiall;
end
else
begin
case(state)
initiall:if(start)
begin
done<=0;
counter<=0;
ss<=0;
state<=copy;
end
else begin
done<=1;
state<=completed;
end
copy: begin
temp<=data;
state<=clk_gen;
end
clk_gen:begin
sclk=~ sclk;
state<=shifting;
end
shifting:begin
mosi<=temp[7];
temp<={temp[6:0],1'b0};
sclk<=~ sclk;
counter=counter+1;
if(counter==8)
begin
done<=1;
state<=completed;
end
else begin
state<=clk_gen;
end
end
completed:begin
ss<=1;
state<=initiall;
end
endcase
end
end
assign sck=sclk;
endmodule
D) CLOCK_DIVIDER Module:-
module CLOCK_DIVIDER(
input clr,
input clock,
output reg clk
);
integer a=0;
always @ (posedge clock)
begin
if(clr)
begin
clk<=0;
end
else if(a==2)
begin
a=0;
clk=~clk;
end
else begin
a=a+1;
end
end
endmodule
E) ROM Memory Module:-
module ROM(
input [2:0] Address,
output reg [7:0] data
);
reg [7:0] ROM [7:0];
initial begin
ROM[3'b000]=8'h0A;
ROM[3'b001]=8'h0A;
ROM[3'b010]=8'h0B;
ROM[3'b011]=8'h0C;
ROM[3'b100]=8'h0D;
ROM[3'b101]=8'h0E;
ROM[3'b110]=8'h0F;
ROM[3'b111]=8'h10;
//data<=ROM[Address];
end
always @(Address)
begin
data <=ROM[Address];
end
endmodule
Note: This Project can be downloaded from here.
-------------------------------------- ----Happy Learning--------------------------------------------
can you provide testbench for the top module
ReplyDelete