Posts Issued in December, 2022

posted by sakurai on December 9, 2022 #567

2段FIFOの検討

BSVのデフォルトである2段FIFOの動作を、新規設計するつもりで一から検討します。

2段FIFOなのでレジスタを2段持ち、それぞれd0とd1と名前を付けます。ライブラリと同様にd0からFIFO出力を固定的に出力するものとします。

図%-%.1
図567.1 2段FIFOの動作
図567.1の説明を以下に示します。
  • 2段FIFOのFIFOの有効段の数は0個、1個、2個なので、その数をステートと扱えば、サブシステムのステートにはState0~State2の3種類があることになります。
  • 一度に2個をキューに入れることはできないので、エンキュー(以下ENQ)の場合は、State0⇒State1⇒State2のように遷移します。
  • 逆にデキュー(以下DEQ)の場合はState2⇒State1⇒State0のように遷移します。
  • State0のときにDEQ、State2のときにENQが起きるとエラーとなります(赤色で表現)。
  • DEQの場合に後段へデータを出力しますが、必ずd0から出力するように制御します。そのため、ENQの際にはステートにより格納する段が変わります。
  • 具体的にはState0のときにENQであればd0に格納
  • State1のときにENQであればd1に格納
  • State2のときにENQであればエラー(前述)、DEQであればd0を外に移してd1をd0に移し、d1を無効化
  • State1のときにDEQであればd0を外に移してd0を無効化
  • 上記無効化のフラグビットは不要です。ステート遷移先はステートと遷移条件で決まり、ステートにより有効段はあらかじめ確定しているためです。具体的にはState0は有効段無し、State1は有効段はd0のみ、State2は有効段はd0とd1と定まっています。

左矢前のブログ 次のブログ右矢

posted by sakurai on December 8, 2022 #566

1段FIFOのシミュレーション

本来はFIFOは1段で良いはずですが、なぜ2段となっているのでしょうか?ここだけ見ると物量が倍になってしまいます。たいした物量ではないものの、1段で済ませないかを検討します。

前述のように、bscが出力するシミュレーションモデルではFIFO2という2段FIFOを使用しています。このモデルを1段FIFOに入れ替えたいのですが、bsimモデルは触ることができないので、verilogモデルのほうを修正します。上位において次のようにFIFO2を呼び出しているところをテキストエディタでFIFO1に修正します。

    // submodule exs                                                                                  
    FIFO2 #(.width(32'd32), .guarded(32'd1)) exs(.RST(RST_N),
                                                 .CLK(CLK),
                                                 .D_IN(exs$D_IN),
                                                 .ENQ(exs$ENQ),
                                                 .DEQ(exs$DEQ),
                                                 .CLR(exs$CLR),
                                                 .D_OUT(exs$D_OUT),
                                                 .FULL_N(exs$FULL_N),
                                                 .EMPTY_N(exs$EMPTY_N));

次にverilogシミュレーションを行うと、以下のような波形が得られます。

図%%.1
図566.1 PCパイプラインタイムチャート

1ステージのレイテンシが2サイクルとなってしまいます。

これは、emtpyやfullは1サイクルおきにアサートされ、そのため2サイクルに1度しかキューに投入できないためです。従ってスループットが半分、レイテンシが倍になってしまいます。FIFOの動作上、空でないと上位からは詰められず、一方で詰まってないと下位からは引き取れないので、交互動作になるのは当然です。

通常のパイプラインはエンキューとデキューが同時に行えるので、これは改善できるかもしれません。


左矢前のブログ 次のブログ右矢

posted by sakurai on December 7, 2022 #565

verilogシミュレーション

verilogのソースを覗いてみると、FIFO2というモジュールがインスタンスされていました。ソースの一部を示します。

  // submodule ifs
   FIFO2 #(.width(32'd32), .guarded(32'd1)) ifs(.RST(RST_N),
                                                .CLK(CLK),
                                                .D_IN(ifs$D_IN),
                                                .ENQ(ifs$ENQ),
                                                .DEQ(ifs$DEQ),
                                                .CLR(ifs$CLR),
                                                .D_OUT(ifs$D_OUT),
                                                .FULL_N(ifs$FULL_N),
                                                .EMPTY_N(ifs$EMPTY_N));

ライブラリ中のFIFO2($BLUESPECDIR/Verilog/FIFO2.v)のソースを見ると、中心部分は以下のようになっており、レジスタ2段によるFIFOです。

         data0_reg  <= `BSV_ASSIGNMENT_DELAY
                       {width{d0di}} & D_IN | {width{d0d1}} & data1_reg | {width{d0h}} & data0_reg ;
         data1_reg <= `BSV_ASSIGNMENT_DELAY
                      d1di ? D_IN : data1_reg ;

ただし、FIFOなので、2段とはいってもレイテンシが必ず2サイクルになるわけではありません。First-In, First-Outなので、1段目に入れたものをデキューしようとすると、そのまま1段目から供給するロジックとなっています。上記のように、data0は入力か、1段目か、0段目の選択となっており、data1が入力か1段目の選択となっています。

  assign                 D_OUT = data0_reg ;

であることから、data0が出力側レジスタであることがわかります。一方、data1がD_INを入力とする場合は、d1diという信号がTrueの時であり、その論理は、

  wire                   d1di = ENQ & empty_reg ;

となっています。これはFIFOがemtpy(負論理なので)でなく、かつENQされたときというように読めますが、後で条件を調査します。


左矢前のブログ 次のブログ右矢

posted by sakurai on December 6, 2022 #564

verilogシミュレーション

ついでにverilogでもシミュレーションしてみます。

$ bsc -verilog Tb.bsv
Verilog file created: mkTb.v
$ iverilog top.v mkTb.v -y /usr/local/lib/Bluespec/Verilog/ -o mkTb.exev
$ ./mkTb.exev
VCD info: dumpfile verilog.vcd opened for output.
------
------
pc_if = 00000000
------
pc_if = 00000004
pc_id = 00000000
------
pc_if = 00000008
pc_id = 00000004
pc_ex = 00000000
------
pc_if = 0000000c
pc_id = 00000008
pc_ex = 00000004
pc_ma = 00000000
------
pc_if = 00000010
pc_id = 0000000c
pc_ex = 00000008
pc_ma = 00000004
pc_wb = 00000000
------
pc_if = 00000014
pc_id = 00000010
pc_ex = 0000000c
pc_ma = 00000008
pc_wb = 00000004

のように正しくパイプラインが動作しました。また、gtkwaveで取得したタイムチャートを図564.1に示します。

gtkwファイルはここ

図%-%.1
図564.1 PCパイプラインタイムチャート

左矢前のブログ 次のブログ右矢

posted by sakurai on December 5, 2022 #563

bsimシミュレーション

このソースに対して、以下のようにコンパイル、bsimシミュレーションを実行します。太字が入力部分です。

$ bsc -sim Tb.bsv; bsc -sim -e mkTb -o mkTb.exe
Elaborated module file created: mkTb.ba
Bluesim object created: mkTb.{h,o}
Bluesim object created: model_mkTb.{h,o}
Simulation shared library created: mkTb.exe.so
Simulation executable created: mkTb.exe
$ ./mkTb.exe -V
------
------
pc_if = 0000
------
pc_if = 0004
pc_id = 0000
------
pc_if = 0008
pc_id = 0004
pc_ex = 0000
------
pc_if = 000c
pc_id = 0008
pc_ex = 0004
pc_ma = 0000
------
pc_if = 0010
pc_id = 000c
pc_ex = 0008
pc_ma = 0004
pc_wb = 0000
------
pc_if = 0014
pc_id = 0010
pc_ex = 000c
pc_ma = 0008
pc_wb = 0004

のように正しくパイプラインが動作しました。また、gtkwaveで取得したタイムチャートを図563.1に示します。

図%-%.1
図563.1 PCパイプラインタイムチャート

左矢前のブログ 次のブログ右矢

posted by sakurai on December 3, 2022 #562

BSVのFIFO調査

BSVにおいて、パイプラインレジスタとしてFIFOを使用できるか調査します。まず、PCパイプラインを設計します。PCパイプラインとは、各ステージのPCを保持することで、例えば各ステージにおいてEIT(例外、割り込み、トラップ)が起きた場合、EITが起こった命令アドレスをハンドラに正しく伝えるためのものです。

Tb.bsv

  import FIFO::*;

  (* synthesize *)
  module mkTb (Empty);

     Reg#(int) pc    <- mkReg(0);
     FIFO#(int) ifs    <- mkFIFO;
     FIFO#(int) ids    <- mkFIFO;
     FIFO#(int) exs    <- mkFIFO;
     FIFO#(int) mas    <- mkFIFO;
     FIFO#(int) wbs    <- mkFIFO;

     // <PC>
     rule pc_stage;
        if (pc > 100) $finish(0);
        $display("------");
        ifs.enq(pc);
        pc <= pc + 4;
     endrule

     // <IF>
     rule if_stage;
        let pc_if = ifs.first; ifs.deq;
        $display (" pc_if = %04h", pc_if);
        ids.enq (pc_if);
     endrule

     // <ID>
     rule id_stage;
        let pc_id = ids.first; ids.deq;
        $display (" pc_id = %04h", pc_id);
        exs.enq (pc_id);
     endrule

     // <EX>
     rule ex_stage;
        let pc_ex = exs.first; exs.deq;
        $display (" pc_ex = %04h", pc_ex);
        mas.enq (pc_ex);
     endrule

     // <MA>
     rule ma_stage;
        let pc_ma = mas.first; mas.deq;
        $display (" pc_ma = %04h", pc_ma);
        wbs.enq (pc_ma);
     endrule

     // <WB>
     rule wb_stage;
        let pc_wb = wbs.first; wbs.deq;
        $display (" pc_wb = %04h", pc_wb);
     endrule

  endmodule: mkTb

ここで、<PC>は入力レジスタは無いため、ここではFIFOは使用しません(が、後で検討します)。単にPCレジスタを設置するだけです。これは<PC>(PCステージ)のPCということになります。通常はPC<=PC+4ですが、分岐時等ではオフセットが足されます。

各ステージでは、入力されたPCをデキューし、表示し、サイクルの終わりで次のステージにそのPCをエンキューします。

図%-%.1
図562.1 FIFOのDequeuとEnqueueの関係
従って、同一サイクルでエンキューしたものをデキューする(バイパス、パススルー)ことはできません。

左矢前のブログ 次のブログ右矢

posted by sakurai on December 2, 2022 #561

画面の縦横の入れ替えの変更

過去記事において、本来VRAMの読み出しアドレスのxとyを入れ替えるつもりが、たまたまwrite側のアドレスマルチプレクサが目につき、それを流用しました。が、動作的な欠点が目立ちました。具体的には、画面書き換えの途中で切り替えると、縦図形と横図形が混在したり、初期画面を動的に準備する負担が大きくなっていました。

これを本来のread側アドレスマルチプレクサに変更します。ただし、write側マルチプレクサに追加してread側マルチプレクサの増設が必要になります。

write側アドレスマルチプレクサは名前をWriteMuxに変更しましたが、元の単なるセレクタに戻しました。新たにReadMuxモジュールを設置し、読み出し側アドレスのxとyを入れ替えます。実際にはyはx(a[7:0])をそのまま用い、xはy(a[15:8])を256から引いたものを用います。

$$ \begin{eqnarray} \left\{ \begin{array}{l} x&\Leftarrow&256-y \\ y&\Leftarrow&x \end{array} \right. \end{eqnarray} $$

図561.1に改造後のブロック図を示します。

図%%.1
図561.1 VRAMモジュール

以下にBSVソースを示します。処理はwrite側で実施した入れ替えとほとんど同様です。

ReadMux.bsv:

// アドレス型の定義
typedef Bit#(16) Addr_t;

// マルチプレクサのインタフェース定義
interface ReadMux_ifc;
   (* prefix="" *)
   // 出力アドレスを生成するメソッド
   method Addr_t outp(Bool sel, Addr_t a);
endinterface

// マルチプレクサの実装
(* synthesize, always_ready = "outp", no_default_clock, no_default_reset *)
module mkReadMux(ReadMux_ifc);

   // 出力アドレスを生成するメソッド
   method Addr_t outp(Bool sel, Addr_t a);
      // xa: aの下位8ビット
      Bit#(8) xa = a[7:0];
      // yax: aの上位8ビットから算出
      Int#(10) yax = 256 - signExtend(unpack(a[15:8]));
      // ya: yaxを8ビットにトリミング
      Bit#(8) ya = truncate(pack(yax));

      // selがTrueなら加工したアドレスを返す
      if (sel) return {xa, ya};
      // selがFalseなら元のアドレスをそのまま返す
      else return a; 
   endmethod

endmodule

この変更により、ボード上のdip swを切り替えることで、リアルタイムに画面の縦横変換ができるようになりました。


左矢前のブログ 次のブログ右矢

posted by sakurai on December 1, 2022 #560

show_fields.blade.phpの修正

後半のビューの修正です。showアクションの2次ビューであるshow_field.blade.phpを修正します。修正前はcategory_idは数字になっています。

      <p>{{ $doc->category_id }}</p>

このビューの画面表示です。

図%%.1
図560.1 showアクション初期画面

これを次のように修正します。indexアクションと同様の修正であり、categoryを全て集めておき、category_idにより表を引きます。

      <p>{{ $categories[$doc->category_id] }}</p>

このビューの画面表示です。

図%%.2
図560.2 showアクション修正後画面

これによりlaravelのスキャフォールディング修正は全て完了しました。


左矢前のブログ 次のブログ右矢


ページ: