ZynqでAXI-GPIOとVivado HLSでのIP作成を試す

f:id:tohkaf:20151220221446j:plain

Xilinx社のZynqで遊んで見る。今回はPSからLチカ出来ることが目標。ついでにボタン入力も。

並びにVivado HLSでのIP作成とそれを使用したデザインが動作するか、確認した。

開発フローについて

  1. Verilog or HLSでIPを作る
  2. VivadoでBlockDesignを作る(PSの設定やAXIのメモリマップはここで行う)
  3. VerilogにBlockDesignのラッパを吐き出す
  4. Synthesisする
  5. ピンアサインや制約を決定する。
  6. Implementationしてbitファイルを作成

ここまではMicroBlaze等を使っていなければ以前のSpartan6とかArtix7と大差なかった(IDEは違うが
ここからはソフト作成のための部分

  1. Export HardwareしてSDKを起動する
  2. SDKでソフトを書く
  3. bitをFPGAに書き込み
  4. ソフトをデバッグ

どうにもうまく動かなくてやり直していたおかげて軽く雰囲気はつかめたので結果オーライとする。
PSの設定やConstrantsはボードを作ったメーカーが作ってくれているみたい(Zyboの場合はDigilentのサイトからダウンロードできる

AXI-GPIO

f:id:tohkaf:20151220221615p:plain

AXIーGPIOを作成、出力の幅と入出力を設定してAXIにぶら下げる。

f:id:tohkaf:20151220222412p:plain

Synthesis後にピンアサイン

xgpio.hにある関数で基本は制御できるようだ。

AXIにぶら下げたモジュールのアドレスはbspプロジェクトのxparameters.hにある。

www.youtube.com

#include <xil_printf.h>
#include "xil_cache.h"
#include "xgpio.h"

typedef unsigned long long u64;
unsigned int* memcpy_hls_baseaddr = XPAR_XAXIHP_MEMCPY_0_S_AXI_AXILITES_BASEADDR;
int main(){
	//Xil_DCacheDisable();
	uint64_t i,j;
	XGpio led,slide_sw,push_sw;
	XGpio_Config led_config, slide_sw_config, push_sw_config;
	//led
	led_config.DeviceId = XPAR_AXI_GPIO_LED_DEVICE_ID;
	XGpio_CfgInitialize(&led, &led_config, XPAR_AXI_GPIO_LED_BASEADDR);
	XGpio_SetDataDirection(&led, 1, 0);
	//slide_sw
	slide_sw_config.DeviceId = XPAR_AXI_GPIO_SLIDE_SWITCH_DEVICE_ID;
	XGpio_CfgInitialize(&slide_sw, &slide_sw_config, XPAR_AXI_GPIO_SLIDE_SWITCH_BASEADDR);
	XGpio_SetDataDirection(&slide_sw, 1, 1);
	//push_sw
	push_sw_config.DeviceId = XPAR_AXI_GPIO_PUSH_SWITCH_DEVICE_ID;
	XGpio_CfgInitialize(&push_sw, &push_sw_config, XPAR_AXI_GPIO_PUSH_SWITCH_BASEADDR);
	XGpio_SetDataDirection(&push_sw, 1, 1);

	for(i = 0 ; ; ++i){
		uint8_t slide_sw_data = XGpio_DiscreteRead(&slide_sw, 1);
		uint8_t push_sw_data = XGpio_DiscreteRead(&push_sw, 1);
		xil_printf("slide = %x push = %x\r\n", slide_sw_data, push_sw_data);
		XGpio_DiscreteWrite(&led, 1, slide_sw_data ^ push_sw_data);
	}
	return 0;
}

何故かXGpio_SetDataDirectionで落ちる挙動に見舞われていたが、プロジェクトごと作りなおしたら直ってしまったため不明。判明したら再度書く。

Vivado HLS memcpy

PL側からDRAMにバースト転送したいと思った時にHLSだがアドレスを指定してmemcpyをする入門がslideshareにあったのでやった。非常にわかりやすくて良い。

www.slideshare.net

// AXILiteS
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of axihp_in
//        bit 31~0 - axihp_in[31:0] (Read/Write)
// 0x14 : reserved
// 0x18 : Data signal of axihp_out
//        bit 31~0 - axihp_out[31:0] (Read/Write)
// 0x1c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
	memcpy_hls_baseaddr[4] = src_data;
	memcpy_hls_baseaddr[6] = dst_data;


HLSが吐き出したx[モジュール名]_hw.hの中のファイルを読めばどう使えばいいのかは推測できるようだった。

次はverilogからAXI-GPIOやmemcpyと同等のことをやることと、デバッグやシミュレーション周りも確認しておきたい。

コード

#include <xil_printf.h>
#include "xil_cache.h"
#include "xgpio.h"

typedef unsigned long long u64;
unsigned int* memcpy_hls_baseaddr = XPAR_XAXIHP_MEMCPY_0_S_AXI_AXILITES_BASEADDR;
int main(){
	//Xil_DCacheDisable();
	uint64_t i,j;
	XGpio led,slide_sw,push_sw;
	XGpio_Config led_config, slide_sw_config, push_sw_config;
	//led
	led_config.DeviceId = XPAR_AXI_GPIO_LED_DEVICE_ID;
	XGpio_CfgInitialize(&led, &led_config, XPAR_AXI_GPIO_LED_BASEADDR);
	XGpio_SetDataDirection(&led, 1, 0);
	//slide_sw
	slide_sw_config.DeviceId = XPAR_AXI_GPIO_SLIDE_SWITCH_DEVICE_ID;
	XGpio_CfgInitialize(&slide_sw, &slide_sw_config, XPAR_AXI_GPIO_SLIDE_SWITCH_BASEADDR);
	XGpio_SetDataDirection(&slide_sw, 1, 1);
	//push_sw
	push_sw_config.DeviceId = XPAR_AXI_GPIO_PUSH_SWITCH_DEVICE_ID;
	XGpio_CfgInitialize(&push_sw, &push_sw_config, XPAR_AXI_GPIO_PUSH_SWITCH_BASEADDR);
	XGpio_SetDataDirection(&push_sw, 1, 1);

	int is_mismatch = 0x0;
	volatile u64 src_data[256], dst_data[256];
	//initialize
	for(i = 0 ; i < 256 ; ++i){
		src_data[i] = i;
	}
	//axihp_memcpy
	memcpy_hls_baseaddr[4] = src_data;
	memcpy_hls_baseaddr[6] = dst_data;
	//ap idle
	if(memcpy_hls_baseaddr[0] & 0x4){
		xil_printf("memcpy start\r\n");
		memcpy_hls_baseaddr[0] |= 0x1;//ap_start
	}
	//wait for ap_idle assert
	while(!(memcpy_hls_baseaddr[0] & 0x4));

	//validate
	for(i = 0 ; i < 256 ; ++i){
		xil_printf("src_data[%d] = %d dst_data[%d] = %d\r\n", i, src_data[i], i, dst_data[i]);
		if(src_data[i] != dst_data[i])
			is_mismatch = 0x1;
	}
	xil_printf("memcpy %s\r\n", is_mismatch ? "fail" : "success");

	for(i = 0 ; ; ++i){
		uint8_t slide_sw_data = XGpio_DiscreteRead(&slide_sw, 1);
		uint8_t push_sw_data = XGpio_DiscreteRead(&push_sw, 1);
		xil_printf("slide = %x push = %x\r\n", slide_sw_data, push_sw_data);
		XGpio_DiscreteWrite(&led, 1, slide_sw_data ^ push_sw_data);
	}
	return 0;
}