verilog書く人

自称ASIC設計者です。どなたかkaggle一緒に出ましょう。

雑記:IDE使用時にPythonで標準出力をリダイレクトした時のちょっとしたトラブルシューティング

Pythonでは標準出力(いわゆるprint関数の中に入れると文字がコンソールに出ちゃう奴)の出力先をファイルに変える(=リダイレクトする)のも簡単だ。

 

↑このへんを参考にしつつ

>>> import sys
>>> sys.stdout = open('out.txt', 'w')
>>> print('uwaaaaa')

と書くだけで、コンソールにuwaaaaaと表示される代わりに、out.txtと言うファイルが実行ディレクトリで作成され、その中にuwaaaaaというメッセージが書き込まれるのである。

標準出力をコンソールに戻すのも基本的には簡単でsys.stdoutにsys.__stdout__を代入してやればよい。すなわち、

>>> import sys
>>> sys.stdout = open('out.txt', 'w')
>>> print('uwaaaaa')
>>> sys.stdout = sys.__stdout__
>>> print('yamerooooo')

と実行するとuwaaaaaはout.txtに表示されるが、yameroooはコンソールに表示されるのである。

 

ところが、コマンドプロンプトなどからPythonを実行していない場合、例えば、PyscripterなどのIDE上でPythonを実行していると、uwaaaaaはout.txtに表示されるが、yameroooはどこにも表示されないことに気づいた。

これについて調べてみるとどうもPyscripterではsys.__stdout__ではなく別の標準出力を最初から使っている事が原因のようだ↓

https://groups.google.com/forum/#!topic/pyscripter/rhtXtinfmGM

 

Pyscripter固有の解決法を施して、他の環境では動かないコードが入るのは避けたいしな、、と悩んでいたが、シンプルにsys.stdoutの初期の実体が何であれ、それを一時的に確保して後で用が済んだら戻してやればよいと言うことに気づいた。

すなわち、

>>> import sys
>>> temp_sysout = sys.stdout >>> sys.stdout = open('out.txt', '
w') >>> print('uwaaaaa') >>> sys.stdout = temp_sysout >>> print('yamerooooo')

としてやればよい。これで「yamerooooo」はコンソールに表示されるのである。勿論IDEだけでなく、コマンドプロンプトでも動作する。

 

Veriloggenを使ってみた、そしてVerilogメタプログラミングの新地平を見た

以前から気になっていたshtaxxx氏作Veriloggenを実際に使ってみた。

これは…熱い!

 

 

私が書いてみたコード↓

 

import sys
import os
from veriloggen import *

def CKDIV():

def gen_divided_clk(m, clk):
div_clk = m.Reg(clk.name + "_divided", 1)
m.Always(Posedge(clk))(
If(rst)(
div_clk(0)
).Else(
div_clk(div_clk + 1)
)
)
return div_clk

m = Module('CLK_DIVIDER')
clk = m.Input('CLK')
rst = m.Input('RST')

clk_temp = clk
for i in range(3):
clk_temp = gen_divided_clk(m, clk_temp)

return m

if __name__ == '__main__':
ckdiv = CKDIV()
print(ckdiv.to_verilog())

for文の中でクロック分周を繰り返しているのが肝。

生成されたverilog↓

module CLK_DIVIDER
(
input [0:0] CLK,
input [0:0] RST

);
reg [(1 - 1):0] CLK_divided;
reg [(1 - 1):0] CLK_divided_divided;
reg [(1 - 1):0] CLK_divided_divided_divided;
always @(posedge CLK)
begin
if(RST) begin
CLK_divided <= 0;
end
else begin
CLK_divided <= (CLK_divided + 1);
end
end
always @(posedge CLK_divided)
begin
if(RST) begin
CLK_divided_divided <= 0;
end
else begin
CLK_divided_divided <= (CLK_divided_divided + 1);
end
end
always @(posedge CLK_divided_divided)
begin
if(RST) begin
CLK_divided_divided_divided <= 0;
end
else begin
CLK_divided_divided_divided <= (CLK_divided_divided_divided + 1);
end
end
endmodule

 

うは!意図通りできてる!!!

for文に入れるだけで三回クロックが分周できてる!!!

 

これは、楽しいです。

 

私が考えたveriloggenのメリットとしては、

・verilogのダサい文法が回避できる(begin - endとか)

・柔軟にメタプログラミング的なことが出来る。genvarでもある程度できるが、規則的にレジスタ名を生成するようなことはできない。

・抽象レベルでの回路定義。なんかちょっとパラメータ入れたらおっきいコード吐くみたいな。

 

今後機能がたくさん追加されるようなので、おっかけとくことにします。

verilog向けコードメトリクス算出機能をリリース

pyverilog_toolboxにverilog向けコードメトリクス算出機能をリリースしました。

 

モジュール/ファンクション/レジスタレベルで全インスタンスについてメトリクスを算出し、それぞれのメトリクスの合計スコアと上位メンバを表示します。スコアが大きいほど複雑なやつです。。

 

使い方

 例によってテストコードで。

pyverilog_toolboxをインストール後、

コマンドプロンプト上で
(Pyverilog_toolboxをインストールしたディレクトリ)/verify_tool
に移動し、

$ Python metrics_calculator.py ../testcode/metrics_func.v

 

と入力します。適宜、.vファイルは測定対象に変更してください。

出力は

 

module metrics
total: 19
average: 19 each score: TOP: 19 Number of input ports: 4 Number of output ports: 0 Number of registers: 1 Number of clocks: 1 Number of resets: 1 register metrics
total: 1
average: 1 each score: ('TOP', 0): 1 Number of branch: 0 Max nest: 1 function metrics
total: 9
average: 9 each score: ('TOP.md_always0.al_block0.al_functioncall0', 0): 9 Number of branch: 1 Max nest: 2 Number of variables: 2

 

てなような感じでプロファイルが出てきます。

モジュール/レジスタ/ファンクションが複数ある場合は上位から表示されます。

沢山あると大変なので、表示数上限を設定しており、設定ファイルにより上限を変更することができます。(設定ファイル記述法の詳細はgithubにて)

メトリクス判断基準の詳細

モジュールレベル
・input portの数
output portの数
regの数
clkの種類(=非同期ドメイン数)
rstの種類

デフォルトではただレジスタが増えるよりもポートの増加に対して厳しい判定をします。
(ポートが多い=疎結合の原則に反する。)

またクロックドメインの数によっても大きく上昇するので、非同期ドメインが存在するモジュールはなるべく小さく作りスコアを抑えるべき、ということになります。


レジスタレベル
条件分岐(if,else if, else)の数
ネストの数
デフォルトでは条件分岐が増える以上にネストの数に厳しいです。どんな言語でも過剰なネストは悪だと思うので。

ファンクションレベル
条件分岐(if,else if, else)の数
ネストの数
読み込み変数の数
こちらもデフォルトでは条件分岐が増える以上にネストの数に厳しいです。
また、変数が多いと読み込み順を誤りやすくなるなど、
可読性が下がると推定します。

 

考え中のことと活用法


メトリクスの指標としてよく使われる行数カウントは現在のところ未実装です。
実装がめんどくさかった、というのと、
数えりゃいーじゃん、というのと、
verilogの行数はregの数×if文の分岐数で大体決まってくるので、
そこまで重要ではないかなと。要望があれば足すかもしれません。


また正直パラメータは適当です。私の感覚で決めました。
納得いかん!という型は設定ファイルによって書き換えることが出来ます。
詳細はgithubにて。

またスコアが高いとか低いとか絶対値それ自体には大きな意味はありません。

機能の大きさに伴ってスコアが増えるのは当たり前ですし、
ポートがない空のモジュールを作ればそれがスコアゼロで最高のVerilogコードと言うことになってしまいます。

むしろリファクタリング前と後でどれだけ減ったかとか、
あるいは他のモジュール/レジスタと比べて突出して複雑なものがあるから、
リファクタリングしたほうがよいサインなのかもという風に、
相対値に着目したほうが有意義かと思います。

また、pyverilog_toolboxに実装されているcodeclone_finder(同一レジスタの発見器)や、
unreferenced_finder(未使用変数の発見器)を使ってメトリクススコアを減らすのもよいでしょう。


追伸:

そういえばpyverilogのissueにsympyによる論理最適化なんてのが追加されております。熱い。

メトリクス測定→スコア高い奴をオートでリファクタリング→メトリクス現象なんてできたらワクワクですなあ。

他のissuesも気になります。pyverilogの更なる進化に期待。

 

pyverilog_toolboxをpipにうpるまでの熱い戦い&トラブル時のための備忘録

というわけで、pyveriog_toolboxをpip経由でラクチンインストールするための準備を整えた。

なんかpyverilogもいつのまにか(前から?)pipに対応していたので、私も時代に追いつくべく。

もちろんpipアップロードは初体験です。

 

基本的に参考にしたのは

こちらの素晴らしきブログである。

といいつつ少しつまづいたので備忘録を。

 

事件その1、認証失敗

 

まず、上記ブログにおいてpypiのアカウント作成後、

$ python setup.py register

を試したところ、

$ Server(401) basic auth failed

みたいなエラーが出てしまった。なんやねんなんやねんと大騒ぎつつ色々試していたが、サイトに情報を登録するだけではアカウント登録できていなかったのである。

pypiでアカウント作成後、登録しているメールに

 

To complete your registration of the user "(ユーザー名)" with the python module

index, please visit the following URL:

https://***

という内容のメールが来るので、慌てず深呼吸をしつつリンクをクリックしてアカウント登録を完了しましょう。

それ以外には.pypircファイルのユーザー名やパスワードのミスが考えられます。ホームディレクトリに作成した.pypircの内容を確認しましょう。

 

もう一度registerを試したところ

$ Server(200): OK

を無事確認。

 

事件その2、アップロードできない事件

上記事件1を解決後、どや顔で

$ python setup.py upload

叩いたところ

$ error: No dist file created in earlier command

 

となってしまた。これもね、全然分からないのでエラーメッセージをググリマス。

Adding a Package to PyPi For the 1st Time. - Jamie Curle

 

そしたらこちらのブログに「特に明確な理由はねーがこっちのコマンドを試した」

$ python setup.py sdist upload

と書いてあったので私も特に理由はねーが試した。

 

すると、

$ Server(200): OK

を確認。よくわかんないけどできてそうだからいっか、

 

事件その3、ダウンロードできない事件

アップロードに成功したので今度はpip installを試してみる。

$ pip install pyverilog_toolbox

 

すると、

$ Could not find any downloads that satisfy the requirements pyverilog_toolbox

 

あばばばば、と奇声をあげながら色々叫んでいるうちに、よくよく調べてみると\pyverilog_tool_box.egg-infoなるディレクトリができていることに気づく。

あれなんでtoolとboxの間に"_"が入っているんだっけ?と思いsetup.pyを見てみると、

 

from setuptools import setup, find_packages

import re
import os

version = '0.0.0'

def read(filename):
return open(os.path.join(os.path.dirname(__file__), filename)).read()

try:
import pypandoc
read_md = lambda f: pypandoc.convert(f, 'rst')
except ImportError:
print("warning: pypandoc module not found, could not convert Markdown to RST")
read_md = lambda f: open(f, 'r').read()

setup(name='pyverilog_tool_box',
version=version,
description='Pyverilog-based verification/design tools',
keywords = 'Verilog, Register Map, code clone',
author='Ryosuke Fukatani',
author_email='nannyakannya@gmail.com',
url='https://github.com/fukatani/Pyverilog_toolbox',
license="Apache License 2.0",
packages=find_packages(),
package_data={ 'pyverilog_toolbox' : ['testcode/*'], },
)

となっているのである。nameがまちがっとるやん。

【誤】

name='pyverilog_tool_box'

【正】

name='pyverilog_toolbox'

とまあしょぼいミスをなおして、パッケージ作成、登録、アップロードを行うと、

 

$ pip install pyverilog_toolbox

 

を叩くと無事インストールが出来た。ふう。

テストも動いた。と見せかけて1個動かなかった。どうやらテストに不備があるようなので近日修整予定。

 

 

 

というわけで、pyverilog_toolboxもpipでのインストールに対応しました。色々とドキュメントを直さないと。

コードクローンファインダー for verilog

コードクローン(Code Clone)

(上記サイトより引用)

コードクローンとは

コードクローンとはソースコード中での類似または一致した部分を表します. コードクローンは,「コピーとペースト」によるプログラミングや,意図的に同一処理を繰り返して書くことにより,プログラムテキスト中に作りこまれます.

コードクローンの弊害

一般的に,コードクローンはソフトウェアの保守を困難にする要因の一つである,といわれています. 例えば,あるコードクローン上にフォールトが発見された場合に,開発者はそれと対応する全てのコードクローンを確認して,必要があれば全てのコードクローンに同様の修正を行う必要があるからです. 特に大規模システムでは,チームによる開発が通常行われており,上述したように一人の開発者がサブシステムを確認し,全てのコードクローンに対して一貫した修正を行うことはきわめて困難な作業であるといえます.


そのような事態を例えばCなどの言語ではコードクローンファインダーとして、
CCFinderのようなソフトが発表されている。

verilogでもこういうのあったら便利だけどニッチな言語だから見当たらないので、
Pyverilogを利用して作ってみました。実はPyverilog作者のshtaxxx氏が発案者だったりします。感謝。

 

開発指針

私が感じているCなどのソフトウェア言語とverilogの違い。

C
・ほとんどのコードはメソッド(関数orプロシージャ)の中に存在する。

Verilog
・ほとんどのコードはalways文(そしてそのほとんどはFFに代表されるレジスタ)の中に存在
verilogにもfunction文はあるが、使用頻度は多くない。
また参照はmoduleの中でのみ行われるため、クローンを探す需要はあまりない。


というわけでalways文で記述されるレジスタのクローンを探すことにしました。
verilogにおいてクローンを減らすということは回路面積を減らすことにもつながります。

 

機能説明

現在codeclone_finderで発見することができるクローンは以下の二つ。

・論理的に全く同一なレジスタ

例えばこのようなこーど。

always @(posedge CLK or negedge RST) begin
    if(RST) begin
        reg1 <= 1'b0;
    end else begin
        reg1 <= IN;
    end
end

assign in1 = IN;

always @(posedge CLK or negedge RST) begin
    if(RST) begin
        reg2 <= 1'b0;
    end else begin
        reg2 <= in1;
    end
end

in1とIN1の実体は同じであるためreg1とreg2は毎クロック必ず同じ値を保持します。
reg2の記述を消し、reg1を使いまわすべきです。

・論理的に全く反転しているレジスタ

always @(posedge CLK or negedge RST) begin
    if(RST) begin
        reg2 <= 1'b0;
    end else begin
        reg2 <= in1;
    end
end

always @(posedge CLK or negedge RST) begin
    if(RST) begin
        reg3 <= 1'b1;
    end else begin
        reg3 <= !in1;
    end
end

上記のコードでは、reg2とreg3は毎クロック必ず同じ値を保持します。

遅延のために後ろにインバータを使いたくないというような非常に稀な状況を除いて、このようなリファクタリングが有効です。

always @(posedge CLK or negedge RST) begin
    if(RST) begin
        reg2 <= 1'b0;
    end else begin
        reg2 <= in1;
    end
end

assign reg3 = !reg2;

これで保守性が上がるとともにレジスタが1個減ります。

codeclone_finderの使い方

まずは

 

を参考に、Pyverilog_toolboxをインストールしてください。

そして
コマンドプロンプト上で
(Pyverilog_toolboxをインストールしたディレクトリ)/verify_tool
に移動し、

$ Python codeclone_finder xxxx.v

と入力します。(xxxx.vは自分で用意したverilogコード)
試しにPyverilog_toolboxに梱包されているテストコードで動かすと

$ Python codeclone_finder ../testcode/reg_clone.v

Invert reg pairs: *1, *2, *3
Clone reg pairs: *4, *5

のようにコードクローンを指摘してくれます。

 

最後に

将来的には類似していてリファクタリングが可能なレジスタを見つけたり、
リファクタリングまで児童でしてくれるブラウザが作れればいいかなーと思いつつ。

*1:TOP.sub.reg1, 0), (TOP.reg4, 0

*2:TOP.reg1, 0), (TOP.reg4, 0

*3:TOP.reg3, 0), (TOP.reg4, 0

*4:TOP.reg3, 0), (TOP.sub.reg1, 0

*5:TOP.sub.reg1, 0), (TOP.reg1, 0

Pyverilog_toolboxの紹介

拙作、Pyverilog_toolboxについて紹介してみる。


Pyverilog_toolboxはPyverilogの構文解析機能を用いて、verilogによるデジタル設計/検証を助けるツールとして開発を行いました。

現在実装されている機能は、組み合わせ回路ループファインダー、コードクローンファインダー、レジスタマップアナライザー、カウンタアナライザーです。

これらの機能の紹介は後で説明するとして、まずはインストールの方法について説明します。

インストール方法

主にwindows環境について。

Python:

2.7系の最新版をインストールする。3系は…私は動かしたことはありません。

具体的なインストール方法は以下を参照してください↓

http://sphinx-users.jp/gettingstarted/install_windows.html 

Icarus verilog:

↓ここでインストーラをダウンロードし、起動。

Icarus Verilog for Windows

筆者はiverilog-0.9.7_setup.exe (latest stable release)と記述されるバージョン、

つまり最新の安定版を使用している。(2015/05/23現在)

Pyverilog:

↓のページの"InstallationI"を読んでインストール。

http://shtaxxx.github.io/Pyverilog/

 

他にもPGraphvizとPygraphvizを入れることで、

データフローの図示といったPyverilogの素敵機能を発動できるので本当はやりたいんだけどwindos環境ではなんか難しくてできんかった…。Pyverilog_toolboxには必要ないのでとりあえず省略。jinja2も入れても入れなくてもええです。

Pyverilog_toolbox:

(2015/7/8追記)

pipに対応しました。

$ pip install pyverilog_toolbox

でOKです。

上記ページの、右のほうにある"Download ZIP"をクリックし、任意のディレクトリで解凍する。

もしくはgithubを使用できる環境があるなら

$git clone https://github.com/fukatani/Pyverilog_toolbox.git

でいれてもよい。

 

動作確認(飛ばしてもよい):

コマンドプロンプトを立ち上げ、

(Pyverilog_toolbox展開先のディレクトリ)/pyverilog_toolbox/testcode

に移動する。

 

$ python test_ra.py

 

と打ち、

Ran 10 test in ****s

OK

というメッセージが表示されれば無事全ての機能が動いている。

 

試しにcombloop_finderを使ってみる。

組み合わせ回路ループと言うのはなかなかに厄介な問題である。

組み合わせ回路ループとは例えば以下の図のような回路のことで、

f:id:segafreder:20150523160526p:plain

 赤い矢印の部分のようにループにFFが挟まれていない回路のことである。

 

一度組み合わせ回路ループが出来てしまうと、発振してシミュレーションが全く進まなくなったり、ラッチが形成され思わぬ動作をしてしまったり、上司から冷たい目で見られたり、世界を崩壊から救えなかったりする危険性がある。
勿論セレブリチーなCADを使えば発見できるのだが、お金の力で環境を整えられない場合にはエンジニアが目で探すことになる。これがまた、しばしばノーヒントで大変な作業なのだ。


そのような問題を(無料で!)発見してくれるのが、
Pyverilog_toolboxに含まれるcombloop_finderです。

使い方は簡単で、コマンドプロンプト

 (Pyverilog_toolbox展開先のディレクトリ)/pyverilog_toolbox/verify_tool

に移動し、

python combloop_finder.py xxxx.v

を実行すればよい。ここでxxxx.vは任意のverilogファイルである。testcodeを使用したい場合、

python combloop_finder.py ../combloop.v

としてみましょう。

組み合わせ回路ループがある回路では

CombLoopException: Combinational loop is found @TOP.wire3

と例外として投げられる。

基点となっている信号(この場合はTOP.wire3)

も表示されるので探しに行く必要がない。便利だ。


組み合わせ回路ループが存在しない回路では

There is no combinational loop.

とメッセージが表示されます。

 

最後に注意として、

Pyverilogで読み込まれる回路のトップモジュール名はデフォルトでは"TOP"となっている。そしてTOPというモジュールが無い場合はエラーを吐く。

トップモジュール名が"TOP"でないときは、-tオプションでトップモジュール名を指定する。例えば先のcombloop_finderを使用する場合、

 

python combloop_finder.py xxxx.v -t TOP_DIG

 

のようにすることで、"TOP_DIG"モジュールをトップとして扱うことが出来る(存在しない場合はエラー)。


他の機能にはついてはまた今度。ミスとか質問とかあったらお気軽にどぞ。

Pyverilogの紹介

verilogで設計をしてる時に、回路の構造をプログラム的に処理することにより、

何か悪い構造などをサーチしたいときがある。

そんなとき、私が頼りにしているのがPyverilogだ。

 

Verilog HDLのデザイン解析・コード生成のためのPythonベースのオープンソースツールキットPyverilogをリリースしました - shtaxxx日記

 

オープンソースで当然フリー。プログラミング言語Pythonによって記述されている。

インストールにはいくつか条件があるが、私はコード生成器 (ast_code_generator)やデータフロー可視化のためのGraphvizとPygraphviz は使わず、最低限必要なIcarus Verilogのみをインストールして使用している。

 

Pyverilogでたとえばデータフロー解析器を使用すると、各信号がどのような式で記述されているかを理解することが出来る。

 

例えば

 

  always @(posedge CLK) begin
    if(RST) begin
      reg0[1:0] <= 0;
      reg1 <= 0;
    end else if(WRITE) begin
      case(ADDR)
        0:reg0[1:0] <= WRITE_DATA;
        1:reg1 <= WRITE_DATA[0];
      endcase
    end
  end

  always @* begin
    case(ADDR)
      0:READ_DATA[1:0] = reg0[1:0];
      1:READ_DATA[1:0] = {1'b0,reg1};
    endcase
  end

 のような読み書き可能なレジスタを解析してみる。

 すると、

Bind:
(Bind dest:TOP._rn1_READ_DATA msb:'d1 lsb:'d0 tree:(Concat Next:(IntConst 1'b0),(Terminal TOP.reg1)))
(Bind dest:TOP.reg0 msb:'d1 lsb:'d0 tree:(Branch Cond:(Terminal TOP.RST) True:(IntConst 0) False:(Branch Cond:(Terminal TOP.WRITE) True:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 0)) True:(Terminal TOP.WRITE_DATA)))))
(Bind dest:TOP.reg1 tree:(Branch Cond:(Terminal TOP.RST) True:(IntConst 0) False:(Branch Cond:(Terminal TOP.WRITE) True:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 0)) False:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 1)) True:(Partselect Var:(Terminal TOP.WRITE_DATA) MSB:(IntConst 0) LSB:(IntConst 0)))))))
(Bind dest:TOP._rn0_READ_DATA msb:'d1 lsb:'d0 tree:(Partselect Var:(Terminal TOP.reg0) MSB:(IntConst 1) LSB:(IntConst 0)))
(Bind dest:TOP.READ_DATA msb:'d1 lsb:'d0 tree:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 0)) True:(Terminal TOP._rn0_READ_DATA) False:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 1)) True:(Terminal TOP._rn1_READ_DATA))))

 

のような形で出てくる。

なんのこっちゃという感じではあるが、PythonプログラムがverilogHDLにおける構造を理解できるということはレジスタマップ推定や組み合わせ回路ループの把握などを有料のEDAなしにできるということである。

 

次回はこれらの実装であるPyverilog_toolboxライブラリについて紹介したい。