SPI设备间的数据传输之所以又被称为数据交换,是因为 SPI协议规定一个 SPI设备
不能在数据通信过程中仅仅只充当一个"发送者(Transmitter)“或者"接收者
(Receiver)”.在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据(不管主
设备好还是从设备),相当于该设备有一个 bit 大小的数据被交换了.一个 Slave 设备要想能够
接收到 Master 发过来的控制信号,必须在此之前能够被 Master 设备进行访问。 所以,
Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选,把想要访问的 Slave 设备选上.
在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样,如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致 SPI物理模块最终失效.因此,在程序中一般都会在 SPI传输完数据后,去读取 SPI设备里的数据,即使这
些数据在我们的程序里是无用的(虽然发送后紧接着的读取是无意义的,但仍然需要从寄存器中读出来)。
`timescale 1ns / 1ps
// 主机master
//两个从机 8'd01 8'd02
module top#(parameter SIZE=16)(
input clk ,
input rst_n ,
input [7:0] addr ,
input valid ,
input [SIZE-1:0] data ,
input miso ,//实际中是从机返回主机的数据线 //仿真里面可以用mosi简化代替
output reg[SIZE-1:0]data_c,//对返回的数据线进行数据读取
output wire valid_c,//读取完结束信号
output reg mosi ,
output wire [1:0]cs , //相当于是有2个从机 引脚约束的话是有2个
output reg sclk
);
parameter CLK_DIV=100;
parameter IDEL = 2'b01;
parameter BUSY = 2'b10;
parameter cong1= 2'b01;
parameter cong2= 2'b10;
reg [1:0] state ;
reg fin ;
reg [SIZE-1:0] data_t;
reg [9:0] cunt ;
reg [4:0] cunt_b;
assign valid_c=fin;
always @(posedge clk ) begin
if(state==BUSY&&cunt==49)
data_c<=data_c<<1;
else if(state==BUSY&&cunt==50)
data_c[0]<=mosi;
else
data_c<=data_c;
end
//数据的存储
always @(posedge clk ) begin
if(state==IDEL&&valid==1)
data_t<=data;
else if(state==BUSY&&cunt==49) //取计数中间信号最稳定
data_t<=data_t<<1;
else
data_t<=data_t;
end
//状态的转移
always @(posedge clk ) begin
if(!rst_n)
state<=IDEL;
else if(state==IDEL&&valid==1) //因为直接有地址了所以不需要再延长这个valid信号
state<=BUSY;
else if(state==BUSY&&fin==1)
state<=IDEL;
else
state<=state;
end
//产生一个计数器对时钟周期计数
always @(posedge clk ) begin
if(state==IDEL)
cunt<=0;
else begin
if(cunt==CLK_DIV-1)
cunt<=0;
else
cunt<=cunt+1;
end
end
//对sclk计数
always @(posedge clk ) begin
if(state==IDEL)
cunt_b<=0;
else begin
if(cunt==CLK_DIV-1)
cunt_b<=cunt_b+1;
else
cunt_b<=cunt_b;
end
end
//sclk的产生
always @(posedge clk ) begin
if(state==IDEL)
sclk<=0;
else if(fin==1)
sclk<=0;
else begin
if(cunt<CLK_DIV/2)
sclk<=1;
else
sclk<=0;
end
end
//fin产生
always @(posedge clk) begin
if(cunt==CLK_DIV-1&&cunt_b==15)
fin<=1;
else
fin<=0;
end
//cs的产生
assign cs[0]=(addr==8'd01)?1:0; //从机1
assign cs[1]=(addr==8'd02)?1:0; //从机2
//对mosi的输出
always @(posedge clk ) begin
if(state==IDEL)
mosi<=0;
else begin
if(cunt==1) //或者条件写成cunt==0 表示第一个最高位bit的值 mosi应该拉高还是置低的
mosi<=data_t[SIZE-1];
else
mosi<=mosi;
end
end
endmodule
仿真激励文件
`timescale 1ns / 1ps
module tb(
);
reg clk ;///
reg rst_n;///
reg [7:0] addr ;///
reg valid;///
reg [15:0] data ;///
wire mosi ;
wire [1:0] cs ;
wire sclk ;
initial begin
clk=1 ;
rst_n<=0 ;
#100
rst_n<=1 ;
#100
addr<=8'd02;
valid<=1;
data<=16'b1011_0000_0000_1111;
#20
valid<=0;
end
always #10 clk=~clk ;
top #( . SIZE (16 )
)u_top(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [7:0] */.addr (addr ),
/*input */.valid (valid ),
/*input [SIZE-1:0] */.data (data ),
/*input */.miso ( mosi ),//从机返回主机的数据线
/*output reg[SIZE-1:0]*/.data_c ( ),//对返回的数据线进行数据读取
/*output wire */.valid_c( ),//读取完结束信号
/*output reg */.mosi (mosi ),
/*output wire [1:0] */.cs (cs ), //相当于是有2个从机 引脚约束的话是有2个
/*output reg */.sclk (sclk )
);
rx_spi u_rx1(
/*input */.clk (clk ),
/*input */.rst_n(rst_n),
/*input */.mosi (mosi ),
/*input */.sclk (sclk ),
/*input */.cs (cs[1]), //一位宽 例化的时候比如这个是从机连线就是cs[1]
/*output [7:0]*/.data ( ),
/*output */.valid( )
);
endmodule