0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Shrike-LiteでAtCoder問題を解く(36):ABC471A - 4つの条件を並列判定する

0
Last updated at Posted at 2026-08-16

はじめに

前回は、ABC471の各問題をFPGA目線で眺めました。

今回は小手調べとして、A問題「Nine or Nein」をShrike-Liteへ実装します。

A問題なので処理そのものは簡単です。

今回は、4つの条件を順番に計算するのではなく、4つとも並列に判定する回路にしてみます。


問題

正の整数A,Bが与えられます。

次の値のうち、少なくとも1つが9ならNine、そうでなければNeinです。

A + B
A - B
A * B
A / B

制約は、

1 <= A <= 100
1 <= B <= 100

です。


4つとも同時に判定する

ソフトウェアなら、4つの式を順番に評価しても十分です。

FPGAなので、今回はそれぞれの判定回路を並べます。

             ┌─ A+B == 9 ─┐
             ├─ A-B == 9 ─┤
A, B ────────┼─ A*B == 9 ─┼─ OR ─→ Nine / Nein
             └─ A/B == 9 ─┘

ただし、このためだけに汎用の乗算器や除算器を作る必要はありません。

乗算

正の整数どうしの積が9になる組は、

1 * 9
3 * 3
9 * 1

だけです。

したがって、3組との比較だけで判定できます。

除算

Bは正なので、

A / B == 9

は、

A == 9 * B

と考えられます。

さらに、

9 * B = (B << 3) + B

なので、乗算器も不要です。

減算も、

A - B == 9

ではなく、

A == B + 9

として判定します。


RTL

判定部分の実装は次のようになりました。

wire        sum_nine;
wire        sub_nine;
wire        mul_nine;
wire [10:0] b_times_nine;
wire        div_nine;
wire        nine_result;

assign sum_nine = (a_reg + b_reg) == 8'd9;
assign sub_nine = a_reg == (b_reg + 8'd9);

assign mul_nine = ((a_reg == 8'd1) && (b_reg == 8'd9)) ||
                  ((a_reg == 8'd3) && (b_reg == 8'd3)) ||
                  ((a_reg == 8'd9) && (b_reg == 8'd1));

assign b_times_nine = ({3'b000, b_reg} << 3) + {3'b000, b_reg};
assign div_nine = {3'b000, a_reg} == b_times_nine;

assign nine_result =
    sum_nine |
    sub_nine |
    mul_nine |
    div_nine;

別々の組み合わせ回路で4種類の判定を同時に行い、結果のORを出力しています。


SPI通信

今回もSPIテンプレートV3を使います。

SPIクロックは4MHzです。

入力はA,Bともに1byteで収まるので、そのまま2byteで送ります。

byte 0 : A
byte 1 : B

回答も1bitあれば十分なので、STATUSと同じ1byteへ押し込みました。

bit 7   : VALID
bit 6   : ERROR
bit 5:1 : 0
bit 0   : ANSWER

正常時は、

0x80 : Nein
0x81 : Nine

です。

VALIDが立ったbyteに答えも入っているので、回答を取りに行くための追加転送はありません。


実装とテストはCodexへ

今回は先にSPEC_abc471a.mdを作り、SPIテンプレートV3と直前のABC468C実装を参照資料として置いたうえで、Codexへ実装を依頼しました。

実装対象は主に、

ffpga/src/main.v
firmware/micropython/abc471a_test.py

です。

spi_target.vと参照資料は変更しない条件にしました。

ホスト側の実装とテストは約7分で完了しました。

AとBはそれぞれ1..100なので、組み合わせは全部で10,000通りしかありません。

そこでIcarus Verilogで全組み合わせを総当たりチェックしています。

SUMMARY EXHAUSTIVE_CASES=10000 PROTOCOL_CASES=6 FAILURES=0 PASS

範囲外入力などのプロトコルテストも合わせてPASSしました。


合成結果

ForgeFPGA Workshopで合成し、Shrike-Liteへ書き込みました。

面積には十分な余裕がありますね。

Shrike-LiteでAtCoder問題を解く(36)_001.png

50MHz制約に対して、到達可能クロックは80.25MHz、タイミングも余裕があります。


実機テスト

MicroPython側では、公式4例に加えて各演算条件、境界値、エラーケースなど20ケースを実行しました。

結果は、

SUMMARY TOTAL=20 PASS=20 FAIL=0 PASS

となりました。

回答時間は2ms程度で収まっています。ACが取れそうですね。


まとめ

ABC471AをShrike-Liteへ実装しました。

A問題らしい、小さくて素直な専用回路になったと思います。

次はE問題に挑戦する予定です。

こちらは998244353のモジュラ計算が入ってくるので、今回ほど素直にはいかなさそうです。


おまけ:AtCoder提出用Verilog

せっかくなので、AtCoderへ提出できるVerilogのコードサンプルも置いておきます。

クロックも遅延も一切考慮しない、Icarus vvp環境にやさしいコードです。

module main;
    integer A, B;
    integer ret;
    reg nine;

    initial begin
        ret = $fscanf(32'h80000000, "%d %d", A, B);

        nine =
            (A + B == 9) ||
            (A - B == 9) ||
            (A * B == 9) ||
            (A == 9 * B);

        if (nine)
            $display("Nine");
        else
            $display("Nein");
    end
endmodule

Verilogのintegerは32bit符号付き整数なので、減算結果が負数になる場合への配慮はいりません。

ただし、Verilogの整数除算でA / B == 9と書くと、たとえば37 / 4 == 9も真になってしまいます。

そのため、Shrike-Lite実装と同じくA == 9 * Bへ変形しています。

上記コードをAtCoderのコードテストで走らせてみると20ms程度で実行完了するみたいです。


前回:
Shrike-LiteでAtCoder問題を解く(35):Interlude - ABC471の各問題をFPGA目線で見てみる

次回:
Shrike-LiteでAtCoder問題を解く(37) ABC471E(前編) - レジスタ数個なら入ると思った

コード全文と実装資料:(今回もコード実装やそのドキュメント化は生成AIの助けを借りています)
第36回コード全文と実装資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?