拓冰建站拓冰建站
首页 / 资讯中心 / 正文

AXI-Lite 从波形理解:VALID/READY 握手到底怎么看

前言很多 FPGA 新人第一次看 AXI-Lite 波形时会盯着一大排信号发懵AWVALID拉高了为什么还没有写进去WREADY已经是 1为什么不算一次写传输RVALID连续拉高两拍到底传了一次还是两次写地址和写数据为什么不在同一个周期到达BVALID为什么不能只拉高一个周期这些问题的根源通常不是信号太多而是没有真正理解 AXI 的VALID/READY握手机制。看 AXI-Lite 波形时不要一上来就看“整笔读写事务”。正确的方法是先把 AXI-Lite 拆成五个独立通道再分别寻找每个通道的握手时刻。一、先记住最重要的结论AXI 每个通道的传输条件只有一个transfer VALID READY;更准确地说在某个时钟上升沿到来时如果VALID和READY同时为 1那么该通道完成一次传输。注意两个关键词必须是VALID和READY同时为 1必须在时钟上升沿进行判断。因此VALIDREADY是否发生传输00不传输01不传输10不传输等待接收方11在当前上升沿完成一次传输只看VALID或者只看READY都不能判断传输已经完成。二、AXI-Lite 为什么有五个通道AXI-Lite 一共有五个独立通道。通道主要信号方向作用写地址通道 AWAWADDR、AWVALID、AWREADY主设备到从设备传输写地址写数据通道 WWDATA、WSTRB、WVALID、WREADY主设备到从设备传输写数据写响应通道 BBRESP、BVALID、BREADY从设备到主设备返回写入结果读地址通道 ARARADDR、ARVALID、ARREADY主设备到从设备传输读地址读数据通道 RRDATA、RRESP、RVALID、RREADY从设备到主设备返回读数据AXI-Lite 的一次写操作包含三个通道AW 写地址 W 写数据 ↓ B 写响应一次读操作包含两个通道AR 读地址 ↓ R 读数据和读响应这里最容易犯的错误是把写地址通道和写数据通道当成一个通道。实际上AW和W是两个独立通道它们有各自的VALID/READY握手。三、VALID 和 READY 分别代表什么可以把一次握手理解成两个人交接物品。1. VALID我这里有有效数据VALID由发送方产生。例如在写地址通道中主设备是发送方AWVALID1表示当前AWADDR是有效写地址。在读数据通道中从设备是发送方RVALID1表示当前RDATA和RRESP有效。2. READY我现在可以接收READY由接收方产生。例如AWREADY1从设备可以接收写地址WREADY1从设备可以接收写数据RREADY1主设备可以接收读数据。3. 两者同时为 1 才完成交接发送方说“数据准备好了”接收方也说“我能接收”当前上升沿才完成传输。四、VALID/READY 必须遵守的三个规则规则一发送方不能等待 READY 后才拉高 VALID错误思路if (ready) valid 1b1;如果接收方的逻辑又是if (valid) ready 1b1;那么双方都会等待对方先拉高信号最后形成死锁发送方等待 READY 接收方等待 VALID 两边一直都是 0正确规则是发送方有有效数据后就应该主动拉高 VALID不能依赖 READY。接收方可以等待VALID后再拉高READY发送方不可以反过来等待。规则二VALID 拉高后必须保持到握手完成假设发送方在某个周期拉高了VALID但此时READY0VALID 1 READY 0此时没有完成传输发送方不能在下一个周期直接撤销VALID。必须保持VALID继续为 1对应的地址、数据、控制信号保持稳定。直到出现VALID 1 READY 1并在上升沿完成握手后发送方才可以撤销VALID或者切换到下一笔数据。规则三READY 可以提前拉高接收方可以在还没有看到VALID时提前把READY拉高。例如VALID 0 READY 1这表示接收方已经准备好但此时还没有数据因此不会发生传输。等到发送方拉高VALID后VALID 1 READY 1就可以在当前上升沿完成传输。如果接收方始终有能力接收数据通常可以让READY长时间保持为 1。五、从读波形理解 AXI-Lite一次 AXI-Lite 读操作包含两个阶段读地址握手读数据握手。下面每一列都表示对应时钟上升沿采样到的信号值。采样上升沿 C0 C1 C2 C3 C4 ------------------------------------------------ ARVALID 0 1 1 0 0 ARREADY 0 0 1 0 0 ARADDR - A A - - ↑ 读地址握手 RVALID 0 0 0 1 1 RREADY 1 1 1 0 1 RDATA - - - D D ↑ 读数据握手C1主设备给出读地址ARVALID 1 ARREADY 0 ARADDR A主设备表示地址A有效但从设备还没有准备好接收。因此 C1 上升沿没有发生读地址传输。主设备必须继续保持ARVALID 1 ARADDR A不能修改地址也不能提前撤销ARVALID。C2读地址完成握手ARVALID 1 ARREADY 1在 C2 上升沿读地址通道完成一次传输。从设备在该上升沿接收地址A。握手完成后主设备可以撤销ARVALID。C3从设备返回读数据RVALID 1 RREADY 0 RDATA D从设备已经准备好数据D但主设备暂时不能接收。因此 C3 上升沿没有完成读数据传输。此时从设备必须保持RVALID 1 RDATA D RRESP 不变C4读数据完成握手RVALID 1 RREADY 1在 C4 上升沿主设备正式接收读数据D。一笔 AXI-Lite 读操作到这里才完成。六、读波形中最常见的错误错误一RVALID 只拉高一个周期错误代码if (read_data_ready) s_axi_rvalid 1b1; else s_axi_rvalid 1b0;如果RVALID拉高的那个周期中主设备的RREADY0读数据就没有完成握手。下一周期又把RVALID拉低主设备将永远收不到这笔数据。正确写法应该是if (read_data_ready) s_axi_rvalid 1b1; else if (s_axi_rvalid s_axi_rready) s_axi_rvalid 1b0;也就是RVALID 一旦拉高只有完成 R 通道握手后才能拉低。错误二反压期间修改 RDATA下面这种波形是错误的周期 C1 C2 C3 RVALID 1 1 1 RREADY 0 0 1 RDATA D0 D1 D2在 C1、C2 中RVALID 1 RREADY 0主设备还没有接收数据因此RDATA必须保持稳定。正确波形应该是周期 C1 C2 C3 RVALID 1 1 1 RREADY 0 0 1 RDATA D D D七、一个简单的读通道 RTL 写法下面代码只允许保留一笔未完成的读数据适合解释基本握手逻辑。wire ar_fire; wire r_fire; assign s_axi_arready !s_axi_rvalid; assign ar_fire s_axi_arvalid s_axi_arready; assign r_fire s_axi_rvalid s_axi_rready; always_ff (posedge ACLK) begin if (!ARESETn) begin s_axi_rvalid 1b0; s_axi_rdata 0; s_axi_rresp 2b00; end else begin // 接收到读地址 if (ar_fire) begin case (s_axi_araddr[5:2]) 4h0: begin s_axi_rdata reg0; s_axi_rresp 2b00; // OKAY end 4h1: begin s_axi_rdata reg1; s_axi_rresp 2b00; // OKAY end default: begin s_axi_rdata 0; s_axi_rresp 2b10; // SLVERR end endcase s_axi_rvalid 1b1; end // 读数据完成握手 else if (r_fire) begin s_axi_rvalid 1b0; end end end这段代码有一个重要特征当RVALID 1 RREADY 0代码不会修改RDATA、RRESP和RVALID因此能够自动满足反压期间保持稳定的要求。八、从写波形理解 AXI-Lite一次 AXI-Lite 写操作包含三个阶段写地址通道 AW 握手写数据通道 W 握手写响应通道 B 握手。重点是写地址和写数据不要求在同一个周期到达。下面是一种合法波形采样上升沿 C0 C1 C2 C3 C4 C5 C6 ---------------------------------------------------------------- AWVALID 0 1 1 0 0 0 0 AWREADY 0 0 1 0 0 0 0 AWADDR - A A - - - - ↑ 写地址握手 WVALID 0 0 0 1 1 0 0 WREADY 1 1 1 0 1 0 0 WDATA - - - D D - - ↑ 写数据握手 BVALID 0 0 0 0 0 1 1 BREADY 1 1 1 1 1 0 1 ↑ 写响应握手C2写地址完成握手在 C2 上升沿AWVALID 1 AWREADY 1从设备接收到写地址A。此时写数据还没有完成握手所以整笔写事务还不能认为已经完成。C4写数据完成握手在 C4 上升沿WVALID 1 WREADY 1从设备接收到写数据D。只有写地址和写数据都接收完成从设备才具备执行写操作并返回写响应的条件。C5从设备产生写响应BVALID 1 BREADY 0从设备已经产生写响应但主设备暂时没有接收。此时从设备必须保持BVALID 1 BRESP 不变C6写响应完成握手BVALID 1 BREADY 1在 C6 上升沿写响应被主设备接收。整笔 AXI-Lite 写事务完成。九、写数据可以先于写地址到达很多新人会默认写操作一定是先 AWADDR 再 WDATA但 AXI 并没有规定必须采用这个顺序。下面三种情况都是合法的。情况一地址先到AW 握手 ↓ W 握手情况二数据先到W 握手 ↓ AW 握手情况三地址和数据同周期到达AW 握手 W 握手因此从设备必须能够独立接收写地址和写数据。十、为什么下面这种写法有问题很多 AXI-Lite 从设备会这样写wire write_en; assign write_en s_axi_awvalid s_axi_awready s_axi_wvalid s_axi_wready;这段代码要求AW 通道握手 和 W 通道握手必须发生在同一个周期。但如果写地址在 C2 握手写数据在 C4 握手C2AWVALID AWREADY 1 C4WVALID WREADY 1那么没有任何一个周期满足四个信号同时为 1。结果就是地址已经接收数据也已经接收write_en却永远没有拉高寄存器没有被写入。这不是 AXI 总线的问题而是从设备 RTL 错误地把两个独立通道绑在了一起。十一、正确处理 AW 和 W 通道的基本思路正确方法是分别记录写地址是否已经收到 写数据是否已经收到例如使用两个标志aw_pending w_pending当地址握手完成aw_pending 1b1;当数据握手完成w_pending 1b1;当两个标志都有效后再执行写操作并产生BVALID。下面是一段只支持一笔未完成写事务的示例。logic aw_pending; logic w_pending; logic [ADDR_WIDTH-1:0] awaddr_q; logic [DATA_WIDTH-1:0] wdata_q; logic [DATA_WIDTH/8-1:0] wstrb_q; wire aw_fire; wire w_fire; wire b_fire; assign s_axi_awready !aw_pending !s_axi_bvalid; assign s_axi_wready !w_pending !s_axi_bvalid; assign aw_fire s_axi_awvalid s_axi_awready; assign w_fire s_axi_wvalid s_axi_wready; assign b_fire s_axi_bvalid s_axi_bready; // 当前周期握手的数据优先否则使用之前缓存的数据 wire [ADDR_WIDTH-1:0] wr_addr; wire [DATA_WIDTH-1:0] wr_data; wire [DATA_WIDTH/8-1:0] wr_strb; assign wr_addr aw_fire ? s_axi_awaddr : awaddr_q; assign wr_data w_fire ? s_axi_wdata : wdata_q; assign wr_strb w_fire ? s_axi_wstrb : wstrb_q; // 包含当前周期刚完成的握手 wire have_aw; wire have_w; wire write_commit; assign have_aw aw_pending || aw_fire; assign have_w w_pending || w_fire; assign write_commit !s_axi_bvalid have_aw have_w; always_ff (posedge ACLK) begin if (!ARESETn) begin aw_pending 1b0; w_pending 1b0; awaddr_q 0; wdata_q 0; wstrb_q 0; s_axi_bvalid 1b0; s_axi_bresp 2b00; reg0 0; end else begin // 独立接收写地址 if (aw_fire) begin awaddr_q s_axi_awaddr; aw_pending 1b1; end // 独立接收写数据和字节使能 if (w_fire) begin wdata_q s_axi_wdata; wstrb_q s_axi_wstrb; w_pending 1b1; end // 地址和数据都收到后执行写操作 if (write_commit) begin aw_pending 1b0; w_pending 1b0; if (wr_addr[5:2] 4h0) begin for (int i 0; i DATA_WIDTH/8; i) begin if (wr_strb[i]) begin reg0[i*8 : 8] wr_data[i*8 : 8]; end end s_axi_bresp 2b00; // OKAY end else begin s_axi_bresp 2b10; // SLVERR end s_axi_bvalid 1b1; end // 写响应完成握手后撤销 BVALID else if (b_fire) begin s_axi_bvalid 1b0; end end end这段代码的重点不是地址译码而是AW 独立接收 W 独立接收 两个通道都完成后才产生 BVALID该示例为了方便理解只允许一笔未完成的写事务。更高吞吐率的设计通常需要加入skid bufferFIFO多笔 outstanding 管理更完整的地址和响应控制。十二、WSTRB 不能随便忽略以 32 位 AXI-Lite 数据总线为例WDATA[31:0] WSTRB[3:0]每一位WSTRB控制一个字节WSTRB 位对应数据WSTRB[0]WDATA[7:0]WSTRB[1]WDATA[15:8]WSTRB[2]WDATA[23:16]WSTRB[3]WDATA[31:24]例如WSTRB 4b0010表示只有WDATA[15:8]这个字节有效。寄存器更新时常见写法如下for (int i 0; i DATA_WIDTH/8; i) begin if (s_axi_wstrb[i]) begin reg_data[i*8 : 8] s_axi_wdata[i*8 : 8]; end end如果完全忽略WSTRB软件执行字节写或半字写时可能意外覆盖其他字节。十三、VALID 连续为 1到底算几次传输不能只看VALID持续了几个周期还要看每个上升沿的READY。情况一READY 一直为 0周期 C1 C2 C3 VALID 1 1 1 READY 0 0 0发生传输次数0 次因为没有任何一个上升沿满足VALID READY情况二VALID 和 READY 连续为 1周期 C1 C2 C3 VALID 1 1 1 READY 1 1 1发生传输次数3 次每个上升沿都会完成一次独立传输。如果发送方只想发送一次数据那么第一次握手完成后就应该撤销VALID。如果发送方希望连续发送三笔数据可以保持VALID1并在每次握手后更新下一笔数据。十四、如何在 RTL 中生成正确的 VALID下面以主设备产生AWVALID为例。wire aw_fire; assign aw_fire m_axi_awvalid m_axi_awready; always_ff (posedge ACLK) begin if (!ARESETn) begin m_axi_awvalid 1b0; m_axi_awaddr 0; end else begin // 没有等待中的地址时装载一笔新请求 if (!m_axi_awvalid start_write) begin m_axi_awvalid 1b1; m_axi_awaddr write_addr; end // 完成握手后才能撤销 VALID else if (aw_fire) begin m_axi_awvalid 1b0; end end end关键点是else if (aw_fire)而不是else m_axi_awvalid 1b0;当AWVALID 1 AWREADY 0时AWVALID和AWADDR必须继续保持。十五、用断言检查反压期间是否保持稳定仿真时可以使用 SystemVerilog Assertion 检查 AXI 握手。检查写地址保持稳定assert property ( (posedge ACLK) disable iff (!ARESETn) m_axi_awvalid !m_axi_awready | m_axi_awvalid $stable(m_axi_awaddr) );检查读数据保持稳定assert property ( (posedge ACLK) disable iff (!ARESETn) s_axi_rvalid !s_axi_rready | s_axi_rvalid $stable({s_axi_rdata, s_axi_rresp}) );检查写响应保持稳定assert property ( (posedge ACLK) disable iff (!ARESETn) s_axi_bvalid !s_axi_bready | s_axi_bvalid $stable(s_axi_bresp) );这些断言主要检查当 VALID 已经拉高但 READY 还没有拉高时发送方有没有提前撤销 VALID或者修改有效载荷。十六、看 AXI-Lite 波形的正确步骤以后在 Vivado、Verdi、QuestaSim 或 ModelSim 中看 AXI-Lite 波形可以按照下面的顺序。第一步把五个通道分组不要把所有信号混在一起。建议按下面顺序排列AWADDR AWVALID AWREADY WDATA WSTRB WVALID WREADY BRESP BVALID BREADY ARADDR ARVALID ARREADY RDATA RRESP RVALID RREADY第二步只寻找 VALID 和 READY 同时为 1 的上升沿可以在脑中定义aw_fire AWVALID AWREADY; w_fire WVALID WREADY; b_fire BVALID BREADY; ar_fire ARVALID ARREADY; r_fire RVALID RREADY;先把所有fire时刻找出来再分析地址和数据。第三步检查等待期间的数据是否稳定当发现VALID 1 READY 0立刻检查对应载荷AWADDR、AWPROTWDATA、WSTRBBRESPARADDR、ARPROTRDATA、RRESP。这些信号必须保持稳定。第四步写操作分别检查 AW 和 W不要只找AWVALID AWREADY WVALID WREADY应该分别确认AW 是否完成过握手 W 是否完成过握手两者可以出现在不同周期。第五步检查响应有没有被接收写请求完成后还需要检查BVALID BREADY读请求完成后还需要检查RVALID RREADY只看到BVALID或RVALID拉高并不代表响应已经被接收。十七、AXI-Lite 新人最常见的七个误区误区一READY 为 1 就代表发生了传输错误。还需要同时满足VALID 1误区二VALID 拉高一个周期就够了错误。如果这一周期READY0就没有完成传输。误区三写地址和写数据必须同周期到达错误。AW 和 W 是两个独立通道。误区四BVALID 可以只打一拍错误。如果BREADY0必须继续保持BVALID和BRESP。误区五RREADY 为 0 时可以更新 RDATA错误。只要RVALID1且没有握手RDATA和RRESP就必须保持稳定。误区六VALID 连续高三拍只算一次传输不一定。如果 READY 也连续高三拍那么会发生三次传输。误区七AXI-Lite 协议只能有一笔未完成事务不准确。AXI-Lite 没有 AXI ID事务必须保持顺序但协议本身允许存在多笔未完成事务。只是很多简单的自定义 AXI-Lite 从设备会通过控制READY限制为一次只处理一笔事务。十八、最后总结看 AXI-Lite 波形只需要抓住下面几句话1. AXI-Lite 有五个独立通道。 2. 每个通道都有自己的 VALID/READY 握手。 3. 只有在上升沿 VALID 和 READY 同时为 1才完成一次传输。 4. 发送方不能等待 READY 后才拉高 VALID。 5. VALID 拉高后必须保持到握手完成。 6. VALID1、READY0 时地址、数据和响应必须保持稳定。 7. 写地址 AW 和写数据 W 可以在不同周期完成握手。 8. BVALID 必须等写地址和写数据都被接收后才能产生。AXI-Lite 看起来信号很多但真正的核心只有一个fire valid ready;把五个通道的fire分别找出来绝大多数 AXI-Lite 波形问题都会变得清楚。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门