目的
AXIを理解する
対象
FPGA初心者の方
目次
AXIプロトコル
AXIプロトコルでは、5つのチャネルが定義されます。
※ここで言う「チャネル」とは、 AXI4のプロトコルレベルで特定の種類のデータを伝送する論理的な経路を言います。
他の方の説明を見ると、「バス」と言っている方もいますが、「バス」はその論理的構造を物理的に実装したものを指す場合が多いです。
2 つは読み出しトランザクションに使用されます。
- 読み出しアドレス (Address and control)
- 読み出しデータ (Read data)
マスターがアドレスを送り付けて、スレーブがデータを返します。
3 つは書き込みトランザクションに使用されます。
- 書き込みアドレス (Address and control)
- 書き込みデータ (Write data)
- 書き込み応答 (Write response)
マスターがアドレスとデータを書き込み、スレーブがレスポンスを返します。
重要なポイントとして、
5つのチャネルはそれぞれ独立して動作し、それぞれが個別のハンドシェイクを行う必要があります。
※AXIにおいてのハンドシェイクはValid信号とReady信号を用いて行われるものです。
ハンドシェイクについて詳しくはAXIの基礎を理解するをご確認ください。
5つのチャネルが独立して、ハンドシェイクするイメージ図は下記のようなものです。
AXIの比較
AXI4はAXI-LiteやAXI-Streamと比較して、複雑になりますがパフォーマンスに優れています。
下記にAXI4とAXI-Liteの比較を行っています。
特徴/項目 | AXI4 | AXI4-Lite |
---|---|---|
バースト長 | 最大256ワードのバースト長をサポート | 単一ワードトランザクションのみ |
データバス幅 | 最大128バイト(2のべき乗での使用が可能) | 32ビットまたは64ビット(全幅を使用) |
追加機能(トランザクションID、QoS、ユーザー情報の転送) | サポート | サポートなし、または制限あり |
ロジック規模 | 大規模なロジックを必要とする | 小規模なロジックで済む |
使用用途 | 高機能が必要な場合に使用 | シンプルな制御や低機能が必要な場合に使用 |
Verilog HDL による記述
AXI4のスレーブ側の記述
`timescale 1ns / 1ps
module axi4_slave
(
input wire s_axi_aclk, // AXIクロック信号
input wire s_axi_aresetn, // 非同期リセット信号(低アクティブ)
// 書き込みアドレスチャネルの信号群
input wire [2:0] s_axi_awid, // 書き込みアドレスID
input wire s_axi_awvalid, // 書き込みアドレスが有効であることを示す信号
output reg s_axi_awready, // スレーブが書き込みアドレスを受け入れ可能であることを示す信号
input wire [23:0] s_axi_awaddr, // 書き込みアドレス
input wire [7:0] s_axi_awlen, // 書き込みバーストの長さ
input wire [2:0] s_axi_awsize, // 書き込みデータのサイズ
input wire [1:0] s_axi_awburst, // 書き込みバーストタイプ
input wire [1:0] s_axi_awlock, // ロック信号
input wire [3:0] s_axi_awcache, // キャッシュ制御信号
input wire [2:0] s_axi_awprot, // 保護制御信号
input wire [3:0] s_axi_awqos, // Quality of Service
input wire [4:0] s_axi_awuser, // ユーザー定義信号
// 書き込みデータチャネルの信号群
input wire [2:0] s_axi_wid, // 書き込みデータID
input wire s_axi_wvalid, // 書き込みデータが有効であることを示す信号
output reg s_axi_wready, // スレーブが書き込みデータを受け入れ可能であることを示す信号
input wire [31:0] s_axi_wdata, // 書き込みデータ
input wire [3:0] s_axi_wstrb, // 書き込みストローブ信号(どのバイトが有効かを示す)
input wire s_axi_wlast, // 書き込みバーストの最後であることを示す信号
// 書き込み応答チャネルの信号群
output reg [2:0] s_axi_bid, // 書き込み応答ID
output reg s_axi_bvalid, // 書き込み応答が有効であることを示す信号
input wire s_axi_bready, // マスターが書き込み応答を受け入れ可能であることを示す信号
output reg [1:0] s_axi_bresp, // 書き込み応答ステータス
// 読み込みアドレスチャネルの信号群
input wire [2:0] s_axi_arid, // 読み込みアドレスID
input wire s_axi_arvalid, // 読み込みアドレスが有効であることを示す信号
output reg s_axi_arready, // スレーブが読み込みアドレスを受け入れ可能であることを示す信号
input wire [23:0] s_axi_araddr, // 読み込みアドレス
input wire [7:0] s_axi_arlen, // 読み込みバーストの長さ
input wire [2:0] s_axi_arsize, // 読み込みデータのサイズ
input wire [1:0] s_axi_arburst, // 読み込みバーストタイプ
input wire [1:0] s_axi_arlock, // ロック信号
input wire [3:0] s_axi_arcache, // キャッシュ制御信号
input wire [2:0] s_axi_arprot, // 保護制御信号
input wire [3:0] s_axi_arqos, // Quality of Service
input wire [4:0] s_axi_aruser, // ユーザー定義信号
// 読み込みデータチャネルの信号群
output reg [2:0] s_axi_rid, // 読み込みデータID
output reg s_axi_rvalid, // 読み込みデータが有効であることを示す信号
input wire s_axi_rready, // マスターが読み込みデータを受け入れ可能であることを示す信号
output reg [31:0] s_axi_rdata, // 読み込みデータ
output reg s_axi_rlast, // 読み込みバーストの最後であることを示す信号
output reg [1:0] s_axi_rresp // 読み込み応答ステータス
);
parameter BUSY_SLAVE_TEST = 1; // スレーブがビジー状態かをテストするためのパラメータ
reg s_axis_aresetn_reg; // リセット信号のレジスタ
reg [6:0] memory_address; // メモリアドレス
reg [31:0] data_memory [0:127]; // メモリブロックの宣言
reg [23:0] read_address; // 読み込みアドレスの保持用レジスタ
reg [23:0] write_address; // 書き込みアドレスの保持用レジスタ
reg [31:0] write_data; // 書き込みデータの保持用レジスタ
reg write_assert; // 書き込みアサートフラグ
wire write_address_inrange; // 書き込みアドレスが有効範囲内かどうかを示す信号
wire read_address_inrange; // 読み込みアドレスが有効範囲内かどうかを示す信号
localparam OKAY = 2'b00; // 応答が正常な場合のレスポンスコード
localparam DECERR = 2'b11; // デコーディングエラーのレスポンスコード
reg [3:0] timer; // タイマー
localparam TIMEOUT = 15; // タイムアウト値
reg [7:0] burst_count; // バーストカウント
reg [5:0] lfsr = 6'b100100; // 線形フィードバックシフトレジスタ(LFSR)
wire slave_ready; // スレーブが準備完了かどうかを示す信号
reg slave_was_ready; // スレーブが以前に準備完了だったかを示す信号
localparam WRITE_BASE_ADDRESS = 24'h000000; // 書き込み開始アドレス
localparam WRITE_LAST_ADDRESS = 24'h000200; // 書き込み終了アドレス
localparam READ_BASE_ADDRESS = 24'h000000; // 読み込み開始アドレス
localparam READ_LAST_ADDRESS = 24'h000200; // 読み込み終了アドレス
// 状態の定義
localparam INIT = 0, WRR_READY = 1, WADDR_ACCEPT = 2, WADDR_INRANGE = 3, WADDR_ERROR = 4,
WRITE_READY = 5, DATA_WRITE = 6, WMAST_STALL = 7, WSLAVE_STALL = 8,
WRITE_LAST = 9, WRITE_ERROR = 10,
BRESP_VALID = 11, BRESP_ACCEPT = 12, BRESP_ERROR = 13,
RADDR_ACCEPT = 14, RADDR_INRANGE = 15, RADDR_ERROR = 16,
RDATA_VALID = 17, RMAST_STALL = 18, RSLAVE_STALL = 19,
RDATA_LAST = 20, RDATA_ERROR = 21;
reg [4:0] state, next_state; // 状態と次の状態を保持するレジスタ
reg [31:0] control_register; // コントロールレジスタ
reg [31:0] status_register; // ステータスレジスタ
// クロック信号の立ち上がりエッジでリセットおよびLFSRの制御を行う
always @(posedge s_axi_aclk)
begin
s_axis_aresetn_reg <= s_axi_aresetn;
slave_was_ready <= slave_ready;
if (s_axi_aresetn == 0)
lfsr <= 6'b110101;
else if ((next_state == WRITE_READY) || (next_state == RADDR_INRANGE))
lfsr[5:4] <= 2'b11;
else
lfsr[0] <= lfsr[5] ^ lfsr[4] ^ 1'b1;
lfsr[5:1] <= lfsr[4:0];
end
// スレーブの準備完了信号をLFSRの値に基づいて決定する
assign slave_ready = BUSY_SLAVE_TEST ? lfsr[5] : 1;
// 状態遷移ロジック
always @(posedge s_axi_aclk)
begin
s_axis_aresetn_reg <= s_axi_aresetn;
if (s_axi_aresetn == 0)
state <= INIT;
else
state <= next_state;
end
// 書き込みアドレスが有効範囲内かどうかの判定
assign write_address_inrange = ((write_address >= WRITE_BASE_ADDRESS)
&& (write_address <= WRITE_LAST_ADDRESS)) ? 1 : 0;
// 読み込みアドレスが有効範囲内かどうかの判定
assign read_address_inrange = ((read_address >= READ_BASE_ADDRESS)
&& (read_address <= READ_LAST_ADDRESS)) ? 1 : 0;
// 次の状態を決定するための組み合わせ回路
always @(*) begin
next_state = state;
case (state)
INIT:
next_state = WRR_READY; // 初期化後、準備完了状態へ遷移
WRR_READY:
if (s_axi_awvalid == 1)
next_state = WADDR_ACCEPT; // 書き込みアドレスが有効ならアドレス受け入れ状態へ遷移
else if (s_axi_arvalid == 1)
next_state = RADDR_ACCEPT; // 読み込みアドレスが有効ならアドレス受け入れ状態へ遷移
WADDR_ACCEPT:
if (write_address_inrange == 1)
next_state = WADDR_INRANGE; // 書き込みアドレスが範囲内なら次の状態へ
else
next_state = WADDR_ERROR; // アドレスが範囲外ならエラー状態へ遷移
WADDR_INRANGE:
if (slave_ready == 1)
next_state = WRITE_READY; // スレーブが準備完了なら書き込み準備状態へ遷移
WADDR_ERROR:
next_state = BRESP_VALID; // 書き込みエラー応答を送信
WRITE_READY:
if ((s_axi_wvalid == 1) && ((burst_count == 0) || (s_axi_wlast == 1)))
next_state = WRITE_LAST; // 最後のデータ書き込みへ
else if ((s_axi_wvalid == 1) && (slave_ready == 1))
next_state = DATA_WRITE; // データ書き込み状態へ遷移
else if (timer == TIMEOUT)
next_state = INIT; // タイムアウトしたら初期化へ遷移
DATA_WRITE:
if ((s_axi_wvalid == 1) && (slave_was_ready == 1) &&
((burst_count == 0) || s_axi_wlast == 1))
next_state = WRITE_LAST; // 書き込みが完了したら次の状態へ
else if (s_axi_wvalid == 0)
next_state = WMAST_STALL; // マスターが待機状態なら遅延状態へ
else if ((s_axi_wvalid == 1) && (slave_ready == 0))
next_state = WSLAVE_STALL; // スレーブがビジーなら遅延状態へ
WMAST_STALL:
if ((s_axi_wvalid == 1) && (slave_ready == 1) &&
((burst_count == 0) || (s_axi_wlast == 1)))
next_state = WRITE_LAST; // データ書き込み完了
else if ((s_axi_wvalid == 1) && (slave_ready == 1))
next_state = DATA_WRITE; // データ書き込み状態へ遷移
else if ((s_axi_wvalid == 1) && (slave_ready == 0))
next_state = WSLAVE_STALL; // スレーブがビジーなら遅延状態へ
else if (timer == TIMEOUT)
next_state = WRITE_ERROR; // タイムアウトしたらエラー状態へ
WSLAVE_STALL:
if ((s_axi_wvalid == 1) && (slave_ready == 1) && (burst_count == 0))
next_state = WRITE_LAST; // データ書き込み完了
else if ((s_axi_wvalid == 1) && (slave_ready == 1))
next_state = DATA_WRITE; // データ書き込み状態へ遷移
else if ((s_axi_wvalid == 0) && (slave_ready == 1))
next_state = WMAST_STALL; // マスターが待機状態なら遅延状態へ
else if (timer == TIMEOUT)
next_state = WRITE_ERROR; // タイムアウトしたらエラー状態へ
WRITE_LAST:
next_state = BRESP_VALID; // 書き込み応答を送信
WRITE_ERROR:
next_state = BRESP_VALID; // 書き込みエラー応答を送信
BRESP_VALID:
if (s_axi_bready == 1)
next_state = BRESP_ACCEPT; // マスターが応答を受け取ったら次の状態へ
else if (timer == TIMEOUT)
next_state = INIT; // タイムアウトしたら初期化へ
BRESP_ACCEPT:
next_state = INIT; // 応答が受け取られたら初期化へ
BRESP_ERROR:
next_state = INIT; // エラーが発生したら初期化へ
RADDR_ACCEPT:
if (read_address_inrange == 1)
next_state = RADDR_INRANGE; // 読み込みアドレスが範囲内なら次の状態へ
else
next_state = RADDR_ERROR; // アドレスが範囲外ならエラー状態へ
RADDR_INRANGE:
if ((s_axi_rready == 1) && (slave_ready == 1) && (burst_count == 0))
next_state = RDATA_LAST; // データの読み込み完了
else if ((s_axi_rready == 1) && (slave_ready == 1))
next_state = RDATA_VALID; // 読み込みデータが有効
else if ((s_axi_rready == 0) && (slave_ready == 1))
next_state = RMAST_STALL; // マスターが待機状態なら遅延状態へ
else if (slave_ready == 0)
next_state = RSLAVE_STALL; // スレーブがビジーなら遅延状態へ
else if (timer == TIMEOUT)
next_state = RADDR_ERROR; // タイムアウトしたらエラー状態へ
RADDR_ERROR:
next_state = INIT; // エラーが発生したら初期化へ
RDATA_VALID:
if ((s_axi_rready == 1) && (slave_ready == 1) && (burst_count == 1))
next_state = RDATA_LAST; // データの読み込み完了
else if ((s_axi_rready == 1) && (slave_ready == 0))
next_state = RSLAVE_STALL; // スレーブがビジーなら遅延状態へ
else if (s_axi_rready == 0)
next_state = RMAST_STALL; // マスターが待機状態なら遅延状態へ
RMAST_STALL:
if ((s_axi_rready == 1) && ( slave_ready == 1) && (burst_count == 1))
next_state = RDATA_LAST; // データの読み込み完了
else if ((s_axi_rready == 1) && (slave_ready == 1))
next_state = RDATA_VALID; // 読み込みデータが有効
else if ((s_axi_rready == 1) && (slave_ready == 0))
next_state = RSLAVE_STALL; // スレーブがビジーなら遅延状態へ
else if (timer == TIMEOUT)
next_state = RDATA_ERROR; // タイムアウトしたらエラー状態へ
RSLAVE_STALL:
if ((s_axi_rready == 1) && (slave_ready == 1) && (burst_count <= 1))
next_state = RDATA_LAST; // データの読み込み完了
else if ((s_axi_rready == 1) && (slave_ready == 1))
next_state = RDATA_VALID; // 読み込みデータが有効
else if ((s_axi_rready == 1) && (slave_ready == 1))
next_state = RMAST_STALL; // マスターが待機状態なら遅延状態へ
else if (timer == TIMEOUT)
next_state = RDATA_ERROR; // タイムアウトしたらエラー状態へ
RDATA_LAST:
if (s_axi_rready == 1)
next_state = INIT; // 読み込みデータが受信されたら初期化へ
RDATA_ERROR:
next_state = INIT; // エラーが発生したら初期化へ
default:
next_state = INIT; // デフォルト状態は初期化へ
endcase
end
// 書き込みおよび読み込みデータの処理
always @(posedge s_axi_aclk)
begin
if (s_axi_aresetn == 0) begin
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b0;
s_axi_bid <= 3'b000;
s_axi_bvalid <= 1'b0;
s_axi_bresp <= 2'b00;
s_axi_arready <= 1'b0;
s_axi_rvalid <= 1'b0;
s_axi_rresp <= 2'b00;
s_axi_rid <= 3'b000;
s_axi_rlast <= 1'b0;
end else
case (next_state)
INIT : begin
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b0;
s_axi_bid <= 3'b000;
s_axi_bresp <= 2'b00;
s_axi_arready <= 1'b0;
s_axi_rid <= 3'b000;
s_axi_rvalid <= 1'b0;
s_axi_rresp <= 2'b00;
s_axi_rlast <= 1'b0;
end
WRR_READY : begin
s_axi_awready <= 1'b1;
s_axi_arready <= 1'b1;
end
WADDR_ACCEPT : begin
s_axi_awready <= 1'b0;
s_axi_arready <= 1'b0;
end
WADDR_INRANGE : begin
end
WADDR_ERROR : begin
end
WRITE_READY : begin
s_axi_wready <= 1'b1;
timer <= timer+1;
end
DATA_WRITE : begin
timer <= 0;
s_axi_bresp <= OKAY;
s_axi_wready <= 1'b1;
end
WMAST_STALL : begin
end
WSLAVE_STALL : begin
s_axi_wready <= 1'b0;
end
WRITE_LAST : begin
s_axi_wready <= 1'b0;
end
WRITE_ERROR : begin
s_axi_bresp <= DECERR;
s_axi_wready <= 1'b0;
end
BRESP_VALID : begin
s_axi_bvalid <= 1'b1;
timer <= timer+1;
end
BRESP_ACCEPT : begin
s_axi_bvalid <= 1'b0;
s_axi_bresp <= OKAY;
timer <= 0;
end
BRESP_ERROR : begin
end
RADDR_ACCEPT : begin
s_axi_awready <= 1'b0;
s_axi_arready <= 1'b0;
end
RADDR_INRANGE : begin
s_axi_rresp <= OKAY;
timer <= timer+1;
if (burst_count != 0)
s_axi_rvalid <= 1'b1;
else
s_axi_rvalid <= 1'b0;
end
RADDR_ERROR : begin
s_axi_rresp <= DECERR;
end
RDATA_VALID : begin
s_axi_rvalid <= 1'b1;
s_axi_rresp <= 2'b00;
timer <= 0;
end
RMAST_STALL : begin
timer <= timer+1;
end
RSLAVE_STALL : begin
s_axi_rvalid <= 1'b0;
timer <= timer+1;
end
RDATA_LAST : begin
s_axi_rlast <= 1'b1;
s_axi_rvalid <= 1'b1;
end
RDATA_ERROR : begin
s_axi_rvalid <= 1'b0;
end
endcase
end
// バーストカウントの処理
always @(posedge s_axi_aclk)
begin
if (next_state == WADDR_ACCEPT)
burst_count <= s_axi_awlen;
else if (next_state == RADDR_ACCEPT)
burst_count <= s_axi_arlen;
else if (next_state == DATA_WRITE)
burst_count <= burst_count - 1;
else if (next_state == RDATA_VALID)
burst_count <= burst_count - 1;
end
// 書き込みアドレスの更新
always @(posedge s_axi_aclk)
begin
if (next_state == WADDR_ACCEPT)
write_address <= s_axi_awaddr;
else if ((next_state == DATA_WRITE) && (state != WRITE_READY))
write_address <= write_address+4;
else if ((next_state == WRITE_LAST) && (state != WRITE_READY))
write_address <= write_address+4;
end
// 書き込みデータの保持
always @(posedge s_axi_aclk)
begin
if ((next_state == DATA_WRITE) && (state != WSLAVE_STALL))
write_data <= s_axi_wdata;
else if ((next_state == WSLAVE_STALL) && (state == DATA_WRITE))
write_data <= s_axi_wdata;
else if (next_state == WRITE_LAST)
write_data <= s_axi_wdata;
end
// 書き込みアサート信号の制御
always @(posedge s_axi_aclk)
begin
if ((next_state == DATA_WRITE) || (next_state == WRITE_LAST))
write_assert <= 1'b1;
else
write_assert <= 1'b0;
end
// 読み込みアドレスの更新
always @(posedge s_axi_aclk)
begin
if (next_state == RADDR_ACCEPT)
read_address <= s_axi_araddr;
else if (next_state == RDATA_VALID)
read_address <= read_address+4;
else if ((next_state == RDATA_LAST) && (state != RADDR_INRANGE) && (state != RDATA_LAST) && (state != RSLAVE_STALL))
read_address <= read_address+4;
end
// 書き込みアサート時のメモリ書き込み
always @(posedge s_axi_aclk)
begin
if (write_assert == 1)
case (write_address)
24'h000004: control_register <= write_data;
endcase
end
// 読み込みデータの選択
always@(*)
begin
case (read_address[23:8])
16'h0000 :
case (read_address[7:0])
8'h04 : s_axi_rdata <= control_register;
default : s_axi_rdata <= 32'h0;
endcase
24'h0001: s_axi_rdata <= data_memory[read_address[7:2]];
default : s_axi_rdata <= 32'h0;
endcase
end
// メモリデータの書き込み
always @(posedge s_axi_aclk)
begin
if ((write_assert == 1) && (write_address[23:8] == 16'h0001))
data_memory[write_address[7:2]] <= write_data;
end
endmodule
AXI4のマスター側の記述
`timescale 1ns / 1ps
module axi4_master
(
input wire m_axi_aclk, // AXIクロック信号
input wire m_axi_aresetn, // 非同期リセット信号(低アクティブ)
// 書き込みアドレスチャネルの信号群
output reg [2:0] m_axi_awid, // 書き込みアドレスID
output reg [23:0] m_axi_awaddr, // 書き込みアドレス
output reg [2:0] m_axi_awsize, // 書き込みデータサイズ
output reg [1:0] m_axi_awburst, // 書き込みバーストタイプ
output reg [7:0] m_axi_awlen, // 書き込みバースト長
output reg [1:0] m_axi_awlock, // ロック信号
output reg [3:0] m_axi_awcache, // キャッシュ制御信号
output reg [2:0] m_axi_awprot, // 保護制御信号
output reg [3:0] m_axi_awqos, // Quality of Service
output reg [4:0] m_axi_awuser, // ユーザー定義信号
output reg m_axi_awvalid, // 書き込みアドレスが有効であることを示す信号
input wire m_axi_awready, // スレーブが書き込みアドレスを受け入れ可能であることを示す信号
// 書き込みデータチャネルの信号群
output reg [2:0] m_axi_wid, // 書き込みデータID
output wire [31:0] m_axi_wdata, // 書き込みデータ
output reg [3:0] m_axi_wstrb, // 書き込みストローブ信号(どのバイトが有効かを示す)
output reg m_axi_wlast, // 書き込みバーストの最後であることを示す信号
output reg m_axi_wvalid, // 書き込みデータが有効であることを示す信号
input wire m_axi_wready, // スレーブが書き込みデータを受け入れ可能であることを示す信号
// 書き込み応答チャネルの信号群
input wire m_axi_bid, // 書き込み応答ID
input wire m_axi_bresp, // 書き込み応答ステータス
input wire m_axi_bvalid, // 書き込み応答が有効であることを示す信号
output reg m_axi_bready, // マスターが書き込み応答を受け入れ可能であることを示す信号
// 読み込みアドレスチャネルの信号群
output reg [2:0] m_axi_arid, // 読み込みアドレスID
output reg [23:0] m_axi_araddr, // 読み込みアドレス
output reg [7:0] m_axi_arlen, // 読み込みバースト長
output reg [2:0] m_axi_arsize, // 読み込みデータサイズ
output reg [1:0] m_axi_arburst, // 読み込みバーストタイプ
output reg [1:0] m_axi_arlock, // ロック信号
output reg [3:0] m_axi_arcache, // キャッシュ制御信号
output reg [2:0] m_axi_arprot, // 保護制御信号
output reg [3:0] m_axi_arqos, // Quality of Service
output reg [4:0] m_axi_aruser, // ユーザー定義信号
output reg m_axi_arvalid, // 読み込みアドレスが有効であることを示す信号
input wire m_axi_arready, // スレーブが読み込みアドレスを受け入れ可能であることを示す信号
// 読み込みデータチャネルの信号群
input wire [2:0] m_axi_rid, // 読み込みデータID
input wire [31:0] m_axi_rdata, // 読み込みデータ
input wire [1:0] m_axi_rresp, // 読み込み応答ステータス
input wire m_axi_rlast, // 読み込みバーストの最後であることを示す信号
input wire m_axi_rvalid, // 読み込みデータが有効であることを示す信号
output reg m_axi_rready, // マスターが読み込みデータを受け入れ可能であることを示す信号
// 外部制御信号
input wire write, // 書き込み開始信号
input wire [23:0] write_address, // 書き込みアドレス
input wire [7:0] write_burstlen, // 書き込みバースト長
input wire [31:0] write_data, // 書き込みデータ
input wire read, // 読み込み開始信号
input wire [23:0] read_address, // 読み込みアドレス
input wire [7:0] read_burstlen, // 読み込みバースト長
output reg [31:0] read_data // 読み込みデータ出力
);
parameter BUSY_MASTER_TEST = 0; // マスターがビジー状態かをテストするためのパラメータ
reg [3:0] timer; // タイマー
localparam TIMEOUT = 15; // タイムアウト値
reg [7:0] burst_count; // バーストカウント
reg [31:0] data_gen; // データ生成用レジスタ
reg [5:0] lfsr = 6'b100000; // 線形フィードバックシフトレジスタ(LFSR)
wire master_ready; // マスターが準備完了かどうかを示す信号
reg master_was_ready; // マスターが以前に準備完了だったかを示す信号
// 状態の定義
localparam INIT = 0, STANDBY = 1, WADDR_VALID = 2, WADDR_ACCEPT = 3,
WADDR_ERROR = 4, WDATA_VALID = 5, WSLAVE_STALL = 6,
WMAST_STALL = 7, WDATA_LAST = 8, WDATA_ERROR = 9,
WAIT_RESPONSE = 10, ACCEPT_RESPONSE = 11, RESPONSE_ERROR = 12,
RADDR_VALID = 13, RADDR_ACCEPT = 14, RDATA_VALID = 15,
RSLAVE_STALL = 16, RMAST_STALL = 17, RDATA_LAST = 18, RDATA_ERROR = 19;
reg [4:0] state, next_state; // 現在の状態と次の状態を保持するレジスタ
// 状態遷移ロジック
always @(posedge m_axi_aclk)
begin
if (m_axi_aresetn == 0)
state <= INIT; // リセット時は初期状態に戻る
else
state <= next_state; // 次の状態へ遷移
end
// LFSRの更新とマスター準備完了フラグの更新
always @(posedge m_axi_aclk)
begin
lfsr[0] <= lfsr[5] ^ lfsr[4] ^ 1'b1;
lfsr[5:1] <= lfsr[4:0];
master_was_ready <= master_ready;
end
// マスターの準備完了状態をLFSRに基づいて決定
assign master_ready = BUSY_MASTER_TEST ? lfsr[5] : 1;
// 次の状態を決定するための組み合わせ回路
always @(*) begin
next_state = state;
case (state)
INIT:
next_state = STANDBY; // 初期化後、待機状態へ
STANDBY:
if (write == 1)
next_state = WADDR_VALID; // 書き込み要求があれば書き込みアドレスを送信
else if (read == 1)
next_state = RADDR_VALID; // 読み込み要求があれば読み込みアドレスを送信
WADDR_VALID:
if ((m_axi_awready == 1) && (m_axi_wready == 1) && (burst_count == 0))
next_state = WDATA_LAST; // 最後の書き込みデータ送信状態へ
else if ((m_axi_awready == 1) && (m_axi_wready == 1))
next_state = WDATA_VALID; // 書き込みデータ送信状態へ
else if (m_axi_awready == 1)
next_state = WADDR_ACCEPT; // 書き込みアドレス受信完了
else if (timer == TIMEOUT)
next_state = WADDR_ERROR; // タイムアウトでエラー状態へ
WADDR_ACCEPT:
if ((m_axi_wready == 1) && (burst_count == 0))
next_state = WDATA_LAST; // 最後の書き込みデータ送信状態へ
else if (m_axi_wready == 1)
next_state = WDATA_VALID; // 書き込みデータ送信状態へ
else if (timer == TIMEOUT)
next_state = WDATA_ERROR; // タイムアウトでエラー状態へ
WADDR_ERROR:
next_state = WAIT_RESPONSE; // 書き込みアドレスエラー発生時は応答待機へ
WDATA_VALID:
if ((m_axi_wready == 1) && (master_was_ready == 1) && (burst_count == 0))
next_state = WDATA_LAST; // 最後の書き込みデータ送信状態へ
else if ((m_axi_wready == 1) && (master_ready == 0))
next_state = WMAST_STALL; // マスターがビジー状態で遅延へ
else if (m_axi_wready == 0)
next_state = WSLAVE_STALL; // スレーブがビジー状態で遅延へ
WSLAVE_STALL:
if ((m_axi_wready == 1) && (master_ready == 1) && (burst_count == 0))
next_state = WDATA_LAST; // 最後の書き込みデータ送信状態へ
else if ((m_axi_wready == 1) && (master_ready == 1))
next_state = WDATA_VALID; // 書き込みデータ送信状態へ
else if ((m_axi_wready == 1) && (master_ready == 0))
next_state = WDATA_VALID; // マスターがビジー状態で遅延へ
else if (m_axi_wready == 1)
next_state = WMAST_STALL; // マスターがビジー状態で遅延へ
else if (timer == TIMEOUT)
next_state = WDATA_ERROR; // タイムアウトでエラー状態へ
WMAST_STALL:
if ((m_axi_wready == 1) && (master_was_ready == 1) && (burst_count == 0))
next_state = WDATA_LAST; // 最後の書き込みデータ送信状態へ
else if ((m_axi_wready == 1) && (master_ready == 1))
next_state = WDATA_VALID; // 書き込みデータ送信状態へ
else if ((m_axi_wready == 0) && (master_ready == 1))
next_state = WSLAVE_STALL; // スレーブがビジー状態で遅延へ
else if (timer == TIMEOUT)
next_state = WDATA_ERROR; // タイムアウトでエラー状態へ
WDATA_LAST:
if (m_axi_bvalid == 1)
next_state = ACCEPT_RESPONSE; // 書き込み完了応答受信
else
next_state = WAIT_RESPONSE; // 書き込み完了応答待機
WAIT_RESPONSE:
if (m_axi_bvalid == 1)
next_state = ACCEPT_RESPONSE; // 書き込み完了応答受信
else if (timer == TIMEOUT)
next_state = RESPONSE_ERROR; // タイムアウトでエラー状態へ
ACCEPT_RESPONSE:
next_state = INIT; // 初期化へ遷移
RESPONSE_ERROR:
next_state = INIT; // エラー発生で初期化へ
RADDR_VALID:
if (m_axi_arready == 1)
next_state = RADDR_ACCEPT; // 読み込みアドレス受信完了
else if (timer == TIMEOUT)
next_state = RESPONSE_ERROR; // タイムアウトでエラー状態へ
RADDR_ACCEPT:
if ((m_axi_rvalid == 1) && ((burst_count == 0) || (m_axi_rlast == 1)))
next_state = RDATA_LAST; // 最後の読み込みデータ受信状態へ
else if (m_axi_rvalid == 1)
next_state = RDATA_VALID; // 読み込みデータ受信状態へ
else
next_state = RSLAVE_STALL; // スレーブがビジー状態で遅延へ
RDATA_VALID:
if ((m_axi_rvalid == 1) && (master_was_ready == 1) && ((burst_count == 0) || (m_axi_rlast == 1)))
next_state = RDATA_LAST; // 最後の読み込みデータ受信状態へ
else if ((m_axi_rvalid == 1) && (master_ready == 0))
next_state = RMAST_STALL; // マスターがビジー状態で遅延へ
else if (m_axi_rvalid == 0)
next_state = RSLAVE_STALL; // スレーブがビジー状態で遅延へ
RSLAVE_STALL:
if ((m_axi_rvalid == 1) && (master_ready == 1) && (burst_count == 0))
next_state = RDATA_LAST; // 最後の読み込みデータ受信状態へ
else if ((m_axi_rvalid == 1) && (master_ready == 1))
next_state = RDATA_VALID; // 読み込みデータ受信状態へ
else if (m_axi_rvalid == 1)
next_state = RMAST_STALL; // マスターがビジー状態で遅延へ
RMAST_STALL:
if ((m_axi_rvalid == 1) && (master_was_ready == 1) && ((burst_count == 0) || (m_axi_rlast == 1)))
next_state = RDATA_LAST; // 最後の読み込みデータ受信状態へ
else if ((m_axi_rvalid == 1) && (master_ready == 1))
next_state = RDATA_VALID; // 読み込みデータ受信状態へ
else if (master_ready == 1)
next_state = RSLAVE_STALL; // スレーブがビジー状態で遅延へ
RDATA_LAST:
next_state = INIT; // 読み込み完了後、初期化へ遷移
RDATA_ERROR:
next_state = INIT; // エラー発生で初期化へ
default:
next_state = INIT; // デフォルトで初期化へ遷移
endcase
end
// 各信号の制御とデータの生成
always @(posedge m_axi_aclk)
begin
if (m_axi_aresetn == 0) begin
// リセット時はすべての出力信号を無効化
m_axi_awvalid <= 0;
m_axi_awid <= 0;
m_axi_awaddr <= 0;
m_axi_awprot <= 0;
m_axi_wvalid <= 0;
m_axi_awlen <= 0;
m_axi_awsize <= 0;
m_axi_awburst <= 0;
m_axi_awlock <= 0;
m_axi_awcache <= 0;
m_axi_awqos <= 0;
m_axi_awuser <= 0;
m_axi_wid <= 0;
data_gen <= 0;
m_axi_wstrb <= 0;
m_axi_wlast <= 0;
m_axi_bready <= 0;
m_axi_arvalid <= 0;
m_axi_arid <= 0;
m_axi_araddr <= 0;
m_axi_arlen <= 0;
m_axi_arsize <= 0;
m_axi_arburst <= 0;
m_axi_arqos <= 0;
m_axi_arprot <= 0;
m_axi_arlock <= 0;
m_axi_arcache <= 0;
m_axi_aruser <= 0;
m_axi_rready <= 0;
end else
case (next_state)
INIT: begin
// 初期化状態
m_axi_awvalid <= 0;
m_axi_awaddr <= 0;
m_axi_awburst <= 1;
m_axi_awprot <= 0;
m_axi_wvalid <= 0;
data_gen <= 0;
m_axi_wstrb <= 0;
m_axi_bready <= 0;
m_axi_arvalid <= 0;
m_axi_arburst <= 1;
m_axi_araddr <= 0;
m_axi_arprot <= 0;
m_axi_rready <= 0;
end
STANDBY: begin
// 待機状態
timer <= 0;
end
WADDR_VALID: begin
// 書き込みアドレス送信
m_axi_awvalid <= 1;
m_axi_wvalid <= 1;
m_axi_awaddr <= write_address;
m_axi_awlen <= write_burstlen;
data_gen <= write_data;
burst_count <= write_burstlen;
if (write_burstlen == 0)
m_axi_wlast <= 1;
end
WADDR_ACCEPT: begin
// 書き込みアドレス受信完了
m_axi_awvalid <= 0;
if (m_axi_wready == 1)
data_gen <= data_gen + 1;
end
WADDR_ERROR: begin
// 書き込みアドレスエラー(処理なし)
end
WDATA_VALID: begin
// 書き込みデータ送信
m_axi_wvalid <= 1;
if (burst_count == 1)
m_axi_wlast <= 1;
data_gen <= data_gen + 1;
m_axi_awvalid <= 0;
timer <= 0;
m_axi_bready <= 0;
burst_count <= burst_count - 1;
end
WSLAVE_STALL: begin
// スレーブビジー状態
timer <= timer + 1;
end
WMAST_STALL: begin
// マスタービジー状態
timer <= timer + 1;
m_axi_wvalid <= 0;
end
WDATA_LAST: begin
// 最後の書き込みデータ送信完了
timer <= 0;
m_axi_awvalid <= 0;
m_axi_wvalid <= 0;
m_axi_wlast <= 0;
m_axi_bready <= 1;
end
WDATA_ERROR: begin
// 書き込みデータエラー(処理なし)
end
WAIT_RESPONSE: begin
// 書き込み応答待機
m_axi_bready <= 1;
end
ACCEPT_RESPONSE: begin
// 書き込み応答受信完了
m_axi_bready <= 0;
end
RESPONSE_ERROR: begin
// 応答エラー(処理なし)
end
RADDR_VALID: begin
// 読み込みアドレス送信
m_axi_araddr <= read_address;
burst_count <= read_burstlen;
m_axi_arlen <= read_burstlen;
m_axi_arvalid <= 1;
m_axi_rready <= 1;
end
RADDR_ACCEPT: begin
// 読み込みアドレス受信完了
m_axi_arvalid <= 0;
end
RDATA_VALID: begin
// 読み込みデータ受信
burst_count <= burst_count - 1;
m_axi_rready <= 1;
end
RSLAVE_STALL: begin
// スレーブビジー状態
m_axi_rready <= 1;
end
RMAST_STALL: begin
// マスタービジー状態
m_axi_rready <= 0;
end
RDATA_LAST: begin
// 最後の読み込みデータ受信完了
m_axi_rready <= 0;
end
RDATA_ERROR: begin
// 読み込みデータエラー(処理なし)
end
endcase
end
// 書き込みデータの生成
assign m_axi_wdata = data_gen;
endmodule
テストベンチ
`timescale 1ns / 1ps
module t_axi4_master;
reg s_axi_aclk; // クロック信号
reg s_axi_aresetn; // リセット信号(低アクティブ)
// AXI 書き込みアドレスチャネルの信号群
wire [2:0] s_axi_awid;
wire s_axi_awvalid;
wire s_axi_awready;
wire [23:0] s_axi_awaddr;
wire [7:0] s_axi_awlen;
wire [2:0] s_axi_awsize;
wire [1:0] s_axi_awburst;
wire [1:0] s_axi_awlock;
wire [3:0] s_axi_awcache;
wire [2:0] s_axi_awprot;
wire [3:0] s_axi_awqos;
wire [4:0] s_axi_awuser;
// AXI 書き込みデータチャネルの信号群
wire [2:0] s_axi_wid;
wire s_axi_wvalid;
wire s_axi_wready;
wire [31:0] s_axi_wdata;
wire [3:0] s_axi_wstrb;
wire s_axi_wlast;
// AXI 書き込み応答チャネルの信号群
wire [2:0] s_axi_bid;
wire s_axi_bvalid;
wire s_axi_bready;
wire [1:0] s_axi_bresp;
// AXI 読み込みアドレスチャネルの信号群
wire [2:0] s_axi_arid;
wire s_axi_arvalid;
wire s_axi_arready;
wire [23:0] s_axi_araddr;
wire [7:0] s_axi_arlen;
wire [2:0] s_axi_arsize;
wire [1:0] s_axi_arburst;
wire [1:0] s_axi_arlock;
wire [3:0] s_axi_arcache;
wire [2:0] s_axi_arprot;
wire [3:0] s_axi_arqos;
wire [4:0] s_axi_aruser;
// AXI 読み込みデータチャネルの信号群
wire [2:0] s_axi_rid;
wire s_axi_rvalid;
wire s_axi_rready;
wire [31:0] s_axi_rdata;
wire s_axi_rlast;
wire [1:0] s_axi_rresp;
// マスターからスレーブへの操作指示用の信号
reg write; // 書き込み開始信号
reg [23:0] write_address; // 書き込みアドレス
reg [7:0] write_burstlen; // 書き込みバースト長
reg [31:0] write_data; // 書き込みデータ
reg read; // 読み込み開始信号
reg [23:0] read_address; // 読み込みアドレス
reg [7:0] read_burstlen; // 読み込みバースト長
wire [31:0] read_data; // 読み込みデータ
// AXI4スレーブモジュールのインスタンス化
axi4_slave slave (
.s_axi_aclk (s_axi_aclk),
.s_axi_aresetn(s_axi_aresetn),
.s_axi_awid (s_axi_awid),
.s_axi_awvalid(s_axi_awvalid),
.s_axi_awready(s_axi_awready),
.s_axi_awaddr (s_axi_awaddr),
.s_axi_awlen (s_axi_awlen),
.s_axi_awsize (s_axi_awsize),
.s_axi_awburst(s_axi_awburst),
.s_axi_awlock (s_axi_awlock),
.s_axi_awcache(s_axi_awcache),
.s_axi_awprot (s_axi_awprot),
.s_axi_awqos (s_axi_awqos),
.s_axi_awuser (s_axi_awuser),
.s_axi_wid (s_axi_wid),
.s_axi_wvalid (s_axi_wvalid),
.s_axi_wready (s_axi_wready),
.s_axi_wdata (s_axi_wdata),
.s_axi_wstrb (s_axi_wstrb),
.s_axi_wlast (s_axi_wlast),
.s_axi_bid (s_axi_bid),
.s_axi_bvalid (s_axi_bvalid),
.s_axi_bready (s_axi_bready),
.s_axi_bresp (s_axi_bresp),
.s_axi_arid (s_axi_arid),
.s_axi_arvalid(s_axi_arvalid),
.s_axi_arready(s_axi_arready),
.s_axi_araddr (s_axi_araddr),
.s_axi_arlen (s_axi_arlen),
.s_axi_arsize (s_axi_arsize),
.s_axi_arburst(s_axi_arburst),
.s_axi_arlock (s_axi_arlock),
.s_axi_arcache(s_axi_arcache),
.s_axi_arprot (s_axi_arprot),
.s_axi_arqos (s_axi_arqos),
.s_axi_aruser (s_axi_aruser),
.s_axi_rid (s_axi_rid),
.s_axi_rvalid (s_axi_rvalid),
.s_axi_rready (s_axi_rready),
.s_axi_rdata (s_axi_rdata),
.s_axi_rlast (s_axi_rlast),
.s_axi_rresp (s_axi_rresp)
);
// AXI4マスターモジュールのインスタンス化
axi4_master master (
.m_axi_aclk (s_axi_aclk),
.m_axi_aresetn(s_axi_aresetn),
.m_axi_awid (s_axi_awid),
.m_axi_awvalid(s_axi_awvalid),
.m_axi_awready(s_axi_awready),
.m_axi_awaddr (s_axi_awaddr),
.m_axi_awlen (s_axi_awlen),
.m_axi_awsize (s_axi_awsize),
.m_axi_awburst(s_axi_awburst),
.m_axi_awlock (s_axi_awlock),
.m_axi_awcache(s_axi_awcache),
.m_axi_awprot (s_axi_awprot),
.m_axi_awqos (s_axi_awqos),
.m_axi_awuser (s_axi_awuser),
.m_axi_wid (s_axi_wid),
.m_axi_wvalid (s_axi_wvalid),
.m_axi_wready (s_axi_wready),
.m_axi_wdata (s_axi_wdata),
.m_axi_wstrb (s_axi_wstrb),
.m_axi_wlast (s_axi_wlast),
.m_axi_bid (s_axi_bid),
.m_axi_bvalid (s_axi_bvalid),
.m_axi_bready (s_axi_bready),
.m_axi_bresp (s_axi_bresp),
.m_axi_arid (s_axi_arid),
.m_axi_arvalid(s_axi_arvalid),
.m_axi_arready(s_axi_arready),
.m_axi_araddr (s_axi_araddr),
.m_axi_arlen (s_axi_arlen),
.m_axi_arsize (s_axi_arsize),
.m_axi_arburst(s_axi_arburst),
.m_axi_arlock (s_axi_arlock),
.m_axi_arcache(s_axi_arcache),
.m_axi_arprot (s_axi_arprot),
.m_axi_arqos (s_axi_arqos),
.m_axi_aruser (s_axi_aruser),
.m_axi_rid (s_axi_rid),
.m_axi_rvalid (s_axi_rvalid),
.m_axi_rready (s_axi_rready),
.m_axi_rdata (s_axi_rdata),
.m_axi_rlast (s_axi_rlast),
.m_axi_rresp (s_axi_rresp),
.write (write),
.write_address(write_address),
.write_burstlen(write_burstlen),
.write_data (write_data),
.read (read),
.read_address (read_address),
.read_burstlen(read_burstlen),
.read_data (read_data)
);
// クロック生成(50ns周期、20MHz)
initial
begin
s_axi_aclk = 1'b0;
end
always
#50 s_axi_aclk = ~s_axi_aclk; // 50nsごとにクロックを反転
// テストベンチの初期化とシミュレーションシナリオ
initial
begin
// リセットをアサート
s_axi_aresetn = 0;
repeat(2) @(posedge s_axi_aclk); // 2クロック待機
s_axi_aresetn = 1; // リセット解除
repeat(10) @(posedge s_axi_aclk); // 10クロック待機
// シナリオ1: 単一ワードの書き込み
write = 1;
write_address = 24'h000004; // アドレス 0x000004 に書き込み
write_burstlen = 8'h00; // バースト長 1(単一ワード)
write_data = 32'h55555555; // データ 0x55555555 を書き込む
@(posedge s_axi_aclk); // 1クロック待機
write = 0; // 書き込み完了
// シナリオ2: 単一ワードの読み込み
repeat(20) @(posedge s_axi_aclk); // 20クロック待機
read = 1;
read_address = 24'h000004; // アドレス 0x000004 から読み込み
read_burstlen = 8'h00; // バースト長 1(単一ワード)
@(posedge s_axi_aclk); // 1クロック待機
read = 0; // 読み込み完了
// シナリオ3: バースト書き込み
repeat(20) @(posedge s_axi_aclk); // 20クロック待機
write = 1;
write_address = 24'h000100; // アドレス 0x000100 に書き込み
write_burstlen = 8'h0F; // バースト長 16ワード
write_data = 32'h0; // データ 0 を書き込む
@(posedge s_axi_aclk); // 1クロック待機
write = 0; // 書き込み完了
// シナリオ4: バースト読み込み
repeat(40) @(posedge s_axi_aclk); // 40クロック待機
read = 1;
read_address = 24'h000100; // アドレス 0x000100 から読み込み
read_burstlen = 8'h0F; // バースト長 16ワード
@(posedge s_axi_aclk); // 1クロック待機
read = 0; // 読み込み完了
// シミュレーション終了
repeat(40) @(posedge s_axi_aclk); // 40クロック待機
$finish;
end
endmodule