2016年7月7日木曜日

第5回〜半導体屋だけど正規表現にはお世話になりました

前回のawkのお話の一件以来、仕事の中でミスの混入を防ぐだとか、効率的に仕事をするという観点から、小技として仕事でスクリプト言語を使う頻度が増えました。

 EWSの普及と、自分の席にあるパソコンがネットワークにつながって、パソコンからEWSにリモートログインできるようになったことも機会が増えた理由ではあります。

私が扱った設計データ、内製設計ツール用のネットリストファイル、Verilog HDL、LSIテスターのテストプログラム、テストパターンはみんなテキストファイルで、ものによってはテキストエディタで簡単に開くことが出来ないような巨大なものもあります。ですから、テキストエディタで開くことなく修正、加工をしたいわけです。

そこで役に立つのがsedやawk、perlです。これらのコマンドを使って対象文字列をサーチしながら加工すると、対象ファイルがいくら巨大でも大丈夫です。そして、このsed、awk、perlのスクリプトの中で、加工対象文字列を探すために欠かせないのが正規表現(Regular Expression)です。

正規表現っていう言葉、わかりにくい言葉ですけれど、色んな文字列を一つの文字列で表現するために考えられたもの、と言いましょうか・・・それ自身がわかりにくいですね。

なぜ正規表現か?

半導体設計の仕事で取り扱う設計データには色々なものがありますが、私が担当していた仕事で扱うものは、論理記述(高位記述言語やゲートレベルネットリスト)、テストベクタ、各種設計ツールの設定ファイルやレポートファイルなど、ほとんどががテキストファイルです。

そして、ゲートレベルのネットリストやテストベクタは、テキストエディタで簡単に開くことができないほど巨大なものです。
unix系のOSで使えるテキストエディタの代表的なものにviとGNU Emacsがあります。 
最新の実装はどうなっているか知りませんが、viは/varディレクトリの中に作業ファイルを作ります。/varが置かれるディスク領域は、普通あまり大きく取らないので、割に簡単に容量不足を起こします。そうなるとviはファイルが開けず終了してしまいます。 
GNU Emacsはswap領域を使うのですが、swapを使い切ると動かなくなります。以前の設計者用コンピューター環境では、少なくとも私の仕事中には、こういうことが割に頻繁に起こりました。

したがって、ファイルを開かずに編集したいということになる訳で、それが出来るコマンドとしてunix系OSではsed、awk、perlといった便利なコマンドがあるんですね。

これらのコマンド、ある文字列を探し出して、その文字列が含まれている行について、なにがしかの処理をする、という動きをするコマンドでして、文字列を探し出すための道具として正規表現というものがあります。

この記事の最初の方にWikipediaへのリンクがありますけれど、なかなかわかりにくい文章ですよね。コンピューターサイエンスをかじったことのある人じゃないとなかなか概念的にわかりにくいと思います。

たとえばどんなもの?


私が居た会社では、1990年代の前半に設計環境が大型計算機からunix系OSが走るEWS(Engineering Workstation)へと移行して行きました。しかし、設計ツールがある日一律にEWSのものに切り替わるというものでもなくて、しばらくの間は大型計算機とEWSが共存する環境でした。

そこで問題になるのがデータの共有です。

大型計算機のテキストファイルは、行の後ろに行番号が書かれた固定長データでした。

たとえば前回のawkのエントリーで出て来たネットリストの一部ですが、大型計算機から転送してきたときには、

BLOCK名    BLOCK TERM1,TERM2,TERM3,TERM4,TERM5,TERM6,@      00000001
   TERM7,TERM8,.....                                                                                    00000002
                   INPUT         TERM1,TERM2                                                       00000003
                   OUTPUT     TERM3,TERM4                                                       00000004
                   INOUT        TERM5,TERM6,TERM7                                           00000005
                                                                                                                      00000006
                   END                                                                                            00000007

というような姿をしています。1行のカラム数は、大型計算機でディスクエリアを作ったときに指定したデータの形式で決まったと記憶しています。この後ろの行番号と隙間を埋めているスペースが邪魔なんです。そこで活躍するのがsedコマンドと正規表現です。

普通に考えると、テキストエディタでこのファイルを開いて、1行ずつ加工していく、ということになるのでしょうけれど、上記のデータのファイル名をnetlist.txtとすれば、
sed -e ' s/ ¥+[0-9]¥+$//' netlist.txt > out.txt
とすると、行末の行番号と余分なスペースが取り除かれたout.txtが出来上がります。上記で、
(space)¥+[0-9]¥+$         (space)は1個のスペースを意味しています。
というのが正規表現で、+¥はこれが書かれている1文字手前の文字の1個以上の繰り返しを、[0-9]は0、1、2、3、4、5、6、7、8、9のどれか1文字を、$は行末を表しています。なので、上の正規表現は、
1個以上のスペースに続いて1個以上の数字の並びで行が終わる場合のそのスペースと数字
となって、s/ ¥+[0-9]¥+$//とは、sedへのコマンドで、
1個以上のスペースに続いて1個以上の数字の並びで行が終わる場合のそのスペースと数字を削除(空白文字に変換)
することを指示しているものです。sedは出力を標準出力に出すので、これを必要に応じてファイルにリダイレクトしてあげれば良いのです。これだと、入力ファイルがいくら巨大でも、出力をはき出すディスク領域に空きがあれば編集できますね。

これは前回のエントリーでお伝えしたレイアウト設計をやっていた頃、EDA部門の方から教えてもらった技なのですが、これをシェルスクリプトにして、一緒に仕事をしていたASICグループの他の2人にも標準コマンドとして展開しました。

この¥+はちょっとクセがあって、sedで扱うトラディショナルな正規表現では¥+が1個前の文字の1回以上の繰り返しの意味、 単に+と書くと+文字そのものなんですが、perlの世界で扱う正規表現ではこれが逆になるようです。


一番良く使った利用方法は・・・


perlでしか使えない例ですけど、LSIテスターのデータログやタイミング検証のレポートから測定値を拾い上げて利用したり加工したいことがあります。そのときに便利な使い方があります。

たとえば、下にあるようなデータログがあったとして、


この測定値を拾いたい場合、

/^(\w+) *(.+)V *(.+)V *(.+)V/


これを上記データログに適用すると、()で囲まれた正規表現にマッチした文字列が定義済みの変数$1、$2、$3、$4にそれぞれ入ります。

たとえば、VOHと書かれた行だと、^(\w+)は1個以上の数字もしくはアルファベットで行頭にあるものにマッチ、次以降に出てくる(.+)Vは、1個以上の何かの文字で最後がVの文字列のVを取り除いた部分にそれぞれマッチして、$1にはVOH、$2には2.9、$3には3.0、$4には3.1がそれぞれ入ります。

$1から$4まで全て文字列として拾い上げてますが、perlでは文脈に合わせて変数の型変換が行われるので、$2、$3、$4を数値として扱うと、自動的に文字列から数値に型変換されて、数値として扱えます。
#! /usr/bin/perl
while( <> ){
    if(/^(\w+) *(.+)V *(.+)V *(.+)V/){
 printf( "item %s : min : %f  typ : %f  max : %f\n", $1,$2,$3, $4);
    }
}
上に出て来た正規表現を組み込んだスクリプトを作ってみました。正規表現全てがマッチしたときに、$1には測定アイテム名、$2、$3、$4には測定値のMin、Typical、Max値が文字列として入り、printf文が実行されます。

printf文で$2〜$4を数値として出力するように指定すると、ちゃんと数値として出力されます。つまり、数値として取り扱うことが出来るようになっていることがわかります。

最後に


この手のノウハウ、あくまでも自分が楽をするための小技であって、誰かから習う、というようなものでもないのですけど、勉強しておくのは悪いことではないと思います。たとえば、データ処理のためのスクリプトを書くのに数日かけて、処理自体は数秒、って事も良くある話ですけれど、書くのにかけた数日は引き出しの広さとして次につながるし、スクリプト、及び処理結果の検証をしっかりやれば手作業で変なミスを入れ込んでしまう可能性も少なくなります。

おそらく、優秀な技術者は、こういう本来業務そのもの以外の引き出しが広いのですよ。みんな効率アップ、不良作り込みの防止など色んな意味で自分を助けるためのものを色々持っているんだと思います。

なお、こういう自分オリジナルのツール類は、処理結果の検証をしっかりやらないと、不良を作り込んでしまう危険性も高いので気をつけなきゃダメです。EDA部門が正式にリリースしているツールだって、出来上がったデータは別の検証ツールで検証するんですから。

オリジナルのツールを使ってROMのテストベクタを作ったらそれがバグっていて、容量の半分しかテストできていなくて、社外不良を出してしまった、という製品も過去に聞いたことがありますよ。

0 件のコメント:

コメントを投稿