2016年7月16日土曜日

番外編〜どんなスクリプト書いたっけ?

第1回のテストベクタの加工、そして第5回の正規表現のお話で、sed、awk、そしてperlでスクリプトを書いて、長大なテストベクタの中から加工する箇所を見つけたり、実際に加工したりしたということを書きました。

実際どんなスクリプトを書いたかな?と、思い出しがてらperlのスクリプトを書いてみました。



読者の方々は、プログラムのリストがあってもお読みにならない方が多いと思うのですが、ソフトウエアの話を書いているのにリストの一つも無いと記事に重みがないですし、どなたかが実際にコピー&ペーストして動かしてみたら動かない、などということがあると記事の信頼性に関わりますので、動かないリストを載せることはできません(苦笑)

下記スクリプトは、右のような姿をしたテストベクタのファイルを読んで、以下のような動きをする意図で書かれています。

サブルーチンread_binary
テストベクタが入っている文字列の中から、配列で指定したピン番号のベクタを参照して、それを2進数表記とみなして、これを数値変換して、その数値を返す。

サブルーチンget_one_stp
テストベクタのファイルを読んで(というより標準入力から取り込んで)、継続行マークが付いている場合は行を連結、そしてベクタ部を取り出して余分なスペースを取り除いて連続した文字列にして、その文字列を返す。

サブルーチンwrite_one_step
get_one_stpで読み込んだ形式のテストベクタを、また元の形式のテストベクタ書式に清書して1ステップ分書き出す。

というサブルーチンと、それを呼び出すメインルーチンから出来ています。

テストベクタの修正すべきステップを探すのは、get_one_stpで1ステップずつベクタを読んで、個別ピンの状態をsubstr関数もしくはread_binaryサブルーチンで検査、ベクタの修正はsubstr関数を使ってピンごとに修正、という手順で色々とやったと思います。

LSIの動作として、信号の立ち上がりエッジや立ち下がりエッジを検出しなければならない場合も多いですが、その場合には2ステップ分、もしくは3ステップ分のベクタをバッファに確保して、隣り合うステップでのテストベクタの状態を検査すれば良いと思います。

たとえばあるピンの立ち下がりエッジを検出する場合は、

Nステップにおけるピンの状態 = H、かつN+1ステップにおけるピン状態=L

を満足するNを探すわけです。立ち上がりエッジも同様です。

ちなみにこのテストベクタのようなデータ、ツールからはき出されたものに対してなにがしかの加工をしたい、という場合がほとんどなので、データに対する文法チェックはしていません。ツールからはき出されたデータは、文法エラーは出ないことが保証されていますからね。


     1 #!/usr/bin/perl
     2 
     3 $STPS_CNT = 0;                 # パターンカウンター
     4 
     5 # アドレスバスのピン番号を配列に定義。配列添え字の若い方がLSB方向
     6 @AD = (
     7     42,41,
     8     40,39,38,37,36,35,34,33,32,31,
     9     30,29,28,27,26,25,24,23,22,21,
    10     20,19,18,17,16,15,14,13,12,11
    11     );
    12 
    13 # データバスのピン番号を配列に定義。配列添え字の若い方がLSB方向
    14 @DB = (
    15     92,91,
    16     90,89,88,87,86,85,84,83,82,81,
    17     80,79,78,77,76,75,74,73,72,71,
    18     70,69,68,67,66,65,64,63,62,61
    19     );
    20 
    21 # バス制御信号等を定義
    22 $WR    = 43;
    23 $RD    = 44;
    24 $RESET = 1;
    25 $CLK   = 4;
    26 
    27 while( 1 ) {
    28     $stp_buf = &read_one_step;           # 1ステップ分パターンを読み込み
    29     last if length( $stp_buf == 0 );
    30     printf( "ADDRESS=0x%08d\n", &read_binary( $stp_buf, @AD ) ); # アドレスバスの出力値を拾う
    31     printf( "DATA   =0x%08d\n", &read_binary( $stp_buf, @DB ) ); # データバスの値を拾う
    32     #
    33     # ここで今読み込んでいるパターンに何かしらの加工をする
    34     #
    35     # インストラクション部を指定して、加工後のパターンを書き出す。
    36     &write_one_step( 'NOP /T1 !', $stp_buf ); 
    37 }
    38 #
    39 # バッファに読み込んだテストベクタから、バイナリデータを読んでくる
    40 #
    41 sub read_binary {
    42     local( $pat, @arry, ) = @_;            #引数をローカル変数にコピー
    43     local( $subscript_max ) = $#arry;      #配列の要素数を調べる
    44     local( $i, $return_value ) = (0,0);    #ローカルのワークエリアを定義して初期化
    45     for( $i=0; $i<=$subscript_max; $i++) { #LSB側から1ビットずつ調べる
    46  
    47         #パターンのビットがHかLか、1か0かを調べてビットの重み付けを付けて数値変換
    48         $return_value += 2**$i if( substr( $pat, $arry[$i]-1, 1 ) eq 'H' ) ;
    49         $return_value += 2**$i if( substr( $pat, $arry[$i]-1, 1 ) eq '1' ) ;
    50     }
    51     return $return_value;
    52 }
    53         
    54 #       
    55 # テストベクタを1ステップ読んで、継続行を連結して不要なスペース等を削除してバッファに格納
    56 #
    57 sub read_one_step {
    58     while( <> ) {
    59         local( $buf );
    60         s/;.*//g;              # コメントの削除
    61         $buf = "";             # 一時バッファを空に
    62         # 行入力処理の開始
    63         if( /^ *NOP/ ) {       # 行頭がパターンプログラムのインストラクションかどうか?
    64             # つまり、パターンの始まりかどうか
    65             ++$STPS_CNT;
    66             for(;;) {
    67                 chop;          # 改行コード削除
    68                 s/;.*//g;      # コメントの削除
    69                 $_ =~ s/ +/ /g;  # 複数のスペースを1個のスペースに
    70                 $buf = $buf. $_ ;                    # バッファに入力された行を連結
    71                 #printf("%s\n", $buf );              # デバッグ用出力
    72                 last if (!/@/); # 継続行ではない場合、ループから抜ける
    73                 $_ = <> || die "illegal file structure"; # 継続行の場合標準入力から1行読む
    74             }                      # 変なところでファイルが終わったらそこで終了
    75 
    76             $buf =~ s/.+! *|@| +//g; # 不要な文字の削除
    77             return $buf;
    78         } else {
    79             next;
    80         }
    81     }
    82     return "";
    83 }
    84 
    85 #
    86 # パターンを1ステップ分書き出す
    87 # 10pinごとにスペースを1個はさんで、50ピン出したら継続行マークを挿入して改行
    88 # これはかなりちからわざで書いたので、汚いコードです。
    89 sub write_one_step {
    90     local( $instruction, $pat ) = @_;
    91     local( $writebuff ) = "";
    92     local( $i, $j, $k, $patlen ) = (0, 0, 0, length( $pat ) );
    93     
    94     $writebuff = $instruction . " ";
    95 
    96     while( 1 ) {
    97         if( $i + 10 < $patlen ) {
    98             $k = 10;
    99         } else {
   100             $k = $patlen - $i;
   101         }
   102         $writebuff = $writebuff . substr( $pat, $i, $k ) . " ";
   103         $i += $k;
   104         $j++;
   105         if( ($j == 5) && ($i < $patlen) ) {
   106             $writebuff = $writebuff . "@\n";
   107             $j = 0;
   108      $writebuff = $writebuff . " " x (length( $instruction ) + 1);
   109  }
   110  last if( $i >= $patlen );
   111     }
   112     printf( "%s\n\n", $writebuff );
   113 }
   114 

0 件のコメント:

コメントを投稿