news 2026/5/8 15:27:40

别再死记硬背CPOL和CPHA了!用Verilog在FPGA上跑通SPI的四种模式(附仿真波形)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再死记硬背CPOL和CPHA了!用Verilog在FPGA上跑通SPI的四种模式(附仿真波形)

用Verilog实战SPI协议:从波形分析到FPGA实现的四种模式全解析

SPI协议作为嵌入式系统和FPGA设计中最常用的通信接口之一,其时钟极性和相位配置(CPOL/CPHA)一直是工程师们的痛点。本文将带你从波形本质出发,通过Verilog代码实现一个可配置四种模式的SPI主从机模块,并用Vivado/Modelsim仿真展示不同模式下的波形差异,彻底告别死记硬背。

1. SPI协议核心:时钟极性与相位的本质理解

SPI协议的四种模式本质上只是时钟信号和数据采样时刻的不同组合。我们先抛开抽象定义,直接从物理波形入手:

  • CPOL(时钟极性):决定了时钟信号在空闲状态时的电平

    • CPOL=0:空闲时为低电平
    • CPOL=1:空闲时为高电平
  • CPHA(时钟相位):决定了数据在时钟的哪个边沿被采样

    • CPHA=0:在第一个有效时钟边沿采样
    • CPHA=1:在第二个有效时钟边沿采样

这四种组合形成了SPI的四种工作模式,具体对应关系如下表:

模式CPOLCPHA空闲状态采样边沿发送边沿
000低电平上升沿下降沿
101低电平下降沿上升沿
210高电平下降沿上升沿
311高电平上升沿下降沿

关键提示:实际项目中,主从设备的CPOL/CPHA设置必须完全一致,否则会导致通信失败。务必仔细核对从设备的数据手册。

2. Verilog实现可配置SPI主机模块

下面是一个支持四种模式配置的SPI主机Verilog实现核心代码:

module spi_master #( parameter CPOL = 0, parameter CPHA = 0 )( input clk, input rst_n, input [7:0] tx_data, input tx_valid, output reg tx_ready, output reg sclk, output reg mosi, output reg cs_n ); reg [3:0] state; reg [2:0] bit_cnt; reg [7:0] shift_reg; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= 0; sclk <= CPOL; cs_n <= 1'b1; end else begin case (state) 0: begin // 空闲状态 if (tx_valid) begin cs_n <= 1'b0; shift_reg <= tx_data; bit_cnt <= 0; state <= 1; sclk <= CPOL; end end 1: begin // 准备第一个边沿 sclk <= ~sclk; state <= 2; if (CPHA == 0) mosi <= shift_reg[7]; end 2: begin // 第一个边沿 if (CPHA == 0) shift_reg <= {shift_reg[6:0], 1'b0}; sclk <= ~sclk; state <= 3; if (CPHA == 1) mosi <= shift_reg[7]; end 3: begin // 第二个边沿 if (CPHA == 1) shift_reg <= {shift_reg[6:0], 1'b0}; bit_cnt <= bit_cnt + 1; if (bit_cnt == 7) begin state <= 0; cs_n <= 1'b1; tx_ready <= 1'b1; end else begin state <= 1; end sclk <= ~sclk; end endcase end end endmodule

这段代码的关键设计点:

  1. 使用状态机清晰划分SPI传输的各个阶段
  2. 根据CPHA参数决定数据发送和移位的时机
  3. 通过CPOL参数初始化时钟线的空闲状态
  4. 支持背压机制(tx_ready/tx_valid)实现流控制

3. 四种模式的Modelsim仿真对比

为了直观展示四种模式的区别,我们设计了一个测试平台,发送相同的数据(8'h55)在不同模式下观察波形差异。

3.1 模式0 (CPOL=0, CPHA=0)仿真结果

// 测试用例 initial begin #100; tx_data = 8'h55; tx_valid = 1; #20; tx_valid = 0; #500; $finish; end

模式0的波形特征:

  • 空闲时SCLK为低电平
  • 数据在上升沿被采样
  • 主机在下降沿更新数据

3.2 模式1 (CPOL=0, CPHA=1)仿真结果

模式1的波形特征:

  • 空闲时SCLK仍为低电平
  • 数据在下降沿被采样
  • 主机在上升沿更新数据

3.3 模式2 (CPOL=1, CPHA=0)仿真结果

模式2的波形特征:

  • 空闲时SCLK为高电平
  • 数据在下降沿被采样
  • 主机在上升沿更新数据

3.4 模式3 (CPOL=1, CPHA=1)仿真结果

模式3的波形特征:

  • 空闲时SCLK为高电平
  • 数据在上升沿被采样
  • 主机在下降沿更新数据

调试技巧:在Modelsim中可以将四种模式的波形并排对比,使用不同颜色标注采样时刻,这样差异一目了然。

4. SPI从机设计与主从回环测试

完整的SPI系统需要主从配合,下面是一个与主机兼容的从机设计:

module spi_slave #( parameter CPOL = 0, parameter CPHA = 0 )( input sclk, input mosi, input cs_n, output reg [7:0] rx_data, output reg rx_valid ); reg [7:0] shift_reg; reg sclk_prev; always @(negedge cs_n) begin rx_valid <= 1'b0; end always @(posedge sclk or negedge sclk) begin if (cs_n) begin sclk_prev <= CPOL; end else begin if (CPHA == 0 && sclk != sclk_prev && sclk == ~CPOL) begin // 模式0/2的第一个边沿采样 shift_reg <= {shift_reg[6:0], mosi}; end if (CPHA == 1 && sclk != sclk_prev && sclk == CPOL) begin // 模式1/3的第一个边沿采样 shift_reg <= {shift_reg[6:0], mosi}; end sclk_prev <= sclk; end end always @(posedge cs_n) begin if (!rx_valid) begin rx_data <= shift_reg; rx_valid <= 1'b1; end end endmodule

主从回环测试的关键点:

  1. 确保主从CPOL/CPHA参数设置一致
  2. 添加适当的时序约束
  3. 在FPGA上使用ILA抓取关键信号验证
// 回环测试顶层模块 module spi_loopback_top( input clk, input rst_n ); wire sclk, mosi, miso, cs_n; wire [7:0] tx_data, rx_data; wire tx_valid, tx_ready, rx_valid; spi_master #(.CPOL(0), .CPHA(0)) master ( .clk(clk), .rst_n(rst_n), .tx_data(tx_data), .tx_valid(tx_valid), .tx_ready(tx_ready), .sclk(sclk), .mosi(mosi), .cs_n(cs_n) ); spi_slave #(.CPOL(0), .CPHA(0)) slave ( .sclk(sclk), .mosi(mosi), .cs_n(cs_n), .rx_data(rx_data), .rx_valid(rx_valid) ); // 测试数据生成 reg [7:0] test_data; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin test_data <= 8'h00; end else if (tx_ready) begin test_data <= test_data + 1; end end assign tx_data = test_data; assign tx_valid = tx_ready; endmodule

5. 实际项目中的SPI调试技巧

在真实项目中调试SPI接口时,经常会遇到各种问题。以下是几个实用技巧:

  1. 示波器触发设置

    • 使用CS下降沿作为触发条件
    • 将时钟和数据信号同步捕获
    • 添加解码功能直接显示SPI数据
  2. 常见问题排查

    • 数据错位:检查CPHA设置是否正确
    • 采样不稳定:检查时序约束是否满足
    • 通信完全失败:确认CPOL设置和从设备一致
  3. 性能优化

    • 根据从设备特性选择最高支持时钟频率
    • 考虑使用DMA减少CPU开销
    • 对于长距离传输,添加适当的终端电阻
  4. 多从设备系统设计

    • 常规模式:每个从设备独立CS线
    • 菊花链模式:共享CS线,数据级联传输
// 多从设备选择示例 module spi_multi_slave( input clk, input [1:0] slave_select, output reg [3:0] cs_n ); always @(*) begin case (slave_select) 2'b00: cs_n = 4'b1110; 2'b01: cs_n = 4'b1101; 2'b10: cs_n = 4'b1011; 2'b11: cs_n = 4'b0111; default: cs_n = 4'b1111; endcase end endmodule

掌握SPI协议的核心在于理解时钟与数据的时序关系,通过Verilog实现和仿真可以加深这种理解。实际项目中,建议先通过仿真验证所有四种模式,再上板测试,这样可以大大提高调试效率。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 15:27:37

英雄联盟国服换肤终极指南:3分钟免费解锁全皮肤

英雄联盟国服换肤终极指南&#xff1a;3分钟免费解锁全皮肤 【免费下载链接】R3nzSkin-For-China-Server Skin changer for League of Legends (LOL) 项目地址: https://gitcode.com/gh_mirrors/r3/R3nzSkin-For-China-Server 还在为英雄联盟国服皮肤价格昂贵而烦恼&…

作者头像 李华
网站建设 2026/5/8 15:27:30

使用Nodejs和Taotoken快速构建一个AI客服原型系统

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 使用Nodejs和Taotoken快速构建一个AI客服原型系统 本文面向全栈或Node.js后端开发者&#xff0c;介绍如何利用OpenAI官方风格的Nod…

作者头像 李华
网站建设 2026/5/8 15:27:25

IT支持从救火到赋能:构建高效支持体系与工程师成长路径

1. 从一张漫画说起&#xff1a;IT人的“天花板”式支持如果你在办公室里&#xff0c;突然看到天花板的隔板被顶开&#xff0c;一个戴着眼镜、头发略显凌乱的脑袋探出来&#xff0c;对你说“需要帮忙吗&#xff1f;”&#xff0c;你的第一反应会是什么&#xff1f;是惊吓&#x…

作者头像 李华
网站建设 2026/5/8 15:27:06

在不同网络环境下测试taotoken聚合端点的连接稳定性体验

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在不同网络环境下测试Taotoken聚合端点的连接稳定性体验 作为一名需要频繁调用大模型API的开发者&#xff0c;服务的连接稳定性是影…

作者头像 李华
网站建设 2026/5/8 15:27:05

在Node.js后端服务中集成Taotoken实现稳定的大模型API调用

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在Node.js后端服务中集成Taotoken实现稳定的大模型API调用 对于需要构建AI功能的后端开发者而言&#xff0c;直接对接多个大模型厂…

作者头像 李华