跳至正文

Verilog功能模块–SPI主机和从机(02)–SPI主机设计思路与代码解析

前言

上一篇文章介绍了SPI的四种工作模式及其时序特性,相信各位同学已经掌握了SPI通信的核心原理。

本文用纯Verilog设计了功能完整的4线SPI主机,并详细说明了模块编码思路和使用注意事项,最后分享了源码。

一、模块功能

本Verilog功能模块——SPI主机实现了SPI协议要求的完整时序控制,具体功能如下:

  1. 支持所有4种SPI工作模式

  2. 支持任意数据位宽

  3. 支持任意串行时钟频率fsclk

  4. 支持指定CS下降沿到第一个SCLK边沿的延时

  5. 支持指定最后SCLK边沿到CS上升沿的延时

  6. 支持指定CS高电平持续时间

二、模块框图

三、信号接口

3.1 参数列表

参数名 类型 默认值 说明
SPI_MODE integer 3 SPI模式, 可选0, 1, 2, 3 (默认)
DATA_WIDTH integer 16 单次通信发送或接收数据的位宽, 最小为2, 常见8/16
SCLK_PERIOD_CLK_NUM integer 4 fSCLK, SCLK周期对应CLK数, 必须为偶数, 最小为2
CS_EDGE_TO_SCLK_EDGE_CLK_NUM integer 1 TCC, CS_N下降沿到SCLK的第一个边沿对应CLK数, 最小为1
SCLK_EDGE_TO_CS_EDGE_CLK_NUM integer 3 TCCH, 最后一个SCLK边沿到CS_N上升沿对应CLK数, 最小为3
CS_KEEP_HIGH_CLK_NUM integer 2 TCWH, CS_N低电平后保持高电平的时间对应CLK数, 最小为2
CLK_FREQ_MHZ integer 100 模块工作时钟, 常用100/120

3.2 接口信号列表

信号分组 信号名 方向 说明
外部控制SPI信号 spi_begin input SPI单次通信开始, 高电平有效, 仅在spi_is_busy为低时起作用
spi_end output SPI单次通信结束, 高电平有效, 只会持续一个时钟周期
spi_is_busy output SPI繁忙指示, 高电平表示SPI正在工作
spi_master_tx_data[DATA_WIDTH-1:0] input SPI发送数据, 数据总是高位先发
spi_master_rx_data[DATA_WIDTH-1:0] output SPI接收数据, 最先读出的数据在最高位
spi_master_rx_data_valid output SPI接收数据有效指示,高电平有效
SPI硬线链接 spi_cs_n output 片选, 低电平有效
spi_sclk output SPI时钟, 主机提供
spi_mosi output 主机输出从机输入
spi_miso input 主机输入从机输出
时钟与复位 clk input 模块工作时钟
rstn input 模块复位, 低电平有效

四、编码思路

  1. 通过参数控制SPI模式、数据位宽、时钟频率与三种时序参数

    parameter integer SPI_MODE                     = 3,  // SPI模式, 可选0, 1, 2, 3 (默认)
    parameter integer DATA_WIDTH                   = 16// 单次通信发送或接收数据的位宽, 最小为2, 常见8/16
    parameter integer SCLK_PERIOD_CLK_NUM          = 4,  // fSCLK, SCLK周期对应CLK数, 必须为偶数, 最小为2
    parameter integer CS_EDGE_TO_SCLK_EDGE_CLK_NUM = 1,  // TCC, CS_N下降沿到SCLK的第一个边沿对应CLK数, 最小为1
    parameter integer SCLK_EDGE_TO_CS_EDGE_CLK_NUM = 3,  // TCCH, 最后一个SCLK边沿到CS_N上升沿对应CLK数, 最小为3
    parameter integer CS_KEEP_HIGH_CLK_NUM         = 2,  // TCWH, CS_N低电平后保持高电平的时间对应CLK数, 最小为2
    parameter integer CLK_FREQ_MHZ                 = 100 // 模块工作时钟, 常用100/120

    注意这里的时延并不是真实值,而是以模块时钟CLK为单位的相对值,且因为三段式状态机输出时序逻辑的限制,时延参数并不能到0,有最小值的限制。这对于实际应用是没有影响的,实际应用中这些参数也不可能为0,且即使延时略长(如TCCH=30ns)同样可以正常工作。

  2. 对参数进行有效性检查,限制参数赋值

    //++ 参数有效性检查 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    initial begin
      if (SPI_MODE != 0 && SPI_MODE != 1 && SPI_MODE != 2 && SPI_MODE != 3)
        $error("SPI_MODE must be 0, 1, 2, 3");
      if (DATA_WIDTH < 2)
        $error("DATA_WIDTH must be >= 2");
      if (SCLK_PERIOD_CLK_NUM < 2 || (SCLK_PERIOD_CLK_NUM % 2 != 0))
        $error("SCLK_PERIOD_CLK_NUM must even and >= 2");
      if (CS_EDGE_TO_SCLK_EDGE_CLK_NUM < 1)
        $error("CS_EDGE_TO_SCLK_EDGE_CLK_NUM must >= 1");
      if (SCLK_EDGE_TO_CS_EDGE_CLK_NUM < 3)
        $error("SCLK_EDGE_TO_CS_EDGE_CLK_NUM must >= 3");
      if (CS_KEEP_HIGH_CLK_NUM < 2)
        $error("CS_KEEP_HIGH_CLK_NUM must >= 2");
    end
    //-- 参数有效性检查 ------------------------------------------------------------
  3. 使用三段式状态机控制整个SPI传输过程,状态机如下图所示

五、使用说明

// 外部控制SPI信号
input  wire spi_begin,   // SPI单次通信开始, 高电平有效, 仅在spi_is_busy为低时起作用
output wire spi_end,     // SPI单次通信结束, 高电平有效, 只会持续一个时钟周期
output wire spi_is_busy, // SPI繁忙指示, 高电平表示SPI正在工作
input  wire [DATA_WIDTH-1:0] spi_master_tx_data, // SPI发送数据, 数据总是高位先发
output reg  [DATA_WIDTH-1:0] spi_master_rx_data, // SPI接收数据, 最先读出的数据在最高位
output reg                   spi_master_rx_data_valid, // SPI接收数据有效,高电平有效

外部模块通过以上信号控制SPI读写,相关说明如下:

  1. spi_is_busy为高时,此SPI主机模块不响应任何外部信号,所以,外部模块需要监测spi_is_busy信号,在它为低电平时,发送spi_begin控制spi控制开始

  2. SPI主机模块在会在spi_begin高电平且spi_is_busy低电平时,锁存spi_master_tx_data,作为待发送数据

此接口和AXI Stream接口用法基本一致。

六、仿真验证

见本系列文章——Verilog功能模块–SPI主机和从机(04)–SPI主机从机回环仿真与实测。

七、源码分享

源码在Gitee与Github开源,两平台同步。

Gitee:[Verilog功能模块–SPI主机和从机](https://gitee.com/xuxiaokang/verilog-function-module–SPI-Master-Slave)

Github:[zhengzhideakang/Verilog–SPI-Master-Slave](https://github.com/zhengzhideakang/Verilog–SPI-Master-SlaveC)


如果本文对你有所帮助,欢迎点赞、转发、收藏、评论让更多人看到,赞赏支持就更好了。

如果对文章内容有疑问,请务必清楚描述问题,留言评论或私信告知我,我看到会回复。


徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。

0 0 投票数
文章评分
订阅评论
提醒
0 评论
内联反馈
查看所有评论
目录
0
希望看到您的想法,请您发表评论x