FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

PyMTLを試す

PyMTLは、Pythonの記述からVerilog-HDLを出力するためのツールだ。

http://csl.cornell.edu/~cbatten/pdfs/lockhart-pymtl-micro2014.pdf

1. インストールまで

僕はRTLの開発はWindows+Cygwinの環境を主体にしているので、Cygwin上にインストールする方法を調べた。

ちらのうら - Cygwin上でpipとsetuptoolsをインストールする方法

Cygwinのsetup-x86_64.exeからpython-setuptoolsをインストールして、その後にpipをeays_installからインストールする。

$ easy_install-2.7 pip
Searching for pip
Reading https://pypi.python.org/simple/pip/
Best match: pip 7.1.0
Downloading https://pypi.python.org/packages/source/p/pip/pip-7.1.0.tar.gz#md5=d935ee9146074b1d3f26c5f0acfd120e
Processing pip-7.1.0.tar.gz
Writing /tmp/easy_install-ZhJY7c/pip-7.1.0/setup.cfg
Running pip-7.1.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-ZhJY7c/pip-7.1.0/egg-dist-tmp-AJgGzC
warning: no previously-included files found matching '.coveragerc'
warning: no previously-included files found matching '.mailmap'
warning: no previously-included files found matching '.travis.yml'
warning: no previously-included files found matching 'pip/_vendor/Makefile'
warning: no previously-included files found matching 'tox.ini'
warning: no previously-included files found matching 'dev-requirements.txt'
no previously-included directories found matching '.travis'
no previously-included directories found matching 'docs/_build'
no previously-included directories found matching 'contrib'
no previously-included directories found matching 'tasks'
no previously-included directories found matching 'tests'
creating /usr/lib/python2.7/site-packages/pip-7.1.0-py2.7.egg
Extracting pip-7.1.0-py2.7.egg to /usr/lib/python2.7/site-packages
Adding pip 7.1.0 to easy-install.pth file
Installing pip script to /usr/bin
Installing pip2.7 script to /usr/bin
Installing pip2 script to /usr/bin

Installed /usr/lib/python2.7/site-packages/pip-7.1.0-py2.7.egg
Processing dependencies for pip
Finished processing dependencies for pip

あとは、shitaxxさんのブログのインストール方法に則る。

shtaxxx.hatenablog.com

$ pip install virtualenv
Collecting virtualenv
  Downloading virtualenv-13.1.0-py2.py3-none-any.whl (1.7MB)
    100% |████████████████████████████████| 1.7MB 88kB/s
Installing collected packages: virtualenv
Successfully installed virtualenv-13.1.0

次に、Cygwin上にVerilatorをインストールする。

www.veripool.org

autoconfとか、gccとかもCygwinのデフォルトに入っていないかったので、インストールした。

apt-cyg install autoconf gcc-core flex bison make 

あとはgcc-g++が何故かapt-cygで見付からなかったので、setup-x86_64.exeからインストールした。 そしてVerilatorをインストールしたので、いよいよPyMTLの構築を開始する。

まずは、PyMTLの説明に書いてあるとおり、Virtualenvを構築する。

$ cd 
$ mkdir ./work/venvs
$ cd ./work/venvs
$ virtualenv --python=python2.7 ~/work/venvs/pymtl_env
Running virtualenv with interpreter /usr/bin/python2.7
New python executable in /home/Masayuki/work/venvs/pymtl_env/bin/python2.7
Also creating executable in /home/Masayuki/work/venvs/pymtl_env/bin/python
Installing setuptools, pip, wheel...done.
$ source ~/work/venvs/pymtl_env/bin/activate
(pymtl_env)

そのまま、PyMTLをcloneしてきてから、ビルドする。

$ cd ~/software
$ git clone https://github.com/cornell-brg/pymtl.git
Cloning into 'pymtl'...
remote: Counting objects: 14170, done.
remote: Total 14170 (delta 0), reused 0 (delta 0), pack-reused 14170
Receiving objects: 100% (14170/14170), 4.13 MiB | 617.00 KiB/s, done.
Resolving deltas: 100% (10710/10710), done.
Checking connectivity... done.
Checking out files: 100% (617/617), done.
$ pip install --editable ./pymtl
...
Successfully installed cffi-1.1.2 execnet-1.3.0 greenlet-0.4.7 py-1.4.30 pymtl pytest-2.7.2 pytest-xdist-1.12
(pymtl_env)

2. テストコードを書いてみる

さっそく自分でコード書いてみた。Python慣れないから辛い...

  • decoder.py
from pymtl import *


class MipsDecoder(Model):
    def __init__ (s):
        inst_length = 5
        s.in_ = InPort(inst_length)
        s.out = OutPort(8)

        code_add = 0
        code_sub = 1
        code_mul = 2
        code_div = 3

        @s.combinational
        def comb():
            if s.in_ == code_add:
                s.out = 0
            elif s.in_ == code_sub:
                s.out = 2
            elif s.in_ == code_mul:
                s.out = 4
            elif s.in_ == code_div:
                s.out = 8
  • decoder_test.py
from pymtl import *

from decoder import MipsDecoder

def test_decoder():
    model = MipsDecoder()
    model = TranslationTool( model )
    model.elaborate()

以下の方法でビルドし、Verilog-HDLのコードを吐き出す。

$ py.test decoder_test.py
============================================== test session starts ==============================================
platform cygwin -- Python 2.7.10 -- py-1.4.30 -- pytest-2.7.2
rootdir: /cygdrive/c/usr/work/pymtl/test/examples/decoder, inifile:
plugins: xdist
collected 1 items

decoder_test.py .

=========================================== 1 passed in 1.13 seconds ============================================
(pymtl_env)

出来た!

$ ls -1
__pycache__
decoder.py
decoder.pyc
decoder_test.py
libMipsDecoder_0x791afe0d4d8c_v.so
MipsDecoder_0x791afe0d4d8c.v
MipsDecoder_0x791afe0d4d8c_v.cpp
MipsDecoder_0x791afe0d4d8c_v.py
MipsDecoder_0x791afe0d4d8c_v.pyc
obj_dir_MipsDecoder_0x791afe0d4d8c

$ less MipsDecoder_0x791afe0d4d8c.v
//-----------------------------------------------------------------------------
// MipsDecoder_0x791afe0d4d8c
//-----------------------------------------------------------------------------
// dump-vcd: False
`default_nettype none
module MipsDecoder_0x791afe0d4d8c
(
  input  wire [   0:0] clk,
  input  wire [   4:0] in_,
  output reg  [   7:0] out,
  input  wire [   0:0] reset
);

  // localparam declarations
  localparam code_add = 0;
  localparam code_div = 3;
  localparam code_mul = 2;
  localparam code_sub = 1;



  // PYMTL SOURCE:
  //
  // @s.combinational
  // def comb():
  //             if s.in_ == code_add:
  //                 s.out = 0
  //             elif s.in_ == code_sub:
  //                 s.out = 2
  //             elif s.in_ == code_mul:
  //                 s.out = 4
  //             elif s.in_ == code_div:
  //                 s.out = 8

  // logic for comb()
  always @ (*) begin
    if ((in_ == code_add)) begin
      out = 0;
    end
    else begin
      if ((in_ == code_sub)) begin
        out = 2;
      end
      else begin
        if ((in_ == code_mul)) begin
          out = 4;
        end
        else begin
          if ((in_ == code_div)) begin
            out = 8;
          end
          else begin
          end
        end
      end
    end
  end


endmodule // MipsDecoder_0x791afe0d4d8c
`default_nettype wire

うーん、なんか思ってたのと違うなあ。。。ってか、Pythonでswitchってどう書けばいいんだろう。

Pythonで記述することによる汎用性の向上は素晴しいことだと思う。RTLにバリエーションを持たせなければならないとか、まだ仕様が決まってなくてとりあえず作らなければならないとき(これ、会社にいると本当に良くある)とかに、パッと作るには有効な手段だと思う。 こういうツールが役に立つのって、バリエーションを物凄い数試して、良い物を見付けたりとか、とにかく大量のケースを試すときに役に立つと思うので、そういう意味では性能評価とかにうまく使えればいいんだけれどなあ。 ついでに、C++のモデルも出力してくれればなお嬉しい(Chiselのコードが酷いだけに...)