FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

RISC-V BOOM2 を改造してSoC環境を構築する (1. BOOM2 のブートの仕組み解析)

BOOM (Berkeley Out-of-Order Machine) は、UCBが開発して管理(現在はRISC-VでHPC/AIの参入を発表しているEsperanto?)しているRISC-Vのアウトオブオーダプロセッサだ。

BOOM (Berkeley Out-of-Order Machine) について

これまで、BOOMについては以下のような記事を中心にいろいろと解析してきた。 簡単に言うとBOOMはRISC-Vのアウトオブオーダ実装であり、2-way(2命令同時発行)、4-way(4命令同時発行)が公開されている。

BOOMプロセッサを搭載したSoCシミュレーション環境を作る

これまでRocket-Core, SiFive Freedom SoCを使って作ってきたように、BOOMのコアのRTLを抜き出して、自分のオリジナルSoCを作る環境を構築したい。

BOOMはRocket-Coreと同様にChiselで記述されているので、ChiselからVerilog-hDLに変換し、そのRTLをテストベンチに搭載してRTLシミュレーション環境を構築する。

f:id:msyksphinz:20180221235434p:plain

BOOMのRTLファイルを生成する

Chiselで記述されているBOOMプロセッサのVerilog-HDLを生成するためには、Rocket-Chip GeneratorのリポジトリでBOOMのブランチに移動し、RTLファイルを生成する。

github.com

  • Top.BOOMConfig.v
module Top(
  input   clk,
  input   reset,
  input   io_mem_axi_0_aw_ready,
  output  io_mem_axi_0_aw_valid,
  output [31:0] io_mem_axi_0_aw_bits_addr,
  output [7:0] io_mem_axi_0_aw_bits_len,
  output [2:0] io_mem_axi_0_aw_bits_size,
  output [1:0] io_mem_axi_0_aw_bits_burst,
  output  io_mem_axi_0_aw_bits_lock,
  output [3:0] io_mem_axi_0_aw_bits_cache,
  output [2:0] io_mem_axi_0_aw_bits_prot,
  output [3:0] io_mem_axi_0_aw_bits_qos,
  output [3:0] io_mem_axi_0_aw_bits_region,
  output [4:0] io_mem_axi_0_aw_bits_id,
  output  io_mem_axi_0_aw_bits_user,
  input   io_mem_axi_0_w_ready,
  output  io_mem_axi_0_w_valid,
  output [63:0] io_mem_axi_0_w_bits_data,
...

クロック・リセット・あとはAXIのポートが生成されていることが確認できる。

  • clk : クロック
  • reset : リセット
  • io_mem_axi_0 : Master側のAXIバス. AR, AW, R, W, B のチャネルが用意されている。
  • io_interrupts : 例外入力
  • io_debug : デバッグポート

が生成されていることが確認できた。 とりあえずこれらのポートはすべて0に落として、動作を解析していくことにする。

BOOMでRTLシミュレーション時にデバッグ情報を生成するオプション

BOOMはオプションを何も指定しないと、CPUの動作トレース情報を何も出力してくれない。 これはRocket-Coreとは異なる部分だ。しかし、Chiselの一部分を書き換えるとデバッグ情報を生成してくれるようになる。

...
trait BOOMDebugConstants
{
   val DEBUG_PRINTF        = false // use the Chisel printf functionality
   val DEBUG_ENABLE_COLOR  = false // provide color to print outs. Requires a VIM plugin to work properly :(
   val COMMIT_LOG_PRINTF   = false // dump commit state, for comparision against ISA sim
   val O3PIPEVIEW_PRINTF   = false // dump trace for O3PipeView from gem5
   val O3_CYCLE_TIME       = (1000)// "cycle" time expected by o3pipeview.py
...

この中の、DEBUG_PRINTFをTrueに設定することでデバッグ情報を出力できるようになる。

少し順番が前後するが、上記のオプションを追加することでRTLシミュレーション時にトレース情報を出力してくれるようになる。

--- Cyc=                 347 , ----------------- Ret:                   34 ----------------------------------                                                                                                                                                        [134/1297]
  Dec:  ([0x00820]                        [0x00824]                        ) ctate: (N: _ _ _     R) BMsk:00 Mode:M
(--)  DASM(00000000)  |  (--)  DASM(00000000)  |  )
   fin(0)  [ISA: 0, 0, 0,32] [Phs:  0(-)  0[R](X)  0[R](-) 32[R](-)]   [ISA: 0, 0, 0,32] [Phs:  0(-)  0[R](X)  0[R](-) 32[R](-)] Exct(-                   2) Commit(0) fl: 0x3ffffffffffffffffffffffffff2 (107) is: 0x0000000000000000000000000000 (  0)
                          Branch Unit:  , ,0 PC=0x00800, 0 Targ=0x00804 NPC=0,0x00824 00
 brob[ 0] (2) T=0 m=2 r=28 B
 brob[ 1] (0) T=2 m=0 r=30
 brob[ 2] (0) T=0 m=0 r= 0
 brob[ 3] (0) T=0 m=0 r= 0
 brob[ 4] (0) T=0 m=0 r= 0
 brob[ 5] (0) T=0 m=0 r= 0
 brob[ 6] (0) T=0 m=0 r= 0
 brob[ 7] (0) T=0 m=0 r= 0
 brob[ 8] (0) T=0 m=0 r= 0
 brob[ 9] (0) T=0 m=0 r= 0
 brob[10] (0) T=0 m=0 r= 0
 brob[11] (0) T=0 m=0 r= 0
 brob[12] (0) T=0 m=0 r= 0
 brob[13] (0) T=0 m=0 r= 0
 brob[14] (0) T=0 m=0 r= 0
 brob[15] (0) T=0 m=0 r= 0
 brob[16] (0) T=0 m=0 r= 0
 brob[17] (0) T=0 m=0 r= 0
 brob[18] (0) T=0 m=0 r= 0
 brob[19] (0) T=0 m=0 r= 0
 brob[20] (0) T=0 m=0 r= 0
 brob[21] (0) T=0 m=0 r= 0
 brob[22] (0) T=0 m=0 r= 0
 brob[23] (0) T=0 m=0 r= 0
 predictor: ghist: 0x0005, r_ghist: 0x0005 commit: 0x0005
wakeup_idx: 15, ld is head of ROB:0
         ldq[ 0]=(----_U_ 0) st_dep( 6,m=0000) 0x00438 H T   saq[ 0]=(-------) b:00 0x00438 -> 0x0000000000000000
         ldq[ 1]=(----___ 0) st_dep( 5,m=0000) 0x00000       saq[ 1]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[ 2]=(----___ 0) st_dep( 5,m=0000) 0x00000       saq[ 2]=(-------) b:00 0x0043c -> 0xffffffffffffffff
         ldq[ 3]=(----___ 0) st_dep( 5,m=0000) 0x00000       saq[ 3]=(-------) b:00 0x00100 -> 0x0000000000000000
         ldq[ 4]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 4]=(-------) b:00 0x00438 -> 0x0000000000000000
         ldq[ 5]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 5]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[ 6]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 6]=(-------) b:00 0x0043c -> 0xffffffffffffffff
         ldq[ 7]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 7]=(-------) b:00 0x00000 -> 0x0000000000000000 H E C T
         ldq[ 8]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 8]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[ 9]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 9]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[10]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[10]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[11]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[11]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[12]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[12]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[13]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[13]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[14]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[14]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[15]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[15]=(-------) b:00 0x00000 -> 0x0000000000000000
BrPred1:    (IF1_PC= n/a- Predict:n/a) ------ PC: [--_-  for br_id:(n/a),     next: 0x000004782c ifst:0]
I$ Response: (-) IF2_PC= 0x0000000824 (mask:0x2) DASM(7b002473)DASM(00847413) ----BrPred2:(-,-,0) [btbtarg: 0x00000] jkilmsk:0x3 ->(0x2)
bp2_aligned_pc: 0x0000000820 BHT:(- 0x000004782c, 1) p:0 (1) b:0 j:0 (1) J -
  integer_issue_slot[ 0](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 1](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 2](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 3](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 4](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 5](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 6](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 7](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 8](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 9](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[10](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[11](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[12](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[13](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[14](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[15](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[16](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[17](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  1, 40) PDST:  4 - [ [DASM(42802e23)]  0x0000081c:   2] ri:37 bm=  0 imm=0x43c02
  integer_issue_slot[18](-)(Req:-):wen=  P:(!,!,!) OP:(  0, 20, 62) PDST:  2 X [ [DASM(f1402473)]  0x00000820:  31] ri:38 bm=  0 imm=0xf1402

SoC 構成でのRTLシミュレーション

上記のオプションでRTLシミュレーションを実行してみた。

CPUのフェッチポートを観測していると、どうやら0x1000を何度もフェッチしているようだ。 これはどういうことだろう?

f:id:msyksphinz:20180221232810p:plain

というわけでBootROMを確認してみると、成程、BOOMの場合は最初にプログラムのロードが入って デバッグ割り込みが挿入されないと起動しない仕組みになっているのか。

  • ./src/main/scala/RocketChip.scala
  def makeBootROM()(implicit p: Parameters) = {
    val rom = java.nio.ByteBuffer.allocate(32)
    rom.order(java.nio.ByteOrder.LITTLE_ENDIAN)

    // for now, have the reset vector jump straight to memory
    val resetToMemDist = p(GlobalAddrMap)("mem").start - p(ResetVector)
    require(resetToMemDist == (resetToMemDist.toInt >> 12 << 12))
    val configStringAddr = p(ResetVector).toInt + rom.capacity

    // This boot ROM doesn't know about any boot devices, so it just spins,
    // waiting for the debugger to load a program and change the PC.
    rom.putInt(0x0000006f)                        // loop forever
    rom.putInt(0)                                 // reserved
    rom.putInt(0)                                 // reserved
    rom.putInt(configStringAddr)                  // pointer to config string
    rom.putInt(0)                                 // default trap vector
    rom.putInt(0)                                 //   ...
    rom.putInt(0)                                 //   ...
    rom.putInt(0)                                 //   ...

    rom.array() ++ p(ConfigString).toSeq
  }

面倒なので、とりあえずテストベンチでforce文を突っ込んでデバッグ例外を発生させてみよう。

  • ./tb/tb_freechips_boom2.v
  initial begin
    force tb.duv.uncore_io_prci_0_interrupts_debug = 1'b0;
    #(100ps);
    force tb.duv.uncore_io_prci_0_interrupts_debug = 1'b1;
    #(10ps);
  end

成程、デバッグ例外を挿入すると無限ループから脱出できた。

f:id:msyksphinz:20180221233752p:plain

Freedom E300 Everywhere を改造して外部SRAMからプログラムをロードして実行する

RISC-V の実装であるFreedom E300 Everywhere を改造して、外部にSRAMを接続してそこにプログラムを置き、RISC-V Rocket-Chipからプログラムをフェッチして実行できるようにする。

オリジナルの構成では、Freedom E300 EverywhereではQSPIが乗っていた部分を取り払って、SimpleSRAMというChiselで記述したモジュールを置き、そこにプログラムを配置してRISC-V Rocket-Coreからフェッチしてもらう。

f:id:msyksphinz:20180221011128p:plain
図. Freedom E300 Everywhereの構成図。QSPIの部分を取り払い、SRAMを配置する。今回はこの場所にプログラムを置き、実行してみる。

RISC-Vプログラムの記述

あまり詳しいことはよくわかっていないが、 Freedom E300 Everywhereに搭載されているRocket-Chipは以下の2ステップでプログラムを実行するようになっている。

  1. リセット後は0x10000に配置されたBootROMから初期プログラムをフェッチする(だいたい、外部メモリ領域へのメモリフェッチが入るような、ジャンプコードが入っている)
  2. 外部AXIメモリやDDR3-SDRAMなどの領域から、プログラムをフェッチする。

オリジナルのE300では、QSPIの領域(0x2000_0000)にジャンプし、Linuxなりベアメタルなりのプログラムをロードするようになっている訳だ。

この部分にSimpleSRAMという自作のChiselモジュールを置き、RAMの部分を$readmemhでhexファイルをロードして、プログラムを読み込めるようにしよう。

  • 記述したプログラム (simple_main.c) ※ ちょっとだけFizzBuzzっぽい。
int simple_main ()
{

  (*(volatile unsigned int *)0x20000100) = 0xdeadbeef;

  int total = 0;
  for (int i = 0; i < 100; i++) {
        if ((i % 3) == 0) total += 3;
        if ((i % 5) == 0) total += 5;
        total += i;
  }

  (*(volatile int *)0x20000104) = total;

  return 0;
}

これをコンパイルし、SimpleSRAMにロードするようにしておく。

  initial begin
    $readmemh("../../../program/simple_main/simple_main.hex", tb_sifive_freedom.duv.simpleRAMs_0.memory);
  end

注意点1. SimpleSRAMに接続されるTileLinkはExecutable属性を付けておく

TileLinkの実装に慣れないと、本当にどこで設定されているのかわからない。SimpleSRAMのTileLinkの設定で、executable属性をtrueにしておく。

  • freedom/rocket-chip/src/main/scala/devices/tilelink/SimpleSRAM.scala
    Seq(TLManagerParameters(
      address            = AddressSet.misaligned(c.address, c.depth*beatBytes),
      resources          = new SimpleDevice("ram", Seq("msyksphinz,simpleRAM")).reg("mem"),
      regionType         = RegionType.UNCACHEABLE,
      executable         = true,
      supportsGet        = TransferSizes(1, beatBytes),
      supportsPutPartial = TransferSizes(1, beatBytes),
      supportsPutFull    = TransferSizes(1, beatBytes),
      fifoId             = Some(0))), // requests are handled in order

注意点2. SimpleSRAMを$readmemhを使用する場合は、RANDOMIZE_MEM_INIT define属性を解除する

これを解除しないと、せっかく$readmemhでメモリを初期化したのに、またランダム値で初期化されてしまう。これも最初気がつかなくてハマった。

実行結果

上記の設定を施した上でプログラムをコンパイルSRAMに配置、RTLシミュレーションを行った。 Rocket-Chipではシミュレーション結果が表示されるようになっている。

ちょっとわかりにくいが、8220 cycle で最後の計算をしてメモリ領域にストアしている。きちんと実行できていることが確認できた。

しかし、8000サイクル程度かかっているのはちょっとヒドいなあ。

C0:         31 [1] pc=[00010000] W[r10=00000000][1] R[r 0=00000000] R[r20=e4a715cb] inst=[f1402573] csrr    a0, mhartid
C0:         32 [1] pc=[00010004] W[r 5=20000000][1] R[r 0=00000000] R[r 0=00000000] inst=[200002b7] lui     t0, 0x20000
C0:         33 [1] pc=[00010008] W[r 0=0001000c][1] R[r 5=20000000] R[r 0=00000000] inst=[00028067] jr      t0
C0:         34 [0] pc=[00010008] W[r 0=0001000c][0] R[r 5=00000000] R[r 0=e4a715cb] inst=[00028067] jr      t0
C0:         35 [0] pc=[00010008] W[r 0=0001000c][0] R[r 5=00000000] R[r 0=e4a715cb] inst=[00028067] jr      t0
...
C0:       8217 [0] pc=[20000028] W[r 0=00000000][0] R[r12=000013bd] R[r 0=00000007] inst=[0000e211] bnez    a2, pc + 4
C0:       8218 [0] pc=[20000028] W[r 0=00000000][0] R[r12=000013bd] R[r 0=00000007] inst=[0000e211] bnez    a2, pc + 4
C0:       8219 [0] pc=[20000028] W[r 0=00000000][0] R[r12=000013bd] R[r 0=00000007] inst=[0000e211] bnez    a2, pc + 4
C0:       8220 [1] pc=[2000002c] W[r14=00001420][1] R[r14=000013bd] R[r15=00000063] inst=[0000973e] add     a4, a4, a5
C0:       8221 [1] pc=[2000002e] W[r15=00000064][1] R[r15=00000063] R[r 1=00000063] inst=[00000785] addi    a5, a5, 1
C0:       8222 [1] pc=[20000030] W[r 0=00000000][0] R[r15=00000064] R[r11=00000064] inst=[feb796e3] bne     a5, a1, pc - 20
C0:       8223 [1] pc=[20000034] W[r15=20000000][1] R[r 0=00000000] R[r 0=00000000] inst=[200007b7] lui     a5, 0x20000
C0:       8224 [1] pc=[20000038] W[r 0=20000104][0] R[r15=20000000] R[r14=00001420] inst=[10e7a223] sw      a4, 260(a5)
C0:       8225 [1] pc=[2000003c] W[r10=00000000][1] R[r 0=00000000] R[r 0=00001423] inst=[00004501] jr      a0
C0:       8226 [1] pc=[2000003e] W[r 0=20000040][1] R[r 1=00000000] R[r 0=00000000] inst=[00008082] mv      ra, zero

「30日でできる!OS自作入門」を読み始めた (1. 0日目、1日目、2日目)

遅まきながら、「30日でできるOS自作入門」を読み始めた。

といっても実は私が高校生くらいの時に購入して、GUIをいじりだしたあたりで挫折した記憶のある書籍である。 今回満を持してリベンジである。

30日でできる! OS自作入門

30日でできる! OS自作入門

第0日、第1日の内容は、まあ慣れていることもあって理解できる。

第2日目のあたりで、IPLは理解はしているつもりだけど人に説明できるかというと怪しいので、少し掘り下げて理解しておきたい。 まだ一行も手を動かしていないけど。

Raspberry-Piとか、RISC-Vの実装とかも絡めていければいいかなと思っている。 UNIXの動作の何たるかについては、「はじめてのOSコードリーディング」を一応読破したので、「意味不明!」ということは無いはず。

どれくらい時間がかかるのかわからないけど、頑張ろう。

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

量子プログラミング言語Q#の勉強 (5. 2量子ビットのSWAPを書いてみる)

量子プログラミング言語 Q# を使って量子コンピュータについて勉強しようプロジェクト、量子コンピュータの論理演算の仕組みが少しずつ分かってきたので、次は自分でプログラムを書いてみたい。

幸いなことに、前回の記事にて、2つの量子ビットの状態を入れ替える、というSWAPという操作を勉強した。

2つの量子ビットa, bに対して、SWAP(a, b) を実行して量子ビットの入れ替えを行うためには、 a, b に対してCNOTを3回適用することになる。

f:id:msyksphinz:20180218224051p:plain
図. 量子コンピュータにおける量子ビットa, bを交換するのには、CNOTゲートを量子ビットa, bに対して3回適用すればよい。

これをQ#で実装するとどのようになるのだろうか?

2量子ビットのSWAPをQ#で書いてみる

Q# コードを記述する

CNOTを3回適用すればよいのだから、

とする。CNOTを3回書いて、以下のように記述した。

  • quantum_swap.qs
    operation QuantumSwap(qbit0 : Result, qbit1 : Result) : (Result, Result)
    {
        body
        {
            mutable qresult = (Zero, Zero);
            // mutable result = (0, 0);
            using (register = Qubit[2])
            {
                Set(qbit0, register[0]);
                Set(qbit1, register[1]);

                CNOT(register[0], register[1]);
                CNOT(register[1], register[0]);
                CNOT(register[0], register[1]);

                set qresult = (M(register[0]), M(register[1]));
                ResetAll(register);
            }
            return qresult;
        }
    }

ちなみに、Setは以下のように記述している。qbitという確定したデータを量子ビットに格納する関数だ。

 operation Set(desired: Result, q1: Qubit) : ()
    {
        body
        {
            let current = M(q1);
            if (desired != current)
            {
                X(q1);
            }
        }
    }

Driverを記述する

Driverは2つの量子ビットの初期状態を8回ランダムに生成する。こちらはC#のコードだ。 入力量子ビットの状態、出力量子ビットの位置が逆転していれば成功だ。

  • Driver.cs
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;
using System.Linq;

namespace Quantum.quantum_swap
{
    class Driver
    {
        static void Main(string[] args)
        {
            var sim = new QuantumSimulator();
            var rand = new System.Random();

            foreach (var idxRun in Enumerable.Range(0, 8))
            {
                Result[] initials = new Result[2];
                initials[0] = (rand.Next(2) == 0) ? Result.Zero : Result.One;
                initials[1] = (rand.Next(2) == 0) ? Result.Zero : Result.One;

                Result[] received = new Result[2];
                (received[0], received[1]) = QuantumSwap.Run(sim, initials[0], initials[1]).Result;

                System.Console.WriteLine($"Round {idxRun} Sent : {initials[1]}, {initials[0]}.");
                System.Console.WriteLine($"Result : \t{received[1]}, {received[0]}");

                if (initials[0] == received[1] && initials[1] == received[0]) {
                    System.Console.WriteLine("Quantum Swap Successful!!\n");
                } else
                {
                    System.Console.WriteLine("Quantum Swap Fail...\n");
                }
            }

            System.Console.WriteLine("\n\nPress Enter to exit...\n\n");
            System.Console.ReadLine();
        }
    }
}

実行結果

試行錯誤の上で、コンパイルして実行してみた。 実行結果は以下のようになった。ちゃんとSwapされている。成功だ!

今度は、もう少し複雑なプログラムを実行してみたい。

f:id:msyksphinz:20180218224633p:plain
図. 量子ビットを交換するプログラムをQ#で記述した結果。ちゃんと2つの量子ビットの値が交換されている。

関連記事

量子プログラミング言語Q#の勉強 (4. 量子テレポーテーション)

Microsoft Q# Programmingのガイドには、最初のサンプルプログラムとして量子テレポーテーションが登場している。

これは量子回路で表現すると、以下のようになっている。うん、訳が分からん。

色々と資料を読みながら、量子テレポーテーションについて勉強していこう。

  • Quantum Teleportation

https://www.appi.keio.ac.jp/Itoh_group/abe/pdf/qc2.pdf

https://www.youtube.com/watch?v=mose-W49uF8&index=2&list=PLB1324F2305C028F7

そもそも量子テレポーテーションとは

  • 量子アルゴリズムという程ではないけれども、最も重要な量子の操作の一つ。
  • 量子コンピュータにおいて、データを転送する (たとえその量子ビットが遠く離れていても)。
  • 量子ビットの状態を知ることなく、転送することができる!

  • Alice, Bob, Victorという3人がいる。

    • AliceとBobが量子の状態をエンコードしておき、AliceとVictorがデコードすることで、Bobの状態を知ることができる。
  • 2つの量子ビットがあり、AliceとBobがそれぞれ保持している。

  • VictorとAliceのベル状態のペアを作成する。
    • Aliceの測定により、Bell Pairは 1 つの状態に確定する。
    • → Bobの状態は、Victorのもともとの状態に近い状態になっている。
    • → Aliceはクラシカルな方法で、BobにAliceの状態を伝える。
    • → BobはXをかけることにより、Bobは自分の状態を知ることができる。
f:id:msyksphinz:20180218141408p:plain

Q#プログラミング言語による実装

こう考えてみると、Q#での実装は量子回路をそのまま実装してあるに過ぎない。

    operation Teleport(msg : Qubit, there : Qubit) : () {
        body {

            using (register = Qubit[1]) {
                // Ask for an auxillary qubit that we can use to prepare
                // for teleportation.
                let here = register[0];
            
                // Create some entanglement that we can use to send our message.
                H(here);
                CNOT(here, there);
            
                // Move our message into the entangled pair.
                CNOT(msg, here);
                H(msg);

                // Measure out the entanglement.
                if (M(msg) == One)  { Z(there); }
                if (M(here) == One) { X(there); }

                // Reset our "here" qubit before releasing it.
                Reset(here);
            }

        }
    }
f:id:msyksphinz:20180218141514p:plain
図. 量子テレポーテーションの回路とQ#の対応
f:id:msyksphinz:20180218145226p:plain
図. 量子テレポーテーションのQ#実行結果。送信元から送信先量子ビットの転送ができている。

補足. 量子テレポーテーションの理解にあたり必要な量子回路の変換

1. 量子状態のSWAP

2つの量子状態の交換は、SWAP操作と呼ばれる (参考資料 : https://www.appi.keio.ac.jp/Itoh_group/abe/pdf/qc1.pdf)

f:id:msyksphinz:20180218143357p:plain
2つの量子状態のSWAP

これは、量子ゲートのCNOTを3回組み合わせることで実現できる。

f:id:msyksphinz:20180218143504p:plain

証明

$$\left|a\right>\left|b\right>\rightarrow C_{12}\rightarrow \left|a\right>\left|b\oplus a\right>$$

$$\left|a\right>\left|b\oplus a\right> \rightarrow C_{21}\rightarrow \left|a\oplus(b\oplus a)\right>\left|b\oplus a\right> = \left|b\right>\left|b\oplus a\right>$$

$$\left|b\right>\left|b\oplus a\right> \rightarrow C_{12}\rightarrow \left|b\right>\left|(b\oplus a)\oplus b\right> = \left|b \right>\left|a \right>$$

\left| a\right>\left|b \right>\left| b \right>\left| a\right> に交換することができた。

2. 3量子ビットのCNOTを、4つのCNOTに変換する

f:id:msyksphinz:20180218144559p:plain
図. 3つの量子ビットのCNOTを変換する。

証明

$$\left|a\right>\left|b\right>\left|c\right>\rightarrow C_{12}\rightarrow \left|a\right>\left|(b\oplus a)\right>\left|c\right>$$

$$\left|a\right>\left|(b\oplus a)\right>\left|c\right>\rightarrow C_{23}\rightarrow \left|a\right>\left|(b\oplus a)\right>\left|c\oplus (b\oplus a)\right>$$

$$\left|a\right>\left|(b\oplus a)\right>\left|c\oplus (b\oplus a)\right>\rightarrow C_{12}\rightarrow \left|a\right>\left|(b\oplus a)\oplus a\right>\left|c\oplus (b\oplus a)\right> = \left|a\right>\left|b\right>\left|c\oplus (b\oplus a)\right> $$

$$\left|a\right>\left|b\right>\left|c\oplus (b\oplus a)\right> \rightarrow C_{23}\rightarrow \left|a\right>\left|b\right>\left|c\oplus (b\oplus a)\oplus b\right> = \left|a\right>\left|b\right>\left|c\oplus a\right>$$

これで、ac の状態のCNOTをとることができた。

関連記事

Freechips Project Rocket-Core と SiFive Freedom E300 Everywhere Rocket-Core のブートシーケンス

RISC-Vの実装であるRocket-Chipは、RISC-Vの最新使用に追従しているため最初に見るべきデザインとしてはよくできているが、

  • Chiselで記述されており(初心者には)可読性が低い。
  • 外部のSoCプラットフォームについて情報がない

ことから、なかなかオリジナルのSoCをくみ上げるためのデザインとしてポーティングしにくい。

とはいえ、TileLinkは単なるバスのはずだし、クロックとリセットさえ突っ込めばとりあえずフェッチリクエストが上がってくるはずだ。 そのあたりを解析して、Rocket-Chipを利用したオリジナルSoCプラットフォーム構築のための情報収集を行っていこう。

f:id:msyksphinz:20180211025506p:plain
図. とりあえず実現したいRISC-V SoCブロック。まずはQSPIを取り除いて、だれでもアクセスできるTestRAMを接続したい。

SiFive の Freedom SoCとFreechips ProjectのRocket Chipのブート時の挙動を解析しているのだが、どうも理解できないところが多い。

FreeChips ProjectのRocket Coreが生成しているログ

FreeChips ProjectがRocket CoreのVerilog記述だけを抜き出して、TestBenchを被せてRTLシミュレーションを実行してみた。 最初の20命令を抜き出して、どのような命令を実行しているのかをチェックする。

C0:         42 [1] pc=[0000000800] W[r 0=0000000000000804][1] R[r 0=0000000000000000] R[r12=d72473afd72473af] inst=[00c0006f] j       pc + 0xc
C0:         44 [1] pc=[000000080c] W[r 0=0000000000000004][0] R[r 0=0000000000000000] R[r31=d72473afd72473af] inst=[0ff0000f] fence
C0:         45 [1] pc=[0000000810] W[r 0=515059a2515059a2][1] R[r 8=9178322291783222] R[r18=d72473afd72473af] inst=[7b241073] csrw    dscratch, s0
C0:         50 [1] pc=[0000000814] W[r 8=0000000000000000][1] R[r 0=0000000000000000] R[r20=d72473afd72473af] inst=[f1402473] csrr    s0, mhartid
C0:         68 [1] pc=[0000000818] W[r 0=0000000000000100][0] R[r 0=0000000000000000] R[r 8=0000000000000000] inst=[10802023] sw      s0, 256(zero)
C0:         79 [1] pc=[000000081c] W[r 0=0000000000000400][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C0:         86 [0] pc=[000000081c] W[r 8=0000000000000000][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C0:         89 [1] pc=[0000000820] W[r 8=0000000000000000][1] R[r 8=0000000000000000] R[r 1=0000000000000003] inst=[00147413] andi    s0, s0, 1
C0:         90 [1] pc=[0000000824] W[r 0=0000000000000000][0] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[02041063] bnez    s0, pc + 32
C0:         94 [1] pc=[0000000828] W[r 8=0000000000000000][1] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402473] csrr    s0, mhartid
C0:         97 [1] pc=[000000082c] W[r 0=0000000000000400][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C0:        104 [0] pc=[000000082c] W[r 8=0000000000000000][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C0:        107 [1] pc=[0000000830] W[r 8=0000000000000000][1] R[r 8=0000000000000000] R[r 2=0000000000000003] inst=[00247413] andi    s0, s0, 2
C0:        108 [1] pc=[0000000834] W[r 0=0000000000000000][0] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[fc0418e3] bnez    s0, pc - 48
C0:        112 [1] pc=[0000000838] W[r 0=000000000000083c][1] R[r31=0000000000000003] R[r29=0000000000000003] inst=[fddff06f] j       pc - 0x24
C0:        114 [1] pc=[0000000814] W[r 8=0000000000000000][1] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402473] csrr    s0, mhartid
C0:        117 [1] pc=[0000000818] W[r 0=0000000000000100][0] R[r 0=0000000000000000] R[r 8=0000000000000000] inst=[10802023] sw      s0, 256(zero)
C0:        128 [1] pc=[000000081c] W[r 0=0000000000000400][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C0:        135 [0] pc=[000000081c] W[r 8=0000000000000000][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C0:        138 [1] pc=[0000000820] W[r 8=0000000000000000][1] R[r 8=0000000000000000] R[r 1=0000000000000003] inst=[00147413] andi    s0, s0, 1

Freedom E300 Everywhere の Rocket Coreが生成しているログ

一方で、Freedom E300 Everywhere の Verilog記述だけを抜き出して、TestBenchを被せてRTLシミュレーションを実行してみた。 最初の命令群を抜き出して、どのような命令を実行しているのかチェックする。

C0:         31 [1] pc=[00010000] W[r10=00000000][1] R[r 0=00000000] R[r20=e4a715cb] inst=[f1402573] csrr    a0, mhartid
C0:         34 [1] pc=[00010004] W[r10=20000000][1] R[r 0=00000000] R[r 0=00000000] inst=[20000537] lui     a0, 0x20000
C0:        260 [1] pc=[00010008] W[r 0=20000020][1] R[r10=20000000] R[r 0=00000000] inst=[02052283] lw      t0, 32(a0)
C0:        261 [1] pc=[0001000c] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        262 [1] pc=[00010010] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        263 [1] pc=[00010014] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        264 [1] pc=[00010018] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        265 [1] pc=[0001001c] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        266 [1] pc=[00010020] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        267 [1] pc=[00010024] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        268 [0] pc=[00010028] W[r 5=00c6cf01][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        273 [1] pc=[00010028] W[r 0=00000000][1] R[r 0=00000000] R[r 0=00000000] inst=[00000013] nop
C0:        274 [1] pc=[0001002c] W[r 5=deadc000][1] R[r27=20000003] R[r10=e4a715cb] inst=[deadc2b7] lui     t0, 0xdeadc
C0:        275 [1] pc=[00010030] W[r 5=deadbeef][1] R[r 5=deadc000] R[r15=e4a715cb] inst=[eef28293] addi    t0, t0, -273
C0:        276 [1] pc=[00010034] W[r 0=20000000][0] R[r10=20000000] R[r 5=deadbeef] inst=[00552023] sw      t0, 0(a0)
C0:        287 [1] pc=[00010038] W[r 0=20000004][0] R[r10=20000000] R[r 5=deadbeef] inst=[00552223] sw      t0, 4(a0)
C0:        298 [1] pc=[0001003c] W[r 0=20000008][0] R[r10=20000000] R[r 5=deadbeef] inst=[00552423] sw      t0, 8(a0)
C0:        319 [1] pc=[00010040] W[r11=00010040][1] R[r 0=00000000] R[r 0=00000000] inst=[00000597] auipc   a1, 0x0
C0:        320 [1] pc=[00010044] W[r11=00010050][1] R[r11=00010040] R[r16=deadbeef] inst=[01058593] addi    a1, a1, 16
C0:        321 [1] pc=[00010048] W[r 5=40000000][1] R[r 0=00000000] R[r 0=00000000] inst=[400002b7] lui     t0, 0x40000
C0:        322 [1] pc=[0001004c] W[r 0=00010050][1] R[r 5=40000000] R[r 0=00000000] inst=[00028067] jr      t0

根本的に異なるのは、ブートアドレスが

  • Freechips Project Rocket-Core : PC=0x00000800 (デバッグモジュール)
  • SiFive Freedom E300 Everywhere : PC=0x00010000 (BootROM)

となっており、同じコアを使っているのになぜかスタートアドレスが異なるという事態になっている。

そもそも、何故このような違いが発生するのだろうか?

Freechips Project Rocket-Core のリセット後の挙動

Freechips Project の Rocket Coreは、リセット直後にTileLinkからフェッチが入っており、 0x10040 という不思議なアドレスからフェッチが入っている。

f:id:msyksphinz:20180217224105p:plain
図. Freechips Project Rocket Coreの最初のブートの様子。0x10040という不思議なアドレスからBootROMへの命令フェッチが入っている。

何故だろうと解析をしていったのだが、これはどうもBootROMのhangアドレスによって決められているようだ。やはり、最初にBootROMへのフェッチが入っているのは間違いなさそう。

/** Size, location and contents of the boot rom. */
case class BootROMParams(
  address: BigInt = 0x10000,
  size: Int = 0x10000,
  hang: BigInt = 0x10040,
  contentFileName: String)
case object BootROMParams extends Field[BootROMParams]
0000000000010040 <_hang>:
   10040:       f1402573                csrr    a0,mhartid
   10044:       00000597                auipc   a1,0x0
   10048:       03c58593                addi    a1,a1,60 # 10080 <_dtb>
   1004c:       10500073                wfi
   10050:       bff5                    j       1004c <_hang+0xc>

f:id:msyksphinz:20180217230714p:plain

ここで、WFI(Wait for Interrupt) が実行され、Interrupt待ちの状態になっているが、Coreに対してio_interrupts_debug信号が既にAssertになっている。 これにより、直ぐにDebug割り込みが入り、0x0000_0800にジャンプしているという訳だ。

一方で、SiFive Freedom E300 EverywhereではデフォルトでDebug InterruptがAssertされていない。これにより、デバッグコードにはジャンプしない。

f:id:msyksphinz:20180217230846p:plain

8th RISC-V Workshopはスペイン・バルセロナで開催・発表の募集が開始されました

第8回のRISC-V WorkshopのCall for Paperのアナウンスがありました。

  • 日付 : 2018年5月7日 - 10日
  • 場所 : バルセロナ・スーパーコンピューティング・センター (スペイン・バルセロナ)
  • 発表形式 : 25分または12分

興味のある人は応募してみてはどうでしょう。

また、ISCA 2018と併催予定のCARRV 2018も同時に募集が開始されています。

msyksphinz.hatenablog.com

関連記事