《深入解析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
相关推荐
XMAIPC_Robot2 小时前
基于RK3576+FPGA+AI工业控制器的工地防护检测装备解决方案
人工智能·fpga开发
XMAIPC_Robot14 小时前
基于 ZYNQ UltraScale+ OV5640的高速图像传输系统设计,支持国产替代
linux·数码相机·fpga开发·架构·边缘计算
读书点滴1 天前
关于FPGA软核的仿真(一)
fpga开发
XMAIPC_Robot1 天前
基于 NXP + FPGA+Debian 高可靠性工业控制器解决方案
运维·人工智能·fpga开发·debian·边缘计算
天天爱吃肉82181 天前
【嵌入式(2)深入剖析嵌入式开发:从基础到实战】
嵌入式硬件·fpga开发
ThreeYear_s2 天前
基于FPGA的VGA显示文字和动态数字基础例程,进而动态显示数据,类似温湿度等
fpga开发
GateWorld2 天前
《深入解析UART协议及其硬件实现》-- 第三篇:UART ASIC实现优化与低功耗设计
fpga开发·开源协议
泪水打湿三角裤2 天前
自主设计一个DDS信号发生器
fpga开发
hahaha60162 天前
RK3588和FPGA桥片之间IO电平信号概率性不能通信原因
单片机·嵌入式硬件·fpga开发