当前位置:网站首页>【FPGA】day22-SPI protocol loopback
【FPGA】day22-SPI protocol loopback
2022-08-11 03:42:00 【Spring Breeze Preface】
一、实验概述
1、实验任务
2、SPI协议
(1)简介
SPIProtocol used to configure the chip more,And deal with other high speed data transmission
(2)More than one host from machine
(3)通信原理
MSB:先发高位(与I2C相同)
但SPI不是标准协议,I2C为标准协议
(4)工作模式
二、项目设计
1、项目要求
1、SPI主从机通信;
2、主机模拟FPGA,From machine simulation memory;
3、The host through the command to read or write data from the machine;
4、帧格式–读/写命令(1B)、地址(1B)、数据(XB),写命令-8’ha9;读命令-8’h28.
2、模块框图
三、项目源码
1、顶层模块
module top(
input clk ,
input rst_n ,
input [1:0] key
);
//信号定义
wire key_out ;
wire [7:0] master_dout ;
wire master_dout_vld ;
wire spi_miso ;
wire spi_mosi ;
wire spi_sclk ;
wire spi_cs_n ;
//模块例化
key_debounce #(.KEY_W(3)) u_key(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [KEY_W-1:0] */.key_in (key ),
/*output [KEY_W-1:0] */.key_out (key_out )
);
spi_master u_master(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.start (key_out ),
/*output [7:0] */.dout (master_dout ),
/*output */.dout_vld (master_dout_vld),
/*output */.cs_n (spi_cs_n ),
/*output */.sclk (spi_sclk ),
/*output */.mosi (spi_mosi ),
/*input */.miso (spi_miso )
);
spi_slave u_slave(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*output [7:0] */.dout (slave_dout ),
/*output */.dout_vld (slave_dout_vld ),
/*input */.cs_n (spi_cs_n ),
/*input */.sclk (spi_sclk ),
/*input */.mosi (spi_mosi ),
/*output */.miso (spi_miso )
);
endmodule
2、参数模块
`define CMD_WRITE 8'ha9
`define CMD_READ 8'h28
3、Host top-level module
module spi_master (
input clk ,
input rst_n ,
input write ,
input read ,
output cs_n ,
output sclk ,
output mosi ,
input miso
);
//定义信号
wire req ;
wire [7:0] wr_data ;
wire [7:0] rd_data ;
wire done ;
//模块例化
master_ctrl u_ctrl(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.write (write ),
/*input */.read (read ),
/*output */.req (req ),
/*output [7:0] */.wr_data (wr_data ),
/*input [7:0] */.rd_data (rd_data ),
/*input */.done (done )
);
master_intf u_intf(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.req (req ),
/*input [7:0] */.din (wr_data ),
/*output [7:0] */.dout (rd_data ),
/*output */.done (done ),
/*output */.cs_n (cs_n ),//输出到spi_slave
/*output */.sclk (sclk ),
/*output */.mosi (mosi ),
/*input */.miso (miso )
);
endmodule
4、The host control module
`include "cfg.v"
`timescale 1ns /1ns
module master_ctrl (
input clk ,
input rst_n ,
input write ,
input read ,
output req ,
output [7:0] wr_data ,
input [7:0] rd_data ,
input done
);
//参数定义
localparam IDLE = 4'b0001,
CMD = 4'b0010,
ADDR = 4'b0100,
DATA = 4'b1000;
parameter BYTE_NUM = 8;
//信号定义
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [3:0] cnt_byte ;//字节计数器
wire add_cnt_byte;
wire end_cnt_byte;
reg [3:0] xx ;
reg [7:0] wr_addr ;//写数据地址 256
reg [7:0] rd_addr ;//读数据地址
reg flag ;//Read or write
reg trans_req ;
reg [7:0] trans_data ;
wire idle2cmd ;
wire cmd2addr ;
wire addr2data ;
wire data2idle ;
//FSM
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
state_c <= IDLE ;
end
else begin
state_c <= #1 state_n;
end
end
always @(*) begin
case(state_c)
IDLE :begin
if(idle2cmd)
state_n = CMD ;
else
state_n = state_c ;
end
CMD :begin
if(cmd2addr)
state_n = ADDR ;
else
state_n = state_c ;
end
ADDR :begin
if(addr2data)
state_n = DATA ;
else
state_n = state_c ;
end
DATA :begin
if(data2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
default : state_n = IDLE ;
endcase
end
assign idle2cmd = state_c==IDLE && (write | read);
assign cmd2addr = state_c==CMD && (end_cnt_byte);
assign addr2data = state_c==ADDR && (end_cnt_byte);
assign data2idle = state_c==DATA && (end_cnt_byte);
always @(posedge clk or negedge rst_n) begin //Count the host sending a few bytes
if (rst_n==0) begin
cnt_byte <= 0;
end
else if(add_cnt_byte) begin
if(end_cnt_byte)
cnt_byte <= 0;
else
cnt_byte <= cnt_byte+1 ;
end
end
assign add_cnt_byte = (state_c != IDLE && done);
assign end_cnt_byte = add_cnt_byte && cnt_byte == (xx)-1 ;
always @(*)begin
if(state_c == CMD)begin //发命令
xx = 1;
end
else if(state_c == ADDR)begin //发地址
xx = 1;
end
else begin //发数据
xx = BYTE_NUM;
end
end
//写地址计数器
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
wr_addr <= 0;
end
else if(data2idle & ~flag)begin //写完1次 地址偏移
wr_addr <= wr_addr + BYTE_NUM;
end
end
//读地址计数器
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
rd_addr <= 0;
end
else if(data2idle & flag)begin //读完1次 地址偏移
rd_addr <= rd_addr + BYTE_NUM;
end
end
//flag 读写标志 0:写 1:读
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
flag <= 1'b0;
end
else if(read)begin
flag <= 1'b1;
end
else if(write)begin
flag <= 1'b0;
end
end
//输出
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
trans_req <= 1'b0;
end
else if(idle2cmd | cmd2addr | addr2data)begin
trans_req <= 1'b1;
end
else if(state_c == DATA && add_cnt_byte && ~end_cnt_byte)begin
trans_req <= 1'b1;
end
else begin
trans_req <= 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
trans_data <= 0;
end
else if(idle2cmd)begin //发命令
trans_data <= flag ? `CMD_READ : `CMD_WRITE; //Send written command or read
end
else if(cmd2addr)begin
trans_data <= flag ? rd_addr : wr_addr; //Send written address or read
end
else if(addr2data || state_c == DATA && add_cnt_byte && ~end_cnt_byte)begin //发数据
trans_data <= flag ? 0 : wr_addr + cnt_byte;
end
end
assign req = trans_req;
assign wr_data = trans_data;
endmodule
5、The host interface module
module master_intf(
input clk ,
input rst_n ,
input req ,
input [7:0] din ,
output [7:0] dout ,
output done ,
output cs_n ,//输出到spi_slave
output sclk ,
output mosi ,
input miso
);
//参数定义
localparam IDLE = 3'b001,
TRANS = 3'b010,//发送/采样数据
DONE = 3'b100;
//信号定义
reg [2:0] state_c ;
reg [2:0] state_n ;
reg [4:0] cnt_sclk ;//spi时钟计数器
wire add_cnt_sclk;
wire end_cnt_sclk;
reg [3:0] cnt_bit ;//串并、并串转换
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [7:0] rx_data ;//String and transform the register 采样数据
wire idle2trans ;
wire trans2done ;
wire done2trans ;
wire done2idle ;
reg spi_csn ;//片选
reg spi_sclk ;
reg spi_mosi ;
//FSM
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end
always @(*) begin
case(state_c)
IDLE :begin
if(idle2trans)
state_n = TRANS ;
else
state_n = state_c ;
end
TRANS :begin
if(trans2done)
state_n = DONE ;
else
state_n = state_c ;
end
DONE :begin
if(done2trans)
state_n = TRANS ;
else if(done2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
default : state_n = IDLE ;
endcase
end
assign idle2trans = state_c==IDLE && (req);
assign trans2done = state_c==TRANS && (end_cnt_bit);
assign done2trans = state_c==DONE && (req);
assign done2idle = state_c==DONE && (~req);
//计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_sclk <= 0;
end
else if(add_cnt_sclk) begin
if(end_cnt_sclk)
cnt_sclk <= 0;
else
cnt_sclk <= cnt_sclk+1 ;
end
end
assign add_cnt_sclk = (state_c == TRANS);
assign end_cnt_sclk = add_cnt_sclk && cnt_sclk == (16)-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_bit <= 0;
end
else if(add_cnt_bit) begin
if(end_cnt_bit)
cnt_bit <= 0;
else
cnt_bit <= cnt_bit+1 ;
end
end
assign add_cnt_bit = (end_cnt_sclk);
assign end_cnt_bit = add_cnt_bit && cnt_bit == (8)-1 ;
//输出
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
spi_csn <= 1'b1;
end
else if(idle2trans)begin //Lower before transmission
spi_csn <= 1'b0;
end
else if(done2idle)begin
spi_csn <= 1'b1; //After the completion of the transfer up
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
spi_sclk <= 1'b1;
end
else if(add_cnt_sclk && cnt_sclk == 4-1)begin
spi_sclk <= 1'b0;
end
else if(add_cnt_sclk && cnt_sclk == 12-1)begin
spi_sclk <= 1'b1;
end
end
always @(posedge clk or negedge rst_n)begin //主机输出
if(~rst_n)begin
spi_mosi <= 1'b0;
end
else if(add_cnt_sclk && cnt_sclk == 4-1)begin
spi_mosi <= din[7-cnt_bit];
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
rx_data <= 0;
end
else if(add_cnt_sclk && cnt_sclk == 12-1)begin
rx_data[7-cnt_bit] <= miso;
end
end
assign dout = rx_data;
assign done = trans2done;
assign cs_n = spi_csn;
assign mosi = spi_mosi;
assign sclk = spi_sclk;
endmodule
6、按键消抖模块
module key_debounce #(parameter KEY_W = 3,TIME_20MS = 1000_000)(
input clk ,
input rst_n ,
input [KEY_W-1:0] key_in ,
output reg [KEY_W-1:0] key_out //检测到按下,输出一个周期的高脉冲,其他时刻为0
);
//信号定义
reg [19:0] cnt ;
wire add_cnt ;
wire end_cnt ;
reg add_flag;
reg [KEY_W-1:0] key_r0 ;//同步按键输入
reg [KEY_W-1:0] key_r1 ;//打拍
wire [KEY_W-1:0] nedge ;//检测下降沿
//计数器 检测到下降沿的时候,开启计数器延时20ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = add_flag;
assign end_cnt = add_cnt && cnt == TIME_20MS-1;
//检测到下降沿的时候,拉高计数器计数使能信号,延时结束时,再拉低使能信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
add_flag <= 1'b0;
end
else if(nedge)begin
add_flag <= 1'b1;
end
else if(end_cnt)begin
add_flag <= 1'b0;
end
end
//同步按键输入,并打一拍,以检测下降沿
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= {
KEY_W{
1'b1}};
key_r1 <= {
KEY_W{
1'b1}};
end
else begin
key_r0 <= key_in;//同步
key_r1 <= key_r0;//打拍
end
end
assign nedge = ~key_r0 & key_r1;
//延时20ms结束的时钟周期,输出按键的状态,若按下输出一个周期的高脉冲,否则输出0
[email protected](posedge clk or negedge rst_n)begin
if(~rst_n)begin
key_out <= 0;
end
else begin
key_out <= end_cnt?~key_r1:0;
end
end
endmodule
From the top machine module、控制模块、Interface module no!!!
四、项目 仿真
1、对spiFor the calculation of the host
`timescale 1 ns/1 ns
module master_tb();
//时钟 复位
reg clk ;
reg rst_n ;
//激励信号
reg write ;
reg read ;
reg miso ;
//输出
wire cs_n ;
wire mosi ;
wire sclk ;
//参数定义
parameter CYCLE = 20;
parameter RST_TIME = 3 ;
//模块例化
spi_master u_master(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.write (write ),
/*input */.read (read ),
/*output */.cs_n (cs_n ),
/*output */.sclk (sclk ),
/*output */.mosi (mosi ),
/*input */.miso (miso )
);
//产生时钟
initial begin
clk = 1;
forever #(CYCLE/2)
clk=~clk;
end
//产生复位
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE*RST_TIME);
rst_n = 1;
end
//产生写、读请求
initial begin
#3;
write = 0 ;
read = 0 ;
#(10*CYCLE);
repeat (2) begin //写测试
write = 1'b1;
#(1*CYCLE);
write = 1'b0;
@(posedge u_master.u_ctrl.data2idle) //写完
#(10*CYCLE);
end
repeat (2) begin //读测试
read = 1'b1;
#(1*CYCLE);
read = 1'b0;
@(posedge u_master.u_ctrl.data2idle) //读完
#(10*CYCLE);
end
#(100*CYCLE);
$stop;
end
//miso从机输入
initial begin
#1;
miso = 0 ;
#(10*CYCLE);
forever #(20*CYCLE)
miso = {
$random};
end
endmodule
边栏推荐
- 多串口RS485工业网关BL110
- 【FPGA】设计思路——I2C协议
- 【FPGA】day18-ds18b20实现温度采集
- 论文精度 —— 2017 CVPR《High-Resolution Image Inpainting using Multi-Scale Neural Patch Synthesis》
- [DB operation management/development solution] Shanghai Daoning provides you with an integrated development tool to improve the convenience of work - Orange
- Summary of debugging skills
- Qnet弱网测试工具操作指南
- 程序化交易改变了什么?
- A simple JVM tuning, learn to write it on your resume
- QueryDet: Cascading Sparse Query Accelerates Small Object Detection at High Resolution
猜你喜欢
2022-08-10 The sixth group Hiding spring study notes
C语言之自定义类型------结构体
二叉树相关代码题【较全】C语言
When EasyCVR is connected to the GB28181 device, what is the reason that the device is connected normally but the video cannot be played?
机器学习中什么是集成学习?
Power Cabinet Data Monitoring RTU
Binary tree related code questions [more complete] C language
Day20 FPGA 】 【 - block the I2C read and write EEPROM
Summary of debugging skills
console.log alternatives you didn't know about
随机推荐
What has programmatic trading changed?
STC8H开发(十五): GPIO驱动Ci24R1无线模块
2022-08-10 The sixth group Hiding spring study notes
没想到MySQL还会问这些...
Roewe imax8ev cube battery security, what blackening and swelling are hidden behind it?
C语言之自定义类型------结构体
What problems should we pay attention to when building a programmatic trading system?
【愚公系列】2022年08月 Go教学课程 035-接口和继承和转换与空接口
typedef defines the structure array type
大马驮2石粮食,中马驮1石粮食,两头小马驮一石粮食,要用100匹马,驮100石粮食,如何分配?
互换性与测量技术——表面粗糙度选取和标注方法
The problem that Merge will be lost again after code Revert has been solved
【FPGA】设计思路——I2C协议
图解LeetCode——640. 求解方程(难度:中等)
【FPGA】SDRAM
电力机柜数据监测RTU
Redis老了吗?Redis与Dragonfly性能比较
Is there any way for kingbaseES to not read the system view under sys_catalog by default?
Get the length of the linked list
VIT 源码详解