0
1

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問題を解く(7):ABC466A - Compromise

0
Last updated at Posted at 2026-07-13

はじめに

今回は、AtCoder Beginner Contest 466のA問題をShrike-Liteへ実装してみます。

前回のABC456Aでは、入力値とコマンドを1byteのMOSIフレームへ収めることができました。

しかし、ABC466Aでは、入力値の範囲が-100100です。符号付き8bitのデータが必要になるため、コマンドと入力値を1byteへ同居させることができません。

そこで今回は、次の新しい仕組みを導入します。

  • 1個の入力値を2byteに分割して送る
  • 各byteの上位4bitへ、そのbyteの役割を示すコマンドを入れる
  • FPGAからの返信を有効にしたまま、入力値を連続して送る
  • 答えがNoに確定したら、途中でもEODを送って終了する

前回と共通するテンプレート作成やSPI初期化の説明は簡略化し、マルチバイトSPI通信を中心に整理します。


1. 問題の解法

N個の選択肢があり、各選択肢を選んだときの嬉しさがX_iとして与えられます。

すべてのX_iが負であればYes、1個でも0以上の値があればNoです。

すべての X_i < 0 なら Yes
1個でも X_i >= 0 なら No

FPGA側では、答えを表す1bitを最初に1へ設定しておきます。

ANSWER = 1

入力値を受け取るたびに符号を確認し、0以上の値があればANSWER0へ変更します。

if X_i >= 0:
    ANSWER = 0

一度0になったANSWERは、そのテストケースが終了するまで0のままです。

このため、Noが確定した時点で残りの入力値を送る必要はありません。


2. RP2040とFPGAの役割分担

今回の役割分担は次のとおりです。

処理 担当
テストケースからX_iを取り出す RP2040
符号付き8bitへ変換し、上位・下位4bitに分割する RP2040
SPIで2byteに分けて送る RP2040
2個の4bitデータを8bitへ戻す FPGA
X_i >= 0を判定する FPGA
YesまたはNoに相当する1bitを返す FPGA
期待値との比較、表示、時間測定を行う RP2040

NはFPGAへ送りません。

RP2040が入力値を順番に送り、最後にEODを送ることでデータ列の終了を知らせます。


3. マルチバイトSPI通信の方式を決める

符号付き8bitの値を送る方法として、次の三つが考えられます。

方針 内容
コマンドにメタデータを持たせる 各byteが上位4bitか下位4bitかをコマンドで示す
FPGA側のFSMで順番を管理する 1byte目、2byte目をFPGAの状態で判別する
8bitデータをそのまま流す データの意味を通信順序だけで決める

今回は、各byteへメタデータを持たせる方式を採用します。

MOSIの上位4bitをコマンド、下位4bitをデータとします。

bit 7                     bit 4 bit 3                     bit 0
+------------------------------+-------------------------------+
|          COMMAND[3:0]        |            DATA[3:0]          |
+------------------------------+-------------------------------+

1個のX_iは、次の2byteで送ります。

1byte目:SEND_HIGH + X_iの上位4bit
2byte目:SEND_LOW  + X_iの下位4bit

各byte自身が役割を示すため、FPGA側で「次は上位byteか、下位byteか」を管理する受信FSMは不要です。

FPGAに優しい設計ですね。


4. MOSIコマンドとデータ形式

今回使用するコマンドは次のとおりです。

コマンド COMMAND 用途
NOP 0000 RESET後のWAIT確認
SEND_HIGH 0001 入力値の上位4bitを送る
SEND_LOW 0010 入力値の下位4bitを送り、1個の値を確定する
EOD 0011 データ列の終了
DEBUG 1110 デバッグ用
RESET 1111 内部状態の初期化

コマンドだけを送る場合、下位4bitは0000とします。

コマンド 送信byte
NOP 0x00
EOD 0x30
DEBUG 0xE0
RESET 0xF0

符号付き8bitへの変換

X_iは2の補数形式の符号付き8bitとして送ります。

MicroPython側では、次の式で0~255の値へ変換できます。

encoded = x & 0xFF

上位・下位4bitは次のように取り出します。

high = (encoded >> 4) & 0x0F
low  = encoded & 0x0F

送信byteは次の式で作ります。

tx_high = (SEND_HIGH << 4) | high
tx_low  = (SEND_LOW  << 4) | low

たとえば、X_i=-100は8bitの2の補数で0x9Cです。

X_i      = -100
8bit表現 = 1001_1100 = 0x9C

SEND_HIGH + 1001 = 0001_1001 = 0x19
SEND_LOW  + 1100 = 0010_1100 = 0x2C

FPGAはSEND_HIGHで受け取った1001を保持し、SEND_LOWで受け取った1100と結合します。

1001 + 1100 = 1001_1100 = -100

5. MISOの返信形式

MISOは前回と同様に1byteです。

bit 7 bit 6                           bit 1 bit 0
+--------+----------------------------------+--------+
| VALID  |             reserved             | ANSWER |
+--------+----------------------------------+--------+
bit 名前 内容
7 VALID 有効な判定結果がある場合に1
6~1 予約 常に0
0 ANSWER 1ならYes、0ならNo

返信値は次の三種類です。

MISO 意味
0x81 有効な返信、現在の答えはYes
0x80 有効な返信、答えはNoで確定
0x00 まだ入力値を1個も受信していない

RESET直後は次の状態です。

VALID  = 0
ANSWER = 1

最初のSEND_LOWを受信し、1個目の入力値が完成した時点でVALID1にします。

それ以降、VALIDRESETまで1のままです。

ANSWERは、負の値だけを受信している間は1を維持します。0以上の値を受信したら0へ変更し、その後は0を維持します。


6. SPI通信シーケンス

SPIではMOSI送信とMISO受信が同時に行われますが、MOSIで送ったデータがMISOで受ける受信データに即時に反映されるわけではありません。

SEND_LOWを受信した時点でFPGAが判定結果を更新するため、その結果をRP2040が受け取れるのは次のSPI転送です。

今回は、この1byte分の遅れを利用して、次の入力値のSEND_HIGHを返信読出しにも使います。

MOSI 同時に受信するMISO
SEND_HIGH(X_i) X_(i-1)までの判定結果
SEND_LOW(X_i) X_(i-1)までの判定結果
EOD 最後に完成した入力値までの判定結果

初期化

各テストケースの開始時は、前回と同様です。

RESET → NOP(WAIT)

全入力値が負の場合

すべての値をSEND_HIGHSEND_LOWの順で送ります。

最後のSEND_LOWで更新された結果は、続くEOD送信時のMISOで読み出します。

Noが途中で確定した場合

あるX_i0以上だった場合、FPGAはSEND_LOW(X_i)の受信後にANSWER=0とします。

RP2040は、通常は次の入力値のSEND_HIGHを送ったときに0x80を受信します。

この時点で答えは確定しているため、SEND_LOWは送らず、直ちにEODを送ります。

SEND_LOW(X_i)
    ↓ FPGAがANSWER=0へ更新
SEND_HIGH(X_(i+1))
    ↓ MISOで0x80を受信
EOD

X_(i+1)の上位4bitはFPGAへ届いていますが、SEND_LOWが送られないため入力値としては確定しません。

各byteへSEND_HIGHSEND_LOWのメタデータを付けた効果が、このような途中終了でも現れます。

0以上の値が最後の要素だった場合は、続くEOD送信時に0x80を受信します。

テストケース終了

最終結果を受信した後は、次の順番でWAIT状態へ戻します。

EOD → RESET → NOP(WAIT)

7. FPGA側の実装方針

FPGA側で必要な主なレジスタは次の三つです。

レジスタ 用途
x_high[3:0] SEND_HIGHで受信した上位4bitを保持
reply_valid 1個以上の入力値を処理したことを示す
answer 1ならYes、0ならNo

コマンドごとの処理は次のようになります。

コマンド FPGA側の処理
SEND_HIGH DATA[3:0]x_highへ保存
SEND_LOW {x_high, DATA[3:0]}を1個の値として確定し、符号を判定
EOD 判定結果を保持
RESET VALID=0ANSWER=1へ初期化
NOP 状態を変更しない

X_iが負かどうかは、8bit値の最上位bitで判定できます。

上位4bitを保持しているx_high[3](x_highレジスタの最上位ビット)が符号bitです。

x_high[3] = 1 なら負
x_high[3] = 0 なら0以上

したがって、SEND_LOW受信時の判定は概念的に次のようになります。

reply_valid <= 1'b1;

if (x_high[3] == 1'b0)
    answer <= 1'b0;

一度answer=0になった後は、負の値を受信しても1へ戻しません。

MISOへ返す値は次の形です。

{reply_valid, 6'b000000, answer}

8. MicroPython側の実装方針

MicroPythonファイル名はabc466a_test.py、bitstream名はabc466a.binとします。

SPIの初期化、ピン設定、周波数、CPOL、CPHA、FPGAリセット、1byte送受信処理は既存テンプレートをそのまま使用します。

各テストケースでは次の処理を行います。

  1. RESETを送る
  2. NOPを送りWAIT状態を確認する
  3. X_iを符号付き8bitへ変換する
  4. SEND_HIGHを送る
  5. MISOが0x80ならEODを送り、入力送信を終了する
  6. SEND_LOWを送る
  7. まだ未送信データがあれば4.に戻る、すべての値を送り終えたらEODを送る
  8. 最終的なVALIDANSWERを確認する
  9. RESETNOPを送りWAIT状態へ戻す

実装では、返信確認を関数にしておくと分かりやすくなります。

def decode_reply(rx):
    valid = (rx >> 7) & 0x01
    answer = rx & 0x01
    return valid, answer

VALID=1かつANSWER=0を受信した時点で、答えはNoに確定します。

ANSWER=1の間は、残りの入力値を送信します。

テストケース

公式入力例に加え、境界値と早期終了を確認できるケースを用意します。

TEST_CASES = [
    ([2, 0, -1, 2], "No"),
    ([-5, -2, -1], "Yes"),
    ([-5, 0, -1], "No"),
    ([0, -2, 0, -1], "No"),
    ([-100, -1], "Yes"),
    ([100, -100], "No"),
    ([-100, -100, -100, -100, -100,
      -100, -100, -100, -100, -100], "Yes"),
    ([-1, -1, 0], "No"),
]

各ケースについて、次の情報を1行で表示させます。

  • 入力値
  • 実際に送信したデータ数
  • 早期終了したかどうか
  • 最終RX
  • 期待結果
  • FPGAの結果
  • PASSまたはFAIL
  • 概算処理時間

テンプレートからABC466A用プロジェクトを作る

前回と同様に、atcoder_spi_templateをコピーしてABC466A用プロジェクトを作ります。

主な変更対象は次のファイルです。

abc466a/
├─ abc466a.ffpga
├─ bitstream/
│  └─ abc466a.bin
├─ ffpga/
│  └─ src/
│     ├─ main.v
│     └─ spi_target.v
└─ firmware/
   └─ micropython/
      └─ abc466a_test.py

spi_target.vとMicroPython側のShrike-Lite共通処理は変更しません。

問題固有部分は、main.vのコマンド処理とabc466a_test.pyのテスト・通信処理です。


AIへMicroPython実装を依頼する

依頼文は、前回と重複する共通部分を省略し、今回固有の通信仕様を中心にします。

Shrike-LiteでABC466A - Compromiseをテストするための
MicroPythonプログラムを作成してください。

既存の atcoder_spi_template_test.py を参考にして、
abc466a_test.py を新規作成してください。

bitstream名は abc466a.bin です。

Shrike-Liteへのbitstream書き込み、FPGAリセット、
SPI初期化、ピン設定、周波数、CPOL、CPHA、
1byteのSPI送受信処理は既存テンプレートをそのまま使用してください。

main.v、spi_target.v、abc466a.ffpga、abc466a.binは変更しないでください。

MOSIは上位4bitをCOMMAND、下位4bitをDATAとします。

コマンド:
- NOP       = 0b0000
- SEND_HIGH = 0b0001
- SEND_LOW  = 0b0010
- EOD       = 0b0011
- DEBUG     = 0b1110
- RESET     = 0b1111

各X_iは x & 0xFF で符号付き8bitの2の補数表現へ変換し、
SEND_HIGHで上位4bit、SEND_LOWで下位4bitを送信してください。

MISOはbit7をVALID、bit0をANSWERとします。
- 0x81:有効な返信、Yes
- 0x80:有効な返信、No
- 0x00:有効な返信なし

各ケースの開始時は RESET → NOP としてください。

各入力値についてSEND_HIGHを送信し、
受信したMISOがVALID=1かつANSWER=0なら、
SEND_LOWを送らずにEODを送信して終了してください。

Noが確定していなければSEND_LOWを送信し、
次の入力値へ進んでください。

すべての入力値を送信した場合はEODを送信し、
EOD送信時に受信したMISOを最終結果として使用してください。

最終結果を受信した後は RESET → NOP でWAIT状態へ戻してください。

公式入力例、境界値、全件負、0を含むケース、
先頭・途中・末尾でNoが確定するケースをテストしてください。

各ケースについて入力、送信した値の個数、最終RX、
期待結果、結果、PASS/FAIL、概算処理時間を表示してください。
1件がFAILになっても残りのケースを継続してください。

完成したVerilogコード

以下がmain.vの問題固有部分の実装です。

    // ===== 問題ごとに変更する部分 =====
    // ABC466A - Compromise

    localparam [3:0] CMD_NOP       = 4'b0000;
    localparam [3:0] CMD_SEND_HIGH = 4'b0001;
    localparam [3:0] CMD_SEND_LOW  = 4'b0010;
    localparam [3:0] CMD_EOD       = 4'b0011;
    localparam [3:0] CMD_DEBUG     = 4'b1110;
    localparam [3:0] CMD_RESET     = 4'b1111;

    wire [3:0] cmd;
    wire [3:0] data;
    assign cmd  = rx_data[7:4];
    assign data = rx_data[3:0];

    reg [3:0] x_high;
    reg       reply_valid;
    reg       answer;
    wire      next_answer;

    assign next_answer = answer & x_high[3];

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            x_high      <= 4'b0000;
            reply_valid <= 1'b0;
            answer      <= 1'b1;
            tx_data <= 8'h00;
        end else if (rx_data_valid) begin
            case (cmd)
                CMD_NOP: begin
                end

                CMD_SEND_HIGH: begin
                    x_high <= data;
                end

                CMD_SEND_LOW: begin
                    reply_valid <= 1'b1;
                    answer      <= next_answer;
                    tx_data     <= {1'b1, 6'b000000, next_answer};
                end

                CMD_EOD: begin
                end

                CMD_DEBUG: begin
                end

                CMD_RESET: begin
                    x_high      <= 4'b0000;
                    reply_valid <= 1'b0;
                    answer      <= 1'b1;
                    tx_data     <= 8'h00;
                end

                default: begin
                end
            endcase
        end
    end
    // ===== 問題ごとに変更する部分ここまで =====

前回同様、合成~bitstream生成を行います。

以下は実機合成の利用リソースレポートです。

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

birstreamはabc466a.binにリネームして、Shrike-LiteのUSBドライブにコピーしておきます。


完成したMicroPythonコード

次にMicroPythonコードのABC466A固有部分です。

# ===== ABC466A固有部分 =====
# ABC466A - Compromise
NOP = 0b0000
SEND_HIGH = 0b0001
SEND_LOW = 0b0010
EOD = 0b0011
DEBUG = 0b1110
RESET = 0b1111

TEST_CASES = [
    ([2, 0, -1, 2], "No"),
    ([-5, -2, -1], "Yes"),
    ([-5, 0, -1], "No"),
    ([0, -2, 0, -1], "No"),
    ([-100, -1], "Yes"),
    ([100, -100], "No"),
    (
        [-100, -100, -100, -100, -100,
         -100, -100, -100, -100, -100],
        "Yes",
    ),
    ([-1, -1, 0], "No"),
]


def make_frame(command, data=0):
    return ((command & 0x0F) << 4) | (data & 0x0F)


def parse_reply(rx):
    valid = (rx >> 7) & 0x01
    answer = rx & 0x01

    if valid == 0:
        result = "NO_REPLY"
    elif answer:
        result = "Yes"
    else:
        result = "No"

    return valid, answer, result


def split_signed8(x):
    encoded = x & 0xFF
    high = (encoded >> 4) & 0x0F
    low = encoded & 0x0F
    return high, low


def send_command(command):
    return spi_exchange(make_frame(command, 0))


def reset_to_wait():
    spi_exchange(make_frame(RESET, 0))
    spi_exchange(make_frame(NOP, 0))


def run_case(values, expected):
    reset_to_wait()

    sent_count = 0
    early_stop = 0
    final_rx = 0x00

    # 測定対象は、最初のSEND_HIGH直前から最終結果RXまでとする。
    start_us = time.ticks_us()

    for x in values:
        high, low = split_signed8(x)

        rx = spi_exchange(make_frame(SEND_HIGH, high))
        valid, answer, result = parse_reply(rx)

        if valid and answer == 0:
            early_stop = 1
            final_rx = rx

            eod_rx = send_command(EOD)
            eod_valid, eod_answer, eod_result = parse_reply(eod_rx)
            if eod_valid:
                final_rx = eod_rx
            break

        spi_exchange(make_frame(SEND_LOW, low))
        sent_count += 1
    else:
        final_rx = send_command(EOD)

    elapsed_us = time.ticks_diff(time.ticks_us(), start_us)

    valid, answer, actual = parse_reply(final_rx)
    passed = actual == expected
    status = "PASS" if passed else "FAIL"

    print(
        "VALUES={} TOTAL={} SENT={} EARLY_STOP={} RX=0x{:02X} EXPECT={} RESULT={} {} TIME_US={}".format(
            values,
            len(values),
            sent_count,
            early_stop,
            final_rx,
            expected,
            actual,
            status,
            elapsed_us
        )
    )

    reset_to_wait()
    return passed


total_start_us = time.ticks_us()
pass_count = 0
fail_count = 0

for values, expected in TEST_CASES:
    if run_case(values, expected):
        pass_count += 1
    else:
        fail_count += 1

total_time_us = time.ticks_diff(time.ticks_us(), total_start_us)

print(
    "SUMMARY PASS={} FAIL={} TOTAL_TIME_US={}".format(
        pass_count,
        fail_count,
        total_time_us
    )
)
# ===== 問題ごとに変更する部分ここまで =====

完成したMicroPythonコードはabc466a_test.pyとして、Shrike-LiteのUSBフォルダにコピーしておきます。


動作確認

完成したコードが予定通りに使用になっているか確認してみてください。

確認項目 内容
2byte復元 正数・負数・境界値を正しく8bitへ戻せるか
VALID 最初の入力値処理後から1を維持するか
ANSWER 負の値だけなら1を維持するか
0の判定 0を受信した時点で0へ変わるか
ラッチ 一度0になった後、負の値で1へ戻らないか
EOD 最終返信の読出しに使えるか
早期終了 0x80受信後に残りの入力送信を停止するか
RESET 次のテストケースで初期状態へ戻るか

問題がなさそうなら、Thonnyからabc466a_test.pyを開き、実行します。

以下が手元の実機での実行結果です。

[shrike_flash] FPGA reset done 
[shrike_flash] FPGA reset done 
[shrike_fpga] Starting FPGA flash...
[shrike_fpga] flashing: abc466a.bin
[shrike_flash] FPGA programming done.
VALUES=[2, 0, -1, 2] TOTAL=4 SENT=1 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=1000
VALUES=[-5, -2, -1] TOTAL=3 SENT=3 EARLY_STOP=0 RX=0x81 EXPECT=Yes RESULT=Yes PASS TIME_US=1544
VALUES=[-5, 0, -1] TOTAL=3 SENT=2 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=1364
VALUES=[0, -2, 0, -1] TOTAL=4 SENT=1 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=927
VALUES=[-100, -1] TOTAL=2 SENT=2 EARLY_STOP=0 RX=0x81 EXPECT=Yes RESULT=Yes PASS TIME_US=1109
VALUES=[100, -100] TOTAL=2 SENT=1 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=990
VALUES=[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100] TOTAL=10 SENT=10 EARLY_STOP=0 RX=0x81 EXPECT=Yes RESULT=Yes PASS TIME_US=4445
VALUES=[-1, -1, 0] TOTAL=3 SENT=3 EARLY_STOP=0 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=1631
SUMMARY PASS=8 FAIL=0 TOTAL_TIME_US=32901

無事テストケースで全PASSしました。最大ケースの N=10 の場合で 4.45msで処理が終わっています。ACですね。

テストケースごとのSPI転送回数は、通常終了なら2N+1回、早期終了なら2×SENT+2回です。

処理時間は想定通り、ほぼSPI転送回数に比例しているようです。(実測でSPI転送1回あたり 210~250uSec)

SPI自体は1MHzなので1byteの転送は理論上8µsです。

MicroPythonによる関数呼び出しやCS操作、SPI APIのオーバーヘッドが今の実装では支配的になっていることがわかります。

高速化を進める方法はいくつもあると思いますので、ぜひ皆さんトライしてみてください。


動作確認のおまけ (安易な高速化実験の結果報告)

RP2040の動作クロックを250MHzまで上げて同じ測定を行ってみました。

デフォルト設定クロックは125MHzです。

MicroPythonコードのbitstream書き込みとSPI初期化の間に、クロック変更コードを挟みます。

# ===== 共通部分:FPGAへのbitstream書き込みとリセット =====
shrike.reset()
shrike.flash(BITSTREAM)

reset_pin = Pin(FPGA_RESET, Pin.OUT, value=1)
reset_pin.value(0)
time.sleep_ms(100)
reset_pin.value(1)
time.sleep_ms(100)

# ===== RP2040のMCUクロック変更 =====
machine.freq(250_000_000)
print("CPU_FREQ={}MHz".format(machine.freq() // 1_000_000))

# ===== 共通部分:SPI Masterの初期化 =====
cs = Pin(CS, Pin.OUT, value=1)

spi = SPI(
    0,
    baudrate=1_000_000,
    polarity=0,
    phase=0,
    bits=8,
    firstbit=SPI.MSB,
    sck=Pin(SCK),
    mosi=Pin(MOSI),
    miso=Pin(MISO)
)

結果は以下のようになりました。

CPU_FREQ=250MHz
VALUES=[2, 0, -1, 2] TOTAL=4 SENT=1 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=890
VALUES=[-5, -2, -1] TOTAL=3 SENT=3 EARLY_STOP=0 RX=0x81 EXPECT=Yes RESULT=Yes PASS TIME_US=1083
VALUES=[-5, 0, -1] TOTAL=3 SENT=2 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=988
VALUES=[0, -2, 0, -1] TOTAL=4 SENT=1 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=699
VALUES=[-100, -1] TOTAL=2 SENT=2 EARLY_STOP=0 RX=0x81 EXPECT=Yes RESULT=Yes PASS TIME_US=848
VALUES=[100, -100] TOTAL=2 SENT=1 EARLY_STOP=1 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=709
VALUES=[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100] TOTAL=10 SENT=10 EARLY_STOP=0 RX=0x81 EXPECT=Yes RESULT=Yes PASS TIME_US=3067
VALUES=[-1, -1, 0] TOTAL=3 SENT=3 EARLY_STOP=0 RX=0x80 EXPECT=No RESULT=No PASS TIME_US=1167
SUMMARY PASS=8 FAIL=0 TOTAL_TIME_US=23420

N=10のテストケースで45%程度の速度向上が見込めるようです。(4.45ms → 3.07ms)

ただ、RP2040の動作クロック上限保証は200MHz(@1.15Vコア電圧)ですから、スペック範囲外のオーバークロックです。

個体によっては動かないかもしれませんし、ボードの故障などのトラブルにつながる可能性もあります。

これから暑い季節になります。200MHzを超えるクロック設定については、くれぐれも自己責任でお試しいただくようお願いします。

私は300MHz (MicroPythonでの設定可能な最高クロック) テストで見事にハングアップさせました。


今回のまとめ

今回は、ABC466Aを題材に、1個の入力値を複数byteへ分割して送るSPI通信を設計しました。

MOSIは上位4bitをコマンド、下位4bitをデータとし、符号付き8bitのX_iを次の2byteで送ります。

SEND_HIGH + 上位4bit
SEND_LOW  + 下位4bit

各byteへ役割を示すコマンドを持たせたため、FPGA側で受信順を管理する必要がなくなりました。

FPGA側の判定結果は、最初の入力値を処理した後からVALIDにし、0以上の値を受信した時点でANSWER1から0へ変更します。

SPIの1byte分の返信遅延は、次のSEND_HIGHを返信読出しにも使用することで吸収します。すべての値を送信した場合は、EODを最終返信の読出しに兼用します。

Noが途中で確定した場合は、その時点でEODを送り、残りの入力値の送信を省略します。

前回の1byte完結型から、データ分割、返信の継続、パイプライン、早期終了まで一歩進んだSPI通信になりました。

前回: Shrike-LiteでAtCoder問題を解く(6):ABC456A - Dice

次回: Shrike-LiteでAtCoder問題を解く(8):ABC466B - Representative Balls(制約版)を実装する

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?