Vivadoによる自作IP設計
Vivadoによる自作IP設計
はじめに
前回に続き、「FPGAプログラミング大全 第2版」をやる
本投稿では本書7-1~「基本的なIPの製作」を実施したメモを書きます
環境
FPGA : Xilinx Zybo Z7-20
OS : WSL2 Ubuntu20.04
開発環境 : Vivado ML edition 2022.1 Linux版
やりたいこと
Vivadoを使ってIPを自作し、回路に組み込む
実装
IPの追加
- Tools -> Create and Package New IP
- Create a New AXI4 Peripheral
- Next Steps: Edit IP ->Finish
AXI busについて
- AXIバスは読み出しと書き込みが独立しており、axi_awaddrとaxi_araddrのレジスタアドレスが分離している
- S_AXI_WSTRBはByte Enalbe信号で8bit単位で書き込みを制御
レジスタ書き込み
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
本コードはAXIプロトコルを用いて、スレーブレジスタ0(slv_reg0)へのデータ書き込みを実行する。書き込む対象のアドレスが2'h0(アドレスビットが00)の場合に、データをスレーブレジスタ0に書き込む処理が遂行される。
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ):
書き込みアドレス(axi_awaddr)に基づき、どのスレーブレジスタにデータを書き込むかを決定する。ADDR_LSBとOPT_MEM_ADDR_BITSは、対象となるアドレス範囲を選択するために用いられる。
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
データ幅(C_S_AXI_DATA_WIDTH)をバイト単位(8ビット)で処理するために用いられる。C_S_AXI_DATA_WIDTHは、一度に書き込むことができるデータ幅を定義している。通常は32ビットであるが、他の幅も可能。
if ( S_AXI_WSTRB[byte_index] == 1 )
AXIの書き込みストローブ信号(S_AXI_WSTRB)を使用して、書き込むバイトを選択する。書き込みストローブ信号は、各バイトが有効(1)か無効(0)かを示すビットマスク。有効なバイトのみが書き込まれる。
slv_reg0[(byte_index8) +: 8] <= S_AXI_WDATA[(byte_index8) +: 8];
実際にスレーブレジスタ0にデータを書き込む。slv_reg0の対象バイトに、書き込みデータ(S_AXI_WDATA)の対応するバイトを代入する。ここでの +: 演算子は、部分選択演算子として用いられ、対象となるビット範囲を選択している。
レジスタ読み込み
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
// When there is a valid read address (S_AXI_ARVALID) with
// acceptance of read address by the slave (axi_arready),
// output the read dada
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
書き込み側同様、axi_araddr[3:2]でcase文が分岐
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
該当するslv_regがreg_data_outに代入される
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
その後、slv_reg_rden ->Hのタイミングでreg_data_outがaxi_rdata (=AXI Output)に入る
IPを修正する
- Output portを定義する
- 定義したport[X:0]をslv_reg[X:0]にassignする
- 下位階層のportを追加してSave
Package IPのPackaging Steps ->File Group ->Merge changes from File Group Wizard -> Re-Packaging IP
ここまできたらCreate Block DesignからIPを追加してGenerate Bitstream
Vitisで動作確認して終了
まとめ
- 簡単なIP設計の手順を確認した
- 次はディスプレイ表示回路に自作IPを組み込んで実行を行う
Discussion