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

前言

上一篇文章介绍了Verilog功能模块------SPI主机,包括主机设计思路与使用方法。

本文则用纯Verilog设计了功能完整的4线SPI从机,与网上一些以高频率clk时钟模拟从机不同,本文中的SPI从机工作时钟来源于主机的sclk,符合SPI同步通信的原则。

本文详细说明了模块编码思路和使用注意事项,最后分享了源码。

一、模块功能

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

  1. 同步通信,工作时钟来源于主机的sclk
  2. 支持所有4种SPI模式,通过SPI_MODE参数配置;
  3. 数据位宽通过DATA_WIDTH参数可配置(1-32位);
  4. 采用异步复位设计。

二、模块框图

三、信号接口

3.1 参数列表

参数名 类型 默认值 说明
SPI_MODE integer 3 SPI模式, 可选0, 1, 2, 3 (默认)
DATA_WIDTH integer 16 单次通信发送或接收数据的位宽, 最小为2, 常见8/16

3.2 接口信号列表

信号分组 信号名 方向 说明
外部控制SPI信号 spi_slave_tx_is_busy output SPI繁忙指示, 高电平表示SPI正在工作
spi_slave_tx_data[DATA_WIDTH-1:0] input SPI发送数据, 数据总是高位先发
spi_slave_rx_data[DATA_WIDTH-1:0] output SPI接收数据, 最先读出的数据在最高位
spi_slave_rx_data_valid output SPI接收数据有效指示,高电平有效
SPI硬线链接 spi_cs_n input 片选, 低电平有效
spi_sclk input SPI时钟, 从机提供
spi_mosi input 从机输出从机输入
spi_miso output 从机输入从机输出
复位 arstn input 异步复位, 低电平有效

四、编码思路

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

    verilog 复制代码
    parameter integer SPI_MODE   = 3, // SPI模式, 可选0, 1, 2, 3 (默认)
    parameter integer DATA_WIDTH = 16 // 单次通信发送或接收数据的位宽, 最小为1, 常见8/16
  2. 对参数进行有效性检查,限制参数赋值

    verilog 复制代码
    //++ 参数有效性检查 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    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 <= 0)
        $error("DATA_WIDTH must be >= 1");
    end
    //-- 参数有效性检查 ------------------------------------------------------------
  3. 暂存cs_n信号的上一状态,作为加载发送数据的控制信号

    verilog 复制代码
    //++ 片选状态跟踪 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    reg spi_cs_n_old; // 片选跳变前一瞬间的状态, 跳变结束后会变为新值
    always @(posedge spi_cs_n or negedge spi_cs_n or negedge arstn) begin
      if (~arstn)
        spi_cs_n_old <= 1'b1;
      else
        spi_cs_n_old <= spi_cs_n;
    end
    //-- 片选状态跟踪 ------------------------------------------------------------
    
    
    //++ 发送数据 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    /*
    原则是先采样再移位
    */
    reg [$clog2(DATA_WIDTH+1)-1:0] sample_cnt; // 采样计数
    reg [DATA_WIDTH-1:0] tx_data_lsfr;// 移位寄存器
    
    generate
    if (SPI_MODE == 0 || SPI_MODE == 3) begin // 下降沿移位, 第一个下降沿移位
      always @(negedge spi_sclk or negedge spi_cs_n) begin
        if (spi_cs_n_old)
          tx_data_lsfr <= spi_slave_tx_data;  // 片选下降沿加载发送数据
        else if (~spi_cs_n && sample_cnt != 'd0)
          tx_data_lsfr <= tx_data_lsfr << 1;
        else
          tx_data_lsfr <= tx_data_lsfr;
      end
    end else begin // 上升沿移位, 第一个上升沿不移位
      always @(posedge spi_sclk or negedge spi_cs_n) begin
        if (spi_cs_n_old)
          tx_data_lsfr <= spi_slave_tx_data;  // 片选下降沿加载发送数据
        else if (~spi_cs_n && sample_cnt != 'd0)
          tx_data_lsfr <= tx_data_lsfr << 1;
        else
          tx_data_lsfr <= tx_data_lsfr;
      end
    end
    endgenerate
    
    assign spi_miso = spi_slave_tx_is_busy ? tx_data_lsfr[DATA_WIDTH-1] : 1'bz; // 三态输出控制
    //-- 发送数据 ------------------------------------------------------------

    注意这里的spi_cs_n_old ,它是SPI同步从机最关键的代码,如果用~spi_cs_n,那么在整个传输中,spi_cs_n都是低电平,后续代码都无法执行了,所以只能用spi_cs_n_old,它只在spi_cs_n下降沿执行一次。

五、使用说明

verilog 复制代码
// SPI从机外部控制信号
output wire spi_slave_tx_is_busy, // 指示SPI从机正在发送, 高电平有效
input  wire [DATA_WIDTH-1:0] spi_slave_tx_data,        // 发送数据
output reg  [DATA_WIDTH-1:0] spi_slave_rx_data,        // 接收数据
output reg                   spi_slave_rx_data_valid,  // 接收数据有效

外部模块只需控制spi_slave_tx_data即可,此信号会在每个spi_cs_n下降沿锁存。

六、仿真验证

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

七、源码分享

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

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

Github:zhengzhideakang/Verilog--SPI-Master-Slave: verilog-function-module--SPI-Master-Slave https://github.com/zhengzhideakang/Verilog--SPI-Master-Slave


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

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


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

相关推荐
通信小呆呆1 小时前
FPGA 上的 OFDM 同步:从 S&C 到残差 CFO 的工程化实现
fpga开发·信号处理·同步·ofdm
hahaha601613 小时前
高层次综合基础-vivado hls第三章
算法·fpga开发
XINVRY-FPGA3 天前
XCVU9P-2FLGA2104E Xilinx AMD Virtex UltraScale+ FPGA
人工智能·嵌入式硬件·fpga开发·硬件工程·dsp开发·射频工程·fpga
范纹杉想快点毕业3 天前
ZYNQ7045芯片中UART实现RS422通信详解,50000字解析,C语言,嵌入式开发,软件开发
c语言·笔记·stm32·单片机·嵌入式硬件·mcu·fpga开发
千宇宙航5 天前
闲庭信步使用图像验证平台加速FPGA的开发:第三十课——车牌识别的FPGA实现(2)实现车牌定位
图像处理·计算机视觉·fpga开发·车牌识别
灵风_Brend5 天前
最大最小延时约束
fpga开发
li星野5 天前
打工人日报#20250930
笔记·程序人生·fpga开发·学习方法
9527华安5 天前
FPGA实现SRIO图像视频传输,基于Serial Rapidlo Gen2,提供6套工程源码和技术支持
图像处理·fpga开发·音视频·srio·xilinx
ThreeYear_s5 天前
【FPGA+DSP系列】——(1)CCS创建工程+LED点亮
fpga开发
带刺的坐椅6 天前
Solon Plugin 自动装配机制详解
java·spring·solon·spi