Posts Tagged with "Design"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.
posted by sakurai on October 20, 2022 #533

「ハードウェアインタプリター」に食わせる機械語列が必要です。そこで、この記事を参考に、Fibonacciプログラムをコンパイルし機械語化しました。試みにFibonacciが通るための「インタプリター」を書いていきます。前述のとおりこの「インタプリター」は実行ステージが逆アセンブル相当の表示をするだけのものです。

入力するFibonacciのソースは以下のように短いプログラムです。

fibo.c:

int fib(int n) {
  if(n <= 1) return 1;
  return fib(n-1) + fib(n-2);
}
int main() {
  fib(10);
  for(;;) {}
  return 0;
}

これをクロスコンパイルし、BSVの入力とします。シーケンサの自動生成を利用して1サイクル毎に、命令デコーダに命令を供給します。

Stmt main = seq
    instr <= 32'h074000ef;
    instr <= 32'hfe010113;
    instr <= 32'h00112e23;
    instr <= 32'h00812c23;
    instr <= 32'h00912a23;
    instr <= 32'h02010413;
    instr <= 32'hfea42623;
    instr <= 32'hfec42703;
    instr <= 32'h00100793;
    instr <= 32'h00e7c663;
    instr <= 32'h00100793;
    instr <= 32'h0300006f;
    instr <= 32'hfec42783;
    instr <= 32'hfff78793;
    instr <= 32'h00078513;
    instr <= 32'hfc9ff0ef;
    instr <= 32'h00050493;
    instr <= 32'hfec42783;
    instr <= 32'hffe78793;
    instr <= 32'h00078513;
    instr <= 32'hfb5ff0ef;
    instr <= 32'h00050793;
    instr <= 32'h00f487b3;
    instr <= 32'h00078513;
    instr <= 32'h01c12083;
    instr <= 32'h01812403;
    instr <= 32'h01412483;
    instr <= 32'h02010113;
    instr <= 32'h00008067;
    instr <= 32'hff010113;
    instr <= 32'h00112623;
    instr <= 32'h00812423;
    instr <= 32'h01010413;
    instr <= 32'h00a00513;
    instr <= 32'hf7dff0ef;
    instr <= 32'h0000006f;
endseq;

今回はまだデコーダのテストだけなので、PCは実装していません。


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

posted by sakurai on October 18, 2022 #532

図532.1にBSV版のBitpatのREADMEに掲載されていた使用例を示します。

図%%.1
図532.1 BSVのBitpat関数

挙げられた例はちょうどRISC-Vの命令パターンに一致しており、add命令とaddi命令のデコード部分を示したものです。この要領で、次々に他の命令を実装していくことができます。

本ライブラリの動作は以下の2ステップとなっています。

  1. whenの中のパターンマッチでは可変部をvで表し、固定部をnとビットパターンで記述します。vの幅を指定しないで良いのは使いやすそうです。whenの最後に識別した機能を解釈するための関数名を記述します。
  2. マッチした後に呼ばれる関数では、可変部のみを変数で受け(固定部は捨てる)、処理を実行します。結局vの幅は意識しなければなりません。

このように最初に固定部、次に可変部という考え方に慣れる必要があります。最初は使いにくいと感じましたが、慣れれば気にならないのかもしれません。

プロセッサ設計と言ってもパイプラインでなければ、見方を変えれば、RISC-V機械語のインタプリターをHDLで作成するだけなので、それほど難しいことではありません。ひとつずつデコードし、対応する処理を実装していくだけです。この「ハードウェアインタプリター」の1段目はデコードステップで、2段目は実行ステップになります。


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

RISC-Vプロセッサの設計

posted by sakurai on October 17, 2022 #531

「はじめてのCPU自作」という本を購入したので、これを参考にRISC-Vプロセッサを設計します。ただし、この本ではChiselベースとなっていますが、本稿ではBSVベースとします。またパイプラインプロセッサの経験があるため、最初からパイプラインプロセッサを設計します。

さて、この本を読んでいたらChisel(だかScalaだか)にはBitpatという便利な機能があるようです。

図%%.1
図531.1 ChiselのBitpat関数

命令デコーダを書くのに便利そうなので、BSVにもないのか調べたら、GithubにBitpatという似たようなものがありました。これはAlexandre Joannouさんが作成されたものであり、Readmeには以下のように書いています。

BitPat BitPat is a bit-string pattern matching library for Bluespec, inspired by Morten Rhiger's "Type-Safe Pattern Combinators".

BSVにおいてのパターンマッチライブラリとのことです。便利そうなのでRISC-Vの命令デコーダに採用することにします。


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

posted by sakurai on September 27, 2022 #517

完成したゲームのオープニングからのゲーム開始画面です。動画変換フレームレートの関係で、ゼロの点滅がハッキリと再生されませんが、実際にはきれいに点滅しています。

図%%.1
図517.1 オープニングアニメーションシーケンス

実行のシーケンス

  • 得点表(Score Advance Table)アニメーション表示
  • Fボタンを押す
  • "PUSH ONLY 1PLAYER BUTTON"を表示、CREDIT=01
  • Sボタンを押す
  • "PLAY PLAYER<1>"を表示、CREDIT=00、SCORE<1>をゼロにし、規定回数点滅
  • ゲームスタート

図513.5
図513.5 ボタン配置図


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

posted by sakurai on September 26, 2022 #516

Y字リプレースアニメーションのソース

Y字リプレースアニメーションのソースを示します。Y字リプレースアニメーションもFボタンにより中断するため、各所でFボタンを見ています。

function Stmt replaceY;
   return (seq
      // from right to left
      for (i <= 228; i >= 142; i <= i - 2) seq
         copyArea((pack(i)[1] == 1'b1) ? 68 : 84 , 32, i, 67, 10, 8);
         wait_timer(`TICK_WAIT3);
         if (fbutton) break;
      endseq // for
      if (fbutton) break;
      // from left to right
      for (i <= 136; i <= 226; i <= i + 2) seq
         copyArea((pack(i)[1] == 1'b1) ? 75 : 91 , 107, i, 67, 16, 8);
         wait_timer(`TICK_WAIT3);
         if (fbutton) break;
      endseq // for
      eraseArea(226, 67, 16, 8);
      wait_timer(`TICK_WAIT32);
      if (fbutton) break;
     // from right to left
     for (i <= 226; i >= 136; i <= i - 2) seq
        copyArea((pack(i)[1] == 1'b1) ? 77 : 93 , 117, i, 67, 16, 8);
        wait_timer(`TICK_WAIT3);
        if (fbutton) break;
      endseq // for
      wait_timer(`TICK_WAIT32);
      if (fbutton) break;
      eraseArea(141, 67, 9, 8);
      wait_timer(`TICK_WAIT32);
      if (fbutton) break;
   endseq);
endfunction

これだけでなく、タイマールーチンの中でもFボタンによる中断を見ていますが、ちょっとやり過ぎのようです。実際には多少間引いても体感に影響しないと思います。


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

posted by sakurai on September 23, 2022 #515

オープニングアニメーションのソース

オープニングアニメーションのソースを示します。オープニングアニメーションはFボタン(コイン投入の模擬)により中断するため、各所でFボタンを見ています。

function Stmt openingAnimation;
   return (seq
      // Opening Animation
      foa <= True;
      eraseArea( 0, 41, 255, 199); // erase screen
      eraseArea(25,242, 5, 7); // erase zanki
      stringS1; // PLAY ...
      if (fbutton) break;
      wait_timer(`TICK_WAIT64);
      if (fbutton) break;
      stringS2; // *SCORE ...
      if (fbutton) break;
      wait_timer(`TICK_WAIT32);
      if (fbutton) break;
      stringS3; // =? MYSTERY ...
      if (fbutton) break;
      wait_timer(`TICK_WAIT64);
       if (fbutton) break;
      replaceY; // ^ -> Y
      if (fbutton) break;
      wait_timer(`TICK_WAIT64);
      if (fbutton) break;
      foa <= False;
   endseq);
endfunction

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

posted by sakurai on September 22, 2022 #514

オープニングアニメーションの追加

テスト用のソースを示します。コンパイル時間短縮のため、ゲーム部分をカットしています。

// メインフロー
Stmt main = seq
   while (True) seq
      while (!fbutton) seq
         openingAnimation; // Fボタンによりブレーク
      endseq // while
      openingDisplay;      // 表示のみ、ボタンを待たない
      await(sbutton);        // Sボタンによりブレーク
      openingDisplay2;    // タイマーによりブレーク
      // game start
   endseq // while
endseq;

// CREDIT 01, "PUSH ONLY 1PLAYER BUTTON"
function Stmt openingDisplay;
   return (seq
      eraseArea( 0, 41, 255, 199); // erase screen
      stringS5; // PUSH ONLY ...
      copyArea(10, 162, 217, 241, 5, 7); // CREDIT 00->01
   endseq);
endfunction

// CREDIT 00, "PLAY PLAYER<1>, 00000"
function Stmt openingDisplay2;
   return (seq
      eraseArea( 0, 41, 255, 199); // erase screen
      stringS6; // PLAY PLAYER<1>
      copyArea(2, 162, 217, 241, 5, 7); // CREDIT 01->00
      for (i <= 1; i < 15; i <= i + 1) seq
         // erase zero
         eraseArea(40, 25, 37, 7);
         wait_timer(`TICK_WAIT4); // wait 66.66msec
         stringS7; // 00000
         wait_timer(`TICK_WAIT4); // wait 66.66msec
      endseq // for
   endseq);
endfunction

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

posted by sakurai on September 21, 2022 #513

オープニングアニメーションの追加

この動画の最初にオープニングアニメーションのヒントがあります。

  1. インベーダの種類や点数の紹介、と同時に逆さYを引っ張って行き正立Yに入れ替えます。アニメーションがメインなので、これをopeningAnimationシーケンスと呼び、同名の関数により実行します。Fボタンによりコイン投入を模擬します。
    図%%.1
    図513.1 openingAnimation画面

  2. コインを投入すると、"PUSH ONLY 1PLAYER BUTTON"と表示され、CREDITが+1されます。アニメーションは無いため、これをopeningDisplayシーケンスと呼び、同名の関数により実行します。Sボタンを待ちます。
    図%%.2
    図513.2 openingDisplay画面

  3. Sボタンを押すと、"PLAY PLAYER<1>"と表示され、CREDITが-1されます。同時に得点が"00000"となり、点滅します。これをopeningDisplay2シーケンスと呼び、同名の関数により実行します。
    図%%.3
    図513.3 openingDisplay画面1

    図%%.4
    図513.4 openingDisplay画面2

  4. ゼロ点滅を規定回数実行すると自動的にゲームを開始します。

図513.5にSボタンとFボタンの配置を示します。

図%%.5

図513.5 ボタン配置図

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

posted by sakurai on September 20, 2022 #512

テストベンチ

テストベンチは変わりません。

Uart.bsv

import StmtFSM::*;
import Uart::*;

(* synthesize *)
module mkTb();
      Uart_ifc uart <- mkUart();

      Stmt s = seq
            delay(8);
            uart.load(8'h55);
            uart.load(8'haa);
            uart.load(8'hc3);
            uart.load(8'h3c);
            await(uart.done());
            $finish;
      endseq;
      
      mkAutoFSM(s);

endmodule

Bsimシミュレーション

Bsimシミュレーションのコマンドは次のとおりです。

$ bsc -u -sim Tb.bsv; bsc -sim -e mkTb -o Tb.exec; ./Tb.exec -V bsim.vcd; gtkwave -A bsim.vcd

以下にBsimシミュレーション結果を示します。意外なことに明示的にdone信号を書いたにも関わらず、シミュレーションのダンプの中にdone信号がありませんでした。

図%%.1
図512.1 Bsimシミュレーション波形

テストベンチ内でレジスタにuart.doneを格納するようにしたら、インタフェースにuart.doneが現れました。awaitで使用するくらいでは削除され、レジスタに取って初めて残すようです。

図%%.2
図512.2 Bsimシミュレーション波形

Verilogシミュレーション

Verilogシミュレーションのコマンドは次のとおりです。

$ bsc -u -verilog Tb.bsv; iverilog top.v mkTb.v mkUart.v -o ./mkTb.exev; ./mkTb.exev; gtkwave -A verilog.gtkw

Verilogシミュレーションのほうには当然ですが、uart.done信号が存在します。

図%%.3
図512.3 Verilogシミュレーション波形


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

posted by sakurai on September 19, 2022 #511

UARTの改良点

過去記事においてUARTを設計しましたが、見直したところ改良点が見つかりました。 改良点はdoneフラグの生成です。実はハンドシェークは自動的にenableとreadyの2線で行われるので、doneが無くても良いのですが、test benchで終了を知りたい場合には必要です。

Uart.bsv

import StmtFSM::*;

interface Uart_ifc;
      method Bit#(1) read();
      method Action load(Bit#(8) newdata);
      method Bool done();
endinterface

(* synthesize, always_ready="read, done" *)
module mkUart(Uart_ifc);
      Reg#(Bit#(8)) data <- mkRegU;
      Reg#(Bit#(1)) odata <- mkReg(1'h1); // stop bit

      Stmt s= seq
            odata <= 1'h0; // start bit
            repeat (8) action
                  odata <= data[0];
                  data <= (data >> 1);
            endaction
            odata <= 1'h1; // stop bit
      endseq;

      FSM fsm <- mkFSM(s);

      method Bit#(1) read();
            return odata;
      endmethod
      method Bool done();
            return fsm.done();
      endmethod
      method Action load(Bit#(8) newdata);
            action
                  data <= newdata;
                  fsm.start();
            endaction
      endmethod
endmodule

この記述のように、従来設けてあったレジスタのdoneフラグを削除し、ステートマシンのdoneを上位に返すことで実現します。さらに、インタフェースのreadとdoneは常にreadyであるため、それらのready信号は不要なので削除しています。


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


ページ: