《深入解析SPI协议及其FPGA高效实现》-- 第二篇:SPI控制器FPGA架构设计

第二篇:SPI控制器FPGA架构设计

聚焦模块化设计、时序优化与资源管理

1. 系统级架构设计

1.1 模块化硬件架构

verilog

复制代码
module spi_controller (
  input  wire        clk,          // 系统时钟 (100 MHz)
  input  wire        rst_n,        // 异步复位
  // 配置接口
  input  wire [15:0] clk_div,      // 时钟分频系数
  input  wire [1:0]  spi_mode,     // CPOL/CPHA模式
  input  wire        lsb_first,    // 位传输顺序
  // 数据接口
  input  wire [7:0]  tx_data,      // 发送数据
  output reg  [7:0]  rx_data,      // 接收数据
  output reg         busy,         // 传输状态
  // SPI物理接口
  output wire        sck,          // SPI时钟
  output wire        mosi,         // 主出从入
  input  wire        miso,         // 主入从出
  output wire        cs_n          // 片选 (低有效)
);
// 核心子模块实例化
clock_gen     u_clk_gen(.*);      // 时钟生成
fsm_controller u_fsm(.*);         // 状态机
data_path     u_datapath(.*);     // 数据路径
cs_decoder    u_cs_dec(.*);       // 片选译码
endmodule

关键模块说明

  • 时钟域划分
    高速域 (clk) :配置寄存器访问 (100MHz)
    SPI域 (spi_clk) :SCK同步逻辑 (≤50MHz)
    跨时钟域同步 :TX/RX数据通过异步FIFO交换
1.2 寄存器映射表
地址偏移 寄存器名 位定义 功能
0x00 CTRL [0]:使能 [1]:LSB [3:2]:模式 控制寄存器
0x04 CLK_DIV 16位分频系数 时钟分频
0x08 TX_DATA 8位发送数据 发送缓冲区
0x0C RX_DATA 8位接收数据 接收缓冲区
0x10 STATUS [0]:忙 [1]:传输完成 状态寄存器

2. 时钟生成电路

2.1 可编程分频器设计

verilog

复制代码
module clock_gen (
  input  wire clk,
  input  wire [15:0] div_value,
  output reg  spi_clk
);
  reg [15:0] counter = 0;
  reg clk_phase = 0;  // 相位控制

  always @(posedge clk) begin
    if (counter >= div_value) begin
      counter <= 0;
      clk_phase <= ~clk_phase;  // 翻转相位
    end else begin
      counter <= counter + 1;
    end
  end

  // CPOL控制输出极性
  assign sck = (spi_mode[1]) ? ~clk_phase : clk_phase;
endmodule

关键技术

  • 小数分频实现 :通过累加器实现N.5分频(如3.5分频)
    verilog

    复制代码
    reg [3:0] acc = 0;
    always @(posedge clk) begin
      acc <= acc + 4'd2;         // 步进值=2 (2/4=0.5)
      spi_clk <= (acc < 4'd4);   // 50%占空比
    end
  • 动态时钟切换 :CPOL变化时插入死区时间避免毛刺
    verilog

    复制代码
    always @(posedge clk) begin
      if (mode_changed) begin
        sck <= 1'bz;            // 高阻态保持10ns
        #10; 
        sck <= new_polarity;    // 应用新极性
      end
    end

3. 核心状态机设计

3.1 状态转移图
3.2 Verilog实现

verilog

复制代码
typedef enum {IDLE, START, TX_RX, STOP} spi_state;
spi_state current_state = IDLE;
reg [2:0] bit_cnt = 0;  // 位计数器

always @(posedge spi_clk) begin
  case(current_state)
    IDLE: 
      if (cs_active) begin
        current_state <= START;
        sck <= 1'b0;  // Mode 0初始化
      end
    
    START: 
      current_state <= TX_RX;
  
    TX_RX:
      if (bit_cnt == 7) 
        current_state <= STOP;
      else
        bit_cnt <= bit_cnt + 1;
  
    STOP:
      if (!cs_hold) 
        current_state <= IDLE;
  endcase
end

关键特性

  • CPHA自适应 :通过状态机控制采样沿
    verilog

    复制代码
    // CPHA=0: 上升沿采样,下降沿切换数据
    wire sample_edge = (spi_mode[0]) ? negedge sck : posedge sck;
    wire shift_edge  = (spi_mode[0]) ? posedge sck : negedge sck;
  • 连续传输支持cs_hold信号保持状态机在STOP状态


4. 数据路径优化

4.1 双缓冲机制

verilog

复制代码
reg [7:0] tx_buffer, tx_shift;
reg [7:0] rx_shift, rx_buffer;

// 发送双缓冲
always @(posedge clk) begin
  if (!busy) 
    tx_shift <= tx_buffer;  // 空闲时加载新数据
end

// 接收双缓冲
always @(posedge spi_clk) begin
  if (bit_cnt == 0 && current_state == TX_RX)
    rx_buffer <= rx_shift;  // 传输完成锁存数据
end
4.2 循环移位寄存器

verilog

复制代码
// LSB优先传输实现
always @(shift_edge) begin
  if (current_state == TX_RX) begin
    tx_shift <= lsb_first ? 
                {1'b0, tx_shift[7:1]} : 
                {tx_shift[6:0], 1'b0};
              
    rx_shift <= lsb_first ? 
                {miso, rx_shift[7:1]} : 
                {rx_shift[6:0], miso};
  end
end
4.3 多从机数据隔离

verilog

复制代码
// 基于CS的三态控制
assign mosi = (cs_active) ? tx_shift[7] : 1'bz;
assign miso = (cs_active) ? slave_miso : 1'bz;

// 从设备选择译码
module cs_decoder (
  input  wire [3:0] slave_sel,   // 4位从机选择
  output reg  [7:0] cs_n         // 8个CS信号
);
  always @(*) begin
    cs_n = 8'hFF;              // 默认全关
    if (slave_sel < 8) 
      cs_n[slave_sel] = 1'b0;  // 激活选中从机
  end
endmodule

5. 时序收敛关键策略

5.1 多周期路径约束

tcl

复制代码
# XDC约束示例 (Vivado)
set_multicycle_path 2 -setup -from [get_clocks clk] -to [get_clocks spi_clk]
set_multicycle_path 1 -hold -end
5.2 跨时钟域同步链

verilog

复制代码
// 异步信号三级同步
reg [2:0] sync_miso;
always @(posedge spi_clk) begin
  sync_miso <= {sync_miso[1:0], miso};
end
5.3 关键路径流水化

verilog

复制代码
// 添加流水线提升时序
always @(posedge spi_clk) begin
  // 第1拍:计算下一状态
  next_state <= fsm_logic(current_state); 
  
  // 第2拍:更新状态
  current_state <= next_state;  
end

6. 资源优化技术

6.1 动态部分重配置(Xilinx FPGA)

tcl

复制代码
# 重配置命令(切换SPI模式)
write_cfgmem -format BIN -interface SPIx4 -size 8 -loadbit "up 0x0 new_mode.bin"
6.2 引脚复用技术
复用方案 实现方式 节省IO
QSPI模式 将WP/HOLD引脚用作数据线 2根
三线SPI MOSI/MISO共享SIO线 1根
分时复用 用同一组引脚驱动多组SPI外设 50%

附录:Artix-7资源占用报告

模块 LUTs FFs 最大频率 功耗
时钟生成器 42 32 450 MHz 8 mW
状态机 78 64 350 MHz 12 mW
数据路径 105 80 400 MHz 15 mW
总计 225 176 350 MHz 35 mW
相关推荐
尤老师FPGA10 天前
使用DDR4控制器实现多通道数据读写(十六)
fpga开发·ddr4
HX科技10 天前
STM32给FPGA的外挂FLASH进行升级
stm32·嵌入式硬件·fpga开发·flash·fpga升级
sz66cm10 天前
FPGA基础 -- Verilog 驱动强度(drive strength)与电荷强度(charge strength)
fpga开发
海涛高软10 天前
FPGA深度和突发长度计算
fpga开发
hahaha601610 天前
vivado使用非自带的第三方编辑器
fpga开发
芝士不会写代码11 天前
【FPGA学习】DDS信号发生器设计
学习·fpga开发
9527华安11 天前
国产安路FPGA实现MIPI视频解码转HDMI输出,基于SC500摄像头,提供TD工程源码和技术支持
fpga开发·音视频·csi·mipi·dphy·安路fpga·sc500
可编程芯片开发11 天前
基于FPGA的白噪声信号发生器verilog实现,包含testbench和开发板硬件测试
fpga开发·白噪声·snr
风释雪FPGA11 天前
[XILINX]ZYNQ7010_7020_软件LVDS设计
fpga开发
XINVRY-FPGA11 天前
XCVU47P-2FSVH2892E Xilinx Virtex UltraScale+ FPGA AMD
c语言·c++·人工智能·嵌入式硬件·阿里云·fpga开发·fpga