フィボナッチを求める回路をVHDLとMessagePack-RPCでFPGAに実装してみた(ZYNQ編)

#はじめに
[MessagePack-RPCを使ってFPGAを制御](http://qiita.com/ikwzm/items/2644c6e50a7049c75d49)の続きです。
今回は VHDL で記述したフィボナッチ数を求める回路を MessagePack-RPC で制御する例を示します。
ここでは次のような環境を使っています。

 - FPGA  : Xilinx Zynq Z-7010 AP SoC
 - Board : Digilent ZYBO
 - OS    : Linux 3.18 + Debian 8.2(jessie)

プロジェクトの全ソースコードは[こちら](https://github.com/ikwzm/msgpack-vhdl-examples/tree/master/examples/fibonacci)にあります。

#ブロック図

![akgeo1.jpg](https://qiita-image-store.s3.amazonaws.com/0/24981/27a5a198-6166-ce04-e07a-e92fa95bb9dd.jpeg)



#VHDLで回路を記述

フィボナッチを求める回路は今回はVHDLで次のように記述しました。再帰は使わずにループを使っています。

````VHDL:fib.vhd
library ieee;
use     ieee.std_logic_1164.all;
use     ieee.numeric_std.all;
entity  FIB is
    port (
        CLK    : in  std_logic;
        RST    : in  std_logic;
        GO     : in  std_logic;
        N      : in  std_logic_vector( 7 downto 0);
        BUSY   : out std_logic;
        DONE   : out std_logic;
        O      : out std_logic_vector(63 downto 0)
    );
end     FIB;
architecture RTL of FIB is
    signal    curr_count  :  unsigned(N'length-1 downto 0);
    signal    last_count  :  unsigned(N'length-1 downto 0);
    signal    curr_value  :  unsigned(O'length-1 downto 0);
    signal    prev_value  :  unsigned(O'length-1 downto 0);
    signal    run         :  boolean;
begin
    process (CLK, RST) begin
        if (RST = '1') then
            run        <= FALSE;
            curr_count <= (others => '0');
            last_count <= (others => '0');
            curr_value <= (others => '0');
            prev_value <= (others => '0');
        elsif (CLK'event and CLK = '1') then
            if (run = FALSE) then
                if (GO = '1') then
                    run <= TRUE;
                    last_count <= unsigned(N);
                    curr_count <= to_unsigned(1, curr_count'length);
                    prev_value <= to_unsigned(0, curr_value'length);
                    if (unsigned(N) > 0) then
                        curr_value <= to_unsigned(1, curr_value'length);
                    else
                        curr_value <= to_unsigned(0, curr_value'length);
                    end if;
                end if;
            else
                if (curr_count < last_count) then
                    curr_count <= curr_count + 1;
                    prev_value <= curr_value;
                    curr_value <= curr_value + prev_value;
                else
                    run <= FALSE;
                end if;
            end if;
        end if;
    end process;
    BUSY <= '1' when (run = TRUE) else '0';
    DONE <= '1' when (run = TRUE and curr_count >= last_count) else '0';
    O    <= std_logic_vector(curr_value);
end RTL;
````

#テスト用スクリプト

````ruby:fibonacci_server.rb
require 'msgpack'
require 'serialport'

class FibonacciServer

  def initialize
    @port     = SerialPort.new("/dev/zptty0", 38400)
    @port.read_timeout = 1000
    @port.flow_control = SerialPort::HARD
    @unpacker = MessagePack::Unpacker.new(@port)
    @msgid    = 0
  end

  def call(method, args)
    @msgid = (@msgid+1) % 256
    req = [0, @msgid, method, args].to_msgpack
    @port.write(req)
    @unpacker.each do |obj|
      type, resid, error, result = obj
      if (error == nil) then
        return result
      else
        puts error
        return nil
      end
      break
    end 
  end

  def fib(arg)
    return call('fib', [arg])
  end
end
````

````ruby:test.rb
require_relative 'fibonacci_server'

fib = FibonacciServer.new

(0..90).to_a.each do |n|
  f = fib.fib(n)
  puts "#{n} => #{f}"
end
````

#実行結果

````
shell% ruby test.rb
0 => 0
1 => 1
2 => 1
3 => 2
4 => 3
5 => 5
6 => 8
7 => 13
8 => 21
9 => 34
10 => 55
11 => 89
12 => 144
13 => 233
14 => 377
15 => 610
16 => 987
17 => 1597
18 => 2584
19 => 4181
20 => 6765
21 => 10946
22 => 17711
23 => 28657
24 => 46368
25 => 75025
26 => 121393
27 => 196418
28 => 317811
29 => 514229
30 => 832040
31 => 1346269
32 => 2178309
33 => 3524578
34 => 5702887
35 => 9227465
36 => 14930352
37 => 24157817
38 => 39088169
39 => 63245986
40 => 102334155
41 => 165580141
42 => 267914296
43 => 433494437
44 => 701408733
45 => 1134903170
46 => 1836311903
47 => 2971215073
48 => 4807526976
49 => 7778742049
50 => 12586269025
51 => 20365011074
52 => 32951280099
53 => 53316291173
54 => 86267571272
55 => 139583862445
56 => 225851433717
57 => 365435296162
58 => 591286729879
59 => 956722026041
60 => 1548008755920
61 => 2504730781961
62 => 4052739537881
63 => 6557470319842
64 => 10610209857723
65 => 17167680177565
66 => 27777890035288
67 => 44945570212853
68 => 72723460248141
69 => 117669030460994
70 => 190392490709135
71 => 308061521170129
72 => 498454011879264
73 => 806515533049393
74 => 1304969544928657
75 => 2111485077978050
76 => 3416454622906707
77 => 5527939700884757
78 => 8944394323791464
79 => 14472334024676221
80 => 23416728348467685
81 => 37889062373143906
82 => 61305790721611591
83 => 99194853094755497
84 => 160500643816367088
85 => 259695496911122585
86 => 420196140727489673
87 => 679891637638612258
88 => 1100087778366101931
89 => 1779979416004714189
90 => 2880067194370816120
````

#デザインフロー

##用意するもの

ZYNQ用に回路をインプリメンテーションするためには次の開発ツールを使います。

 - Xilinx Vivado 2015.3 or 2015.4
 - Xilinx SDK 2015.3 or 2015.4


テストスクリプトを実行するためには次の ruby パッケージが必要です。

 - msgpack-ruby
 - serialport


##リポジトリと各submodule をダウンロード

````
shell% git clone git://github.com/ikwzm/msgpack-vhdl-examples.git
shell% cd msgpack-vhdl-examples
shell% git submodule init
shell% git submodule update
````

##IP-Packageを作る

具体的な手順は[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(IP-Package編)](http://qiita.com/ikwzm/items/924fc3fd59bdf7da55a2)と同様です。詳細はそちらを参照してください。
ここでは簡単に次のスクリプトを実行することでIPを作ります。

````
shell% cd examples/fibonacci/fpga/xilinx/zybo_test_vhdl/ip
shell% vivado -mode batch -source create_ip.tcl
````

##論理合成～BOOT.binを作るまで

具体的な手順は[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(ZYNQ論理合成編)](http://qiita.com/ikwzm/items/0254f1d49602095b5cde)と同様です。詳細はそちらを参照してください。
ここでは以下の手順でBOOT.binを作ります。

###まずは論理合成

````
shell% cd examples/fibonacci/fpga/xilinx/zybo_test_vhdl/project
shell% vivado -mode batch -source create_project.tcl
shell% vivado -mode batch -source implementation.tcl
````

###SDKのためにハードウェア情報をエクスポートする.

````
shell% vivado -mode batch -source export_hardware.tcl
````

これで project/project.sdk/design_1_wrapper.hdf が出来ます.

###FSBL(First Stage Boot Loader)を生成する.

````
shell% xsdk -batch -source build_fsbl.tcl
````

###u-bootを生成する.

このプロジェクトでは u-boot の作成は行いません. とりあえず適当に作った u-boot.elf を ./boot に入れておきました.

###BOOT.binを生成する.

````
shell% cd examples/fibonacci/fpga/xilinx/zybo_test_vhdl/boot/
shell% bootgen -image u-boot.bif -w on -o BOOT.bin
````

##ZPTTYドライバをインストール

具体的な手順は[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(TTYドライバ編)](http://qiita.com/ikwzm/items/533b905f7d1eb2e6c4d5)と同様です。

#参照

[MessagePack for VHDL (https://github.com/ikwzm/msgpack-vhdl)](https://github.com/ikwzm/msgpack-vhdl)
[MessagePack for VHDL Examples (https://github.com/ikwzm/msgpack-vhdl-examples)](https://github.com/ikwzm/msgpack-vhdl-examples)
[MessagePack-RPCを使ってFPGAを制御](http://qiita.com/ikwzm/items/2644c6e50a7049c75d49)
[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(アーキテクチャ編)](http://qiita.com/ikwzm/items/6cb623b057a687ff4a8e)
[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(IP-Package編)](http://qiita.com/ikwzm/items/924fc3fd59bdf7da55a2)
[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(ZYNQ論理合成編)](http://qiita.com/ikwzm/items/0254f1d49602095b5cde)
[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(TTYドライバ編)](http://qiita.com/ikwzm/items/533b905f7d1eb2e6c4d5)
[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(MessagePack-Ruby編)](http://qiita.com/ikwzm/items/f742e2c9b5d9642d8857)
[フィボナッチを求める回路をVHDLとMessagePack-RPCでFPGAに実装してみた(シミュレーション編)](http://qiita.com/ikwzm/items/a0ff6cf559f766162951)