🗂

なんでもいいからZynq上のPS-PLをAXIでつないでみる

2023/05/20に公開
参考

上のサイトを参考に、AXI-lite IPテンプレートを作成する。
作成したIPはIPカタログのUser Repositoryに追加されるので、そこからEdit in Packagerを選ぶと編集できる。

ソースコードは主に二つあって

  • ***_v1_0.v: 単なるラッパー
  • ***_v1_0_S00_AXI.v: 本体
    という感じ。

またAXI-liteには5つのチャンネル

  • AW: 書き込みアドレス
  • W: 書き込みデータ
  • B: 書き込み成否
  • AR: 読み出しアドレス
  • R: 読み出しデータ
    があり、それぞれに少なくともvalid/ready/signalの信号線がある。valid/readyが両方上がった時に通信を行うプロトコルになってるらしく、そういうHDLになってる。

AXI write

該当箇所はこの辺。
まずslv_reg_wren信号は、AW/Wチャネル両方のready/validが立った時にアサートされる。つまりデータとアドレス両方送受信準備できたことを示す信号。
case文でどのレジスタに書き込むかを指定している。axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]はアドレスの一部のビットを切り出しているが、今回ADDR_LSB=2, OPT_MEM_ADDR_BITS=1だったので、axi_awaddr[3:2]つまり書き込みアドレスの2ビットを選択している。今回レジスタ数は4に指定しているのでこれでいいんだろう。
for文は1バイトずつ書き込みを行うだけ。1バイトごとにstrobe信号がある模様。

*_v1_0_S00_AXI.v
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

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 read

読み込みはこんな感じ。
読み出しレジスタをaxi_araddrでセレクトして、slv_reg_rdenが立ったらaxi_rdataに出力している。
slv_reg_rdenは3つの信号しかみておらず、rreadyは見てない。

*_v1_0_S00_AXI.v
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    

とりあえずなんか繋げてみる。

以下のように、レジスタ2と3に、レジスタ0の2倍と3倍+1が格納されるように変更。

*_v1_0_S00_AXI.v
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_reg0 * 2;
	2'h3   : reg_data_out <= slv_reg0 * 3 + 1;
	default : reg_data_out <= 0;
      endcase
end

変更を反映してリパッケージ。

作ったIPをブロックデザインに追加してAutoConnectするといい感じに繋いでくれるはず。
PSはGP0のマスタだけ有効にしてある。FCLK_CLK0は100 MHzで出してるのでAXIは100Mで動く。
インプリしてハードウェアエキスポート、petalinuxに読み込んでビルド(必要?)、BOOT.BINに.bitをパッケージする。

petalinux側ではdevmemでこんな感じで動作確認できる。

test_AA:~$ sudo devmem 0x43c00000
0x00000000
test_AA:~$ sudo devmem 0x43c00000 32 2
test_AA:~$ sudo devmem 0x43c00000
0x00000002
test_AA:~$ sudo devmem 0x43c00004
0x00000000
test_AA:~$ sudo devmem 0x43c00008
0x00000004
test_AA:~$ sudo devmem 0x43c0000c
0x00000007
test_AA:~$ sudo devmem 0x43c00010
0x00000002

デバイスツリー確認

いろんな情報によると、PL側の回路もふつうデバイスツリーに追加されるものらしい。
しかし上で動かしたときデバイスツリーにPL回路は含まれていなかったので気持ち悪い。そして動かなかったとき何が悪いのか全然わからなかった。

そういえばpetalinux-configでRemove PL from device treeオプションを有効にしてたことに気づく。
petalinux-buildが失敗するので有効にしていたが、今回は無しでもビルドできた。
これによりめでたく\components\plnx_workspace\device-tree\device-tree\にpl.dtsiが完成する。

pl.dtsi
/*
 * CAUTION: This file is automatically generated by Xilinx.
 * Version: XSCT 
 * Today is: Fri May 19 15:16:59 2023
 */


/ {
	amba_pl: amba_pl {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		ranges ;
		test_AXI_0: test_AXI@43c00000 {
			clock-names = "s00_axi_aclk";
			clocks = <&clkc 15>;
			compatible = "xlnx,test-AXI-1.1";
			reg = <0x43c00000 0x10000>;
			xlnx,s00-axi-addr-width = <0x4>;
			xlnx,s00-axi-data-width = <0x20>;
		};
		zyxclmm_drm {
			compatible = "xlnx,zocl";
		};
	};
};

またpetalinux上では

PL device file
test_AA:~$ ls /proc/device-tree/amba_pl/
#address-cells  compatible  phandle  test_AXI@43c00000
#size-cells     name        ranges   zyxclmm_drm

のようにデバイスファイルが生成される。

Discussion