摘要本文面向 FPGA 应届生、研二研三学生和入行 1 年以内的新人工程师整理时序、CDC、FIFO、状态机、AXI 五类高频面试题的答题模板。内容不背概念重点讲面试怎么答、工程里怎么做、常见坑怎么避并配合代码和时序图帮助理解。目录FPGA 面试答题的基本思路时序Setup、Hold、WNS、TNS 怎么答CDC跨时钟域为什么危险怎么处理FIFO同步 FIFO 和异步 FIFO 怎么讲清楚状态机三段式、编码方式、工程注意点AXIVALID/READY 握手、AXI-Lite、AXI-Stream面试最后 30 秒总结模板1. FPGA 面试答题的基本思路很多 FPGA 新人面试时容易犯一个问题只背定义不讲工程。比如面试官问什么是 CDC如果只回答CDC 是 Clock Domain Crossing跨时钟域。这句话没错但信息量太少。更好的回答结构应该是1. 先说定义这个问题本质是什么。 2. 再说风险为什么会出问题。 3. 再说做法工程中怎么处理。 4. 再说坑点哪些做法是错误的。 5. 最后补一句经验实际项目中怎么检查。例如 CDC 可以这样答CDC 是跨时钟域信号传输问题。因为源时钟和目的时钟没有固定相位关系 目的寄存器采样时可能违反 setup/hold从而产生亚稳态。 工程上不会把跨时钟域信号直接打一拍使用。 单 bit 慢速控制信号一般用两级同步 脉冲信号要展宽、toggle 同步或者握手 多 bit 数据不能简单每一位打两拍通常用异步 FIFO 或握手机制。 项目中还会配合 CDC 检查工具、时序约束和仿真来确认跨域路径是否处理正确。这就是一个比较完整的面试回答。2. 时序Setup、Hold、WNS、TNS 怎么答2.1 面试官问什么是 setup 和 hold可以这样回答setup 和 hold 都是寄存器采样数据时必须满足的时序要求。 setup time 是指数据在采样时钟沿到来之前需要提前稳定的最小时间。 hold time 是指数据在采样时钟沿到来之后还需要继续保持稳定的最小时间。 如果 setup 不满足说明数据来晚了 如果 hold 不满足说明数据变化太早了。可以用一个简单时序图理解采样时钟沿 ↓ clk ────┐ ┌────┐ ┌──── └────┘ └────┘ data xxxx有效数据xxxx ↑ ↑ setup 窗口 hold 窗口 要求 1. 数据在时钟沿之前提前稳定 2. 数据在时钟沿之后保持稳定一小段时间更工程化一点可以继续说setup 主要受时钟周期、寄存器 clk-to-q、组合逻辑延迟、布线延迟影响。 hold 主要看最短路径延迟和时钟偏斜。 setup 不满足时降低频率可能有用 hold 不满足时单纯降低频率一般没用因为 hold 检查和时钟周期关系不大。这是面试中很关键的一句话。2.2 Setup 违例怎么解决面试模板setup 违例说明数据路径太慢数据没有在下一个采样沿之前到达。 我会先看 timing report确认 startpoint、endpoint、clock path、data path 再确认约束是否正确比如主时钟、派生时钟、false path、multicycle path 有没有写错。 如果约束没问题再从设计上优化 1. 对长组合逻辑加流水线 2. 减少一级里的比较器、加法器、乘法器数量 3. 高扇出信号做寄存器复制 4. 合理使用 DSP、BRAM 等硬核资源 5. 必要时做 floorplan 或调整模块层次。典型长路径示例假设有如下代码always (posedge clk) begin y (((a b) * c) d) ^ e; end这一级里同时有加法、乘法、加法、异或组合逻辑可能很长。可以改成流水线reg [31:0] add_ab; reg [31:0] mul_c; reg [31:0] add_d; always (posedge clk) begin add_ab a b; mul_c add_ab * c; add_d mul_c d; y add_d ^ e; end这样吞吐率不一定降低但延迟增加了几个时钟周期。FPGA 设计里经常用面积和延迟换时序。2.3 Hold 违例怎么解决面试模板hold 违例说明数据路径太快数据在采样沿之后保持时间不够。 hold 违例一般不能靠降低时钟频率解决因为 hold 检查主要和最短路径、 时钟偏斜有关而不是和时钟周期直接相关。 工程上通常由布局布线工具自动插入延迟来修 hold。 如果仍然失败我会检查 1. 时钟约束是否错误 2. 是否存在不合理的 generated clock 3. 是否有跨时钟域路径被错误当成同步路径分析 4. 是否有过度约束 5. 是否有特殊 IO 或高速接口路径需要单独约束。一句话总结setup 问题一般是路径太慢hold 问题一般是路径太快。 setup 可以通过加流水线解决hold 通常要检查约束和最短路径。2.4 WNS、TNS 怎么解释面试模板WNS 是 Worst Negative Slack表示最差的一条时序违例路径的 slack。 TNS 是 Total Negative Slack表示所有违例路径的负 slack 总和。 WNS 看最严重的问题有多严重 TNS 看违例面积有多大。 如果 WNS 很差说明至少有一条关键路径非常严重。 如果 WNS 不大但 TNS 很大说明很多路径都略微不满足可能是整体频率目标偏高 或者某个公共模块、公共控制信号影响了大量路径。举个例子Case 1: WNS -2.5 ns TNS -3.0 ns 说明主要是一两条路径特别差。 Case 2: WNS -0.1 ns TNS -500 ns 说明单条路径不严重但违例路径数量很多。 这种情况通常要看是否存在高扇出、全局使能、约束错误或整体架构问题。3. CDC跨时钟域为什么危险怎么处理CDC 是 FPGA 面试高频题。CDC 的核心不是“打两拍”这三个字而是要区分信号类型。3.1 CDC 的本质面试模板CDC 是 Clock Domain Crossing也就是信号从一个时钟域传到另一个时钟域。 因为两个时钟之间没有固定相位关系目的时钟采样源时钟信号时 可能刚好采到数据变化边沿导致目的寄存器违反 setup 或 hold 从而进入亚稳态。 亚稳态不一定能完全消除只能降低传播概率。 所以工程上要用同步器、握手或异步 FIFO 来隔离风险。时序示意src_clk ┌───┐ ┌───┐ ┌───┐ ────┘ └───┘ └───┘ └── src_sig ────────────────┐ └────────── dst_clk ┌───┐ ┌───┐ ┌───┐ ──────┘ └───┘ └───┘ └ ↑ 目的时钟沿刚好采到变化附近 可能产生亚稳态3.2 单 bit 慢速信号两级同步适用场景1. 单 bit 信号 2. 电平信号不是窄脉冲 3. 变化频率不高 4. 允许目的时钟域延迟 1~2 拍看到变化。代码示例module cdc_bit_sync ( input wire dst_clk, input wire dst_rst_n, input wire async_in, output wire sync_out ); (* ASYNC_REG TRUE *) reg sync_ff1; (* ASYNC_REG TRUE *) reg sync_ff2; always (posedge dst_clk or negedge dst_rst_n) begin if (!dst_rst_n) begin sync_ff1 1b0; sync_ff2 1b0; end else begin sync_ff1 async_in; sync_ff2 sync_ff1; end end assign sync_out sync_ff2; endmodule面试时要补一句两级同步不能保证完全没有亚稳态只是让亚稳态有更多时间恢复 降低亚稳态传播到后级逻辑的概率。3.3 脉冲信号跨时钟域不要直接打两拍如果源时钟域产生一个很窄的 pulsesrc_clk 快dst_clk 慢时 src_pulse ____|‾|________________ dst_clk ______|‾|______|‾|_____ 可能目的时钟刚好没采到这个 pulse导致事件丢失。错误写法// 不推荐窄脉冲可能被漏采 always (posedge dst_clk) begin sync1 src_pulse; sync2 sync1; end更稳的方式是 toggle 同步。源时钟域每来一个 pulse就翻转一次 toggle 信号// src_clk domain reg src_toggle; always (posedge src_clk or negedge src_rst_n) begin if (!src_rst_n) src_toggle 1b0; else if (src_pulse) src_toggle ~src_toggle; end目的时钟域同步 toggle再检测变化// dst_clk domain (* ASYNC_REG TRUE *) reg toggle_sync1; (* ASYNC_REG TRUE *) reg toggle_sync2; reg toggle_sync2_d; always (posedge dst_clk or negedge dst_rst_n) begin if (!dst_rst_n) begin toggle_sync1 1b0; toggle_sync2 1b0; toggle_sync2_d 1b0; end else begin toggle_sync1 src_toggle; toggle_sync2 toggle_sync1; toggle_sync2_d toggle_sync2; end end assign dst_pulse toggle_sync2 ^ toggle_sync2_d;这个方法适合事件频率不高的场景。如果事件来得太快目的时钟域还没检测到下一次变化仍然可能丢事件。高吞吐数据流应该用异步 FIFO。3.4 多 bit 信号跨时钟域不能每一位打两拍这是 CDC 面试最容易踩坑的地方。错误思路// 错误示例多 bit bus 每一位单独同步 always (posedge dst_clk) begin data_sync1 data_async; data_sync2 data_sync1; end为什么不行多 bit 信号每一位的延迟、布线、亚稳态恢复时间都可能不同。 目的时钟域可能看到一个混合值 有些 bit 是旧数据有些 bit 是新数据。例如源数据从 8h0F 变成 8hF0理想情况 0000_1111 - 1111_0000 错误跨域后目的端可能看到 1000_1110 1100_0100 这些都不是源端真实出现过的数据。正确做法1. 低速配置类多 bit 数据valid data 握手保证 data 稳定后再采样 2. 高吞吐数据流使用异步 FIFO 3. 特殊状态编码可以用 Gray code因为 Gray code 相邻变化只有 1 bit。3.5 CDC 面试总结模板可以直接背这个版本CDC 的风险是目的时钟域采样异步信号时可能产生亚稳态。 工程上要按信号类型处理 单 bit 慢速电平信号可以用两级同步 窄脉冲不能直接打两拍可以用 pulse 展宽、toggle 同步或者握手 多 bit 数据不能每一位单独同步通常用握手或异步 FIFO FIFO 指针跨域时一般用 Gray code保证相邻指针只有 1 bit 变化。 项目里还需要配合 CDC lint、时序约束和仿真检查。4. FIFO同步 FIFO 和异步 FIFO 怎么讲清楚FIFO 是 First In First Out先进先出。面试里常问同步 FIFO、异步 FIFO、空满判断、Gray code。4.1 同步 FIFO 和异步 FIFO 的区别面试模板同步 FIFO 的读写使用同一个时钟空满判断都在同一时钟域完成。 异步 FIFO 的写端和读端使用不同的时钟 常用于跨时钟域数据传输。 异步 FIFO 的难点不是 RAM 本身而是读写指针跨时钟域同步和空满判断。4.2 同步 FIFO 的空满判断一种常见做法是指针多加 1 bit。假设 FIFO 深度是 2^ADDR_W写指针 wptr: ADDR_W 1 bit 读指针 rptr: ADDR_W 1 bit 低 ADDR_W bit 用于寻址 RAM 最高 bit 用于区分是否绕回。判断逻辑assign empty (wptr rptr); assign full (wptr[ADDR_W] ! rptr[ADDR_W]) (wptr[ADDR_W-1:0] rptr[ADDR_W-1:0]);含义empty读写指针完全相等说明没有数据。 full 读写指针低位相等但最高位不同说明写指针比读指针多绕了一圈。同步 FIFO 简化代码module sync_fifo #( parameter DATA_W 8, parameter ADDR_W 4 )( input wire clk, input wire rst_n, input wire wr_en, input wire [DATA_W-1:0] wr_data, output wire full, input wire rd_en, output reg [DATA_W-1:0] rd_data, output wire empty ); localparam DEPTH (1 ADDR_W); reg [DATA_W-1:0] mem [0:DEPTH-1]; reg [ADDR_W:0] wptr; reg [ADDR_W:0] rptr; assign empty (wptr rptr); assign full (wptr[ADDR_W] ! rptr[ADDR_W]) (wptr[ADDR_W-1:0] rptr[ADDR_W-1:0]); always (posedge clk or negedge rst_n) begin if (!rst_n) begin wptr d0; end else if (wr_en !full) begin mem[wptr[ADDR_W-1:0]] wr_data; wptr wptr 1b1; end end always (posedge clk or negedge rst_n) begin if (!rst_n) begin rptr d0; rd_data d0; end else if (rd_en !empty) begin rd_data mem[rptr[ADDR_W-1:0]]; rptr rptr 1b1; end end endmodule注意实际项目里 FIFO 读数据是同步读还是异步读要看 FPGA RAM 资源和综合结果。 工程中更常用厂商 FIFO IP 或自己封装验证过的 FIFO。4.3 异步 FIFO 的核心Gray code 指针同步异步 FIFO 结构如下写时钟域 wclk 读时钟域 rclk ┌─────────────────────┐ ┌─────────────────────┐ │ 写指针 wbin / wgray │ │ 读指针 rbin / rgray │ └──────────┬──────────┘ └──────────┬──────────┘ │ │ │ 写 RAM │ 读 RAM ▼ ▼ ┌─────────────────────────────────────────────┐ │ dual-port RAM │ └─────────────────────────────────────────────┘ ▲ ▲ │ │ 同步 rgray 到写时钟域 同步 wgray 到读时钟域 用于判断 full 用于判断 empty为什么用 Gray code二进制指针可能多个 bit 同时变化。 例如 0111 - 10004 个 bit 都变。 跨时钟域同步时目的端可能采到中间态。 Gray code 相邻值只变化 1 bit 可以降低跨域采样时读到错误组合的风险。二进制转 Gray codeassign gray (bin 1) ^ bin;4.4 异步 FIFO 空满判断异步 FIFO 常用判断方式empty 读时钟域中如果同步过来的写指针 wgray_sync 等于当前读指针 rgray_next 说明 FIFO 为空。 full 写时钟域中如果下一拍写指针 wgray_next 等于同步过来的读指针 rgray_sync 的高两位取反形式 说明 FIFO 满。满判断常见写法assign full_next (wgray_next { ~rgray_sync[ADDR_W:ADDR_W-1], rgray_sync[ADDR_W-2:0] });这个判断依赖指针多 1 bit且 FIFO 深度为 2 的幂。4.5 异步 FIFO 关键代码骨架下面代码适合学习空满判断和指针同步思路。实际工程建议使用厂商 FIFO IP或者使用经过充分验证的 FIFO 模块。module async_fifo #( parameter DATA_W 8, parameter ADDR_W 4 )( input wire wclk, input wire wrst_n, input wire wr_en, input wire [DATA_W-1:0] wr_data, output wire full, input wire rclk, input wire rrst_n, input wire rd_en, output reg [DATA_W-1:0] rd_data, output wire empty ); localparam DEPTH (1 ADDR_W); localparam PTR_W ADDR_W 1; reg [DATA_W-1:0] mem [0:DEPTH-1]; reg [PTR_W-1:0] wbin; reg [PTR_W-1:0] rbin; reg [PTR_W-1:0] wgray; reg [PTR_W-1:0] rgray; reg [PTR_W-1:0] wgray_rclk_1, wgray_rclk_2; reg [PTR_W-1:0] rgray_wclk_1, rgray_wclk_2; reg full_r; reg empty_r; assign full full_r; assign empty empty_r; function [PTR_W-1:0] bin2gray; input [PTR_W-1:0] bin; begin bin2gray (bin 1) ^ bin; end endfunction // ------------------------------ // 写时钟域同步读指针 Gray // ------------------------------ always (posedge wclk or negedge wrst_n) begin if (!wrst_n) begin rgray_wclk_1 d0; rgray_wclk_2 d0; end else begin rgray_wclk_1 rgray; rgray_wclk_2 rgray_wclk_1; end end // ------------------------------ // 读时钟域同步写指针 Gray // ------------------------------ always (posedge rclk or negedge rrst_n) begin if (!rrst_n) begin wgray_rclk_1 d0; wgray_rclk_2 d0; end else begin wgray_rclk_1 wgray; wgray_rclk_2 wgray_rclk_1; end end // ------------------------------ // 写逻辑 // ------------------------------ wire wr_fire wr_en !full_r; wire [PTR_W-1:0] wbin_next wbin wr_fire; wire [PTR_W-1:0] wgray_next bin2gray(wbin_next); wire full_next (wgray_next { ~rgray_wclk_2[PTR_W-1:PTR_W-2], rgray_wclk_2[PTR_W-3:0] }); always (posedge wclk or negedge wrst_n) begin if (!wrst_n) begin wbin d0; wgray d0; full_r 1b0; end else begin if (wr_fire) begin mem[wbin[ADDR_W-1:0]] wr_data; wbin wbin_next; wgray wgray_next; end full_r full_next; end end // ------------------------------ // 读逻辑 // ------------------------------ wire rd_fire rd_en !empty_r; wire [PTR_W-1:0] rbin_next rbin rd_fire; wire [PTR_W-1:0] rgray_next bin2gray(rbin_next); wire empty_next (rgray_next wgray_rclk_2); always (posedge rclk or negedge rrst_n) begin if (!rrst_n) begin rbin d0; rgray d0; rd_data d0; empty_r 1b1; end else begin if (rd_fire) begin rd_data mem[rbin[ADDR_W-1:0]]; rbin rbin_next; rgray rgray_next; end empty_r empty_next; end end endmodule4.6 FIFO 面试总结模板FIFO 是先进先出缓存常用于速率匹配、数据缓冲和跨时钟域。 同步 FIFO 的读写在同一个时钟域空满判断比较简单 异步 FIFO 的读写在不同时钟域关键是读写指针同步和空满判断。 异步 FIFO 一般不会直接同步二进制指针而是把指针转成 Gray code 后跨域同步 因为 Gray code 相邻值只变化 1 bit可以降低跨域采样错误组合的风险。 empty 通常在读时钟域判断full 通常在写时钟域判断。 工程中还要注意 almost full、almost empty、复位、读写同时发生、RAM 读延迟等问题。5. 状态机三段式、编码方式、工程注意点状态机是 FPGA 设计里最基础但也最能体现代码习惯的部分。5.1 状态机分类面试模板状态机一般分为 Moore 型和 Mealy 型。 Moore 型输出只和当前状态有关输出更稳定但可能多一拍延迟。 Mealy 型输出和当前状态以及输入都有关响应更快但组合路径更复杂 如果处理不好容易产生毛刺。简单理解Moore: output f(state) Mealy: output f(state, input)5.2 三段式状态机三段式状态机通常分为第一段状态寄存器current_state next_state 第二段组合逻辑根据当前状态和输入计算 next_state 第三段输出逻辑根据状态生成输出代码示例module fsm_example ( input wire clk, input wire rst_n, input wire start, input wire done, output reg busy, output reg finish ); localparam ST_IDLE 2d0; localparam ST_WORK 2d1; localparam ST_DONE 2d2; reg [1:0] cur_state; reg [1:0] next_state; // 第一段状态寄存器 always (posedge clk or negedge rst_n) begin if (!rst_n) cur_state ST_IDLE; else cur_state next_state; end // 第二段下一状态组合逻辑 always (*) begin next_state cur_state; case (cur_state) ST_IDLE: begin if (start) next_state ST_WORK; end ST_WORK: begin if (done) next_state ST_DONE; end ST_DONE: begin next_state ST_IDLE; end default: begin next_state ST_IDLE; end endcase end // 第三段输出逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) begin busy 1b0; finish 1b0; end else begin busy 1b0; finish 1b0; case (next_state) ST_IDLE: begin busy 1b0; finish 1b0; end ST_WORK: begin busy 1b1; finish 1b0; end ST_DONE: begin busy 1b0; finish 1b1; end default: begin busy 1b0; finish 1b0; end endcase end end endmodule5.3 状态机编码one-hot、binary、Gray面试模板状态机编码常见有 binary、one-hot、Gray。 binary 编码使用寄存器少适合状态数量多、资源敏感的场景 one-hot 编码每个状态一个 bit寄存器多但组合译码简单在 FPGA 里经常能跑更高频率 Gray 编码相邻状态只变化 1 bit适合某些低功耗或跨域相关场景但普通控制状态机不一定需要。简单比较编码方式特点适合场景binary省寄存器译码可能复杂状态多频率不高one-hot多用寄存器组合逻辑简单FPGA 高频控制Gray相邻状态 1 bit 变化特殊低功耗或跨域相关场景5.4 状态机常见坑坑 1组合逻辑没默认值推断锁存器错误写法always (*) begin case (cur_state) ST_IDLE: begin if (start) next_state ST_WORK; end ST_WORK: begin if (done) next_state ST_DONE; end endcase end如果某些分支没有给next_state赋值综合工具可能推断 latch。正确写法always (*) begin next_state cur_state; case (cur_state) ST_IDLE: begin if (start) next_state ST_WORK; end ST_WORK: begin if (done) next_state ST_DONE; end default: begin next_state ST_IDLE; end endcase end坑 2状态没有 default状态机可能因为复位、干扰、综合优化等原因进入非法状态。所以建议加 defaultdefault: begin next_state ST_IDLE; end面试时可以这样说我写状态机一般会加 default让状态机进入异常状态时能回到 IDLE 或安全状态。 这不是为了仿真好看而是为了工程鲁棒性。坑 3输出用组合逻辑直接驱动容易有毛刺如果输出参与下游控制比如写使能、读使能、启动脉冲建议尽量寄存一拍always (posedge clk or negedge rst_n) begin if (!rst_n) wr_en 1b0; else wr_en wr_en_next; end这样输出更干净也更利于时序收敛。5.5 状态机面试总结模板我一般用三段式状态机第一段寄存当前状态第二段组合逻辑计算下一状态 第三段输出逻辑。 状态编码上FPGA 里 one-hot 比较常见因为寄存器资源相对充足 组合译码简单有利于高频binary 更省寄存器适合状态多的情况。 工程中要注意默认赋值避免 latch case 要有 default 关键控制输出尽量寄存避免毛刺 状态跳转条件要写清楚避免多个条件优先级不明确。6. AXIVALID/READY 握手、AXI-Lite、AXI-StreamAXI 是 FPGA 面试中非常常见的总线协议。新人不用一上来背完整协议先把 VALID/READY 握手讲清楚。6.1 AXI 握手核心规则AXI 的基本握手一次传输发生在时钟上升沿并且 VALID 和 READY 同时为 1。时序图clk ↑ ↑ ↑ ↑ ↑ ↑ VALID 0 1 1 1 0 1 READY 0 0 1 0 1 1 DATA X D0 D0 D1 X D2 | | | └── 这里 VALID1 且 READY1D0 传输成功 | VALID1 但 READY0数据必须保持稳定面试模板AXI 的核心是 VALID/READY 握手。 发送端用 VALID 表示数据有效接收端用 READY 表示可以接收。 只有在某个时钟上升沿 VALID 和 READY 同时为 1传输才真正发生。 VALID 拉高后在握手完成前不能随便拉低DATA 也必须保持稳定。 READY 可以根据接收端缓存情况拉高或拉低用于反压。6.2 AXI 最常见的死锁错误错误做法发送端等 READY 为 1 才拉 VALID 接收端等 VALID 为 1 才拉 READY。这样两边互相等待可能死锁。错误示意// 错误思想VALID 依赖 READY if (ready) valid 1b1; else valid 1b0;正确思想发送端只要有数据就应该拉 VALID 如果 VALID1 但 READY0就保持 VALID 和 DATA 不变 直到 VALID READY 完成传输。示例代码always (posedge clk or negedge rst_n) begin if (!rst_n) begin m_valid 1b0; m_data d0; end else begin if (!m_valid || m_ready) begin if (has_data) begin m_valid 1b1; m_data next_data; end else begin m_valid 1b0; end end end end这段代码的关键是当 m_valid1 且 m_ready0 时寄存器不更新 所以 m_valid 和 m_data 会保持不变。6.3 AXI-Stream 简单寄存器切片AXI-Stream 常见信号TVALID发送端数据有效 TREADY接收端准备好 TDATA 数据 TLAST 一包数据的最后一个 beat TKEEP 字节有效标志常用于非整字宽数据 TUSER 用户自定义 sideband 信号简单 register slicemodule axis_reg_slice #( parameter DATA_W 32 )( input wire clk, input wire rst_n, input wire s_valid, output wire s_ready, input wire [DATA_W-1:0] s_data, output reg m_valid, input wire m_ready, output reg [DATA_W-1:0] m_data ); assign s_ready !m_valid || m_ready; always (posedge clk or negedge rst_n) begin if (!rst_n) begin m_valid 1b0; m_data d0; end else if (s_ready) begin m_valid s_valid; if (s_valid) m_data s_data; end end endmodule这段代码体现了一个核心原则下游没准备好时m_valid 和 m_data 保持不变。 下游准备好或者当前没有缓存数据时才允许接收新数据。工程中如果 READY 路径太长通常会加入 skid buffer 或更完整的 register slice 来切断反压路径。6.4 AXI-Lite 怎么答AXI-Lite 常用于寄存器配置特点是简单、无 burst。AXI-Lite 有 5 个通道通道方向作用AWmaster - slave写地址Wmaster - slave写数据Bslave - master写响应ARmaster - slave读地址Rslave - master读数据和读响应面试模板AXI-Lite 主要用于低速寄存器访问。 它有 5 个独立通道AW 写地址W 写数据B 写响应AR 读地址R 读数据。 写操作不是只有一个通道而是 AW 和 W 分开握手最后通过 B 返回写响应。 读操作通过 AR 发地址再通过 R 返回数据。 工程里要注意 AW 和 W 不一定同一拍到达不能默认写地址和写数据同时有效。这个点很重要。很多新人写 AXI-Lite slave 时默认if (awvalid wvalid) begin // write end这在简单仿真里可能能跑但不是严谨写法。更稳的做法是分别接收 AW 和 W缓存地址和数据等两者都到齐后再执行写操作。6.5 AXI4 和 AXI-Stream 的区别面试模板AXI4 Memory-Mapped 面向地址访问有 AW、W、B、AR、R 五个通道 支持 burst、ID、乱序等特性常用于 CPU、DMA、DDR 控制器之间的数据搬运。 AXI-Stream 面向数据流没有地址通道只有 VALID/READY 和数据及 sideband 信号 常用于视频流、网络包、ADC/DAC 数据流、DMA stream 接口等。简单对比类型是否有地址典型用途AXI-Lite有地址寄存器配置AXI4有地址DDR、DMA、高性能 memory-mapped 访问AXI-Stream无地址连续数据流、视频、网络、采样数据6.6 AXI 常见坑坑 1VALID 等 READYVALID 不应该依赖 READY 才拉高。 发送端有数据就拉 VALID并保持到握手完成。坑 2VALID1、READY0 时数据变化只要 VALID1 且 READY0DATA、TLAST、TKEEP 等信号都要保持稳定。坑 3AXI-Lite 默认 AW 和 W 同拍到达AW 和 W 是独立通道可能不同拍到达需要分别握手缓存。坑 4TLAST 没有和对应数据一起保持AXI-Stream 中 TLAST 是和某个 beat 绑定的。 如果 DATA 要保持TLAST 也要保持。坑 5READY 反压路径过长READY 经常形成很长的组合反压路径。 高频设计里需要 register slice 或 skid buffer 切断路径。6.7 AXI 面试总结模板AXI 最核心的是 VALID/READY 握手。 传输只在时钟上升沿 VALID 和 READY 同时为 1 时发生。 发送端有数据就拉 VALID并在握手完成前保持 VALID 和 DATA 稳定 接收端用 READY 表示自己能否接收用于反压。 AXI-Lite 适合寄存器配置有 AW、W、B、AR、R 五个通道 AXI4 支持 burst适合高性能 memory-mapped 访问 AXI-Stream 没有地址适合连续数据流。 工程中要特别注意 VALID 不能等 READY、DATA 要保持稳定、 AW/W 不能假设同拍、READY 路径过长要加 register slice 或 skid buffer。7. 面试最后 30 秒总结模板如果面试官最后让你总结 FPGA 基础可以这样说FPGA 设计我会重点关注几个方面。 第一是时序能看懂 setup、hold、WNS、TNS知道 setup 违例通常通过流水线、 减少组合逻辑、优化扇出解决hold 违例不能简单靠降频解决。 第二是 CDC跨时钟域信号不能直接使用要按信号类型处理 单 bit 用两级同步脉冲用 toggle 或握手多 bit 数据用握手或异步 FIFO。 第三是 FIFO同步 FIFO 重点是空满判断异步 FIFO 重点是 Gray 指针同步和跨域空满判断。 第四是状态机工程中常用三段式注意默认赋值、default 分支、输出寄存避免 latch 和毛刺。 第五是 AXI核心是 VALID/READY 握手VALID 拉高后数据要保持到握手完成 AXI-Lite 用于寄存器配置AXI-Stream 用于数据流。结尾FPGA 面试不是只背概念。面试官更关心的是你是否知道这个问题在真实工程里为什么会出错以及你会怎么避免。对于新人来说时序、CDC、FIFO、状态机、AXI 是最值得反复打磨的基础模块。如果这五块能讲清楚基本可以证明你不是只会跑 demo而是开始具备 FPGA 工程思维。