目录

  • 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相关推荐

  1. 嵌入式硬件协议: SPI串行外设接口 Serial Peripheral Interface

    简介 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是 一种高速全双工的通信总线.它被广泛地使用在ADC.LCD 等设备与M ...

  2. 谈谈SPI (Serial Peripheral Interface,串行外设接口)

    今天我们来一起聊聊谈谈SPI (Serial Peripheral Interface,串行外设接口): 什么是SPI SPI (Serial Peripheral Interface,串行外设接口) ...

  3. arduino教程-9. 串行外设接口(spi)

    文章目录 相关资料 1. spi针脚 Arduino 串行外设接口 串行外设接口简介 板的SPI引脚 SPI.h 库 SPI.h官方示例 SPI为主机 例子 SPI为从机 例子 相关资料 SPI li ...

  4. 同步和串行的区别_谈谈SPI (Serial Peripheral Interface,串行外设接口)

    什么是SPI SPI (Serial Peripheral Interface,串行外设接口)是Motorola 公司推出的一 种同步串行接口技术,是一种高速的,全双工,同步的通信总线: 它以主从方式 ...

  5. 微雪树莓派PICO笔记——7. SPI(串行外设接口)

    文章目录 SPI简介 硬件连接 通讯协议详解 RP2040 SPI 主要参数 RP2040 SPI 逻辑框图 machine.SPI类函数详解 例程地址 代码示例 代码实现 SPI简介 SPI全称为串 ...

  6. DSP SPI串行外设接口

    1.SPI介绍 1.1 SPI简介 SPI的全称是"Serial Peripheral Interface",意为串行外围接口,是Motorola首先在其MC68HCXX系列处理器 ...

  7. 串行外设接口(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 ...

  8. F28335第十一篇——串行外设接口(SPI)

    摘要 本文大致介绍了F28335中SPI工作原理和大致寄存器.还有很多细节知识没有列出,需要详细了解的同学,可以参考TI官方文档(TI官网免费下载),或者可以看书籍.重点推荐符晓编写的<TMS3 ...

  9. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架

    platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...

最新文章

  1. HTTP 协议入门 — (TCP/IP协议族、通信传输流、URI 与 URL 的区别、Cookie 状态管理、HTTP 支持的方法、状态码类别、HTTP 首部字段)
  2. Python中相见恨晚的技巧(记得收藏)
  3. psp用ps1模拟器_电脑上ps1和fc模拟器资源下载,包含当年ps1上的西游记和霸王的大陆复刻版...
  4. 多核之后,CPU 的发展方向是什么?中科院计算所包云岗提 20 点新思考
  5. VS 2017开发ASP.NET Core Web应用过程中发现的一个重大Bug
  6. 计算机打不出汉字怎么办,电脑打不出字怎么办,教您电脑打不出字怎么解决
  7. myfunc matlab,为matlab匿名函数设置’help’
  8. asp.net Framework 与 asp.net core 知识
  9. python中求众数_Python实现求众数的三种方法
  10. 谷歌Google搜索语法
  11. (转)计算机组成与结构:原码、反码、补码、移码、二进制乘除法运算
  12. 中国首个超250米高“空中连廊”幕墙工程合拢 创多项世界之最
  13. 【Devc++】战斗1.0.1
  14. adb: failed to install xxx Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]
  15. Arduino 播放音乐教程
  16. arm汇编和c语言混合编程实验报告,arm实验ARM汇编和C语言混合编程.doc
  17. 西北大学电影专硕考研考情与难度、参考书及上岸前辈备考经验
  18. 【踩坑实录】hive删除字段报错
  19. 量子计算机的电力,无需电力:基于磁的自旋电子计算机可以匹敌量子计算机的原始计算能力...
  20. CC2541修改蓝牙名称为中文名称

热门文章

  1. 计算机听不到音乐怎么回事,电脑听不到声音怎么办
  2. 移动端性能测试必备工具PerfDog性能狗
  3. 一看即懂的TCP首部确认号和序列号解析!!!
  4. 鲍鱼数据集数据分析和可视化,线性回归预测鲍鱼年龄(基于TensorFlow)
  5. Android常用技巧总结
  6. pboc2.0证书解析
  7. 斯考特·杨(Scott Young)快速学习方法
  8. Spring Boot项目开发流程
  9. 剑客vs刀客 Java vs .NET
  10. SAP所有模块用户出口(User Exits) 一