SPI的通信试验 --verilog (从机-全双工)

时间:2024-12-02 13:07:14

SPI的 有关知识参考FPGA作为主机的通信实验。

本实验中FPGA作为从机通过SPI与MCU等通信的试验,可以在时钟上升沿接收数据并且在时钟下降沿发送数据,模仿全双工模式。接收的

数据作为地址,通过读取ROM中地址的数据然后发送出去。注意 发送完成以及接收完成之后的数据处理的关系。

程序:

顶层文件:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :spi_slave_send.v
** CreateDate :2015.04
** Funtions :FPGA作为从机在接收主机发来的地址信号时,输出数据信号,同时还接收主机发来的地址数据(全双工),一直到CS线拉高,没有信号再从主机中发出。
数据地址都为8bit,spi的时钟在发送完8位之后最好能够间隔一下再发生另外8个时钟
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/ module spi_slave (
clk,
rst_n, spi_cs,
spi_sck,
spi_miso,
spi_mosi, spi_over
);
input clk;
input rst_n; input spi_cs;
input spi_sck;
output spi_miso;
input spi_mosi; output spi_over; //-----------------------------
wire [:] rdata;
wire rover;
wire txd_en;
wire [:] txd_data;
wire txd_over;
spi_slave_rxd spi_slave_rxd_1(
.clk(clk),
.rst_n(rst_n), .spi_cs(spi_cs),
.spi_sck(spi_sck),
.spi_miso(),
.spi_mosi(spi_mosi), .rdata_out(rdata),
.rover(rover)
); data_read data_read_1(
.clk(clk),
.rst_n(rst_n), .rover(rover),
.rdata(rdata), .txd_en(txd_en),
.txd_data(txd_data) );
spi_slave_txd spi_slave_txd_1(
.clk(clk),
.rst_n(rst_n), .txd_en(txd_en),
.txd_data(txd_data), .spi_cs(spi_cs),
.spi_sck(spi_sck),
.spi_mosi(spi_mosi),
.spi_miso(spi_miso), .spi_over(spi_over),
.txd_over(txd_over)
); endmodule

接收程序:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :SPI_slave_rd.v
** CreateDate :2015.04
** 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_rxd (
clk,
rst_n, spi_cs,
spi_sck,
spi_miso,
spi_mosi, rdata_out,
rover
);
input clk;
input rst_n; input spi_cs;
input spi_sck;
input spi_mosi;
output spi_miso; output reg [:] rdata_out;
output reg rover;
//---------------------------------------
reg spi_cs_0,spi_cs_1; /* 延时两个时钟,配合检测时钟边沿 */
reg spi_sck_0,spi_sck_1;
reg spi_mosi_0,spi_mosi_1;
wire spi_sck_pos;
// wire spi_sck_neg;
wire spi_cs_pos;
wire spi_cs_neg;
wire spi_cs_flag;
wire spi_miso_flag;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
{spi_cs_1,spi_cs_0} <= 'b11;
{spi_sck_1,spi_sck_0} <= 'b00;
{spi_mosi_1,spi_mosi_0} <= 'b00;
end
else
begin
{spi_cs_1,spi_cs_0} <= {spi_cs_0,spi_cs};
{spi_sck_1,spi_sck_0} <= {spi_sck_0,spi_sck};
{spi_mosi_1,spi_mosi_0} <= {spi_mosi_0,spi_mosi};
end
end
assign spi_sck_pos = ~spi_sck_1 &spi_sck_0; /* 取上升沿 */
// assign spi_sck_neg = ~spi_sck_0 &spi_sck_1; /* 取下降沿 */
assign spi_cs_pos = ~spi_cs_1&spi_cs_0; /* 取spi_cs上升沿,作为结束信号 */
assign spi_cs_neg = ~spi_cs_0&spi_cs_1; /* 取spi_cs下降沿,作为开始信号 */
assign spi_cs_flag = spi_cs_1;
assign spi_miso_flag = spi_mosi_1;
//----------------------------------------------------------
localparam idel = 'd0;
localparam rxd_sta = 'd1;
localparam rxd_over = 'd2;
reg [:] cnt;
reg [:] rdata;
reg [:] state;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt <= ;
rdata <= ;
state <= idel;
rover <= ;
rdata_out <= ;
end
else
begin
case(state)
idel:
begin
cnt <= ;
rdata <= ;
rover <= ;
rdata_out <= ;
if(spi_cs_neg)
state <= rxd_sta;
else
state <= idel;
end
rxd_sta:
begin
if(spi_cs_flag)
state <= idel;
else
begin
// if((cnt == 8)&&(spi_sck_neg)) /* 更严谨 */
if((cnt == ))
begin
cnt <= ;
state <= rxd_over;
rdata_out <= rdata;
rover <= ;
end
else
begin
if(spi_sck_pos)
begin
cnt <= cnt + ; /* 最后cnt=8 */
rdata['d7 - cnt] <= spi_miso_flag; /* 从最高位到最低位逐渐接收数据 */
end
else
begin
cnt <= cnt ;
rdata <= rdata;
end
end
end
end
rxd_over:
begin
rover<= ;
state <= rxd_sta;
end
default : state <= idel;
endcase
end
end // assign rdata_out = rdata;
// else if(!psi_cs_flag)
// begin
// if(spi_sck_pos)
// begin
// cnt <= cnt + 1; /* 最后cnt=8 */
// rdata[4'd7 - cnt] <= spi_miso_flag; /* 从最高位到最低位逐渐接收数据 */
// end
// else
// begin
// cnt <= cnt ;
// rdata <= rdata;
// end
// end
// else
// begin
// cnt <= 0 ;
// rdata <= rdata; /* 注意:保持,不清除 */
// end
// end
//
// //------------------------
// always @(posedge clk or negedge rst_n)
// begin
// if(!rst_n)
// begin
// rdata_out <= 8'd0;
// rover <= 0;
// end
// else if(spi_cs_pos)
// begin
// rdata_out <= rdata; /* 赋值 */
// rover <= 1; /* 置位 */
// end
// else
// begin
// rdata_out <= rdata_out; /* 注意:保持,不清除 */
// rover <= 0; /* 清除 */
// end
// end endmodule

数据转换程序:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :data_read.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_read(
clk,
rst_n, rover,
rdata, txd_en,
txd_data ); input clk;
input rst_n; input rover;
input [:] rdata; output txd_en;
output [:] txd_data; //------------------------------//
reg r_over_1;
reg [:] r_addr;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
r_addr <= ;
r_over_1 <= ;
end
else if(rover)
begin
r_addr <= rdata;
r_over_1 <= ;
end
else if(txd_en)
r_over_1 <= ;
end reg r_over_1_1;
reg r_over_1_2;
reg r_over_1_3;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
{r_over_1_3,r_over_1_2,r_over_1_1} <= 'b000;
end
else
begin
{r_over_1_3,r_over_1_2,r_over_1_1} <= {r_over_1_2,r_over_1_1,r_over_1};
end
end
assign txd_en = ~r_over_1_3&r_over_1_2; data_rom data_rom_1(
.addr(r_addr),
.data(txd_data)
);
endmodule

rom程序:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :data_rom.v
** CreateDate :2015.04
** Funtions : 简单 rom 文件
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/ module data_rom (
addr, data );
input [:] addr;
output [:] data; // always @(*)
// begin
// case()
// end
assign data = addr + ; endmodule

发送程序:

    /********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :spi_slave_txd.v
** CreateDate :2015.04
** Funtions :FPGA作为从机的发送数据程序
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/ module spi_slave_txd (
clk,
rst_n, txd_en,
txd_data, spi_cs,
spi_sck,
spi_mosi,
spi_miso, spi_over,
txd_over
);
input clk;
input rst_n; input txd_en;
input [:] txd_data;
input spi_cs;
input spi_sck;
input spi_mosi;
output reg spi_miso; output spi_over;
output reg txd_over; //----------------------------------
reg spi_cs_0,spi_cs_1;
reg spi_sck_0,spi_sck_1;
wire spi_sck_neg;
wire spi_over;
wire spi_cs_flag;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
{spi_cs_1,spi_cs_0} <= 'b11;
{spi_sck_1,spi_sck_0} <= 'b00;
end
else
begin
{spi_cs_1,spi_cs_0} <= {spi_cs_0,spi_cs};
{spi_sck_1,spi_sck_0} <= {spi_sck_0,spi_sck};
end
end assign spi_cs_flag = spi_cs_1;
assign spi_sck_neg = (spi_sck_1&(~spi_sck_0));
assign spi_over = ~spi_cs_1&spi_cs_0;
//---------------------------------------------//
localparam idel = 'd0;
localparam txd_sta= 'd2;
localparam txd_data_sta = 'd1;
reg [:] state;
reg [:] cnt;
reg [:] txdata; always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= idel;
cnt <= ;
txdata <= ;
spi_miso <= ; /* 拉高 */
end
else
begin
case(state)
idel:
begin
cnt <= ;
txdata <= ;
spi_miso <= ; /* 拉高 */
if(txd_en)
begin
state <= txd_data_sta; end
else
begin
state <= idel;
end
end
txd_data_sta:
begin
txdata <= txd_data;
state <= txd_sta;
txd_over <=;
end
txd_sta:
begin
if(spi_cs_flag )
state <= idel;
else if(cnt == )
begin
cnt <= ;
txd_over <= ;
state <= txd_data_sta;
end
else
begin
if(spi_sck_neg)
begin
spi_miso <= txdata[-cnt[:]] ; /* 先高位再低位传输 */
cnt <= cnt +; /* 范围:0-8 */
end
else
begin
spi_miso <= spi_miso; /* 保持 */
cnt <= cnt;
end
end
end
default:state <= idel;
endcase
end
end 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 ns/ 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 spi_slave_1(
.clk,
.rst_n, .spi_cs,
.spi_sck,
.spi_miso,
.spi_mosi, .spi_over
); parameter tck = ;
parameter t = /tck; always
#(t/) clk = ~clk; //-------------------------------
/* 模仿spi主机的发送程序,这个task很好,仿顺序操作,可以直观的显示过程 */
task spi_sd;
input [:] data_in;
begin
#(*t); spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[7]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[6]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[5]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[4]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[3]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[2]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[1]
spi_sck = ; spi_mosi= data_in[]; #(*t); spi_sck = ; #(*t); //send bit[0]
spi_sck = ; end
endtask initial
begin
clk = ;
rst_n = ;
spi_cs = ;
spi_sck = ;
spi_mosi = ; #(*t) rst_n = ;
#(*t);
spi_cs = ;
spi_sd('h01);
#(*t);
spi_sd('h04);
#(*t);
spi_sd('h00);
#(*t);
spi_cs = ;
end endmodule

仿真图:

SPI的通信试验 --verilog (从机-全双工)