2021-12-27 「PYNQ を使って Python で手軽に FPGA を活用 (5)」をXSDK実装

### 動作環境

- Windows 10 Pro (v21H2)
- Vivado v2019.1
- FPGA基板: CORA Z7 Zynq－7000評価ボード（Z7－07S）


### 概要

PYNQ を使って Python で手軽に FPGA を活用 (5)
https://www.acri.c.titech.ac.jp/wordpress/archives/36

こちらで紹介されているものはPYNQで動作するように作られている。PYNQ動作だと特定のFPGA基板でしか動かないため、XSDK実装をしてみた (XSDKの実装勉強も兼ねて)。


### Block Design

記事に従って作成した。

<img width="1330" alt="image.png (124.5 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/27/21741/eab92dc9-e933-456e-8828-91d786f830e9.png">



### Address Editor

<img width="781" alt="image.png (12.5 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/27/21741/f6df7213-1f61-4e47-8e14-618564587f72.png">

### XSDK実装

記事のPYNQ用実装を参考にXSDK実装を作成した。
system.mssから作成可能な`xbram_example.c`と`xgpio_example.c`も参考にした。

```helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xbram.h"
#include "xgpio.h"


XBram Bram; // Bramドライバのインスタンス
#define BRAM_DEVICE_0_ID		(XPAR_AXI_BRAM_CTRL_0_DEVICE_ID)
#define BRAM_DEVICE_1_ID		(XPAR_AXI_BRAM_CTRL_1_DEVICE_ID)

#define GPIO_DEVICE_ID (XPAR_GPIO_0_DEVICE_ID)


/*
参考 :
1. xbram_example.c (system.mssのexampleから生成)
2. xgpio_example.c (system.mssのexampleから生成)
*/

#define NUM_LOOP (5) // Read/Write Valueのループ


int main()
{
	init_platform();

	XBram_Config *BramConfigPtr0;
	XBram_Config *BramConfigPtr1;
	XBram BramInst0; // インスタンス用
	XBram BramInst1; // インスタンス用
	XGpio_Config *GpioConfigPtr;
	XGpio GpioInst; // インスタンス用
	u32 data;
	int Status;
	printf("Hello 1145\r\n");


	// 1. Init ====================================

	BramConfigPtr0 = XBram_LookupConfig(BRAM_DEVICE_0_ID);
	if (BramConfigPtr0 == (XBram_Config *)NULL) {
		printf("BRAM0 Config error\r\n");
		return XST_FAILURE;
	}
	Status = XBram_CfgInitialize(&BramInst0, BramConfigPtr0, BramConfigPtr0->CtrlBaseAddress);
	if (Status != XST_SUCCESS) {
		printf("BRAM0 CfgInit error\r\n");
		return XST_FAILURE;
	}
	BramConfigPtr1 = XBram_LookupConfig(BRAM_DEVICE_1_ID);
	if (BramConfigPtr1 == (XBram_Config *)NULL) {
		printf("BRAM1 Config error\r\n");
		return XST_FAILURE;
	}
	Status = XBram_CfgInitialize(&BramInst1, BramConfigPtr1, BramConfigPtr1->CtrlBaseAddress);
	if (Status != XST_SUCCESS) {
		printf("BRAM1 CfgInit error\r\n");
		return XST_FAILURE;
	}
	printf("BRAM0,1 Init Pass\r\n");


	// 2. Read and Write BRAM ==========================

	for(int idx=0; idx < NUM_LOOP; idx++) {
		data = XBram_ReadReg(BramConfigPtr0->MemBaseAddress, 4*idx);
		printf("bram0 %d:%d\n", idx, (unsigned int)data);
	}
	for(int idx=0; idx < NUM_LOOP; idx++) {
		u32 wdata = idx + 20;
		XBram_WriteReg(BramConfigPtr0->MemBaseAddress, 4*idx, wdata);
		//printf("bram0 %d:%d\n", idx, data);
	}
	for(int idx=0; idx < NUM_LOOP; idx++) {
		data = XBram_ReadReg(BramConfigPtr0->MemBaseAddress, 4*idx);
		printf("bram0 %d:%d\n", idx, (unsigned int)data);
	}
	printf("<= not yet copied =>\r\n");
	for(int idx=0; idx < NUM_LOOP; idx++) {
		data = XBram_ReadReg(BramConfigPtr1->MemBaseAddress, 4*idx);
		printf("bram1 %d:%d\n", idx, (unsigned int)data);
	}


	// 3. GPIOによるCtrl制御 ==========================

	GpioConfigPtr = XGpio_LookupConfig(GPIO_DEVICE_ID);
	if (GpioConfigPtr == (XGpio_Config *)NULL) {
		printf("GPIO Config error\r\n");
		return XST_FAILURE;
	}
	Status = XGpio_CfgInitialize(&GpioInst, GpioConfigPtr, XPAR_AXI_GPIO_0_BASEADDR);
	if (Status != XST_SUCCESS) {
		printf("GPIO CfgInit error\r\n");
		return XST_FAILURE;
	}
	printf("GPIO Init Pass\r\n");

	XGpio_WriteReg(XPAR_AXI_GPIO_0_BASEADDR, 4, 0); // AXI GPIO経由でCTRLに書き込む準備
	XGpio_WriteReg(XPAR_AXI_GPIO_0_BASEADDR, 0, 1); // AXI GPIO経由でCTRLに書き込む

	XGpio_WriteReg(XPAR_AXI_GPIO_0_BASEADDR, 4, 0xFFFFFFFF); // AXI GPIO経由でステータスを読み込む準備
	for(int idx=0; idx < NUM_LOOP; idx++) {
		data = XGpio_ReadReg(XPAR_AXI_GPIO_0_BASEADDR, 0);
	}
	printf("<= copied =>\r\n");
	for(int idx=0; idx < NUM_LOOP; idx++) {
		data = XBram_ReadReg(BramConfigPtr1->MemBaseAddress, 4*idx);
		printf("bram1 %d:%d\n", idx, (unsigned int)data);
	}


	// 4. Cleanup ==========================

    cleanup_platform();
    return 0;
}
```

### 動作確認

<img width="733" alt="image.png (12.9 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2021/12/27/21741/38a5abd6-7c88-4b8b-b81d-b4b1d3a3187b.png">

Bram0に書き込んだ値がBram1に書き込まれることを確認できた。

この動作は「Program FPGA」をした直後だけ動作する。
`helloworld`.cにて書き込みする値を変更して二回目の実装をした場合は値は変わらない 。
(`bram_copy.v`との関係もあるのだろう。現在はそれは重要でないため詳細は追わない。)

### 関連

- [PYNQ を使って Python で手軽に FPGA を活用 (5)](https://www.acri.c.titech.ac.jp/wordpress/archives/36) @ ACRi
- [GPIOをソフトウェアから制御する](https://www.tokudenkairo.co.jp/art7/microblaze10.html) @ 特殊電子回路株式会社
- https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/common/xil_io.h
- [ZYBOで遊ぶ01:簡易タイマーIPの自作(3)](https://thuruthurutoru.hatenablog.com/entry/2015/11/01/021638) @ https://thuruthurutoru.hatenablog.com/entry/2015/11/01/021638

情報感謝です。

### その他

- [Building Pynq for Cora-Z7-07S](https://www.dspsandbox.org/building-pynq-for-cora-z7-07z/)


### 前準備

この記事は以下の実装をためそうとしている前段階として実施したもの。

- [AXI CDMA Linux user space example on Zynq UltraScale+ RFSoC](https://support.xilinx.com/s/article/1096735?language=ja)