ZynqでAXI-GPIOとVivado HLSでのIP作成を試す
Xilinx社のZynqで遊んで見る。今回はPSからLチカ出来ることが目標。ついでにボタン入力も。
並びにVivado HLSでのIP作成とそれを使用したデザインが動作するか、確認した。
開発フローについて
- Verilog or HLSでIPを作る
- VivadoでBlockDesignを作る(PSの設定やAXIのメモリマップはここで行う)
- VerilogにBlockDesignのラッパを吐き出す
- Synthesisする
- ピンアサインや制約を決定する。
- Implementationしてbitファイルを作成
ここまではMicroBlaze等を使っていなければ以前のSpartan6とかArtix7と大差なかった(IDEは違うが
ここからはソフト作成のための部分
どうにもうまく動かなくてやり直していたおかげて軽く雰囲気はつかめたので結果オーライとする。
PSの設定やConstrantsはボードを作ったメーカーが作ってくれているみたい(Zyboの場合はDigilentのサイトからダウンロードできる
AXI-GPIO
AXIーGPIOを作成、出力の幅と入出力を設定してAXIにぶら下げる。
Synthesis後にピンアサイン
xgpio.hにある関数で基本は制御できるようだ。
AXIにぶら下げたモジュールのアドレスはbspプロジェクトのxparameters.hにある。
#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; }