波形からAXIを理解する

# 用語
- AXI
Armが公開しているインターフェース規格。データ転送におけるマスターとスレーブ間のポートの種類や使用方法の決まりごと。
- チャネル
データの移動経路のこと。AR, R, AW, W, Bチャネルが存在し、それぞれがVALID・ READY信号などの束を抱える。ARVALID, ARREADY, RVALID, RREADYなどがある。
ちなみに、頭にARがついてるからといって全部マスターが発信・スレーブが受信する信号線とは限らない。ARVALIDはマスタが送信してスレーブが受信するけど、ARREADYはスレーブが送信してマスタが受信する。
- 転送
それぞれのチャネルにおけるデータ移動一回分のこと。VALIDとREADYが同時にHIGHになったタイミングでデータが移動する。
- トランザクション
アドレスを与えてからの一連の転送のこと。
- マスタ
アドレスを送信する側
- スレーブ
アドレスを受信する側

# 基本的な流れ
- READ
A「これ頼むわ！ (ARVALIDアサート)」
B「あいよぉ！ (ARREADYアサート)」
A「待ってる！ (RREADYアサート)」
メモリ読み書き
B「お待たせ！ (RVALIDアサート)」
転送終了

まずARVALIDがアサートされ、同時に「このアドレスからこの大きさでこれだけの個数のデータくれ (例: 0x0番地から4byteデータを4つくれ)」というデータがAから出力されます。
受信側はARVALIDがアサートされたのを見て、それらデータを受け取ります。
AはBの処理完了を待ちます。
しばらくするとデータがBから返りRVALIDがアサートされ終了します。

- WRITE
A「こう頼むわ！ (AWVALIDアサート)」
B「あいよぉ！ (AWREADYアサート)」
A「これ書いてくれや！ (WVALIDアサート)」
B「あいよぉ！ (WREADYアサート)」
A「待ってる！ (BREADYアサート)」
メモリ読み書き
B「完了！ (BVALIDアサート)」
転送終了

まずWRITEのときはAWVALIDがアサートされ、「このアドレスからこの大きさでこれだけの個数のデータを送る」というデータがAから出力されます。
受信側はAWVALIDがアサートされたのを見て、それらデータを受け取ります。
次に書き込むデータを同様の流れで受取ります。
AはBの処理完了を待ちます。
しばらくするとBVALIDがアサートされ終了します。
(何も対策しないとWRITEではREADのようにBからAになにかが返るということがないので、書き込み終了がわかるようにBチャネルが使用されます。)

## 注意点
- このIPがマスタでこのIPがスレーブである、という考え方は良くない。一つのIPが誰かにとってのマスタでありスレーブでもある場合もあるため注意が必要。
- VALIDが必ずREADYに先行するとは限らない。先にREADYがアサートされていることもある。
- 依存関係上、AはBに遠慮してはいけない。 (VALIDをアサートするときに、「READYまだだからあとにしよ...」は絶対に避ける)

# 実験1
シミュレーションから波形を取得します。
## ハードウェア構成
- AR#37425 (以下のサイトから取得)とAXI VIPのみ使用

https://adaptivesupport.amd.com/s/article/37425?language=ja

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/ad5fa6b1-86c9-4a0e-a59b-0164e1ec7af7.png)

- AXI VIPはただ設置しても動いてくれない。テストベンチから起動を指示しないと動いてくれない。
<details><summary>テストベンチ</summary>

```
import axi_vip_pkg::*;
import design_1_axi_vip_0_1_pkg::*; // <>_pkg, <>はDiagram, Property, Property, configから見られる

module axisim_tb;
    logic ACLK, ARESETN, ERROR;
    design_1_wrapper dut(.*);
    
    localparam integer STEP = 10;
    always begin
        ACLK = 0; #(STEP/2);
        ACLK = 1; #(STEP/2);
    end
    
    initial begin
        ARESETN = 1; #(STEP*5);
        ARESETN = 0; #(STEP*20);
        ARESETN = 1; #(STEP*500);
        $stop;
    end
    
    design_1_axi_vip_0_1_slv_mem_t agent; // <>_slv_mem_tの<>は　以上同じ4
    initial begin
        agent = new("AXI Slave Agent", dut.design_1_i.axi_vip_0.inst.IF);
        agent.start_slave();
    end
    
endmodule
```
</details>

## 実際の波形
- READ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/9bd38966-f84d-4c30-b519-87590faa0209.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/14156393-d3cc-4102-8e95-4fc74cd498ea.png)


1. 765000ns: ARVALIDとARREADYが両方ともアサートされる。ARADDRは0、ARLENは0f、ARSIZEは2、ARBURSTは1。これが意味するのは
マスタ「0番アドレスから、16個 (f+1=16)の4Byte (2^2 = 4, 3なら2^3=8Byte)をくれ！」
スレーブ「あいよぉ！」
2. 795000ns: ARREADYがアサートされる。「マスターへ。こっちは次のアドレス受け入れ体勢できてるよ。」ってこと。
3. 825000ns: (RREADYはとっくにアサートされている。) RVALIDがアサートされ、データが16個　バースト転送される。
4. 975000ns: RLASTがアサートされる。トランザクション終了。
以後ARADDRが0x40ずつインクリメントされながら同じトランザクションが発行され続ける。AR#37425の中にいたaxi_masterがそういう役割を持っている。

- WRITE
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/e09e0f2d-b17a-4314-9dd3-cdb6332644ef.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/8dc956af-17a9-4a8a-a266-a59f184a245d.png)


1. 285000ns: AWVALIDとAWREADYがアサートされる。
マスタ「0番アドレスから、16個の4Byteを順に書いて！」
スレーブ「あいよぉ！」
同じタイミングでWVALIDとWREADYがアサートされる。
マスタ「これがデータね！」
スレーブ「あいよぉ！」
2. 295000ns: オーバーラップ転送しているため、次のトランザクションが始まった。 (オーバーラップ転送によりAWVALIDはしばらくずっとアサートされたまま。)
マスタ「40番アドレスから...(以下略)」
2. 745000ns: 最初のトランザクション終了。WLASTとBVALIDとBREADYがアサートされる。

# 実験2
実機上の動作をILAで観察します。
LEDをチカチカさせるときAXI InterconnectとAXI GPIOの間で行われるAXIでの通信の様子を見ますが、寂しいのでAXI Uartliteも参加させます。
## ハードウェア構成
- MicroBlazeとGPIOとAXI Uartliteとその他諸々。 
    - ILAのJTAG通信とAXI UartliteのUART通信は共存できるんですね... 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/dfd95c7e-10dd-49a8-8e3b-755e7a345569.png)

## 実際の波形
- READ
<details><summary>LEDの状態読み取りプログラム </summary>

```c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"

#define LED *((unsigned int *)XPAR_XGPIO_0_BASEADDR)

int main()
{
    init_platform();

    print("Hello World\n\r");
    print("Successfully ran Hello World application");

    for(;;){
        LED = 0x5;
        sleep(1);
        xil_printf("%d\r\n", LED);
        sleep(1);
        LED = 0xa;
        sleep(1);
        xil_printf("%d\r\n", LED);
        sleep(1);
    }

    cleanup_platform();
    return 0;
}


```
</details>


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/798e092b-db6f-4c95-8b3d-2bb937721ea5.png)

画像は`LED = 0xa;`のあとの`xil_printf("%d\r\n", LED);`のときもの。
1. ARADDRに0が出力される (「ここの値くれ」)
1. ARVALIDとRREADYがアサートされる (「いつでも値返してくれていいよ」)
1. ARREADYがアサートされる (「あいよぉ！」)
1. RDATAに値が出力され、RVALIDがアサートされ、終了 (「お待たせ！」)

- WRITE
<details><summary>Lチカプログラム </summary>

```c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"

#define LED *((unsigned int *)XPAR_XGPIO_0_BASEADDR)

int main()
{
    init_platform();

    print("Hello World\n\r");
    print("Successfully ran Hello World application");

    for(;;){
        LED = 0x1;
        sleep(1);
        LED = 0x2;
        sleep(1);
        LED = 0x4;
        sleep(1);
        LED = 0x8;
        sleep(1);
    }

    cleanup_platform();
    return 0;
}

```
</details>


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/88fb8fea-c7c1-400d-9bd4-6069573bef09.png)
画像は`LED = 0x8`したときのもの。
1. WDATAに8が出力される (「この値書き込んで」)
1. AWADDRに0が出力される (LEDはAXI Interconnectから見た0番地にマップされている？)
1. AWVALID, WVALIDがアサートされる (「GPIOくん、こんなかんじで頼むわ！」)
1. AWREADY, WREADYがアサートされる (「あいよぉ！」)
1. BRESPがアサートされる (トランザクション終わり) 

# 参考文献

- FPGAプログラミング大全 Xilinx編 第2版

https://www.amazon.co.jp/FPGA%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%A4%A7%E5%85%A8-Xilinx%E7%B7%A8-%E7%AC%AC2%E7%89%88-%E5%B0%8F%E6%9E%97-%E5%84%AA/dp/4798063266

ILA便利すぎる。