Arty A7 100TボードとMicroBlazeとMIGでHello Worldと時間計測

以下の記事の部分的な和訳・要約の個人用メモです。**外付けDDRメモリを使用して**アプリケーションを実行するまでの手順を解説します。 

https://digilent.com/reference/programmable-logic/guides/getting-started-with-ipi

# 環境
- Hardware
    - Arty A7 100T
    - PCとつなぐ用ケーブル
- Software
    - Vivado 2023.2
    - Vitis Unified IDE 2023.2
    - GTKTerm

# 手順
1. プロジェクト作成
    - `Do not specify sources at this time`にチェック
    - boardファイルを選択。一応`Refresh`してから選択するのが吉。
2. ハードウェア設計
    - `Create Block Design`
    - `Board`タブから`DDR SDRAM`を右クリック、`Auto Connect`を選択
    - `clk_ref_i`ピンを削除し、`MIG`の出力ポート`ui_addn_clk_0`をそこに接続
    - `sys_clk_i`は`xdc`ファイルで登録しておく
    - `Run Connection Automation` (リセットピンの追加)
    - `Clocking Wizard`を追加, `clk_in1`と`MIG`の`ui_clk`を接続 `reset`と`MIG`の`ui_clk_sync_rst`を接続
    - `MicroBlaze`を追加, `Run Block Automation`, `Clock Connection`に`Clocking Wizard`の`clk_out1`を指定, `Local Memory`を`32KB`に変更
    - `Run Connection Automation`
    - `Board`タブから`USB UART`を追加 (`AXI Timer`とかもここで追加する)
    - `Run Connection Automation`
    - `Validate Design`
    - `Create HDL Wrapper`
    - `Generate Bitstream`
    - `Export Hardware...`
    - `Launch Vitis IDE`
![Screenshot_2025-06-11_08-43-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2531876/cf500077-6ec6-4636-8429-8b24882eca20.png)

3. ソフトウェア設計
    - workspaceの指定
    - platformの指定
    - アプリケーションはexampleから適当に`Hello World`を選択
    - ビルド
    - 実行

# おまけ

DDRとBRAMそれぞれに100回ずつREADとWRITEを実行しました。
<details><summary>BRAMとDDRメモリセルでのアクセス速度比較</summary>

```c
#define __MICROBLAZE__
#include <unistd.h>
#include <sys/_intsup.h>
#include <xstatus.h>
#include "xparameters.h"
#include "platform.h"
#include "xil_printf.h"
#include "xtmrctr.h"

#define DDR_BASE 0x80000000
#define BRAM_BASE 0x50

XTmrCtr TimerCounterInst;

int init(){
	int Status;

	Status = XTmrCtr_Initialize(&TimerCounterInst, XPAR_AXI_TIMER_0_BASEADDR);
	if (Status != XST_SUCCESS) {
		xil_printf("Timer Initialize Error\n\r");
        return XST_FAILURE;
	}

	XTmrCtr_SetOptions(&TimerCounterInst, 0, XTC_AUTO_RELOAD_OPTION);
	XTmrCtr_Start(&TimerCounterInst, 0);
    return XST_SUCCESS;
}

int main()
{
    init_platform();

    int res = init();
    if(res != XST_SUCCESS)return 0;

    u32 tStart, tEnd;
    double fval;
    int whole, thousandths;
    int repeatNum = 10000;
    int tmp;

    tStart = XTmrCtr_GetValue(&TimerCounterInst, 0);
    for(int i=0; i<repeatNum; i++){
        *(int *)DDR_BASE = 9; // DDR WRITE
    }
    tEnd = XTmrCtr_GetValue(&TimerCounterInst, 0);
    xil_printf("End - Start = %u\n\r", (unsigned int)(tEnd - tStart));
    fval= (tEnd - tStart) / 100000000.0 * 1000; // ms (周波数100000000.0Hzで除することでsに変換し、1000倍してmsに変換)
    whole = fval; // 整数
    thousandths = (fval - whole) * 1000; // 小数点以下3桁
    xil_printf("Time = %d.%03d ms\n\r", whole, thousandths);

    tStart = XTmrCtr_GetValue(&TimerCounterInst, 0);
    for(int i=0; i<repeatNum; i++){
        tmp = *(int *)DDR_BASE; // DDR READ
    }
    tEnd = XTmrCtr_GetValue(&TimerCounterInst, 0);
    xil_printf("End - Start = %u\n\r", (unsigned int)(tEnd - tStart));
    fval= (tEnd - tStart) / 100000000.0 * 1000; // ms (周波数100000000.0Hzで除することでsに変換し、1000倍してmsに変換)
    whole = fval; // 整数
    thousandths = (fval - whole) * 1000; // 小数点以下3桁
    xil_printf("Time = %d.%03d ms\n\r", whole, thousandths);

    tStart = XTmrCtr_GetValue(&TimerCounterInst, 0);
    for(int i=0; i<repeatNum; i++){
        *(int *)BRAM_BASE = 1; // BRAM WRITE
    }
    tEnd = XTmrCtr_GetValue(&TimerCounterInst, 0);
    xil_printf("End - Start = %u\n\r", (unsigned int)(tEnd - tStart));
    fval= (tEnd - tStart) / 100000000.0 * 1000; // ms (周波数100000000.0Hzで除することでsに変換し、1000倍してmsに変換)
    whole = fval; // 整数
    thousandths = (fval - whole) * 1000; // 小数点以下3桁
    xil_printf("Time = %d.%03d ms\n\r", whole, thousandths);

    tStart = XTmrCtr_GetValue(&TimerCounterInst, 0);
    for(int i=0; i<repeatNum; i++){
        tmp = *(int *)BRAM_BASE; // BRAM READ
    }
    tEnd = XTmrCtr_GetValue(&TimerCounterInst, 0);
    xil_printf("End - Start = %u\n\r", (unsigned int)(tEnd - tStart));
    fval= (tEnd - tStart) / 100000000.0 * 1000; // ms (周波数100000000.0Hzで除することでsに変換し、1000倍してmsに変換)
    whole = fval; // 整数
    thousandths = (fval - whole) * 1000; // 小数点以下3桁
    xil_printf("Time = %d.%03d ms\n\r", whole, thousandths);    

    cleanup_platform();
    return 0;
}
```
</details>

```
End - Start = 7952440 // DDR WRITE
Time = 79.524 ms
End - Start = 7952436 // DDR READ
Time = 79.524 ms
End - Start = 7289934 // BRAM WRITE
Time = 72.899 ms
End - Start = 7289937 // BRAM READ
Time = 72.899 ms
```
経過したクロック数に多少の誤差はあれどDDRとBRAM間でそこまでの差は見られませんでした。

# 参考
https://qiita.com/haihuiliyuyuan/items/d97490739209bb8aa187

https://qiita.com/haihuiliyuyuan/items/6a406cf9117cdb7587e4

https://github.com/viktor-nikolov/MicroBlaze-DDR3-tutorial/tree/main

https://www.acri.c.titech.ac.jp/wordpress/archives/7284

https://cc.cqpub.co.jp/lib/system/doclib_item/1144/

https://digilent.com/reference/programmable-logic/guides/getting-started-with-ipi