verilog实现SPI从机

大概描述一下,下面的代码包括三个部分,

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中仿真通过。

 

你可能感兴趣的:(数字电路设计)