toms.log
2020/05/23
HDLでFizzBuzz
プログラムの定番問題をHDLで解くには.
Wikipedia[[1](#wikipedia)]より > 最初のプレイヤーは「1」と数字を発言する。次のプレイヤーは直前のプレイヤーの次の数字を発言していく。ただし、3で割り切れる場合は「**Fizz**」(Bizz Buzzの場合は「**Bizz**」)、5で割り切れる場合は「**Buzz**」、両者で割り切れる場合(すなわち15で割り切れる場合)は「**Fizz Buzz**」(Bizz Buzzの場合は「**Bizz Buzz**」)を数の代わりに発言しなければならない。 有名なFizzBuzz問題をHDLで記述してみよう. ## プログラム C言語でFizzBuzz問題の主要なポイントを記述すると以下のようなものになる. ```c for (int i=0;i
## HDLでの実装 HDLで記述する場合, コンソールは使えない(シリアル通信などはFizzBuzz問題から逸脱する)ため, 代わりのインターフェースを考える. ### インターフェース | Name | I/O | P/N | Description | | -------------------- | ---- | ---- | --------------------------------------------------- | | RESET_n | I | N | Reset | | CLK | I | P | Clock | | SINK_READY | O | P | Sink data ready | | SINK_VALID | I | P | Sink data valid | | SINK_DATA[31:0] | I | P | Sink data: Max Fizz Buzz count | | SOURCE_VALID | O | P | Source data valid | | SOURCE_DATA[31:0] | O | P | Source data: Fizz Buzz count | | SOURCE_FIZZBUZZ[2:0] | O | P | Source Fizz Buzz selector(2:FizzBuzz,1:Buzz,0:Fizz) | 入出力はほとんどAvalon-STに従う. `SINK_VALID`が有効のときの`SINK_DATA[31:0]`の値をループの最大値として演算を行い, `SOURCE_DATA[31:0]`に値を出力する. ただし`SOURCE_FIZZBUZZ[2:0]`は`Fizz`, `Buzz`, `FizzBuzz`を選択する信号で, それぞれに対応するビットが立つ. ![interface](https://raw.githubusercontent.com/toms74209200/FIZZ_BUZZ/master/docs/interface.svg) ### ループ `for`文によるループは`i`をインクリメントして活用するというところにポイントがある. そこでHDLではカウンタを用意してそのカウンタの値を参照する. `for`文内の初期化式 (`int i=0;`) と再初期化式 (`i++`) に当たる. 出力に使うので, カウンタの信号種別は`integer`ではなく`std_logic_vector`である. ```vhdl signal cnt : std_logic_vector(SINK_DATA'range); -- Fizz Buzz counter ~~ process (CLK, RESET_n) begin if (RESET_n = '0') then cnt <= (others => '0'); elsif (CLK'event and CLK = '1') then if (busy_i = '1') then if (done_i = '1') then cnt <= cnt; else cnt <= cnt + 1; end if; elsif (SINK_VALID = '1') then cnt <= cnt + 1; end if; end if; end process; ``` `for`文内の継続条件式 (`i
'0'); product_5i <= (others => '0'); elsif (CLK'event and CLK = '1') then if (busy_i = '1') then product_3i <= cnt * FixedNum1d3; product_5i <= cnt * FixedNum1d5; elsif (SINK_VALID = '1') then product_3i <= (others => '0'); product_5i <= (others => '0'); end if; end if; end process; -- ============================================================================ -- Fizz Buzz selector -- ============================================================================ fizz_buzz_i <= "100" when (product_3i(cnt'left downto cnt'left-15) = X"FFFF" and product_5i(cnt'left downto cnt'left-15) = X"FFFF") else "010" when (product_5i(cnt'left downto cnt'left-15) = X"FFFF") else "001" when (product_3i(cnt'left downto cnt'left-15) = X"FFFF") else (others => '0'); ``` ここで判別したい数 (`cnt`) と除数を小数にしたもの (`FixdNum1d*`) のビット幅は32のため, これらの積は64ビットになる. また除数を小数にしたもの (`FixdNum1d*`) の小数点位置はMSB (32) だから積の小数点位置は31ビット目にあたる. 条件は31ビット目以下16ビットが0xFFFFかどうかである. ## シミュレーション コンソールでそれっぽく表示させるために, テストベンチシミュレーションを使う. テストベンチのソースはVHDLだと記述がややこしいので, SystemVerilogを使う. といってもとくに特別な操作は必要なく, `SOURCE_FIZZBUZZ` に合わせて出力する文字列を変えるだけだ.
これをコマンドラインで実行すると, プログラムで作ったFizzBuzzと同じような出力を得られる. もしFPGA実機で実行するとその実行時間はクロックの周波数に依存して, クロック毎にFizzBuzzに対応する出力を行う. シリアル通信などを使ってコンソールに出力すると, 当然その通信がボトルネックになる. そのためこの通信を加味するとCPUよりも実行時間は長くなるだろう. FPGAの場合はこういった通信がボトルネックになることが多い. ## 参考 [1]
[Fizz Buzz - Wikipedia](https://ja.wikipedia.org/wiki/Fizz_Buzz) [2]
[toms.log: ハードウェアで素数を数える](https://toms-log.blogspot.com/2019/03/blog-post_18.html)
0 件のコメント:
コメントを投稿
次の投稿
前の投稿
ホーム
登録:
コメントの投稿 (Atom)
0 件のコメント:
コメントを投稿