公開日:2025/05/10
title.png

34th Season


#34-7 真性乱数発生器(SSCG方式)

 Cmod A7ボード(FPGA)を使って、オリジナル16ビットCPUコアMoscoviumの開発を進めています。合わせて、ソフトウェアの開発環境としてBlockly for Moscoviumも整備してきました。

 Blocklyで疑似乱数を発生するカスタムブロックを作ったのですが、応用プログラムを作っていた時に、ハタと困ったのは乱数の種をどうするか?でした。乱数の種……、いつも、ちょっと困るんだぁ。

 前回、真性乱数を発生させるアルゴリズムについて考案したので、具体的に回路設計を進めて完成させます。

真性乱数発生器の設計

 SSCG方式を採用した真性乱数発生器TRNG32ユニットを設計して行きます。ブロック図を以下に示し、解説を続けます。

a347_trng32.png

※薄黄色の箱はCPUからアクセス可能なI/Oレジスタ、薄緑色の箱はハードマクロ、青破線はRTLとハードマクロの境界線です。

I/Oレジスタ仕様

 以下表にI/Oレジスタマップを示し、解説を続けます。

Addr Name RW Bit:15〜8 Bit:7〜0 Memo
0xf1b0 trngrandh RW RAND[31:16] true random number high word
0xf1b2 trngrandl RW RAND[15:0] true random number low word
0xf1b4 reserved
0xf1b6 reserved
0xf1b8 trng0cnth R CNT0[31:16] zero counter high word
0xf1ba trng0cntl R CNT0[15:0] zero counter low word
0xf1bc trng1cnth R CNT1[31:16] one counter high word
0xf1be trng1cntl R CNT1[15:0] one counter low word

 BUSC出力のセレクト信号はbcs_trng_nです。上表の範囲でアサートされるので、TRNG32ユニット内ではアドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。

  I/Oレジスタ仕様:

32ビットデータアクセス

 TRNG32ユニットには、32ビットレジスタが3組存在します。
 Nihoniumの32ビットモード(NHビット=1)でロングワードアクセスする事が可能です。I/O空間は16ビットバスなので、Nihoniumがロングワードアクセス命令を実行すると、バスステートコントローラBUSCがワードアクセス×2回に自動的に分割してアクセスします。

 Moscoviumや、Nihoniumの16ビットモード(NHビット=0)、Tennessineでは、ロングワードアクセスをサポートしていないため、以下の規則に従ってアクセスしてください。

  レジスタアクセス規則:

 32ビットレジスタを16ビットバスからアクセスできる様、一度にアクセスできない下位(/上位)ワードを保持する一時レジスタを持っているため、上の規則に則ってアクセスしなければなりません。

デザイン

 Blockly for Moscoviumで乱数ブロックを使う時の乱数の種をどうするか?と言う命題から始まっていますので、Blockly向けに起こしたマイコンNHOC113DB9808に、TRNG32ユニットを追加しました。

 Cmod A7ボード等、XilinxのFPGAに書き込むのに必要な、Moscoviumマイコンの設計データ(Vivadoのプロジェクトファイル一式を含む)はGitHubで提供しています。以下バナーよりアクセスしてください。

btn_moscovium.png

 デザイン(回路)をCmod A7ボードに書き込む手順については、こちらをご確認ください。

コンパイル

 TRNG32ユニット追加版NHOC113DB9808のコンパイル(論理合成・配置配線)結果を以下に示します。

  コンパイル結果:

 機能的には同じ物に見える(※)MMCMマクロとPLLマクロなのですが、それぞれ最大5個づつ使えるので、用途に合わせて使い分ける事が肝要です。

※実際Vivadoでは、同じIP Generatorウィザード内のラジオボタンでMMCMマクロとPLLマクロを切り替えられます。

予測不可能な実験

 では実験です。通常、新しいユニットを設計した時は、まず、シミュレーションを回して論理動作を検証する所からスタートするのですが、今回に限っては、そうは行きません。シミュレーションでは、ジッタもメタステーブル状態も発生しないためです。
 動作検証は全て実機(FPGA)を使って実施しなくては、なりません。

 TRNG32ユニットを搭載した、NHOC113DB9808等のデザインをCmod A7ボードへ書き込んでください。今回の実験ではシリアルモニタしか使わないので、以上でハードウェアの準備は完了です。以下に実験中の様子を示します(写真は使い回し)。

a274_expr.jpg


 続いて、以下にリンクする評価用プログラムをアセンブルし、Cmod A7ボード上のNihoniumマイコンへ書き込んでください。

  評価用プログラム:mcoc_trng_reg_rand.asm

 このプログラムは、以下に示す様に動作します。

 Nihoniumマイコンにシステムリセットを掛けると、一回だけプログラムを実行して、以下の様な実行結果をシリアルモニタに送信して来ます。実行が終わると無限ループになっていて、動作を停止します。普段シミュレーションで決着しているプログラムを、今回はFPGAで実行しているため、この様な動作となっています。
 以下に、待ち時間T=100μsとした時の実行結果を示します。

  実験サンプル:実行結果ログ
  RDY:
  trngrand:
  2b581bde
  e3e79615
  e721111b
  1d890e23
  :
  : (中略)
  :
  e40b6d4a
  85a0193f
  03ee190d
  3b1566c4
  trng1cnt+0cnt:
  00000966
  trng0cnt:
  00000024
  trng1cnt:
  00000942

 trngrandレジスタ値は、もし、雑音♒が1度も発生しなかった場合、毎回同じ値(固定値)しか読み出せません。上のログに示した様に、異なる値が読み出せたという事は、動作フロウ2)に示した待ち時間Tの間に何回か雑音が発生したことを示しています。雑音の発生数は、待ち時間Tが長くなればなるほど多くなります。

 待ち時間Tを変化させながら上の様なログを取り、都度、固有なtrngrandレジスタ値の数を調べた結果を以下表及びグラフに示します。

待ち時間T 固有のtrngrandレジスタ値 グラフ
0μs 0cyc 12÷1,000個が固有の値だった。 a347_tmuniq.png
1μs 24cyc 46÷1,000個が固有の値だった。
2μs 48cyc 183÷1,000個が固有の値だった。
5μs 120cyc 677÷1,000個が固有の値だった。
10μs 240cyc 971÷1,000個が固有の値だった。
20μs 480cyc 1,000÷1,000個が固有の値だった。
20μs 480cyc 9,999÷10,000個が固有の値だった。
100μs 2,400cyc 10,000÷10,000個が固有の値だった。

 勘違いされない様に、念のため言及しておきますが、この評価は真性乱数trngrandレジスタをクリアしてから、どのくらいの時間が経つと「真の乱数」と見なせる値が読み出せるのか?を確かめる(評価する)ための物です。それを確かめるため、一々trngrandレジスタを0にクリアしてから、待ち時間後に読み出すという処理を繰り返しています。

 通常使用状態では、trngrandレジスタにデータを書き込む(クリアする)事は無い(と言うより禁止な)ので、trngrandレジスタには電源投入からの経過時間中ずっと、電源とクロックが供給される限り、雑音ビットが織り込まれ続けています。基本的に「待ち時間」を気にする必要は無く、いつでも読み出してOKです。

 この評価から知りたかった事は、最初の電源投入時に限り、32ビットの真性乱数trngrandレジスタが、「真に乱数」だと見なせるためには、どのくらいの時間を待てば良いか?です。
 上の結果(表・グラフ)から、固有値が読み出せる割合は、待ち時間T=0〜10[μs]では線形に増加し、T≒10μs=240サイクル(@24MHz)で飽和していることが読み取れます。32ビット真性乱数を生成するための時間は、約10μs=240サイクル程と見なして良いでしょう。

※なお、FPGAに於ける「電源投入時」とは、本当に電源を投入して回路が動作し始めた時に加えて、新しい回路を書き込んで動作を開始した時を含みます。

0/1出現確率

 前節で示した結果ログには、もう一つ、雑音ビットの0/1出現数と言う、重要な結果が示されています。当該部分を以下に抜粋します。

  trng1cnt+0cnt:
  00000966
  trng0cnt:
  00000024
  trng1cnt:
  00000942

 このログより、以下のことが判りました。

 0と1の出現確率が全く均衡していません。

 PLL方式の場合、基本的には0⇔1トグルするタイミングでメタステーブル状態が発生して雑音源となります。このため0/1出現確率が不均衡だと、

と言う、結果に繋がります。

 雑音ビットの0/1出現確率を1:1近辺に調整したい所です。

出現確率の調整

 データとクロックの到達時間が同時変化となるFFに於いて、データとクロックの位相関係が少しずれていることで、取り込む値が1出力側に偏っている、と考えられます。このことから最初は、データとクロックの位相差を調整する事がゴールへ繋がると考えました。

 PLL2の出力位相を変化させると、データとの到達タイミングが前後して調整可能なはずです。そこで、VivadoのIP GeneratorでPLL2を開き、以下の様に出力位相(Phase)を変化させて、実験を繰り返してみました。

a347_pll2ipgn.png

 しかし、結果は何も変わらず、観測される現象に変化が見られませんでした。

 ダメ元で、出力クロックのデューティ(Duty)を変化させたところ、当たりでした。
 手元の環境では、デューティを小さく設定して行くと、trng0cnt→増加、trng1cnt→減少して行くことが観測できました。最終的にデューティ=25%としたところ、

  trng1cnt+0cnt:
  0000096c
  trng0cnt:
  000004a9
  trng1cnt:
  000004c3

 0/1出現確率を、ほぼ1:1に調整することができました。


 調整後もう一度、待ち時間Tを変化した評価を実施した所、待ち時間T≒5μsで固有の値が991÷1,000個と、ほぼ飽和したことを確認しました。CPUクロック数換算では約120サイクルです。厳密にみるとT≒5μsでは、まだ飽和していませんので、少しマージンを確保して160サイクルくらいと考えてください。
 DUE搭載のTRNGユニットは84サイクルでしたので、ほぼ同等の性能と見て良いでしょう。

真性乱数の出現頻度

 UNOやDUEの時に確認してあったので、TRNG32で生成した真性乱数の度数分布グラフを作成して確認しておきたいと思います。

a347_histg.pnga135_histg.png

 上左図が今回設計したTRNG32ユニットの結果、上右図はDUE搭載のTRNGユニットで評価した時の結果(再掲)です。
 この評価だけでは全く不足で、これだけ見ても何の担保にもなりませんが、見る限り、DUE搭載TRNGユニットと同等で、目立って偏った出力は見られませんでした。


 普段は耳障りだったり忌み嫌われる「雑音」ですが、今回のようなテーマでは沢山発生してくれた方が好ましく、心地良く思えるのは不思議です。


btn_arduino.png

btn_prev.png btn_next.png

home