串行外设接口(Serial Peripheral Interface, SPI)逻辑设计部分 - spi_slave
目录
- 1. (csn == 1'b1) 时
- 1.1. spi_slave 完成 Master读写握手 的最短时间
- 1.2. spi_slave 完成 Slave读写握手 的最短时间
- 2. (csn == 1'b0) 时
- (CPOL ^ CPHA == 0)时
- (CPOL ^ CPHA == 1)时
- 3. 信号的复位
- 4. 代码
先设计再写代码!先设计再写代码!先设计再写代码!
设计时候要画波形!画波形!还是TMD画波形!
spi slave最大的难度在于它是一个slave,但是与spi master之间没有握手信号,这就导致每次与spi master之间作数据交换的时候,spi slave可能还没准备好,数据传输的可靠性不行,这也是SPI本身的缺陷。
片选信号拉低表示数据传输开始,可是如果此时spi还没有把上一次传输的数据输出、或是这次发送给spi master的数据还没有准备好,就会产生意料不到的情况。
spi slave是否开启传输,这就是spi slave的两种状态,奇葩的是这两种状态的转换是spi master控制的,而且开启传输的时刻spi slave未知。对,咱们要基于这样的状态机对spi slave进行设计。
当spi slave 检测到 被片选中后,可以用计数器计算什么时候这次传输结束。
1. (csn == 1’b1) 时
此时不发生传输,spi slave 可以与用户端进行数据交换,但是csn随时都可能拉低,这里咱们计算一下spi slave与用户端完成读写的最快时间
也就是说,如果spi master两次传输的时间间隔小于这个最快时间,就必定会使spi master与spi slave交换的数据无法被用户那边读出、写入新数据。
所以可以让spi master在每次完成传输之后等待一段时间再开启下一次传输,这就需要spi master与spi slave相互配合。
1.1. spi_slave 完成 Master读写握手 的最短时间
即spi_slave与spi_master完成一次传输后,将从spi_master收到的新数据写给用户、并从用户那边读出下一次发送给spi_master的数据,所使用的最短时间。
无论是spi slave通过异步FIFO与用户端作跨时钟域隔离,还是直接与sck域模块作交互,都一样。
那么什么样的时序对应Master读写握手最短时间呢?
以与异步FIFO交互为例
在与spi master交互完成之后,得先把移位寄存器中的新数据写出去,然后才能让新数据给移位寄存器。
所以在移位寄存器移位结束立刻拉高wr_en,此时最快的情况应该是FIFO那边的full就为低,所以一拍就将移位寄存器中的数据写入FIFO。
由于Master读新数据需要两拍,所以最快情况应该是在与spi master交互结束之后立刻拉高rd_en,在下一拍就将rdata读入移位寄存器。
当CPOL ^ CPHA == 0时,移位寄存器除0bit外其他位都要在下降沿读新数据,但第0bit还是得在上升沿读取,所以是两拍。
当CPOL ^ CPHA == 1时,移位寄存器都是在上升沿读取,所以也是两拍。
1.2. spi_slave 完成 Slave读写握手 的最短时间
如果是Slave读写,也是要求spi_slave先把获取的数据发给用户端,再从用户端获取新的数据,时序图如下
2. (csn == 1’b0) 时
在csn拉低时表示与spi master进行寄存器数据交换,整个过程与spi master类似,也是根据CPOL和CPHA两种模式进行移位,也需要移位寄存器来判断什么时候csn拉高。
(CPOL ^ CPHA == 0)时
即对应{CPOL,CPHA} == 2'b00 || 2'b11;
时,此时是sck上升沿采样mosi至移位寄存器、sck下降沿驱动miso,大端模式
别忘了在sck上升沿处驱动csn
的拉低,csn == 1'b0
的第一个沿是下降沿,即先驱动、再采样。
注意移位寄存器不要违背触发器单沿触发的设计要求
即移位寄存器赋值data_shift[7:1] <= tx_data[7:1];
、移位都是在下降沿进行。而采样data_shift[0] <= mosi;
则是在上升沿进行
那么判断传输结束还是需要计数器,由于驱动miso是在下降沿,所以计数器也是在下降沿,波形图与spi master一样。
(CPOL ^ CPHA == 1)时
即对应{CPOL,CPHA} == 2'b10 || 2'b01;
时,此时是sck下降沿采样mosi至移位寄存器、sck上升沿驱动miso,大端模式
同样是先采样、再驱动。先采样的话,data_shift
就需要额外1bit存储这个先采样来的值,如下图
注意这种情况下没有违背单沿触发要求,因为读取新数据data_shift[8:1] <= tx_rdata;
或是驱动移位data_shift << 1
均在posedge sck
时刻
传输结束的过程也是使用计数器,如下图
这样就可以直到什么时候csn拉高,进而与用户端的某些交互信号就可以按时拉高。
3. 信号的复位
在实际仿真过程中发现spi slave中的信号有时未被复位、一直为x的情况,为什么呢?分析下
按理说,在rstn为低时应该完成了复位,结果波形显示复位失败,原因只有一个——sck没有了。
原因就是,rstn不仅对spi slave中的信号复位,还在sck产生模块baud_clk_gen中也对sck进行了复位,那么在rstn为低期间sck才由1’bx变为恒定值,sck压根就没有上升沿或下降沿,因此spi slave中的信号一直为x。
例如CLK_FREQ/BAUD_RATE = 2、CPOL=1时波形如下
可见posedge sck或negedge sck时刻,从未有rstn为低的时候,故spi slave中的信号就不会被复位
解决方法是分别为sck和spi模块各设置一个复位信号,这样的话先对sck时钟进行复位,保证sck能够正确产生,再对spi_master和spi_slave进行同步复位,保证spi模块的信号能够正确产生。
例如
4. 代码
module spi_slave#(parameter CHIP_SEL_NUM = 3,parameter DATA_WIDTH = 16,parameter ASYNC_FIFO_WIDTH = 4096,parameter CPOL = 0,parameter CPHA = 1)(input rstn,input clk,input [DATA_WIDTH-1:0] tx_data,input tx_data_val,output tx_fifo_full,input rx_req,output [DATA_WIDTH-1:0] rx_data,output rx_data_val,output rx_fifo_empty,input sck,input mosi,output miso,input csn);localparam BIT_CNT_WIDTH = $clog2(DATA_WIDTH+1);reg tx_fifo_rd_en;
wire [DATA_WIDTH-1:0] tx_fifo_rdata;
wire tx_fifo_rdata_val;
wire tx_fifo_empty;
reg miso_r;
wire [DATA_WIDTH-1:0] rx_fifo_rdata;
reg rx_fifo_wr_en;
wire rx_fifo_full;
reg [BIT_CNT_WIDTH-1:0] bit_cnt;generate if(CPOL ^ CPHA == 1'b0) beginreg [DATA_WIDTH-1:0] data_shift;always@(negedge sck) beginif(!rstn)data_shift[DATA_WIDTH-1:1] <= 'b0;else if(csn) beginif(tx_fifo_rdata_val)data_shift[DATA_WIDTH-1:1] <= tx_fifo_rdata[DATA_WIDTH-1:1];endelsedata_shift[DATA_WIDTH-1:1] <= data_shift[DATA_WIDTH-2:0];endalways@(posedge sck) beginif(!rstn)data_shift[0] <= 1'b0;else if(csn) beginif(tx_fifo_rdata_val)data_shift[0] <= tx_fifo_rdata[0];endelsedata_shift[0] <= mosi;endassign rx_fifo_rdata = data_shift;always@(negedge sck) beginif(!rstn)miso_r <= 1'b0;else if(!csn) miso_r <= data_shift[DATA_WIDTH-1];endassign miso = miso_r;always@(negedge sck) beginif(!rstn)bit_cnt <= DATA_WIDTH;else if(!csn)bit_cnt <= bit_cnt - 1;elsebit_cnt <= DATA_WIDTH;endalways@(posedge sck) beginif(!rstn)rx_fifo_wr_en <= 1'b0;else if(csn) beginif(rx_fifo_wr_en && !rx_fifo_full)rx_fifo_wr_en <= 1'b0;endelse if(bit_cnt == 0)rx_fifo_wr_en <= 1'b1;endalways@(posedge sck) beginif(!rstn)tx_fifo_rd_en <= 1'b1;else if(csn) beginif(tx_fifo_rd_en && !tx_fifo_empty)tx_fifo_rd_en <= 1'b0;endelse if(bit_cnt == 0) tx_fifo_rd_en <= 1'b1;endend
else beginreg [DATA_WIDTH:0] data_shift;always@(posedge sck) beginif(!rstn)data_shift[DATA_WIDTH:1] <= 'b0;else if(csn) beginif(tx_fifo_rdata_val)data_shift[DATA_WIDTH:1] <= tx_fifo_rdata;endelsedata_shift[DATA_WIDTH:1] <= data_shift[DATA_WIDTH-1:0];endassign rx_fifo_rdata = data_shift[DATA_WIDTH:1];always@(negedge sck) beginif(!rstn)data_shift[0] <= 1'b0;else if(!csn) data_shift[0] <= mosi;endalways@(posedge sck) beginif(!rstn)miso_r <= 1'b0;else if(!csn) miso_r <= data_shift[DATA_WIDTH];endassign miso = miso_r;always@(posedge sck) beginif(!rstn)bit_cnt <= DATA_WIDTH;else if(!csn)bit_cnt <= bit_cnt - 1;elsebit_cnt <= DATA_WIDTH;endalways@(posedge sck) beginif(!rstn)rx_fifo_wr_en <= 1'b0;else if(csn) beginif(rx_fifo_wr_en && !rx_fifo_full)rx_fifo_wr_en <= 1'b0;endelse if(bit_cnt == 1)rx_fifo_wr_en <= 1'b1;endalways@(posedge sck) beginif(!rstn)tx_fifo_rd_en <= 1'b1;else if(csn) beginif(tx_fifo_rd_en && !tx_fifo_empty)tx_fifo_rd_en <= 1'b0;endelse if(bit_cnt == 1) tx_fifo_rd_en <= 1'b1;endendendgeneratelocalparam ADDR_WIDTH = $clog2(ASYNC_FIFO_WIDTH/DATA_WIDTH);async_fifo#(.ASYNC_FIFO_DEPTH (ASYNC_FIFO_WIDTH ),.WDATA_WIDTH (DATA_WIDTH ),.RDATA_WIDTH (DATA_WIDTH ),.PROG_DEPTH (ASYNC_FIFO_WIDTH ),.ADDR_WIDTH (ADDR_WIDTH ))tx_async_fifo(.rstn (rstn ),.wclk (clk ),.wdata ( tx_data ),.wr_en ( tx_data_val ),.full ( tx_fifo_full ),.rclk ( sck ),.rd_en ( tx_fifo_rd_en ),.rdata ( tx_fifo_rdata ),.valid ( tx_fifo_rdata_val ),.empty ( tx_fifo_empty ),.pfull ( ));async_fifo#(.ASYNC_FIFO_DEPTH (ASYNC_FIFO_WIDTH ),.WDATA_WIDTH (DATA_WIDTH ),.RDATA_WIDTH (DATA_WIDTH ),.PROG_DEPTH (ASYNC_FIFO_WIDTH ),.ADDR_WIDTH (ADDR_WIDTH ))rx_async_fifo(.rstn (rstn ),.wclk (sck ),.wdata ( rx_fifo_rdata ),.wr_en (rx_fifo_wr_en ),.full (rx_fifo_full ),.rclk ( clk ),.rd_en ( rx_req ),.rdata ( rx_data ),.valid ( rx_data_val ),.empty ( rx_fifo_empty ),.pfull ( ));endmodule
串行外设接口(Serial Peripheral Interface, SPI)逻辑设计部分 - spi_slave相关推荐
- 嵌入式硬件协议: SPI串行外设接口 Serial Peripheral Interface
简介 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是 一种高速全双工的通信总线.它被广泛地使用在ADC.LCD 等设备与M ...
- 谈谈SPI (Serial Peripheral Interface,串行外设接口)
今天我们来一起聊聊谈谈SPI (Serial Peripheral Interface,串行外设接口): 什么是SPI SPI (Serial Peripheral Interface,串行外设接口) ...
- arduino教程-9. 串行外设接口(spi)
文章目录 相关资料 1. spi针脚 Arduino 串行外设接口 串行外设接口简介 板的SPI引脚 SPI.h 库 SPI.h官方示例 SPI为主机 例子 SPI为从机 例子 相关资料 SPI li ...
- 同步和串行的区别_谈谈SPI (Serial Peripheral Interface,串行外设接口)
什么是SPI SPI (Serial Peripheral Interface,串行外设接口)是Motorola 公司推出的一 种同步串行接口技术,是一种高速的,全双工,同步的通信总线: 它以主从方式 ...
- 微雪树莓派PICO笔记——7. SPI(串行外设接口)
文章目录 SPI简介 硬件连接 通讯协议详解 RP2040 SPI 主要参数 RP2040 SPI 逻辑框图 machine.SPI类函数详解 例程地址 代码示例 代码实现 SPI简介 SPI全称为串 ...
- DSP SPI串行外设接口
1.SPI介绍 1.1 SPI简介 SPI的全称是"Serial Peripheral Interface",意为串行外围接口,是Motorola首先在其MC68HCXX系列处理器 ...
- 串行外设接口(Serial Peripheral Interface, SPI)逻辑设计部分 - spi_master
目录 1. baud_clk_gen 1.1. 代码 2. spi_master 2.1. IDLE:等待写入 2.2. WAIT_CS:等待选通 2.3. TRANS:传输 (CPOL ^ CPHA ...
- F28335第十一篇——串行外设接口(SPI)
摘要 本文大致介绍了F28335中SPI工作原理和大致寄存器.还有很多细节知识没有列出,需要详细了解的同学,可以参考TI官方文档(TI官网免费下载),或者可以看书籍.重点推荐符晓编写的<TMS3 ...
- platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架
platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...
最新文章
- HTTP 协议入门 — (TCP/IP协议族、通信传输流、URI 与 URL 的区别、Cookie 状态管理、HTTP 支持的方法、状态码类别、HTTP 首部字段)
- Python中相见恨晚的技巧(记得收藏)
- psp用ps1模拟器_电脑上ps1和fc模拟器资源下载,包含当年ps1上的西游记和霸王的大陆复刻版...
- 多核之后,CPU 的发展方向是什么?中科院计算所包云岗提 20 点新思考
- VS 2017开发ASP.NET Core Web应用过程中发现的一个重大Bug
- 计算机打不出汉字怎么办,电脑打不出字怎么办,教您电脑打不出字怎么解决
- myfunc matlab,为matlab匿名函数设置’help’
- asp.net Framework 与 asp.net core 知识
- python中求众数_Python实现求众数的三种方法
- 谷歌Google搜索语法
- (转)计算机组成与结构:原码、反码、补码、移码、二进制乘除法运算
- 中国首个超250米高“空中连廊”幕墙工程合拢 创多项世界之最
- 【Devc++】战斗1.0.1
- adb: failed to install xxx Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]
- Arduino 播放音乐教程
- arm汇编和c语言混合编程实验报告,arm实验ARM汇编和C语言混合编程.doc
- 西北大学电影专硕考研考情与难度、参考书及上岸前辈备考经验
- 【踩坑实录】hive删除字段报错
- 量子计算机的电力,无需电力:基于磁的自旋电子计算机可以匹敌量子计算机的原始计算能力...
- CC2541修改蓝牙名称为中文名称