FC2カウンター FPGAの部屋 Zynq-7000(ZC702)のLinuxでMIOに接続されているLEDを制御2

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

Zynq-7000(ZC702)のLinuxでMIOに接続されているLEDを制御2

Zynq-7000(ZC702)のLinuxでMIOに接続されているLEDを制御”の続き。

前回はDS23(LED) をLinuxで制御したが、今回はAXI GPIOに接続されたSW5を押した時にDS23(LED) を点灯させて、その後消灯させてみようと思う。これが出来れば、”Zynq-7000(ZC702)でビットファイル入りのLinuxのブートイメージが入ったSDカードを作る”で作ったビットファイル入りのLinuxブートイメージで間違いなくビットファイルをコンフィギュレーションしていることがわかった。

(注)今回のブログでは、”Zynq-7000 EPP Concepts, Tools, and Techniques A Hands-On Guide to Effective Embedded System Design UG873 (v14.1) May 31, 2012”の19ページからの”Embedded System Design Using the Zynq Processing System and Programmable Logic”チュートリアル(通称、チュートリアル2)で作成したビットファイルでコンフィギュレーションされた回路をZynqのLinux上から動作させている。
このチュートリアルについては、”Zynq-7000(ZC702)のチュートリアル2をやってみた1(XPSプロジェクトの生成)”の記事とそれに続く記事を参照のこと。


さて、それでは、やってみよう。手順は前回と同じだが、もう一度書いておく。

・SDカードをZC702に挿入し、電源ON。

・Linuxが立ち上がる。IPアドレスを10.10.70.120 にセットする。(ifconfig eth0 10.10.70.120 netmask 255.255.255.0)

・パソコンで、SDKを立ちあげて、チュートリアル2のワークスペースを指定する。

・SDKのRemote Absolute File Path for C/C++ Application でLinuxのリモートデバックをするファイルを作成する。(”Zynq-7000(ZC702)のLinuxチュートリアル4(リモートデバック2)”参照)

今回のCソースを下に貼る。

#define MIO_GPIO_BASE    0xE000A000
#define AXI_GPIO_BASE    0x41200000

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <unistd.h>

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

// I/O access

volatile unsigned *setup_io(off_t addr);

int main()
{
    volatile unsigned *mask_data_0_lsw, *mask_data_0_msw;
    volatile unsigned *data_0, *data_0_ro;
    volatile unsigned *dirm_0, *oen_0; 
    volatile unsigned *mio_gpio;
    volatile unsigned *axi_gpio, *axi_gpio_tri;
    int axi_gpio_val, axi_gpio_val_old;

    mio_gpio = setup_io((off_t)MIO_GPIO_BASE);
    axi_gpio = setup_io((off_t)AXI_GPIO_BASE);

    mask_data_0_lsw =     mio_gpio;
    mask_data_0_msw =     mio_gpio + 1;        // 0x4番地
    data_0 =             mio_gpio + 0x10;    // 0x40番地
    data_0_ro =         mio_gpio + 0x18;    // 0x60番地
    dirm_0 =             mio_gpio + 0x81;    // 0x204番地
    oen_0 =             mio_gpio + 0x82;    // 0x208番地
    axi_gpio_tri =        axi_gpio + 1;        // 0x4番地
    
    *(dirm_0) = *(dirm_0)|0x400;    // gpio[10]を出力に設定
    *(oen_0) = *(oen_0)|0x400;        // gpio[10]をイネーブル
    *(axi_gpio_tri) = 0x1;            // axi_gpio入力設定

    printf("AXI_GPIO_TRI = %x\n", *(axi_gpio_tri));

    printf("MASK_DATA_0_LSW = %x\n", *(mask_data_0_lsw));
    printf("MASK_DATA_0_MSW = %x\n", *(mask_data_0_msw));
    printf("DATA_0 = %x\n", *(data_0));
    printf("DATA_0_RO = %x\n", *(data_0_ro));
    printf("DIRM_0 = %x\n", *(dirm_0));
    printf("OEN_0 = %x\n", *(oen_0));

    *(data_0) = *(data_0)&(~(0x400));    // DS23 (LED) 消灯
    printf("DATA_0 = %x\n", *(data_0));

    axi_gpio_val_old = *(axi_gpio);
    while(1){
        axi_gpio_val = *(axi_gpio);
        if (axi_gpio_val)
            *(data_0) = *(data_0)|(0x400);    // DS23点灯
        else
            *(data_0) = *(data_0)&(~(0x400));    // DS23消灯

        if (!axi_gpio_val && axi_gpio_val_old)
            break;    // axi_gpio が1から0になった立下りでループを抜ける
        axi_gpio_val_old = axi_gpio_val;
    }

    printf("AXI_GPIO = %x\n", *(axi_gpio));

    printf("Hello World\n");

    return 0;
}


AXI_GPIO_BASEの値は、xparameter.h からいただいてきた。xparameter.h がどこにあるかというと、SDKのProject Explorer のBSP(hello_world_bsp_0) -> ps7_cortexa9_0 -> include にある。プログラムはDS23を消灯して、SW5の値をDS23に設定する。SW5が押された時にDS23を点灯して、離された時にDS23を消灯してからreturn する。
なぜ、hello world があるかというとhello_worldプロジェクトを書き換えているからだ。hello world はそのままで残している。
setup_io() は前回から、物理アドレスを引数に入れて関数をコールし、返り値に論理アドレスを返すように書き換えた。基本的なコードは、”RPi Low-level peripherals”のsetup_io() のままだ。
どこを書き換えたか説明する。最初に関数の宣言を書き換えた。コメントアウトしてあるのが元の関数宣言だ。

volatile unsigned *setup_io(off_t mapped_addr)
// void setup_io()


グローバルだった変数をローカル変数に変更して、setup_io() に追加した。

    int  mem_fd;
    char *gpio_mem, *gpio_map;


mmap() の部分を書き変えた。最後の引数のGPIO_BASEを、関数が呼ばれた時の引数のmapped_addr に書き換えている。

   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      mapped_addr
   );


最後にreturn() を書き加えた。コメントアウトしてあるのが、書き換えた行だ。

   // gpio = (volatile unsigned *)gpio_map;
   return((volatile unsigned *)gpio_map);


・これでCソースは完成したので、セーブすれば、オートでビルドされる。

・下図がDebugモードに移行したSDKだ。この状態ではConsole にgdb のデバッグメッセージが出ている。これを抑制するために、ピンクの四角で囲ったVerbose console mode をクリックして解除する。
ZC702_Linux_54_120720.png

Verbose console mode をクリックして解除すると、gdb のメッセージが抑制されて、printfで書かれたメッセージがよく見えるようになる。

・RunメニューからResumeをクリックするとソフトウェアが動作して、SW5を押すとDS23が点灯し、SW5を話すとDS23が消灯してソフトウェアからリターンした。

実行時のSDKのConsole に表示されたメッセージを下に貼っておく。

zynq> gdbserver :1234 /Apps/linux_hello_world_0.elf null
Process /Apps/linux_hello_world_0.elf created; pid = 655
Listening on port 1234
Remote debugging from host 10.10.70.121
AXI_GPIO_TRI = 1
MASK_DATA_0_LSW = 2ab2
MASK_DATA_0_MSW = 681
DATA_0 = 6812ab2
DATA_0_RO = 6802a32
DIRM_0 = 480
OEN_0 = 480
DATA_0 = 6c12ab2
AXI_GPIO = 0
Hello World

Child exited with status 0
GDBserver exiting
zynq>
zynq> exit


これで、PL部分に構築されたAXI GPIOを、PS部分のARMプロセッサで動作するLinuxで使うことが出来た。つまり、ビットファイル入りのLinuxブートイメージで、間違いなくビットファイルをコンフィギュレーション出来ているということになる。

おまけとして、ZynqのLinuxにソフトウェアをロードしてリモートデバックを実行しているので、実体はZynqのLinux上のApps -> linux_hello_world_0.elf となる。当然、Zynq上でlinux_hello_world_0.elf を実行すると同じ結果になる。
ZC702_Linux_55_120720.png

(注)mmap() はroot権限でしか使えません。
  1. 2012年07月20日 05:11 |
  2. Zynq
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog19.fc2.com/tb.php/2204-7b97dcb1
この記事にトラックバックする(FC2ブログユーザー)