Parallella Fan!
Home記事一覧BBS

FPGAに独自の回路を追加する

ParallellaのFPGAに独自の回路を追加して、その回路をCPUから制御できるようにします。

FPGA初心者向けにFPGA関連情報へのリンクをまとめたページを公開しました。「ZEROからのFPGA : FPGAの基礎知識」こちらも参考にしてください。

基本設計

Parallellaに搭載されているXilinx社のZynqチップは、デュアルコア ARM Cortex A9 CPUとFPGAを統合したSoC(System-on-a-chip)です。
チップ内の各デバイスの接続にはARM社の提唱するAMBA(Advanced Microcontroller Bus Architecture) AXI(Advanced eXtensible Interface)バス・アーキテクチャが採用されており、基本的にCPUとFPGAに実装する回路はこのインターフェースを介して通信を行うことになります。


Zynqブロック図

通信の起点となる側にはAXI Masterインターフェースを、リクエストを受ける側にはAXI Slaveインターフェースを実装します。
今回はFPGAの回路側にAXI4-Lite Slaveインターフェースを実装し、CPU側からはメモリー・マップト・I/Oでアクセスして制御します。AXI4-LiteはAXI4のサブセット版で、実装を容易にするために機能が簡略化されています。



SlaveはMasterからのリクエストに応じてWrite、Readの処理を行います。(図では省略していますが、実際はWrite時もSlave側から「データ受け取り完了」を示す信号が返ります。)Slaveは常に受身の状態で動作し、自分の方からデータを読み書きすることはできません。



ユーザー回路に割り当てる物理アドレスは自由に決められますが、Parallella標準の設計では0x60000000から広い空きエリアがあるので、そこを使うことにします。

プロジェクト作成

「ParallellaのFPGAビットストリームを生成する」で作成したプロジェクトをさらにテンプレートにして、メニュー→「File」→「Save Project As...」で「parallella_hw_projects」ディレクトリ以下に新しいプロジェクトを作成してください。



再度複製したプロジェクトでは、「version.v」と「system.xmp」はすでにImportされた状態になっているはずです。
「getbits」、「bit2bin.bif」、「dummy.elf」ファイルと「bitbin」ディレクトリは「Save Project As...」ではコピーされないため、手動で新しいプロジェクトのディレクトリにコピーし、修正します。詳しくは「ParallellaのFPGAビットストリームを生成する:bit.binファイルの生成」を参照してください。

サンプルIPコアのダウンロードと展開

AXI4-Lite Slaveインターフェースを使った簡単なIPコア(Intellectual Property Core:再利用可能な機能ブロック)を作りました。二つのパラメーターを読み込んで、その和を返すだけのHelloWorld的なサンプルです。これをParallellaのビットストリームに組み込みます。

端末で、

cd ~/parallella_hw_projects

wget https://cellspe.matrix.jp/files/hello_axi.tar.gz

tar xzf hello_axi.tar.gz

cd hello_axi

mkdir -p ../user_ip/edk/pcores

mv hello_axi_v1_00_a ../user_ip/edk/pcores/

IPリポジトリを追加


Design Sources→parallella_z7_top→system_stub→system.xmpをダブルクリックするか、右クリックして「Open File」を選択します。
XPSのライセンス・エラーは「OK」、「Close」で閉じます。


XPSが起動したら「Add/Remove IP Repositories」ボタンをクリックします。


IPのファイルが格納されているディレクトリ(IPリポジトリ)を設定します。
「/mnt/windowsC/Adapteva...」はゴミデータなので選択して「×」ボタンをクリックして削除します。
次に、「+」ボタンをクリックします。


ディレクトリ選択画面が開くので「~/parallella_hw_projects/user_ip」ディレクトリを選択して「Choose」をクリックします。元の画面に戻るので「OK」で閉じます。


IPリポジトリに変更を加えた時には「Rescan User IP Repositories」ボタンをクリックします。

IPの追加と接続


「IP Catalog」を下の方にスクロールさせると「Project Peripheral Repository1」が出来ているのでその中の「User」→「Hello AXI」を右クリックして「Add IP」を選択します。確認ダイアログが出るので「Yes」で進めます。


これはIPのコンフィギュレーション画面ですが今回は特に設定する必要がないのでそのまま「OK」をクリックします。


IPの接続を自動で行うか手動で行うかの選択画面です。自動で行うと意図しない設定が行われてしまう場合があるので手動で設定します。「User will make necessary connections and settings」を選択して「OK」をクリックします。


「System Assembly View」の「Bus Interfaces」タブをクリックするとこの画面が出ます。ここでバスの接続を設定します。Processing_system7_0→M_AXI_GP0(AXI Master General Purpose ポート)がaxi_interconnect_1に接続されていることが分かります。これに「Hello AXI」を接続する設定をします。

「hello_axi_0」の「+」をクリックし、S_AXI(AXI Slave)の「No connection」と書かれている部分をクリックします。


「axi_interconnect_1」を選択して「OK」をクリックします。


hello_axi_0がaxi_interconnect_1を経由してM_AXI_GP0に接続されました。


「System Assembly View」の「Ports」タブをクリックしてポート接続設定を開きます。「axi_interconnect_1」を展開するとクロックのポートである「INTERCONNECT_ACLK」に「processing_system7_0::FCLK_CLK0」が接続されていることが分かります。hello_axi_0にも同じクロックを供給します。

「hello_axi_0」を展開し、「S_AXI_ACLK」の「Connected Port」欄のアイコンをクリックします。


「processing_system7_0」の「FCLK_CLK0」を選択してチェックマークをクリックします。


「System Assembly View」の「Address」タブをクリックしてメモリマップの設定を開きます。
「Unmapped Addresses」を展開して「hello_axi_0」の「Base Address」の欄をクリックして「0x60000000」と入力します。


「hello_axi_0」の行の右端の「Lock」にチェックを入れます。


「Perform Project Design Rule Checks」ボタンをクリックしてIPの接続にエラーがないか確認します。下のメッセージ欄にいくつかWARNINGが出ますがErrorが出なければOKです。


メニュー→「File」→「Exit」でXPSを終了し、PlanAheadに戻ります。

IPのソースコードをプロジェクトに追加


PlanAheadでソースコードを編集できるように、IPのソースコードをプロジェクトに追加します。
「Add Sources」ボタンをクリックします。


「Add or Create Design Sources」を選択して「Next」をクリックします。


「Add Files」ボタンをクリックします。


IP本体のソースコードを追加します。
~/parallella_hw_projects/user_ip/edk/pcores/hello_axi_v1_00_a/hdl/verilog/hello_axi.v を選択して「OK」をクリックします。


「Copy sources into project」はチェックを付けないで「Finish」をクリックします。


もう一度「Add Sources」ボタンをクリックし、今度は「Add or Create Simulation Sources」を選択して「Next」をクリックします。


「Add Files」ボタンをクリックします。


シミュレーション用のソースコードを追加します。
~/parallella_hw_projects/hello_axi/hello_axi_testbench/hello_axi_testbench.v を選択して「OK」をクリックします。


「Copy sources into project」はチェックを付けないで「Finish」をクリックします。

ソースコードの閲覧、編集




プロジェクトに追加したソースコードは「Sources」ウィンドウでファイル名をダブルクリックすると内蔵エディタで開くことができます。ソースコードはVerilog HDLで書いています。Parallellaの公式ビットストリームのロジックも基本的にVerilog HDLで書かれています。

このIPのソースコードはBSD 2-Clauseライセンスで公開します。

IP本体:hello_axi.v
/*
  Copyright (c) 2014, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module hello_axi
  #(
    // user parameters
    parameter C_STATE_S_ADDR = 3'd0,
    parameter C_STATE_S_W_DATA = 3'd1,
    parameter C_STATE_S_W_RESPONSE = 3'd2,
    parameter C_STATE_S_R_DATA = 3'd3,
    parameter C_STATE_S_R_READY = 3'd4,
    parameter C_MAP_LEN_IN_BITS = 32'd2,
    parameter C_MAP_ADDR_MASK = (32'd1 << (C_MAP_LEN_IN_BITS + 32'd2)) - 32'd1,
    parameter C_MAP_ADDR_MASK_N = ~C_MAP_ADDR_MASK,
    // system parameters (will be overwritten by XPS)
    parameter C_S_AXI_DATA_WIDTH = 32,
    parameter C_BASEADDR = 32'hFFFFFFFF,
    parameter C_HIGHADDR = 32'h00000000
    )
  (
   input         S_AXI_ACLK,
   input         S_AXI_ARESETN,
   input [31:0]  S_AXI_AWADDR,
   input [2:0]   S_AXI_AWPROT,
   input         S_AXI_AWVALID,
   output        S_AXI_AWREADY,
   input [31:0]  S_AXI_WDATA,
   input [3:0]   S_AXI_WSTRB,
   input         S_AXI_WVALID,
   output        S_AXI_WREADY,
   output [1:0]  S_AXI_BRESP,
   output        S_AXI_BVALID,
   input         S_AXI_BREADY,
   input [31:0]  S_AXI_ARADDR,
   input [2:0]   S_AXI_ARPROT,
   input         S_AXI_ARVALID,
   output        S_AXI_ARREADY,
   output [31:0] S_AXI_RDATA,
   output [1:0]  S_AXI_RRESP,
   output        S_AXI_RVALID,
   input         S_AXI_RREADY
   );

  // output port registers
  reg            s_axi_awready;
  reg            s_axi_wready;
  reg            s_axi_bvalid;
  reg            s_axi_arready;
  reg [31:0]     s_axi_rdata;
  reg            s_axi_rvalid;

  // user registers
  reg [2:0]      state_s;
  reg [31:0]     s_address;
  reg [31:0]     reg_a;
  reg [31:0]     reg_b;

  // output port assignment
  assign S_AXI_AWREADY = s_axi_awready;
  assign S_AXI_WREADY = s_axi_wready;
  assign S_AXI_BRESP = 2'b00;
  assign S_AXI_BVALID = s_axi_bvalid;
  assign S_AXI_ARREADY = s_axi_arready;
  assign S_AXI_RDATA = s_axi_rdata;
  assign S_AXI_RRESP = 2'b00;
  assign S_AXI_RVALID = s_axi_rvalid;

  // AXI4-Lite slave
  always @(posedge S_AXI_ACLK)
    begin
      if (S_AXI_ARESETN == 1'b0)
        begin
          s_axi_awready <= 1'b0;
          s_axi_wready <= 1'b0;
          s_axi_bvalid <= 1'b0;
          s_axi_arready <= 1'b0;
          s_axi_rvalid <= 1'b0;
          state_s <= C_STATE_S_ADDR;
        end
      else
        begin
          case (state_s)
            C_STATE_S_ADDR:
              begin
                // address decode
                if ((S_AXI_AWVALID == 1'b1) && ((S_AXI_AWADDR & C_MAP_ADDR_MASK_N) == C_BASEADDR))
                  // write: get the address
                  begin
                    s_address <= S_AXI_AWADDR;
                    s_axi_awready <= 1'b1;
                    state_s <= C_STATE_S_W_DATA;
                  end
                else if ((S_AXI_ARVALID == 1'b1) && ((S_AXI_ARADDR & C_MAP_ADDR_MASK_N) == C_BASEADDR))
                  // read: get the address
                  begin
                    s_address <= S_AXI_ARADDR;
                    s_axi_arready <= 1'b1;
                    state_s <= C_STATE_S_R_DATA;
                  end
              end

            // write: get the data
            C_STATE_S_W_DATA:
              if (S_AXI_WVALID == 1'b1)
                begin
                  case (s_address[3:2])
                    2'd0:
                      // addr: 0xXXXXXXX0
                      begin
                        // a = input data
                        reg_a <= S_AXI_WDATA;
                      end
                    2'd1:
                      // addr: 0xXXXXXXX4
                      begin
                        // b = input data
                        reg_b <= S_AXI_WDATA;
                      end
                    default: ;
                  endcase
                  s_axi_awready <= 1'b0;
                  s_axi_wready <= 1'b1;
                  s_axi_bvalid <= 1'b1;
                  state_s <= C_STATE_S_W_RESPONSE;
                end

            // write: wait for bready
            C_STATE_S_W_RESPONSE:
              if (S_AXI_BREADY == 1'b1)
                begin
                  s_axi_wready <= 1'b0;
                  s_axi_bvalid <= 1'b0;
                  state_s <= C_STATE_S_ADDR;
                end

            // read: pass the data
            C_STATE_S_R_DATA:
              begin
                case (s_address[3:2])
                  2'd2:
                    // addr: 0xXXXXXXX8
                    begin
                      // output data = c
                      s_axi_rdata <= reg_a + reg_b;
                    end

                  default: ;
                endcase
                s_axi_arready <= 1'b0;
                s_axi_rvalid <= 1'b1;
                state_s <= C_STATE_S_R_READY;
              end

            // read: wait for rready
            C_STATE_S_R_READY:
              if (S_AXI_RREADY == 1'b1)
                begin
                  s_axi_rvalid <= 1'b0;
                  state_s <= C_STATE_S_ADDR;
                end

            default: ;
          endcase
        end
    end
endmodule
テストベンチ:hello_axi_testbench.v
/*
  Copyright (c) 2014, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


`timescale 1ns / 1ps

module hello_axi_testbench;

  parameter STEP = 10; // 100MHz

  reg                                  S_AXI_ACLK;
  reg                                  S_AXI_ARESETN;
  reg [31:0]                           S_AXI_AWADDR;
  reg [2:0]                            S_AXI_AWPROT;
  reg                                  S_AXI_AWVALID;
  wire                                 S_AXI_AWREADY;
  reg [31:0]                           S_AXI_WDATA;
  reg [3:0]                            S_AXI_WSTRB;
  reg                                  S_AXI_WVALID;
  wire                                 S_AXI_WREADY;
  wire [1:0]                           S_AXI_BRESP;
  wire                                 S_AXI_BVALID;
  reg                                  S_AXI_BREADY;
  reg [31:0]                           S_AXI_ARADDR;
  reg [2:0]                            S_AXI_ARPROT;
  reg                                  S_AXI_ARVALID;
  wire                                 S_AXI_ARREADY;
  wire [31:0]                          S_AXI_RDATA;
  wire [1:0]                           S_AXI_RRESP;
  wire                                 S_AXI_RVALID;
  reg                                  S_AXI_RREADY;

  hello_axi
    #(
      .C_BASEADDR ( 32'h60000000 ),
      .C_HIGHADDR ( 32'h6000FFFF )
      )
  hello_axi_0
    (
     .S_AXI_ACLK ( S_AXI_ACLK ),
     .S_AXI_ARESETN ( S_AXI_ARESETN ),
     .S_AXI_AWADDR ( S_AXI_AWADDR ),
     .S_AXI_AWPROT ( S_AXI_AWPROT ),
     .S_AXI_AWVALID ( S_AXI_AWVALID ),
     .S_AXI_AWREADY ( S_AXI_AWREADY ),
     .S_AXI_WDATA ( S_AXI_WDATA ),
     .S_AXI_WSTRB ( S_AXI_WSTRB ),
     .S_AXI_WVALID ( S_AXI_WVALID ),
     .S_AXI_WREADY ( S_AXI_WREADY ),
     .S_AXI_BRESP ( S_AXI_BRESP ),
     .S_AXI_BVALID ( S_AXI_BVALID ),
     .S_AXI_BREADY ( S_AXI_BREADY ),
     .S_AXI_ARADDR ( S_AXI_ARADDR ),
     .S_AXI_ARPROT ( S_AXI_ARPROT ),
     .S_AXI_ARVALID ( S_AXI_ARVALID ),
     .S_AXI_ARREADY ( S_AXI_ARREADY ),
     .S_AXI_RDATA ( S_AXI_RDATA ),
     .S_AXI_RRESP ( S_AXI_RRESP ),
     .S_AXI_RVALID ( S_AXI_RVALID ),
     .S_AXI_RREADY ( S_AXI_RREADY )
     );

  // generate clock
  always #(STEP / 2)
    begin
      S_AXI_ACLK = ~S_AXI_ACLK;
    end

  // fake axi master write
  task fake_axi_master_write
    (
     input [31:0] addr,
     input [31:0] data
     );
    begin
      S_AXI_AWADDR = addr;
      S_AXI_AWVALID = 1'b1;
      #STEP;
      S_AXI_AWVALID = 1'b0;
      S_AXI_WDATA = data;
      S_AXI_WVALID = 1'b1;
      #STEP;
      S_AXI_WVALID = 1'b0;
      S_AXI_BREADY = 1'b1;
      #STEP;
      S_AXI_BREADY = 1'b0;
      #STEP;
    end
  endtask

  // fake axi master read
  task fake_axi_master_read
    (
     input [31:0] addr
     );
    begin
      S_AXI_ARADDR = addr;
      S_AXI_ARVALID = 1'b1;
      repeat(2) #STEP;
      S_AXI_ARVALID = 1'b0;
      S_AXI_RREADY = 1'b1;
      #STEP;
      S_AXI_RREADY = 1'b0;
      #STEP;
    end
  endtask

  // initialize
  initial
    begin
      // initialize
      S_AXI_ACLK = 1'b1;
      S_AXI_ARESETN = 1;
      #STEP;

      // reset
      S_AXI_ARESETN = 0;
      #STEP;
      S_AXI_ARESETN = 1;
      #STEP;

      repeat(2) #STEP;

      fake_axi_master_write(32'h60000000, 32'd1);
      fake_axi_master_write(32'h60000004, 32'd2);
      fake_axi_master_read(32'h60000008);
    end
endmodule

シミュレーターで動作を確認


Flow Navigatorウィンドウ→Project Manager→Run Behavioral Simulationボタンをクリックします。


シミュレーションに使用するトップモジュールを指定します。「...」ボタンをクリックします。


「hello_axi_testbench」を選択して「OK」をクリックします。


「Launch」をクリックします。


HDLシミュレーター「ISim」が起動します。回路をシミュレートして信号の波形やレジスタの値などを表示することができます。左側のオブジェクトパネルからポートやレジスタ名を波形ビューワにドラッグ&ドロップしたり並び替えたりしてレイアウトすることができます。
デフォルトでは数値は2進表示ですが、波形ビューワのオブジェクト名を右クリックして Radix → Hexadecimal を選択すると16進表示できます。
ここではあらかじめレイアウトした設定ファイルを開きます。
「Open」ボタンをクリックするとファイル選択画面が出るので
~/parallella_hw_projects/hello_axi/hello_axi_testbench/hello_axi_testbench.wcfg を開きます。


画面の右上の「Re-launch」ボタンをクリックし、「+」「-」のズームアイコンをクリックして表示幅を調整するとこのような画面になります。
この回路ではアドレス0x60000000にWriteするとレジスタAに値が入り、アドレス0x60000004でレジスタBに入り、そしてアドレス0x60000008をReadするとレジスタAとレジスタBの和が返されるようになっています。
S_AXI_AWADDRが書き込みアドレス、S_AXI_WDATAが書き込みデータ、S_AXI_RDATAが読み込みデータを表しているので、この波形からレジスタAに1がセットされ、レジスタBに2がセットされ、レジスタAとレジスタBの和である3が返されていることが分かります。
このテスト用の信号を出力しているのが hello_axi_testbench.v です。これは擬似的にAXI Masterデバイスを模倣してIPに信号を送っています。

HDLソースコードを修正した後、「Re-launch」をクリックすると即座にシミュレートされます。
開発の流れとしては、ロジック本体とテストベンチのソースコードを書き、ISimでシミュレートして動作の検証をした後、ビットストリームを生成して実機で動作させるという手順になります。(ビットストリームの生成には2〜30分かかるので、あまり頻繁には行えません。)

メニュー→File→ExitでISimを終了してビットストリームを生成します。

ビットストリームの生成


PlanAheadの「Sources」ウィンドウでDesign Sources→parallella_z7_top→system_stubを展開し、「system_i-system(system.xmp)」を右クリックして「Reset Output Products」を選択します。


この画面の内容は状況によって変化しますが、「OK」でXPSプロジェクトの生成ファイルがリセットされます。この作業は特にIPのソースコードを修正した場合などに必要です。
次に、「ParallellaのFPGAビットストリームを生成する」と同様の手順でビットストリームの生成、parallella.bit.binへの変換を行います。この手順は省略します。

IPの論理合成時のログは、
プロジェクトディレクトリ/プロジェクト名.srcs/sources_1/edk/parallella_70x0_x/synthesis/system_hello_axi_0_wrapper_xst.srp に出力されています。このログファイルにはエラーメッセージ、使用スライス、最大動作周波数などが書かれています。

ビットストリームの転送

(標準のビットストリーム parallella.bit.bin はあらかじめバックアップを取っておいてください。)

ネットワーク経由でビットストリームの転送を行うために以下のようなスクリプトを作っておくと便利です。

PCからParallellaにビットストリームを転送するスクリプト
PCの ~/parallella_hw_projects/プロジェクトのディレクトリ/ 以下に置きます。
以降、「192.168.0.123」の部分はParallellaのIPアドレスに置き換えてください。(ParallellaのIPアドレスはルータの管理画面などから確認してください。)

scpbitbin.sh
#!/bin/sh
scp bitbin/parallella.bit.bin linaro@192.168.0.123:

実行方法:
sh scpbitbin.sh

実行すると parallella.bit.bin がParallellaのホームディレクトリに転送されます。

PCからParallellaにsshでログインするスクリプト

sshparallella.sh
#!/bin/sh
ssh linaro@192.168.0.123

実行方法:
sh sshparallella.sh

Parallellaで parallella.bit.bin をSDカードのBOOTパーティションにコピーするスクリプト
Parallellaのホームディレクトリに置きます。

copybin.sh
#!/bin/sh
sudo mount /dev/mmcblk0p1 /mnt
sudo cp parallella.bit.bin /mnt/
sudo sync
sudo umount /mnt

実行方法:Parallellaにsshでログインして、
sh copybin.sh

そして
sudo reboot
すると新しいビットストリームが読み込まれます。

ドライバの展開、ビルド、実行

ドライバのtarballをParallellaに転送します。

cd ~/parallella_hw_projects/hello_axi

scp hello_axi_driver.tar.gz linaro@192.168.0.123:

ssh linaro@192.168.0.123

(以降Parallellaで)
tar xzf hello_axi_driver.tar.gz

cd hello_axi_driver

make

sudo ./main



ドライバと言っていますが実際はデバイスドライバは書かずに /dev/mem デバイスを使ってアプリケーションからMMIOでIPを直接制御しています。

通常、OSの上で動くアプリケーションは仮想アドレス空間にしかアクセスできませんが、/dev/mem を使うとアプリケーションから物理アドレス空間に簡単にアクセスすることができます。実行時にroot権限が必要なので通常のアプリケーションでは使用されませんが、開発や実験の際には便利に使えます。

main.c
/*
  Copyright (c) 2014, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <time.h>

#define DEVMEM "/dev/mem"
#define BASE_ADDR 0x60000000
#define REG_LEN 8

uint32_t input_num(const char *str)
{
  printf("%s: ", str);
  fflush(stdin);
  uint32_t a;
  if (scanf("%d", &a) != 1)
  {
    perror("input error");
    abort();
  }
  return a;
}

int main(int argc, char *argv[])
{
  int fd = open(DEVMEM, O_RDWR | O_SYNC);
  if (fd == -1)
  {
    perror("cannot open /dev/mem");
    abort();
  }

  off_t phy_addr = BASE_ADDR;
  size_t length = 4 * REG_LEN;
  uint32_t *mem = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, phy_addr);

  if (mem == MAP_FAILED)
  {
    perror("map failed");
    abort();
  }

  while (1)
  {
    volatile uint32_t *reg = mem;
    *(reg + 0) = input_num("a");
    *(reg + 1) = input_num("b");
    printf("a + b = %d\n", *(reg + 2));
  }

  munmap(mem, length);
  close(fd);
  return 0;
}