verilog書く人

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

JavaのTreesetをPythonで

「できらあ!!」

「え!!同じデータ構造をPythonで!?」

 

というわけでJavaのTreesetに当たるデータ構造がPythonで欲しくなったので、自分で実装してみました。

 

treeset.pyをコピーして使ってください。

 

Treesetはsetのように、重複が存在しないデータ集合ですが、setと違い、初期時にデータがソートされます。また、新しい要素が追加されるたびに、二分木探索によって適切な位置に挿入されます。

使い方は例えば、こんな感じ。

from treeset import TreeSet

# 初期化時に重複要素は消える、またソートが行われる ts = TreeSet([3,7,2,7,1,3]) print(ts) >>> [1, 2, 3, 7]
# 4を加えると、3と7の間に挿入される ts.add(4) print(ts) >>> [1, 2, 3, 4, 7]

 

インデックスによる取得、要素を指定してリムーブもできます。

 

# 三番目の要素を取得
print(ts[2])
>>> 3
# 最後の要素を取得
print(ts[2])
>>> 3
# Treesetに7があれば消去
ts.remove(7) print(ts) >>> [1, 2, 3, 4] ts.pop(
2)
# インデックスをpop
print(ts) >>> [1, 2, 4]

 

中身はシンプルでbisectモジュールを使って、適宜、中身のデータをソートしているだけです。

 

from treeset import TreeSet

class TreeSet(object)

def __init__(self, elements):
self._treeset = []
for element in elements:
self.add(element)
    def add(self, element):
# 要素が既に存在しなければbisect.insertで適切な位置に挿入
if element in self._treeset: continue
self.add(element)
bisect.insort(self._treeset, element)

 

よかったらお試しください。

chainerで気軽にスケールできる事前学習器付きニューラルネット生成

のためのコードを書きました。

 

やりたいこと

1.スケーラブル/コンフィギュラブルなニューラルネット生成

ニューラルネットでは難しい問題を解かせるためには中間層の数を増やす必要があります、

 

chainerでは普通は↓こんな風にハードコーディングして層を増やしていきますが、

import chainer.functions as F
h1 = F.dropout(F.relu(self.model.l1(x)), train=train) h2 = F.dropout(F.relu(self.model.l2(h1)), train=train) h3 = F.dropout(F.relu(self.model.l3(h2)), train=train) FunctionSet(l1=F.Linear(784, 100), l2=F.Linear(100, 100), l3=F.Linear(100, 10),


そうではなく、[784,100,100,10]と配列を食わせるだけで、
784次元の入力層、100次元の第一中間層、100次元の第二中間層、10次元の出力層という風にネットを生成し気軽に層数、次元数を変えながら解析が進められるようにします。

続きを読む

PythonだけでRaspberry piのGPIOピンをALT0に設定するための話

raspberry Pi2を最近いじっているのですが、GPIOピンからクロックをチップに供給したいことがありました。

そのためには、例えばGPIOピン5を通常の入出力ではなく、ALT0 (alternative function 0)に設定する必要があります。

他にもクロック出力を設定できるピンがありますが、詳しくはraspiに搭載されているARMプロセッサであるBCM2835のデータシートを参照してみてください(102pくらい)。↓

https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

 

結局色々調べたのですが、Pythonだけで完結できず、こちらのページ↓を参考にしてcプログラムでクロックを出力することに成功。(感謝!)

こじ研(Raspberry Pi)

 

しかし、データ取得・解析にはPythonを使っていたので、できれば動いているコードはPythonだけで統一したいなあと思い、必要なライブラリを実装しました。

 

そのために、RPIOライブラリのカスタマイズを行います。

ただし、私が使っているのはraspberry Pi2なのですが、現在RPIOはraspberry Piに対応していない模様。raspberry Pi2でも使えるようカスタマイズされた

tylerwowen/RPIO · GitHub

を改造し、ALT0への対応を行います。

 

■成果物

できました。フォーク元にプルリクエストを投げましたが、どうなることやら無事速攻でマージされました、感謝。

 

 

■インストール、つかいかた

raspberry Piターミナル上で

 

git clone https://github.com/fukatani/RPIO.git

git clone https://github.com/tylerwowen/RPIO.git

cd RPIO

python setup.py install

 

とうち、ここで完了と思いきや、なぜか私の環境では

 

f:id:segafreder:20151220003925p:plain

 

dist-packagesに未解凍のファイルが置かれます(左下)。シュール。

 

なんででしょうね。前世の行いでしょうかね。知ってる人、教えてくださいね。

しょうがないので、手作業で解凍してやりインストール完了です。

 

例えばPythonスクリプトにて

 

import RPIO

RPIO.setup(5, RPIO.ALT0)

 

とすると、5ピンからGPCLK1(大体20MHz)がでます。嬉しいです。

 

ちなみにGPCLKは波形がグチャグチャでそれなりに高周波ノイズが激しいので、クロックの波形を直で突っ込むとSPIなどの波形がゆがむことがあります。最悪、通信が失敗し、かえって来たデータが信用できなくなるかもしれません。

そういう場合はクロックの後ろに抵抗(100ohmくらい?)つけてみましょう。

高周波成分が除去されて、通信が成功するようになるはずです。

 

なんか今ハードウェアエンジニアっぽいこといってしまった、、、

 

■追伸

うーん、RPIOの一部テストが通りません。フォーク元も。Pi2はピン配変わったからかしら。

raspberry Pi用のTravis CIがあったらいろいろはかどるのになー。

やる気があるひと修正してみましょう(人まかせ)。

 

雑記: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の更なる進化に期待。