文章原地址:
http://www.cnblogs.com/fhyfhy/p/4429302.html
SPI通信实验---verilog(FPGA作为从机,使用可读可写)
本实验讲究实用性,故设计思想为:主机先向从机发送地址,若是向从机写入数据,则向从机发送数据,若是读取从机数据,则向从机发送时钟,然后在时钟下降沿读取数据即可。cs信号上升沿作为SPI通信的结束信号。rom程序只是做测试使用。
每次发送16个时钟信号,前八个是地址和命令,后八个是数据。其中:前8个时钟接受的数据的最高位决定着这次通信是读取数据还是写入数据,最高位为1,则是读取数据,为0则是写入数据。
程序:
/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :spi_slave_2.v
** CreateDate :2015.004
** Funtions :spi通信试验。FPGA作为从机,与主机进行通信。先接收主机发来的地址,再根据地址最高位来判断是读数据还是些数据,
然后从机是接收数据还是送出数据。地址最高位为高则是读取数据,否则为写数据.上升沿接收数据,下降沿发送数据
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/
module spi_slave_2 (
clk,
rst_n,
spi_cs,
spi_sck,
spi_miso,
spi_mosi,
spi_over
);
input clk;
input rst_n;
input spi_cs;
input spi_sck;
input spi_mosi;
output reg spi_miso;
output spi_over;
//-----------------------------//
reg spi_cs_2,spi_cs_1;
reg spi_sck_2,spi_sck_1;
reg spi_mosi_2,spi_mosi_1;
wire spi_cs_pos;
wire spi_cs_flag;
wire spi_sck_neg;
wire spi_sck_pos;
wire spi_mosi_flag;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
{spi_cs_2,spi_cs_1} <= 2'b11;
{spi_sck_2,spi_sck_1} <= 2'b00;
{spi_mosi_2,spi_mosi_1} <= 2'b00;
end
else
begin
{spi_cs_2,spi_cs_1} <= {spi_cs_1,spi_cs};
{spi_sck_2,spi_sck_1} <= {spi_sck_1,spi_sck};
{spi_mosi_2,spi_mosi_1} <= {spi_mosi_1,spi_mosi};
end
end
assign spi_cs_pos = ~spi_cs_2 &spi_cs_1;
assign spi_cs_flag = spi_cs_2;
assign spi_sck_neg = ~spi_sck_1&spi_sck_2;
assign spi_sck_pos = ~spi_sck_2&spi_sck_1;
assign spi_mosi_flag = spi_mosi_2;
assign spi_over = spi_cs_pos;
//----------------------------------------//
localparam idel = 4'd0;
localparam rxd_addr = 4'd1;
localparam jude_wr_rd = 4'd2;
localparam rxd_data = 4'd3;
localparam rxd_over = 4'd4;
localparam txd_data = 4'd5;
localparam txd_over = 4'd6;
localparam end_sta = 4'd7;
reg [3:0] state;
reg [3:0] cnt;
reg [7:0] raddr;
reg [7:0] rdata;
reg [7:0] tdata;
reg rover_flag;
reg wover_flag;
reg rd_flag;
wire [7:0] data_out;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= 4'd0;
cnt <= 0;
raddr <= 8'd0;
rdata <= 8'd0;
tdata <= 8'd0;
rover_flag <= 0;
wover_flag <= 0;
rd_flag <= 0;
spi_miso <= 1;
end
else if(!spi_cs_flag)
begin
case(state)
idel:
begin
state <= rxd_addr;
cnt <= 0;
raddr <= 8'd0;
rdata <= 8'd0;
tdata <= 8'd0;
rover_flag <= 0;
wover_flag <= 0;
rd_flag <= 0;
spi_miso <= 1;
end
rxd_addr:
begin
if(cnt == 8)
begin
cnt <= 0;
state <= jude_wr_rd;
end
else if(spi_sck_pos)
begin
cnt <= cnt + 1;
raddr[7 - cnt[2:0]] <= spi_mosi_flag;
end
end
jude_wr_rd:
begin
if(raddr[7] == 1)
state <= rxd_data;
else
begin
state <= txd_data;
rd_flag <= 1;
end
end
rxd_data:
begin
if(cnt == 8)
begin
cnt <= 0;
state <= rxd_over;
end
else if(spi_sck_pos)
begin
cnt <= cnt + 1;
rdata[7 - cnt[2:0]] <= spi_mosi_flag;
end
end
rxd_over:
begin
rover_flag <= 1;
state <= end_sta;
end
txd_data:
begin
tdata <= data_out;
if(cnt == 8)
begin
cnt <= 0;
state <= txd_over;
end
else if(spi_sck_pos)
begin
cnt <= cnt + 1;
spi_miso <= tdata[7 - cnt[2:0]];
end
end
txd_over:
begin
wover_flag <= 1;
state <= end_sta;
end
end_sta:
begin
rover_flag <= 0;
wover_flag <= 0;
state <= end_sta;
end
default:state <= 4'd0;
endcase
end
else
begin
state <= 4'd0;
cnt <= 0;
raddr <= 8'd0;
rdata <= 8'd0;
tdata <= 8'd0;
rover_flag <= 0;
wover_flag <= 0;
rd_flag <= 0;
spi_miso <= 1;
end
end
data_rom data_rom_1 (
.clk(clk),
.rst_n(rst_n),
.wr(rover_flag),
.rd(rd_flag),
.addr(raddr[6:0]),
.data_in(rdata),
.data_out(data_out)
);
endmodule
ROM:
/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :data_rom.v
** CreateDate :2015.04
** Funtions : 简单的数据读写存储程序,配合测试
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/
module data_rom (
clk,
rst_n,
wr,
rd,
addr,
data_in,
data_out
);
input clk;
input rst_n;
input wr;
input rd;
input [6:0] addr;
input [7:0] data_in;
output reg [7:0] data_out;
reg [7:0] table_1 [7:0];
wire [7:0] table_2 [7:0];
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
table_1[7] <= 0;
table_1[6] <= 0;
table_1[5] <= 0;
table_1[4] <= 0;
table_1[3] <= 0;
table_1[2] <= 0;
table_1[1] <= 0;
table_1[0] <= 0;
data_out <= 0;
end
else if(wr)
begin
table_1[addr] <= data_in;
end
else if(rd)
data_out <= table_1[addr];
else
begin
table_1[7] <= table_1[7];
table_1[6] <= table_1[6];
table_1[5] <= table_1[5];
table_1[4] <= table_1[4];
table_1[3] <= table_1[3];
table_1[2] <= table_1[2];
table_1[1] <= table_1[1];
table_1[0] <= table_1[0];
data_out <= data_out;
end
end
assign table_2[7] = table_1[7];
assign table_2[6] = table_1[6];
assign table_2[5] = table_1[5];
assign table_2[4] = table_1[4];
assign table_2[3] = table_1[3];
assign table_2[2] = table_1[2];
assign table_2[1] = table_1[1];
assign table_2[0] = table_1[0];
endmodule
测试程序:
/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :spi_slave_tb.v
** CreateDate :2015.04
** Funtions :测试文件
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/
`timescale 1 ns/1 ns
module spi_slave_tb ;
reg clk;
reg rst_n;
reg spi_cs;
reg spi_sck;
wire spi_miso;
reg spi_mosi;
wire spi_over;
spi_slave_2 spi_slave_2_1(
.clk,
.rst_n,
.spi_cs,
.spi_sck,
.spi_miso,
.spi_mosi,
.spi_over
);
parameter tck = 24;
parameter t = 1000/tck;
always
#(t/2) clk = ~clk;
//-------------------------------
/* 模仿spi主机的发送程序,这个task很好,仿顺序操作,可以直观的显示过程 */
task spi_sd;
input [7:0] data_in;
begin
#(5*t);
spi_sck = 0; spi_mosi= data_in[7]; #(5*t); spi_sck = 1; #(5*t); //send bit[7]
spi_sck = 0; spi_mosi= data_in[6]; #(5*t); spi_sck = 1; #(5*t); //send bit[6]
spi_sck = 0; spi_mosi= data_in[5]; #(5*t); spi_sck = 1; #(5*t); //send bit[5]
spi_sck = 0; spi_mosi= data_in[4]; #(5*t); spi_sck = 1; #(5*t); //send bit[4]
spi_sck = 0; spi_mosi= data_in[3]; #(5*t); spi_sck = 1; #(5*t); //send bit[3]
spi_sck = 0; spi_mosi= data_in[2]; #(5*t); spi_sck = 1; #(5*t); //send bit[2]
spi_sck = 0; spi_mosi= data_in[1]; #(5*t); spi_sck = 1; #(5*t); //send bit[1]
spi_sck = 0; spi_mosi= data_in[0]; #(5*t); spi_sck = 1; #(5*t); //send bit[0]
spi_sck = 0;
end
endtask
initial
begin
clk = 0;
rst_n = 0;
spi_cs = 1;
spi_sck = 0;
spi_mosi = 1;
#(20*t) rst_n = 1;
#(10*t);
spi_cs = 0;
spi_sd(8'h81);
#(50*t);
spi_sd(8'h04);
#(50*t);
#(50*t);
spi_cs = 1;
#(20*t);
spi_cs = 0;
spi_sd(8'h01);
#(50*t);
spi_sd(8'h00);
#(50*t);
spi_cs = 1;
end
endmodule
仿真图:
图中可以看出,第一次输入8‘h81,意味着向01的地址写入数据。第二个数8’h04,则是要写入的数据。然后写入数据8‘h01,则意味着要读取01地址的数据,然后发送8个时钟则是再读取数据。