AXI總線 詳細整理
- AXI總線概述
- 時鐘與復位
- AXI的5個通道
- 寫入資料的流程
- 讀取資料的流程
- 握手依賴關系
- 突發傳輸機制
- 讀/寫回應結構
- Outstanding、Out-of-Order、Interleaving
- AXI4、AXI4-Lite、AXI4-Stream
- AXI4仿真實體
- AXI4-Lite仿真實體
- AXI4-Stream仿真實體
AXI總線概述
AXI(Advanced eXtensible Interface)總線是AMBA總線架構中,最新并且性能做好的一個總線標準,AXI的設計目標是可以在高時鐘頻率下運行,并在延滯時間長的狀況下仍可達成高資料吞吐率,AXI總線將讀/寫請求與讀/寫結果相互分離、將資料寫入和資料讀出的信號相分離,可以同時進行寫入和讀出動作,從而最大限度地提高總線的資料吞吐率,
由若干master設備和slave設備通過一些形式的interconnect組成的典型的系統如下圖所示,AXI總線即可作為其中的Interface,實作資料通信,

amba_axi_protocol_spec的下載地址:
https://static.docs.arm.com/ihi0022/g/IHI0022G_amba_axi_protocol_spec.pdf?_ga=2.83582867.447931395.1591251023-1939436544.1591251023
時鐘與復位
Clock
每個AXI interface都有一個時鐘信號ACLK,所有的輸入信號在ACLK的上升沿采樣,所有的輸出信號在ACLK的上升沿之后發生變化,
在master和slave interface上,輸入與輸出信號之間必須沒有組合邏輯路徑,
Reset
AXI協議使用低電平有效的復位信號ARESETn,復位信號可以被異步置位,但是復位的釋放必須與ACLK的上升沿是同步的,
During reset,需要遵循下面的interface requirement:
(1)A master interface must drive ARVALID, AWVALID, and WVALID LOW.
(2)A slave interface must drive RVALID and BVALID LOW.
(3)All other signals can be driven to any value.
在復位之后,master被允許驅動ARVALID, AWVALID, or WVALID 為高電平的最早的時刻是ARESETn為高電平之后的一個ACLK的上升沿,如下圖所示,

AXI的5個通道
AXI總線的master和slave的埠分為5個雙向流量控制的通道,如下圖所示,所謂雙向流量控制是指發送端用valid表示資料是有效的,接收端用ready表示可以接受資料;只有在vaild和ready同時為1時,資料才成功被傳送,vaild/ready機制可以使發送接收雙方都有能力控制傳輸速率,
AXI規定:
(1)A source is not permitted to wait until READY is asserted before asserting VALID .
(2)When VALID is asserted, it must remain asserted until the handshake occurs.


寫入資料的流程
(1)master通過寫地址通道發出寫入請求;
(2)master通過寫資料通道發送寫入的資料;
(3)slave在完成寫入動作后(寫資料通道last),通過寫回應通道發回確認資訊,

讀取資料的流程
(1)master通過讀地址通道發出讀取請求;
(2)slave通過讀資料通道將讀取的資料傳給master,

握手依賴關系
在下面的依賴關系圖中:單箭頭信號表示其指向的信號可以在箭頭起始信號置起之前或之后置起;雙箭頭信號表示其指向的信號必須在箭頭起始信號置起之后置起,
(1)讀傳輸事務依賴關系
下圖表示,RVALID必須等到ARVALID和ARREADY握手后才可以置位,

(2)AXI3寫傳輸事務依賴關系
slave必須等待master的WLAST信號置位,才能將BVALID置位,

(3)AXI4和AXI5寫傳輸事務依賴關系
slave必須等待master的WLAST信號置位,才能將BVALID置位,

突發傳輸機制
無論是讀寫操作,AXI 總線支持,或者說基于突發傳輸(Burst Transaction),
突發傳輸的流程:
a.主機在讀/寫地址通道寫入起始地址(AxADDR)以及突發傳輸的長度(AxLEN)、寬度(AxSIZE)、型別(AxBURST)等資訊;
b.從機將在起始地址開始,依次接收主機傳輸的寫資料,或者讀取連續地址上的資料,作為讀資料傳輸給主機,
注意:單次 burst 傳輸中的資料,其地址不能跨越 4KB 邊界,
(1)突發傳輸長度
突發傳輸長度(burst length),指一次突發傳輸中包含的資料傳輸(transfer)數量,在協議中使用AxLEN信號控制(AWLEN和WRLEN),



突發傳輸長度在不同的模式下有一些限制,包括:
a.對于WRAP模式,突發傳輸長度僅能為2,4,8,16
b.在一次突發傳輸中,地址不能跨越4KB地址邊界
c.一次突發傳輸不能在完成所有資料傳輸前提前結束
(協議中多次強調,通信雙方都不能在傳輸事務的所有 Transfer 完成前提前結束,哪怕發生錯誤,也要走完整個傳輸事務的流程,但是主機也有辦法減少傳輸的資料,在寫傳輸事務中,發送方可以通過置低所有的寫有效位(WSTRB),使寫資料無效,在讀傳輸事務中,主機可以直接丟棄讀取到的資料,)
(2)突發傳輸寬度
突發傳輸寬度(burst size),指傳輸中的資料位寬,具體地,是每周期傳輸資料的位元組數量,在協議中使用AxSIZE信號控制(AWSIZE和ARSIZE),



突發傳輸資料寬度不能超過資料總線本身的位寬,而當資料總線位寬大于突發傳輸寬度時,將根據協議的相關規定,將資料在部分資料線上傳輸(A3.4.3章節Narrow transfers),


(3)突發傳輸型別
突發傳輸型別(burst type),型別共有 3 種,分別為 FIXED,INCR 以及 WRAP,使用 2 位二進制表示,在協議中使用 AxBURST信號控制(AWBURST和ARBURST),



FIXED 型別: burst 中所有資料都使用起始地址,該模式適合對某個固定地址進行多次資料更新,比如讀寫一個FIFO時,讀寫地址就是固定的,
INCR 型別最為常用:后續資料的地址在初始地址的基礎上進行遞增,遞增幅度與傳輸寬度相同,適合對于RAM、DDR等通過地址映射(mapped memory)的存盤介質進行讀寫操作,
WRAP 型別:比較特殊,首先根據起始地址得到繞回邊界地址(wrap boundary)與最高地址,當前地址小于最高地址時,WRAP 與 INCR 型別完全相同,地址遞增,但到遞增后的地址到達最高地址后,地址直接回到繞回邊界地址,再進行遞增,就這樣回圈往復,最高地址由繞回邊界地址計算得到:wrap boundary + (N_bytes x burst_len),根據協議手冊上表示,WRAP 適合對 cache 的訪問,
讀/寫回應結構
AXI協議提供讀/寫事務的回應信號:對于讀操作,回應信號在讀地址通道RRESP[1:0];對于寫操作,回應信號在寫回應通道BRESP[1:0],

OKAY 常規訪問成功
EXOKAY 獨占訪問成功
SLVERR 從機錯誤
DECERR 解碼錯誤
注意:在寫傳輸事務中,單個寫回復針對的是整個 burst,而不是 burst 中單個 transfer,但是在讀傳輸事務中,從機可以為突發傳輸中每一個讀傳輸資料產生不同的讀回復信號,
Outstanding、Out-of-Order、Interleaving

Outstanding:master 不必等待命令執行結束就可以發送下一命令
Out-of-Order:對于相同ID的指令,必須要順序完成;對于不同ID的指令,可以亂序完成,
Interleaving:亂序傳輸時不同ID之間的資料可以內插,但是要保證每個ID的資料順序,
(注意:AXI4的WID信號被去掉了,因此AXI4的寫資料通道不能實作Out-of-Order和Interleaving)

Outstanding是指正在進行中的,未完成的,形象表達就是說“在路上”,相比于正常情況下的主機和從機的讀寫操作,如果outstanding能力N>1,寫操作:主機可以連續發出N組寫地址和寫資料,這期間如果沒有寫回應回傳,則等待,如果有寫回應回傳,則回傳了幾個就可以接著發幾組,也就是說在路上的寫回應最多是N,對于讀操作是同樣的道理,
當一個master向一個或多個slave發出資料塊讀/寫請求時,AXI總線要求在同一個資料塊內,每一個資料的讀/寫的完成必須按照順序,但資料塊之間,或讀取與寫入之間,AXI總線并不要求完成讀/寫的順序與發出讀/寫請求的順序保持一致,也就是允許讀/寫事務的亂序(Out-of-Order),AXI總線的每個通道上的每一個讀/寫請求、資料結果,以及寫入回應都包括一個4bit的讀/寫事務識別序號ID[3:0],根據識別序號,主控模塊可以辨別接收到的讀取資料或是寫入回應與發出的讀/寫請求之間的對應關系,不同ID的資料可以內插(Interleaving),通過ID號可以對資料進行識別,
AXI4、AXI4-Lite、AXI4-Stream

AXI4仿真實體
由于Vivado軟體中包含很多AXI協議的IP核,因此在Vivado中進行仿真,
在Vivado2019.1中,呼叫AXI BRAM Controller (4.1) IP核,
設定Memory Depth 為262144,
BRAM Instance 選擇Internal,

tb_axi_bram.v
`timescale 1ns / 1ps
// Company:
// Engineer:
//
// Create Date: 2020/12/22
// Author Name: Sniper
// Module Name: tb_axi_bram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module tb_axi_bram;
//parameter
parameter ADDR_WIDTH = 20;
parameter DATA_WIDTH = 32;
//input
reg aclk;
reg aresetn;
// write address channel
wire [0:0] m_axi_awid;
wire [ADDR_WIDTH-1:0] m_axi_awaddr;
wire [7:0] m_axi_awlen; // Burst Length: 0-255
wire [2:0] m_axi_awsize; // Burst Size: Fixed 3'b011
wire [1:0] m_axi_awburst; // Burst Type: Fixed 2'b01(Incremental Burst)
wire m_axi_awlock; // Lock: Fixed 1'b0
wire [3:0] m_axi_awcache; // Cache: Fiex 4'b0011
wire [2:0] m_axi_awprot; // Protect: Fixed 3'b000
wire [3:0] m_axi_awqos; // QoS: Fixed 4'b0000
wire [0:0] m_axi_awuser; // User: Fixed 32'd0
wire m_axi_awvalid;
wire m_axi_awready;
// write data channel
wire [DATA_WIDTH-1:0] m_axi_wdata;
wire [DATA_WIDTH/8-1:0] m_axi_wstrb;
wire m_axi_wlast;
wire [0:0] m_axi_wuser;
wire m_axi_wvalid;
wire m_axi_wready;
// write response channel
wire [0:0] m_axi_bid;
wire [1:0] m_axi_bresp;
wire [0:0] m_axi_buser;
wire m_axi_bvalid;
wire m_axi_bready;
// read address channel
wire [0:0] m_axi_arid;
wire [ADDR_WIDTH-1:0] m_axi_araddr;
wire [7:0] m_axi_arlen;
wire [2:0] m_axi_arsize;
wire [1:0] m_axi_arburst;
wire m_axi_arlock;
wire [3:0] m_axi_arcache;
wire [2:0] m_axi_arprot;
wire [3:0] m_axi_arqos;
wire [0:0] m_axi_aruser;
wire m_axi_arvalid;
wire m_axi_arready;
// read data channel
wire [0:0] m_axi_rid;
wire [DATA_WIDTH-1:0] m_axi_rdata;
wire [1:0] m_axi_rresp;
wire m_axi_rlast;
wire [0:0] m_axi_ruser;
wire m_axi_rvalid;
wire m_axi_rready;
// User-interface
reg master_rst;
reg wr_start;
reg [ADDR_WIDTH-1:0] wr_addrs;
reg [7:0] wr_len;
reg [DATA_WIDTH-1:0] wr_data;
wire wr_handshake;
wire wr_busy;
wire wr_done;
reg rd_start;
reg [ADDR_WIDTH-1:0] rd_addrs;
reg [7:0] rd_len;
wire [DATA_WIDTH-1:0] rd_data;
wire rd_handshake;
wire rd_busy;
wire rd_done;
initial
begin
aresetn = 0;
aclk = 0;
master_rst = 0;
wr_start = 0;
wr_addrs[ADDR_WIDTH-1:0] = 0;
wr_len[7:0] = 0;
wr_data[DATA_WIDTH-1:0] = 0;
#100;
@(posedge aclk);
aresetn <= 1;
@(posedge aclk);
wr_addrs <= 0;
wr_len[7:0] <= 15;
@(posedge aclk);
if(!wr_busy)
wr_start <= 1;
@(posedge aclk);
wr_start <= 0;
repeat(30) @(posedge aclk);
wr_addrs <= 1024;
wr_len[7:0] <= 255;
@(posedge aclk);
if(!wr_busy)
wr_start <= 1;
@(posedge aclk);
wr_start <= 0;
end
initial
begin
rd_start = 0;
rd_addrs[ADDR_WIDTH-1:0] = 0;
rd_len[7:0] = 0;
repeat(500) @(posedge aclk);
@(posedge aclk);
rd_addrs <= 1024;
rd_len[7:0] <= 255;
@(posedge aclk);
if(!rd_busy)
rd_start <= 1;
@(posedge aclk);
rd_start <= 0;
repeat(300) @(posedge aclk);
@(posedge aclk);
rd_addrs <= 0;
rd_len[7:0] <= 15;
@(posedge aclk);
if(!rd_busy)
rd_start <= 1;
@(posedge aclk);
rd_start <= 0;
end
//clock
always #5 aclk = ~aclk;
always @(posedge aclk)
if(wr_handshake)
wr_data = wr_data + 1;
// AXI WRITE //
localparam W_IDLE = 3'd0;
localparam WA_WAIT = 3'd1;//write address
localparam WA_START = 3'd2;
localparam WD_WAIT = 3'd3;//write data
localparam WD_PROC = 3'd4;
localparam WR_WAIT = 3'd5;//write response
localparam WR_DONE = 3'd6;
reg [2:0] wr_state;
reg [ADDR_WIDTH-1:0] reg_wr_addrs;
reg [7:0] reg_wr_len;
reg reg_awvalid;
reg reg_wvalid;
reg reg_w_last;
// Write State
always @(posedge aclk or negedge aresetn)
begin
if(!aresetn)
begin
wr_state <= W_IDLE;
reg_wr_addrs <= 0;
reg_wr_len <= 0;
reg_awvalid <= 0;
reg_wvalid <= 0;
reg_w_last <= 0;
end
else
begin
if(master_rst)
begin
wr_state <= W_IDLE;
end
else
begin
case(wr_state)
W_IDLE:
begin
if(wr_start)
begin
wr_state <= WA_WAIT;
reg_wr_addrs <= wr_addrs;
reg_wr_len <= wr_len;
end
reg_awvalid <= 0;
reg_wvalid <= 0;
reg_w_last <= 0;
end
WA_WAIT:
begin
wr_state <= WA_START;
end
WA_START:
begin
wr_state <= WD_WAIT;
reg_awvalid <= 1;
end
WD_WAIT:
begin
if(m_axi_awvalid & m_axi_awready)
begin
wr_state <= WD_PROC;
reg_awvalid <= 0;
reg_wvalid <= 1;
end
end
WD_PROC:
begin
if(m_axi_wvalid & m_axi_wready)
begin
if(reg_wr_len == 0)
begin
wr_state <= WR_WAIT;
reg_wvalid <= 0;
end
else
reg_wr_len <= reg_wr_len - 1;
if(reg_wr_len == 1)
reg_w_last <= 1;
else
reg_w_last <= 0;
end
end
WR_WAIT:
begin
if(m_axi_bvalid & m_axi_bready)
begin
//reg_wr_status[1:0] <= m_axi_bresp[1:0];
//if(reg_w_last)
//begin
wr_state <= WR_DONE;
//end
//else
//begin
//wr_state <= WA_WAIT;
//reg_wr_addrs <= reg_wr_addrs + 32'd2048;
//end
end
end
WR_DONE:
begin
wr_state <= W_IDLE;
end
default:
begin
wr_state <= W_IDLE;
end
endcase
end//if(master_rst)
end//if(!aresetn)
end
assign m_axi_awid = 1'b0;
assign m_axi_awaddr[ADDR_WIDTH-1:0] = reg_wr_addrs[ADDR_WIDTH-1:0];
assign m_axi_awlen[7:0] = reg_wr_len[7:0];
assign m_axi_awsize[2:0] = $clog2(DATA_WIDTH/8);//3'b010;
assign m_axi_awburst[1:0] = 2'b01;
assign m_axi_awlock = 1'b0;
assign m_axi_awcache[3:0] = 4'b0011;
assign m_axi_awprot[2:0] = 3'b000;
assign m_axi_awqos[3:0] = 4'b0000;
assign m_axi_awuser[0] = 1'b0;
assign m_axi_awvalid = reg_awvalid;
assign m_axi_wdata[DATA_WIDTH-1:0] = wr_data[DATA_WIDTH-1:0];
assign m_axi_wstrb[DATA_WIDTH/8-1:0] = {(DATA_WIDTH/8){1'b1}};
assign m_axi_wlast = reg_w_last;
assign m_axi_wuser = 0;
assign m_axi_wvalid = reg_wvalid;
assign m_axi_bready = m_axi_bvalid;
assign wr_handshake = m_axi_wvalid & m_axi_wready;
assign wr_busy = (wr_state == W_IDLE)? 1'b0 : 1'b1;
assign wr_done = (wr_state == WR_DONE);
// AXI READ //
localparam R_IDLE = 3'd0;
localparam RA_WAIT = 3'd1;
localparam RA_START = 3'd2;
localparam RD_WAIT = 3'd3;
localparam RD_PROC = 3'd4;
localparam RD_DONE = 3'd5;
reg [2:0] rd_state;
reg [ADDR_WIDTH-1:0] reg_rd_addrs;
reg [7:0] reg_rd_len;
reg reg_arvalid;
reg reg_r_last;
// Read State
always @(posedge aclk or negedge aresetn)
begin
if(!aresetn)
begin
rd_state <= R_IDLE;
reg_rd_addrs <= 0;
reg_rd_len <= 0;
reg_arvalid <= 0;
end
else
begin
case(rd_state)
R_IDLE:
begin
if(rd_start)
begin
rd_state <= RA_WAIT;
reg_rd_addrs <= rd_addrs;
reg_rd_len <= rd_len;
end
reg_arvalid <= 0;
end
RA_WAIT:
begin
rd_state <= RA_START;
end
RA_START:
begin
rd_state <= RD_WAIT;
reg_arvalid <= 1;
end
RD_WAIT:
begin
if(m_axi_arvalid & m_axi_arready)
begin
rd_state <= RD_PROC;
reg_arvalid <= 0;
end
end
RD_PROC:
begin
if(m_axi_rvalid & m_axi_rready)
begin
if(m_axi_rlast & reg_r_last)
rd_state <= RD_DONE;
else
reg_rd_len <= reg_rd_len - 1;
if(reg_rd_len == 1)
reg_r_last <= 1;
else
reg_r_last <= 0;
end
end
RD_DONE:
begin
rd_state <= R_IDLE;
end
endcase
end//if(!aresetn)
end
// Master Read Address
assign m_axi_arid = 1'b0;
assign m_axi_araddr[ADDR_WIDTH-1:0] = reg_rd_addrs;
assign m_axi_arlen[7:0] = reg_rd_len[7:0];
assign m_axi_arsize[2:0] = $clog2(DATA_WIDTH/8);//3'b010;
assign m_axi_arburst[1:0] = 2'b01;
assign m_axi_arlock = 1'b0;
assign m_axi_arcache[3:0] = 4'b0011;
assign m_axi_arprot[2:0] = 3'b000;
assign m_axi_arqos[3:0] = 4'b0000;
assign m_axi_aruser[0] = 1'b0;
assign m_axi_arvalid = reg_arvalid;
assign m_axi_rready = m_axi_rvalid;
assign rd_handshake = m_axi_rvalid & m_axi_rready;
assign rd_busy = (rd_state == R_IDLE)? 1'b0 : 1'b1;
assign rd_done = (rd_state == RD_DONE);
//DUT
axi_bram_ctrl_0 U_axi_bram_ctrl_0
(
//global signal
.s_axi_aclk(aclk),
.s_axi_aresetn(aresetn),
//write address channel
.s_axi_awaddr(m_axi_awaddr),//[19:0]
.s_axi_awburst(m_axi_awburst),//[1:0]
.s_axi_awcache(m_axi_awcache),//[3:0]
.s_axi_awlen(m_axi_awlen),//[7:0]
.s_axi_awlock(m_axi_awlock),
.s_axi_awprot(m_axi_awprot),//[2:0]
.s_axi_awready(m_axi_awready),
.s_axi_awsize(m_axi_awsize),//[2:0]
.s_axi_awvalid(m_axi_awvalid),
//write data channel
.s_axi_wdata(m_axi_wdata),//[31:0]
.s_axi_wlast(m_axi_wlast),
.s_axi_wready(m_axi_wready),
.s_axi_wstrb(m_axi_wstrb),//[3:0]
.s_axi_wvalid(m_axi_wvalid),
//write response channel
.s_axi_bready(m_axi_bready),
.s_axi_bresp(m_axi_bresp),//[1:0]
.s_axi_bvalid(m_axi_bvalid),
//read address channel
.s_axi_araddr(m_axi_araddr),//[19:0]
.s_axi_arburst(m_axi_arburst),//[1:0]
.s_axi_arcache(m_axi_arcache),//[3:0]
.s_axi_arlen(m_axi_arlen),//[7:0]
.s_axi_arlock(m_axi_arlock),
.s_axi_arprot(m_axi_arprot),//[2:0]
.s_axi_arready(m_axi_arready),
.s_axi_arsize(m_axi_arsize),//[2:0]
.s_axi_arvalid(m_axi_arvalid),
//read data channel
.s_axi_rdata(m_axi_rdata),//[31:0]
.s_axi_rlast(m_axi_rlast),
.s_axi_rready(m_axi_rready),
.s_axi_rvalid(m_axi_rvalid)
);
/*
initial
begin
$dumpfile("tb_axi_bram.vcd");
$dumpvars(0,tb_axi_bram);
end
initial #1000 $finish;
*/
endmodule
仿真結果:
(1)burst寫入資料
仿真波形與“寫資料的流程”是一致的,


(2)burst讀取資料
仿真波形與“讀資料的流程”是一致的,


AXI4-Lite仿真實體

在Vivado2019.1中,呼叫AXI Uartlite (2.0) IP核,配置如下,

寫資料時序如下所示:(可以看出AWREADY和WREADY是同時反饋的,表示地址和資料是同時寫入;BVALID在下一個時鐘回傳)

AXI4-Stream仿真實體
AXI4-Stream協議比較簡單就不做實體仿真了,只要valid和ready同時為1,就一直寫入資料,直到valid & ready & last,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/239193.html
標籤:其他
上一篇:亞馬遜無貨源是不是適合新手?
