大概描述一下,下面的代码包括三个部分,
spi_slave:完全可综合的SPI从机,地址0处的寄存器最低位为1时进入读模式,该位为0时是正常的写模式,仿真时定义了10个寄存器;
cmd_final:测试代码,用于构建task并发出读写命令,一个SPI伪主机;
spi_slave_tb:testbench,将伪主机和从机连接起来。
module spi_slave(cs,sdi,sdo,sck,data,rst);
input cs,sdi,sck,rst;
output wire sdo;
output wire [79:0]data;
reg [7:0]data_o[9:0];
reg [4:0]counter;
reg [7:0]addr;
reg [7:0]data_buf;
reg [7:0]dout_buf;
wire rw;//读写使能位
assign sdo=dout_buf[7];
assign rw=data_o[0][0];
assign data={data_o[9],data_o[8],data_o[7],data_o[6],data_o[5],data_o[4],data_o[3],data_o[2],data_o[1],data_o[0]};
always@(posedge sck or negedge rst)
begin
if(~rst)begin
addr<=0;
data_buf<=0;
data_o[0]<=0;
data_o[1]<=0;
data_o[2]<=0;
data_o[3]<=0;
data_o[4]<=0;
data_o[5]<=0;
data_o[6]<=0;
data_o[7]<=0;
data_o[8]<=0;
data_o[9]<=0;
dout_buf<=0;
end
else begin
if(counter<=7)
addr<={addr[6:0],sdi};
else begin
if((rw==1)&&(addr!=8'b0))begin
if(counter==8)dout_buf<=data_o[addr];
else dout_buf<={dout_buf[6:0],1'b0};
end
else begin
if((counter>7)||(counter<15))
data_buf<={data_buf[6:0],sdi};
if(counter==15)data_o[addr]<={data_buf[6:0],sdi};//SPI先发送高位,后发送低位,所以应该将数据从buf的低位串行进去。
end
end
end
end
always@(posedge sck or posedge cs)
begin
if(cs)
counter<=0;
else begin
if(counter==16)counter<=16;
else counter<=counter+1;
end
end
endmodule
//Verilog HDL for "jzh", "cmd_v0" "functional"
`timescale 1ns/1ps
module cmd_final(output SCLK,output reg SEN,output reg MOSI,input MISO ,input RST);
// RST is 0 to enable normal operation.
parameter PRD=10;
integer x,y;
reg sclk_mask,clk;
initial
begin
clk=0;
sclk_mask=1;
SEN=1;
end
always #(PRD/2) clk=((~clk)|RST);//generate 5MHz SPI CLK.
assign SCLK=~(clk|(sclk_mask));//此处从机硬件不前置反相器。
/****************************************************/
task spi_write;
input [7:0]addr,num;
integer i;
begin
SEN=0;
# (PRD/4);
//A little waiting is suggested.
@(posedge clk)MOSI<=addr[7];
sclk_mask<=0;
for (i=1;i<=7;i=i+1)
begin
@(posedge clk)MOSI<=addr[7-i];
end
// send addr;
for (i=0;i<=7;i=i+1)
begin
@(posedge clk)MOSI<=num[7-i];
end
//send value.
# (PRD/2);//waiting half period to let the slave capture data.
#(PRD/4) SEN=1;
#(PRD/4) sclk_mask=1;
end
endtask
/*****************************************************/
// BEGIN POINT
always
begin
#20;
wait(~RST);//if not,the task will be run in time 0.
spi_write(8'h00,8'h00);//data format is offset binary.
#300;
spi_write(8'h02,8'hc0);
#100;
spi_write(8'h02,8'b10101010);
#100;
spi_write(8'h06,8'h35);
#100;
spi_write(8'h08,8'h2b);
#100;
spi_write(8'h00,8'h01);//进入读模式
#100;
spi_write(8'h06,8'h01);
#100;
spi_write(8'h02,8'h01);
#100;
spi_write(8'h00,8'h00);//进入写模式
#100;
spi_write(8'h06,8'h01);
//spi_write(8'h2b,8'h60);
#530000000;
spi_write(8'h2d,24);
#5000;
spi_write(8'h2d,30);
#5000;
spi_write(8'h2d,40);
#530000000;
end
endmodule
`timescale 1ns/1ps
module spi_slave_tb ;
wire sdi ;
reg rst ;
wire sck ;
wire [31:0] data ;
wire sdo ;
wire cs ;
spi_slave
slave0 (
.sdi (sdi ) ,
.rst (rst ) ,
.sck (sck) ,
.data (data ) ,
.sdo (sdo ) ,
.cs (cs ) );
cmd_final master0 (
.SCLK (sck),
.SEN (cs),
.MOSI(sdi),
.MISO(sdo),
.RST(rstb) );
initial begin
rst=0;
#50 rst=1;
#3000 $stop;
end
assign rstb=~rst;
endmodule
以上代码可在modelsim中仿真通过。