尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

110.Vivado 可直接运行!FPGA 按键消抖 + SPI 主机 + AXI4-Lite 从机全套源码

110.Vivado 可直接运行!FPGA 按键消抖 + SPI 主机 + AXI4-Lite 从机全套源码 摘要FPGA接口设计是数字系统设计的核心环节,直接决定系统吞吐率与可靠性。本文以Xilinx 7系列器件为硬件基础,从最基础的GPIO时序约束出发,逐步深入到SPI主机控制器设计,最终落地到AXI4-Lite总线接口的完整实现。文章提供三段可直接运行于Vivado 2018.3及以上版本的Verilog代码,覆盖组合逻辑、时序逻辑与总线协议三大范式,并给出时序收敛的量化分析方法。通过阅读本文,读者可以建立从物理引脚到片上总线的全链路设计思维。应用场景FPGA接口设计广泛应用于以下领域:工业控制:PLC数字量输入输出模块,需要处理毫秒级开关信号并保证无毛刺采样。高速数据采集:ADC/DAC芯片控制,涉及SPI或JESD204B协议,时钟频率可达100MHz以上。嵌入式系统互联:FPGA作为协处理器挂载到ARM处理器总线,必须遵循AMBA AXI协议。芯片验证:FPGA原型验证平台需要将RTL设计映射到真实引脚,接口时序必须与目标芯片匹配。不同场景对接口设计的要求差异巨大:低速控制接口关注毛刺抑制,高速接口关注时序裕量,总线接口关注协议一致性。本文选取三类典型接口,覆盖上述全部需求。核心原理接口设计的本质是时序控制。任何FPGA接口都可以抽象为三个要素:数据、时钟、控制信号。设计的核心任务是在满足建立时间和保持时间的前提下,实现数据的正确采样与驱动。建立时间(Tsu)指时钟有效沿到来之前数据必须保持稳定的最短时间。保持时间(Th)指时钟有效沿之后数据必须保持稳定的最短时间。FPGA内部触发器的Tsu和Th由工艺决定,通常为纳秒级。接口设计的全部工作,就是通过约束文件(XDC)告诉综合工具外部器件的时序要求,并确保内部路径满足这些约束。对于输出接口,需要计算输出延迟(Output Delay)并设置输出约束。对于输入接口,需要计算输入延迟(Input Delay)并设置输入约束。对于总线接口,还需要考虑协议握手信号的相对时序关系。掌握这些约束方法,是接口设计从“能跑”到“可靠”的分水岭。详细步骤第一步:GPIO基础接口设计GPIO是最简单的接口形式,但“简单”不等于“可靠”。设计一个按键消抖模块,要求:采样频率1kHz,消抖时间10ms,输出单周期脉冲。这需要组合逻辑与时序逻辑的配合。第二步:SPI主机控制器设计SPI协议包含四根线:SCLK、MOSI、MISO、CS。设计一个模式0(CPOL=0,CPHA=0)的主机控制器,支持8位数据宽度,时钟分频系数可配置。核心是状态机设计,需要处理传输启动、数据移位、传输完成三个状态。第三步:AXI4-Lite从机接口设计AXI4-Lite是ARM AMBA总线协议的精简版本,包含写地址通道(AW)、写数据通道(W)、写响应通道(B)、读地址通道(AR)、读数据通道(R)。设计一个寄存器组从机,支持4个32位寄存器,通过接口实现读写操作。完整可运行代码代码一:GPIO按键消抖模块`timescale 1ns / 1ps module debounce_pulse ( input wire clk, // 系统时钟,50MHz input wire rst_n, // 异步复位,低有效 input wire key_in, // 按键输入,按下为低电平 output reg pulse_out // 单周期脉冲输出 ); // 参数定义 parameter CLK_FREQ = 50_000_000; // 时钟频率50MHz parameter SAMPLE_FREQ = 1_000; // 采样频率1kHz parameter DEBOUNCE_TIME = 10; // 消抖时间10ms // 分频计数器:产生1kHz采样时钟 localparam DIV_CNT_MAX = CLK_FREQ / SAMPLE_FREQ - 1; localparam SAMPLE_CNT_MAX = SAMPLE_FREQ / 1000 * DEBOUNCE_TIME - 1; reg [15:0] div_cnt; reg sample_clk; reg [3:0] sample_cnt; reg key_sync1, key_sync2; // 两级同步器 reg key_filtered; reg key_prev; // 分频逻辑 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin div_cnt = 0; sample_clk = 0; end else begin if (div_cnt == DIV_CNT_MAX) begin div_cnt = 0; sample_clk = ~sample_clk; end else begin div_cnt
返回列表