ARTIQのエッジカウンター、入力パルスをまとめて数える仕組み

青緑と紫の二つの淡いガラス皿へ細い光の線が集まり、それぞれ一つの真珠になる。時間窓ごとの集約を表した抽象画。

量子実験の読み出しでは、検出器から届く電気パルスを、決めた時間の中で何回受け取ったか調べることがあります。必要なのが合計数なら、パルス一つずつの到着時刻を計算機へ運ばず、入力に近い回路で数えてから合計を渡す方法が使えます。

実験制御システム ARTIQ の EdgeCounter(エッジカウンター)は、この役割を担う公開実装です。エッジとは、信号が低い状態から高い状態へ、またはその逆へ変わる瞬間を指します。ここでは公式ソースの固定版を読み、入力イベントをまとめる仕組みと、その代わりに残らなくなる情報を見ていきます。

簡単にまとめると?

  • 通常の TTL 入力が変化一回ごとにイベントを送るのに対し、EdgeCounter は指定した時間窓の合計を一つの入力イベントで送れます。
  • 合計が同じでも、パルスの間隔や到着順の細部は異なります。個々の到着時刻が必要なら、集約する前の情報を残す方法が必要です。
  • 入力の待ち行列の負担は減らせますが、待ち行列の容量とカウンターの上限は別です。公開コードの幅指定だけで、実機の安全な入力速度や最大計数を決めることはできません。

数える場所を入力側へ移す

ARTIQ の RTIO は、時刻の付いた入出力イベントを扱う仕組みです。通常の TTL 入力では、検出対象にした立ち上がりや立ち下がりが、それぞれ入力イベントになります。入力 FIFO は、それらを読み出し順に待たせる待ち行列です。読み出しが追い付かなければ、保存できる量が問題になります。

通常入力の TTLInOut.count() も、利用者には合計数を返します。ただし、その手前では個々の入力イベントを受け取り、数えています。戻り値が合計数であることと、FIFO に合計だけを流すことは同じではありません。

EdgeCounter では、FPGA に構成された回路、すなわちゲートウェア側にカウンターを置きます。検出対象の変化があると数値を増やし、要求された時点で合計を入力イベントとして返します。標準のゲート関数を使う場合、時間窓一つにつき結果の入力イベントは一つです。大量の個別イベントを、少数の合計値に置き換える構成になっています。

6 回の立ち上がりを、二つの合計にまとめる例

時刻 0〜10 と 10〜20 の二つの時間窓を考えます。単位は説明用の任意単位とし、境界から離れた時刻にだけ立ち上がりが起きるとします。これは実機の検出速度やクロック間隔を表す数値ではありません。

時間窓 立ち上がりの時刻 合計
0〜10 2、4、6 3
10〜20 12、13、18 3

個別イベントを送る方式なら、検出した立ち上がりに対応する入力イベントは計 6 個です。EdgeCounter の標準ゲートでそれぞれの窓の合計を要求すると、結果の入力イベントは計 2 個になり、読み出す値は順に 3、3 です。この例に限れば、結果を運ぶ入力イベント数は 3 分の 1 になります。

二つの時間窓で計6回の立ち上がりを検出し、個別の6イベントまたは合計3と3の2イベントとして運ぶ比較。最初の窓の到着時刻を変えても合計3は同じになる。
説明用の自作図。時間窓内のエッジ数だけを集約する例であり、実機の波形、FIFO 容量、通信量の測定ではありません。窓の境界と同時に起きるエッジは例に含めていません。

ただし、これは入力イベント数の比較です。ゲートを開閉する出力イベントや設定処理は別に必要です。総通信量、実行時間、消費電力まで 3 分の 1 になるという意味ではありません。また、立ち上がりと立ち下がりの両方を数える設定なら、一つのパルスで二つの変化を数える場合があります。何を一回とするかは、設定と実際の信号で確かめる必要があります。

合計を残すと、到着間隔は復元できない

最初の窓に届いた時刻が 2、4、6 ではなく、1、2、7 だったとしても、合計は 3 のままです。前者の隣り合う間隔は 2、2、後者は 1、5 ですが、合計値 3 からどちらだったかは分かりません。集約は情報量を減らす処理でもあります。

同じ長さの時間窓で検出数を比べたいときには、この集約が役立ちます。一方、個々の到着間隔や他の入力との時間関係を調べたいときには、合計数だけでは足りません。通常の TTL 入力では timestamp_mu() により次の入力イベントの時刻を取得する方法が用意されています。読み出しや保存が追い付く条件の下で、必要な時刻列を残す設計を選びます。

EdgeCounter の fetch_timestamped_count() が返す時刻にも注意が必要です。これは合計を返すよう要求した入力イベントの時刻で、通常はゲートが閉じる時刻に対応します。数えた各エッジの到着時刻を返す関数ではありません。「時刻付きの合計」と「個々の時刻列」は、使える分析が異なります。

窓の指定と、結果を待つ処理を分ける

固定版の gate_rising_mu() が呼ぶ内部処理は、まずカウンターをゼロにする指定と立ち上がり検出の設定を送ります。続いて時間軸のカーソルを窓の長さだけ進め、検出を止める設定と合計を返す要求を送ります。立ち下がりだけ、または両方を対象とする関数もあります。

この指定は、現在の時間軸に RTIO イベントを置く操作です。関数を呼んだ CPU の瞬間と、指定時刻に装置側で動く瞬間を同一視しません。リセットや結果送信の指定には、回路のクロック周期上の処理も伴います。ゲート境界ぎりぎりのパルスをどちらの窓に含めるかは、この単純な例だけでは決められません。

複数の窓を先に指定し、途中で合計を読み出さずに進める使い方は、公式ドライバーに明記されています。後から fetch_count() を繰り返すと、要求した結果を順に受け取れます。各窓で値をリセットする標準関数なら、先の例は 3 の次に 3 を返し、二つ目が累積値 6 になるわけではありません。

ただし、fetch_count() 自体は、結果が来るまで待つ関数です。途中の読み出しを省けることは、後の読み出しが常に即時に終わるという意味ではありません。時刻付きの関数はタイムアウトを指定でき、期限までに結果がなければ時刻に −1 を返します。

入力 FIFO の混雑と、計数の上限は別の問題

複数の入力を同時に測るとき、一方の入力を読み出している間に、もう一方の個別イベントがたまる場合があります。EdgeCounter は一つの窓の結果を一イベントへまとめるため、こうした入力 FIFO の負担を減らす設計です。それでも、結果を読み出さずに無限個の窓を重ねられるわけではありません。実機の FIFO 容量や結果の読み出し方は、別途確認する条件です。

さらに、回路内のカウンターにも有限の幅があります。今回確認したゲートウェアは、カウンターが所定の最大値に達すると、それ以上の増加を止める飽和型の記述になっています。入力イベントを減らす対策だけでは、この数値の上限を広げられません。

ここは版の照合を省けない箇所でもあります。同じ固定版で、ゲートウェアの飽和判定は 2**counter_width - 1、ドライバーの CounterOverflow 例外の比較値は 2**(gateware_width - 1) - 1 です。両方に 31 を代入すると、前者は 2,147,483,647、後者は 1,073,741,823 になり、式は一致しません。

このコードの読み取りだけで、搭載済み装置の有効な計数上限や、すべての飽和を例外で検出できることまでは保証できません。運用では、実際のゲートウェアとドライバーの組合せ、設定、期待する例外処理を確認する必要があります。ドライバーの gateware_width 引数はオーバーフロー処理に使われる値であり、変更するだけで FPGA のカウンターが広がるものでもありません。回路の幅を変えるには、ゲートウェアの再構築が必要です。

必要な情報から測り方を選ぶ

知りたいこと 残すべき情報 設計で確認する点
同じ長さの窓で何回検出したか 窓ごとの合計 検出するエッジ、窓の境界、計数上限、結果の読み出し
パルス間隔や到着の時間関係 個々の時刻列 タイムスタンプの取得と保存が追い付くか
複数チャネルの窓ごとの結果 チャネルと窓に対応する合計 結果の順序と、未読結果がたまる量

EdgeCounter の中心は、数え方を入力側の回路へ移し、必要な合計だけを運ぶことです。時刻列を後から使う予定があるか、合計だけで判断できるかを先に決めると、この方式を選ぶ理由も限界も明確になります。

本稿は 2026 年 10 月 18 日に保全した公開実装の読解です。プログラムやゲートウェアを実機へ導入した結果ではなく、入力速度、検出漏れ、パルス幅の限界を測定したものでもありません。

ARTIQの開発や実験機器の提供を手がけるM-Labsの事業については、cyclewaveの企業解説で紹介しています。

M-Labsの企業解説を読む

出典

  1. M-Labs / ARTIQ, EdgeCounter の公式ドライバー。モジュール解説、ゲート関数、結果取得、オーバーフロー処理。
  2. M-Labs / ARTIQ, SimpleEdgeCounter の公式ゲートウェア。エッジ検出、結果送信、飽和型カウンターの記述。
  3. M-Labs / ARTIQ, Core drivers reference。TTLInOut の count / timestamp_mu と EdgeCounter の各関数。保存時の表示版は 9.9698+a7303a7。

ソースの固定 commit は a7303a7a536935a2e4a7b8d1a537ca89f567772f。公式マニュアルとコードは同じ実装系列の説明であり、独立した二つの性能実証として数えていません。