FPGA開発日記

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

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

関連記事

量子プログラミング言語Q#の勉強 (3. Bell状態から量子の状態を測定するサンプルプログラム)

量子プログラミングの何たるかについて、MicrosoftのQ#プログラミング言語チュートリアルを読みながら進めている。 量子プログラミングについて少しわかってみたので、一番最初のサンプルプログラムを読み解いていきたい。

参考にしているのは、これまでと同様にQ#のチュートリアルサイトだ。今回は "Writing a Quantum Program" を読みながら、量子プログラミングの一番最初のプログラムを学んでいく。

docs.microsoft.com

ベル状態とは

これは正直まだ理解しきっていない。しかしベルといくらいだし、量子ビットの状態がまだ確定していない状態、つまり0でも1でもない状態であると考えることができるだろう。

Q#では、量子ビットを定義して、最初にそれを初期化する処理が入っている。下記のプログラムでは、量子ビットqubits[0]を値initialで初期化し、M()操作を使って量子ビットの値を測定している。

using (qubits = Qubit[1])
{
   for (test in 1..count)
   {
       Set (initial, qubits[0]);    // 量子ビットqubits[0]を、値 initialで初期化する

       let res = M (qubits[0]);   // Mは、量子ビットqubits[0]を測定する。これにより量子ビットqubits[0]の値は0 or 1に確定される。

当然、initial=0ならば測定される値は0になるはずだし、initial=1ならば測定される値は1になるはずだ。これは簡単。

Hadamard ゲートを使って量子もつれ状態を作る

次に、量子ビットにHadamardゲートを適用して、量子のもつれ状態を作る。これにより、量子ビット01かわからない状態になってしまう。 ちなみに、Hadamardゲートというのは、行列の形式で記述すると、

$$ H=\dfrac{1}{\sqrt{2}}\begin{bmatrix}1&&1\\1&&-1\end{bmatrix} $$

量子ビットの0/1の状態から、0/1の等しい重みへの写像となるので、これで量子ビットは 0/1のどちらかわからなくなる。 さて、どっちなんだろう?次に、この量子ビットともう一つの量子ビットをCNOTで制御してみる。

Set (initial, qubits[0]);
Set (Zero, qubits[1]);

H(qubits[0]);
CNOT(qubits[0],qubits[1]);
let res = M (qubits[0]);

qubits[0]は現在もつれ状態。qubits[1]=0で初期化されている。 CNOTは前回説明したとおりだ。"qubits[0]が1ならば、qubits[1]を反転する、qubits[0]が0ならば、qubits[0]はそのまま"というゲートのため、

  • qubits[0]=1 ならば、 qubits[1]=NOT(0)=1
  • qubits[0]=0 ならば、 qubits[1]=0

となり、必ずqubits[0]=qubits[1]となることが確認できる。

これがサンプルプログラムの最後の出力に反映されている。

using (qubits = Qubit[2])
{
    for (test in 1..count)
    {
        Set (initial, qubits[0]);
        Set (Zero, qubits[1]);

        H(qubits[0]);
        CNOT(qubits[0],qubits[1]);
        let res = M (qubits[0]);

        if (M (qubits[1]) == res) 
        {
            set agree = agree + 1;
        }

        // Count the number of ones we saw:
        if (res == One)
        {
            set numOnes = numOnes + 1;
        }
    }

出力結果は以下だ。注目すべきなのは以下の2点。

  • qubits[0]の状態は、Hゲートの作用により確率1/2で0か1のどちらかに転んでいる。
  • ただし、qubits[1]の状態は、CNOTの作用により必ずqubits[0]と同じ値に転がっている。つまり、agreeは常に真となっている。
Init:Zero 0s=499  1s=501  agree=1000
Init:One  0s=490  1s=510  agree=1000

なるほど、ここまでは理解できた。次に、Teleportのプログラム解読に取り組みたい。

関連記事