🏩

Vivadoによる自作IP設計

2023/03/30に公開

Vivadoによる自作IP設計

はじめに

前回に続き、「FPGAプログラミング大全 第2版」をやる
https://amzn.to/40isQyG

本投稿では本書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