FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

ゼロから作るDeep Learning ③ のPython実装をRubyで作り直してみる(ステップ7/ステップ8)

ゼロから作るDeep Learning ❸ ―フレームワーク編

ゼロから作るDeep Learning ❸ ―フレームワーク編

  • 作者:斎藤 康毅
  • 発売日: 2020/04/20
  • メディア: 単行本(ソフトカバー)

ゼロから作るDeep Learning ③を買った。DezeroのPython実装をRubyに移植する形で独自に勉強している。次はステップ7とステップ8。

バックプロパゲーションを自動化するために、VariableおよびFunctionに変更を加える。Variableには、変数自分自身を作成したFunctionを覚えるためのメンバ変数を追加し、Functionには自分の生成した変数を記録する。

  • step07.rb
class Variable
  def initialize(data)
    @data = data
    @grad = nil
    @creator = nil
  end
  def set_creator(func)
    @creator = func
  end

  def backward()
    f = @creator
    if f != nil then
      x = f.input
      x.grad = f.backward(@grad)
      x.backward()
    end
  end

  attr_accessor :data, :grad, :creator
end
class Square < Function
  def forward(x)
    return x.map{|i| i ** 2}
  end
  def backward(gy)
    x = @input.data
    gx = x.zip(gy).map{|i0, i1| i0 * i1 * 2.0}
    return gx
  end
end

ポイントとなるのはVariablebackward()において、さらにbackward()を呼び出すことで変数から変数へと逆方向にジャンプしていくことだ。これを繰り返して最終的な始点に計算を向けていく。

A = Square.new()
B = Exp.new()
C = Square.new()

x = Variable.new([0.5])
a = A.call(x)
b = B.call(a)
y = C.call(b)

y.grad = [1.0]
y.backward()
puts(x.grad)
3.297442541400256
  • ステップ8:backward()再帰をループに置き換える

ステップ7のbackward()の実装では、バックプロパゲーションを実現するのに再帰的にbackward()を呼び出した。これでも良いのだが今後の拡張性と速度向上を見据えてループの実装に置き換える。

これは簡単で、いわゆるツリーの探索方式と考えて良い。さらに今回の場合はDFSだろうがBFSだろうがノード自身が前の値を覚えているためどっちを使っても良い。従ってキューを作ってループに変更しても特に問題ない。

  • step08.rb
class Variable
  def initialize(data)
    @data = data
    @grad = nil
    @creator = nil
  end
  def set_creator(func)
    @creator = func
  end

  def backward()
    funcs = [@creator]
    while funcs != [] do
      f = funcs.pop
      x = f.input
      y = f.output
      x.grad = f.backward(y.grad)
      if x.creator != nil then
        funcs.push(x.creator)
      end
    end
  end

  attr_accessor :data, :grad, :creator
end

同様にテストを実行し、同じ結果が得られた。

A = Square.new()
B = Exp.new()
C = Square.new()

x = Variable.new([0.5])
a = A.call(x)
b = B.call(a)
y = C.call(b)

y.grad = [1.0]
y.backward()
puts(x.grad)
3.297442541400256

RISC-V Vector Extension v0.9 が Readyになっているようなので要点をまとめる

RISC-V Vector ExtensionのGitHubリポジトリに静かにv0.9のタグが打たれた。 v0.8とv0.9の間にどれだけの差分があるか、調べておかなければならない。

  • v0.8のタグの付いたRevision

github.com

  • v0.9のタグの付いたRevision

github.com

という訳でGitリポジトリの差分を取ってざっくりと変更点をチェックしていく。

git diff -w 0.8..0.9 ./v-spec.adoc

変更点

  • 要素シフトのための命令追加。これまでslideupslidedownの亜種は整数レジスタとの間のみ通信する命令であったが、vfslide1up, vfslide1down命令が追加された。
vfslide1up.vf   vd, vs2, rs1, vm        # vd[0] = f[rs1], vd[i+1] = vs2[i]
vfslide1down.vf vd, vs2, rs1, vm        # vd[i] = vs2[i+1], vd[vl-1] = f[rs1]
  • 整数拡張命令。SEW/2のサイズからSEWに拡張する命令が新規追加された。
  • 整数と浮動小数点の変換命令のバリエーション追加。fcsrを無視してC言語/Javaでデフォルトとなるtruncateの丸めモードを使用する命令の追加。
# Convert float to unsigned integer, truncating.
vfcvt.rtz.xu.f.v  vd, vs2, vm
# Convert float to signed integer, truncating.
vfcvt.rtz.x.f.v   vd, vs2, vm

# Convert float to double-width unsigned integer, truncating.
vfwcvt.rtz.xu.f.v vd, vs2, vm   
# Convert float to double-width signed integer, truncating.
vfwcvt.rtz.x.f.v  vd, vs2, vm

# Convert double-width float to unsigned integer, truncating.
vfncvt.rtz.xu.f.w vd, vs2, vm
# Convert double-width float to signed integer, truncating.
vfncvt.rtz.x.f.w  vd, vs2, vm
  • ロードストア命令のバリエーション変更。vlw.v, vlh.v, vlb.v, vle.vなどのバリエーションだったが、取り扱うビット数によってバリエーションが変わる方式に変更。まあ確かに、この方が分かりやすい。
Unit-stride Load Strided Load Indexed Load Unit-stride Store Strided Store Indexed- Ordered Store Indexed- Unordred Store
8-bit vle8.v vlse8.v vlxei8.v vse8.v vsse8.v vsxei8.v vsuxei8.v
16-bit vle16.v vlse16.v vlxei16.v vse16.v vsse16.v vsxei16.v vsuxei16.v
32-bit vle32.v vlse32.v vlxei32.v vse32.v vsse32.v vsxei32.v vsuxei32.v
64-bit vle64.v vlse64.v vlxei64.v vse64.v vsse64.v vsxei64.v vsuxei64.v
128-bit vle128.v vlse128.v vlxei128.v vse128.v vsse128.v vsxei128.v vsuxei128.v
256-bit vle256.v vlse256.v vlxei256.v vse256.v vsse256.v vsxei256.v vsuxei256.v
512-bit vle512.v vlse512.v vlxei512.v vse512.v vsse512.v vsxei512.v vsuxei512.v
1024-bit vle1024.v vlse1024.v vlxei1024.v vse1024.v vsse1024.v vsxei1024.v vsuxei1024.v
  • アトミック命令も同様。ビットサイズに応じてバリエーションが変更された。
8-bit 16-bit 32-bit 64-bit
Amoswap vamoswapei8.v vamoswapei16.v vamoswapei32.v vamoswapei64.v
Amoadd vamoaddei8.v vamoaddei16.v vamoaddei32.v vamoaddei64.v
Amoxor vamoxorei8.v vamoxorei16.v vamoxorei32.v vamoxorei64.v
Amoand vamoandei8.v vamoandei16.v vamoandei32.v vamoandei64.v
Amoor vamoorei8.v vamoorei16.v vamoorei32.v vamoorei64.v
Amomin vamominei8.v vamominei16.v vamominei32.v vamominei64.v
Amomax vamomaxei8.v vamomaxei16.v vamomaxei32.v vamomaxei64.v
Amominu vamominuei8.v vamominuei16.v vamominuei32.v vamominuei64.v
Amomaxu vamomaxuei8.v vamomaxuei16.v vamomaxuei32.v vamomaxuei64.v

このvma, vtaの扱い方だが、ベクトルレジスタ内のinactive要素とtail要素をどのように取り扱うかということを規定している。

f:id:msyksphinz:20200519011026p:plain

vtype.vta vtype.vma Tail Elements Inactive Elements
0 0 undisturbed undisturbed
0 1 undisturbed agnostic
1 0 agnostic undisturbed
1 1 agnostic agnostic

Undisturbedの場合は、その要素は前の値から変更しない。一方でAgnosticの場合は、前の値から変更しないか、All-1を書き込むかのどちらかとなる。これは実装に依存する。また、vma, vtaのビットフィールド自体もRead Onlyなので、OoOのポリシに従って便利な方を選べばよい。

ゼロから作るDeep Learning ③ のPython実装をRubyで作り直してみる(ステップ5/ステップ6)

ゼロから作るDeep Learning ❸ ―フレームワーク編

ゼロから作るDeep Learning ❸ ―フレームワーク編

  • 作者:斎藤 康毅
  • 発売日: 2020/04/20
  • メディア: 単行本(ソフトカバー)

ゼロから作るDeep Learning ③を買った。DezeroのPython実装をRubyに移植する形で独自に勉強している。次はステップ5とステップ6。

この章は理論的な部分なので実装は省略。理論の部分は説明をしっかり読むこと。

ステップ5のバックプロパゲーションを実装する。Rubyでの実装に置き換える。まずgradメンバ変数を作ってそこに勾配を確認する。

  • step06.rb
class Variable
  def initialize(data)
    @data = data
    @grad = nil
  end

  attr_accessor :data, :grad
end

SquareExpバックプロパゲーションの機能を実装していく。 y = \text{Square}(x) = x^{2}y = \text{Exp}(x) = e^{x}微分を実装するので、  \dfrac{dy}{dx} = \dfrac{d\text{Square}(x)}{dx} = 2x,  \dfrac{dy}{dx} = \dfrac{d\text{Exp}(x)}{dx} = e^{x} となる。backward()は以下のような実装となった。

class Square < Function
...
  def backward(gy)
    x = @input.data
    gx = x.zip(gy).map{|i0, i1| i0 * i1 * 2.0}
    return gx
  end
class Exp < Function
...
  def backward(gy)
    x = @input.data
    gx = x.zip(gy).map{|i0, i1| Math.exp(i0) * i1}
    return gx
  end

これにより順方向の伝搬を行った後の逆伝搬は以下のようにテストできる。

A = Square.new()
B = Exp.new()
C = Square.new()

x = Variable.new([0.5])
a = A.call(x)
b = B.call(a)
y = C.call(b)

puts(y.data)

y.grad = [1.0]
b.grad = C.backward(y.grad)
a.grad = B.backward(b.grad)
x.grad = A.backward(a.grad)

puts(x.grad)
1.648721270700128
3.297442541400256

ステップ4のnumerical_diff()と同様の結果が得られた。

コンパイラのための新しい中間表現 MLIR の論文を読む

LLVM IRについて調べていると、最近よくMLIRという言葉を目にするようになった。MLIRは"Multi-Level Intermediate Representation"の略称であって決して"Machine Learning"ではないのだが、LLVM IRを置き換えるために開発されている新しい中間表現形式である。

LLVM IRももちろん柔軟性のある中間表現形式であるが、より柔軟かつ幅広いDSLの中間表現を受け入れるために開発されているものだ。Googleに在籍していたLLVM CreaterのChris Lattnerによって開発されたものである。ちなみにChris Lattnerは本論文が発行されたときにはすでにSiFiveに移っている。

https://arxiv.org/abs/2002.11054

この論文を読みながら、MLIRについて理解を進めていくことにしよう。


MLIRの動機

LLVM IRを代表するような中間言語を使用するシステムは大部分においてうまく動作をしているが、より抽象度の高いプログラミング言語を用いる場合はLLVM IRだけでは上手く行かないケースが散見される。例えば、Swift/Rust/Juliaなどは直接LLVM IRには変換されず、複数段階のIR表現形式を使用して最終的にLLVM IRに変換されている。

f:id:msyksphinz:20200517124959p:plain

論文中では、既存のIRについて以下のような表現が使用されている。

  • LLVM中間表現(IR)は大まかには「ベクトルを持つC」
  • JVMは「ガベージコレクタを持つオブジェク ト指向型システム」の抽象化

C++コードなどの非常に抽象化されたソースコードの解析はLLVM IRでは非常に難しく、これを解決するのがMLIRと言うことになっている。

  1. スタティック・シングル・アサインメント(SSA)ベースのIRデータ構造の標準化
  2. 宣言的システムによりdialects(方言)を数多くサポートする
  3. 共通のインフラストラクチャの提供

MLIRの基本

以下がMLIRの基本的な考え方となっている。

  • すべてをカスタマイズできるようにする。これにより将来の問題にも適用できるようにする。
  • SSAとリージョンを導入する。SSAはともかく、リージョンの概念を導入して入れ子による制御フローやデータフローをサポートする。LLVM IRではリージョンによる入れ子の表現はあまり見たことが無く(というかほとんどLLVM IRはアセンブリに近しい気がする)、リージョンの概念を導入することで問題を抽象化する。
  • プログレッシブな変換。既にLLVM IRについても、LLVM IRから始まりSelectionDAG、MachineInstr、MCInstへと段階的に変換が行われているが、これをより柔軟化する。
  • 高レベルのセマンティクスを維持する。これは一度表現形式を低レベルに変換してしまうと効率的な解析が難しくなることに起因する。可能な限り高レベルの表現形式を維持する。
  • IR検証。IRを検証可能な表現形式で維持する。
  • 宣言的な書き替えパタン。つまり変換パタンなどの定義を非常に簡単に定義できなければならない。
  • ソース位置の追跡とトレーサビリティ。つまりシステム内で変換結果を見ても元のプログラミング言語との対応が取れやすくしなければならない。

MLIRの詳細

これは例を見ながら説明していく。以下はMLIRの例である。

f:id:msyksphinz:20200517130702p:plain

まず、MLIRの一番大きな単位は命令「オペレーション」から成る。ここではaffine.forという操作が一番大きな単位となっている。affine.forは引数として%arg0を持っている。このオペレーションはリージョンを持っており、リージョンの中にはブロックが定義されている。

ブロックの中には複数のオペレーションで構成されている。この例だと、affine.load, std.mulf, std.addfなどだ。

f:id:msyksphinz:20200517130721p:plain

各オペレーションには属性を付けることができる。この例だとこの属性が何を意味しているのかが良く読み取れないが、データや定数などの情報を属性として持つことができる。

MLIRはカスタマイズした型と標準型を使用することができる。memrefはメモリ参照のための型、f32は単精度浮動小数点のための型だ。これらを使ってMLIRを構築していく。

f:id:msyksphinz:20200517130748p:plain

ゼロから作るDeep Learning ③ のPython実装をRubyで作り直してみる(ステップ3/ステップ4)

ゼロから作るDeep Learning ❸ ―フレームワーク編

ゼロから作るDeep Learning ❸ ―フレームワーク編

  • 作者:斎藤 康毅
  • 発売日: 2020/04/20
  • メディア: 単行本(ソフトカバー)

ゼロから作るDeep Learning ③を買った。DezeroのPython実装をRubyに移植する形で独自に勉強している。次はステップ3とステップ4。

  • ステップ3:関数を連結する。

関数を連結するために、新しくExpクラスを作った。

class Exp < Function
  def forward(x)
    return x.map{|i| Math.exp(i)}
  end

さらにcallが呼ばれたときはforwardが実行されるようにメソッドを変更している。

class Function
   def call(input)
     x = input.data
     y = forward(x)
     output = Variable.new(y)
     return output
   end
    ...

これを使ってテストをしてみる。SquareExpを使って連結して関数を作った。

 A = Square.new()
 B = Exp.new()
 C = Square.new()

 x = Variable.new([0.5])
 a = A.call(x)
 b = B.call(a)
 y = C.call(b)
 puts(y.data)
1.648721270700128

上手く行った。

  • ステップ4:合成微分

次にnumerial_diff関数を実装した。単純に差分を取って数値微分を行う関数だ。

 def numerical_diff(f, x, eps=1e-4)
   x0 = Variable.new(x.data.map{|i| i - eps})
   x1 = Variable.new(x.data.map{|i| i + eps})
   y0 = f.call(x0)
   y1 = f.call(x1)
   return (y1.data.zip(y0.data).map{|i1, i0| i1 - i0}).map{|i| i / (2 * eps)}
 end

結構ややこしいことになっている。配列を受け取ることを前提に作っているので、各配列にmapを使ったりzipを使ったりしないといけないのがつらいが、どうにか動くようになった。

 f = Square.new()
 x = Variable.new([2.0])
 dy = numerical_diff(f, x)

 puts(dy)
4.000000000004

さらにこれらの関数オブジェクトを合成するためのf_funcを作ったのだが、これをnumerial_diffに渡すためにはどうすればよいのかはたと困ってしまった。

 def f_func(x)
   a = Square.new()
   b = Exp.new()
   c = Square.new()
   return c.call(b.call(a.call(x)))
 end

しかしRubyにはmethodがある。関数をメソッドとしてオブジェクト化し引数としてnumerial_diffに渡してしまうという作戦だ。

 def f_func(x)
   a = Square.new()
   b = Exp.new()
   c = Square.new()
   return c.call(b.call(a.call(x)))
 end
 x = Variable.new([0.5])
 dy = numerical_diff(method(:f_func), x)

 puts dy
3.2974426293330694

上手く行ったようだ。

ゼロから作るDeep Learning ③ のPython実装をRubyで作り直してみる(ステップ1/ステップ2)

ゼロから作るDeep Learning ❸ ―フレームワーク編

ゼロから作るDeep Learning ❸ ―フレームワーク編

  • 作者:斎藤 康毅
  • 発売日: 2020/04/20
  • メディア: 単行本(ソフトカバー)

ゼロから作るDeep Learning ③を買った。昨年のQiitaでも自分でビルドシステムを作ってみた通り、こういうプラットフォームを作る話は大好きだ。さっそくステップ1から読み始めているが、ステップ60まであって非常に分厚いし、なかなか一気読みはつらい。

しかも実装が多分に含まれているので、これを理解しなければどうにも読破した気にはならない。かといってPythonを写経してもあまり面白くないので、似たような系統のスクリプトRubyを使ってプラットフォームを再実装することはできないだろうかと思いやってみることにした。

本書はPythonを使ってDezeroというオリジナルのディープラーニングフレームワークを実装している。これをRubyに移していくという訳だ。と言ってもPythonの大量のライブラリにはどうにも勝てそうにない気がするのだが、できるところまで頑張っていこう。

  • ステップ1:変数クラスの宣言

Dezeroの最初のステップではまず変数を格納するVariableクラスを作っている。これはRubyでも簡単だ。単純にVariableクラスを作って値を格納するためのdataメンバを追加した。

  • step01.rb
#!/usr/bin/env ruby

class Variable
  def initialize(data)
    @data = data
  end

  attr_accessor :data
end

テストを作って確認する。データを格納してそれを表示する。

data = [1.0]
x = Variable.new(data)
puts(x.data)

x.data = [2.0]
puts(x.data)
1.0
2.0

できた!まずは準備完了だ。

  • ステップ2:関数クラスの宣言

変数のためのVariableクラスができたら、次は関数のためのFunctionクラスを作る。Functionクラスをインスタンス化してできたオブジェクトは、A = Function()とかやるとA(x)でその関数を呼び出せるようになっているのだが、Rubyで同じことをする仕組みが分からない。仕方が無いのでcallメソッドを作って代用した。

  • step02.rb
class Function
  def call(input)
    x = input.data
    y = x.map{|i| i ** 2}
    output = Variable.new(y)
    return output
  end

  def forward(x)
    raise NotImplementedError
  end
end

上記の関数だと、A = Function.new()した後にA.call([2.0])として呼び出すことで関数を実行できる。

FunctionをオーバーライドしてSquareを作った。

class Square < Function
  def forward(x)
    return x.map{|i| i ** 2}
  end
end

これで関数呼び出しを行う。テストを作って実行した。

x = Variable.new([10])
f = Function.new()
y = f.call(x)
puts(y.class)
puts(y.data)

f2 = Square.new()
y2 = f2.call(x)
puts(y2.class)
puts(y2.data)
Variable
100
Variable
100

上手く行っている。

RISC-V Getting Started GuideのLinuxビルドを試す(4. Failする要因を確認)

f:id:msyksphinz:20200503214338p:plain

前回のシミュレーションのトレース情報を確認していると、どうも途中から猛烈に例外に飛んでいることが分かった。

        <FunctionCall 69225526 mcall_console_putchar (0x0000000080001b58)>
          <FunctionCall 69225538 htif_console_putchar (0x000000008000232a)>
          <Return 69225571 htif_console_putchar>
        <Return 69225576 mcall_console_putchar>
      <Return 69225582 mcall_trap>
      <FunctionCall 69226995 illegal_insn_trap (0x0000000080002c38)>
      <Return 69227070 illegal_insn_trap>
      <FunctionCall 69227151 mcall_trap (0x0000000080001d34)>
      <Return 69227184 mcall_trap>
      <FunctionCall 69278356 mcall_trap (0x0000000080001d34)>

場所を確認してみると、どうもCSRにアクセスしようとして当該レジスタが存在せず例外にジャンプしているらしい。

  69226945:S:Sv39:ffffffe00007949a:P00008027949a:    9702:c.jalr     x14                  :r14=>ffffffe00024b890 r01<=ffffffe00007949c pc<=ffffffe00024b890
  69226946:S:Sv39:ffffffe00024b890:P00008044b890:    1141:c.addi     x02,0x30             :r02=>ffffffe000435cf0 r02<=ffffffe000435ce0
  69226947:S:Sv39:ffffffe00024b892:P00008044b892:    e422:c.sdsp     x08,0x08             :r08=>ffffffe000435d20 r02=>ffffffe000435ce0 (0000000080635ce8)<=ffffffe000435d20
  69226948:S:Sv39:ffffffe00024b894:P00008044b894:    0800:c.addi4spn x08,0x40             :r02=>ffffffe000435ce0 r08<=ffffffe000435cf0
  69226949:S:Sv39:ffffffe00024b896:P00008044b896:02000793:addi       x15,x00,0x020        :r15<=0000000000000020
  69226950:S:Sv39:ffffffe00024b89a:P00008044b89a:1047a073:csrrs      x00,0x104,x15        :r15=>0000000000000020 sie=>0000000000000222 sie<=0000000000000222
<Error: CSR Read Address c01 is invalid.>
<Info: VAddr = 0xffffffe00024b89e PTEAddr = 0x00000000806eac00 : PPTE = 0x000000002019a801>
<Info: VAddr = 0xffffffe00024b89e PTEAddr = 0x000000008066a008 : PPTE = 0x00000000201000cf>
<Info: TLB[75] <= 0x000ffffffe00024b(0x000000008044b000)>
<Info: Converted Virtual Address is = 0x000000008044b89e>
<Info: GenerateException Code=2, TVAL=0000000000000000 PC=ffffffe00024b89e,000000008044b89e>
<Info: Exception. ChangeMode from SuprevisorMode to MachineMode>
<Info: Set Program Counter = 0x0000000080000004>
  69226951:S:Sv39:ffffffe00024b89e:P00008044b89e:c01027f3:csrrs      x15,0xc01,x00        :medeleg=>000000000000b109 mepc<=ffffffe00024b89e mcause<=0000000000000002 mtval<=0000000000000000 mtvec=>0000000080000004 mstatus=>0000000a00000180 mstatus<=0000000a00000900 pc<=0000000080000004 mip<=0000000000000000
  69226952:M:Sv39:0000000080000004:P000080000004:34011173:csrrw      x02,0x340,x02        :mscratch=>000000008000bec0 r02=>ffffffe000435ce0 mscratch<=ffffffe000435ce0 r02<=000000008000bec0
  69226953:M:Sv39:0000000080000008:P000080000008:1a010863:beq        x02,x00,0x1b0        :r02=>000000008000bec0
  69226954:M:Sv39:000000008000000c:P00008000000c:04a13823:sd         x10,0x050(x02)       :r02=>000000008000bec0 r10=>0000000000009c40 (000000008000bf10)<=0000000000009c40
  69226955:M:Sv39:0000000080000010:P000080000010:04b13c23:sd         x11,0x058(x02)       :r02=>000000008000bec0 r11=>ffffffe07a1884c0 (000000008000bf18)<=ffffffe07a1884c0
  69226956:M:Sv39:0000000080000014:P000080000014:342025f3:csrrs      x11,0x342,x00        :mcause=>0000000000000002 r11<=0000000000000002
  69226957:M:Sv39:0000000080000018:P000080000018:0805d263:bge        x11,x00,0x084        :r11=>0000000000000002 pc<=000000008000009c
  69226958:M:Sv39:000000008000009c:P00008000009c:00113423:sd         x01,0x008(x02)       :r02=>000000008000bec0 r01=>ffffffe00007949c (000000008000bec8)<=ffffffe00007949c

0xc01はTIMERレジスタだ。そういえばTIMERレジスタは一時的に無効にしていたんだっけ。

  • swimmer_riscv/build/riscv_spr_table.rb
 $sysreg_table.push(Array[0xC00,  'URO',     'cycle'        , Array[Array[xlen-1, 0, 'cycle'         , 'RW', 0]]])
 # $sysreg_table.push(Array[0xC01,  'URO',     'time'         , Array[Array[xlen-1, 0, 'time'          , 'RW', 0]]])
 $sysreg_table.push(Array[0xC02,  'URO',     'instret'      , Array[Array[xlen-1, 0, 'instret'       , 'RW', 0]]])
 $sysreg_table.push(Array[0xC03,  'URO',     'hpmcounter3'  , Array[Array[xlen-1, 0, 'hpmcounter3'   , 'RW', 0]]])

コメントアウトを解除し、実装の部分もコメントアウトを解除した。

  • swimmer_riscv/src/riscv_sysreg_impl.cpp
 /*
 template <typename Xlen_t>
 CsrAccResult CsrEnv::Read_TIME (Xlen_t *data, PrivMode mode)
 {
   *data = time.time;
   return CsrAccResult::Normal;
 }
 */

 /*
 template <typename Xlen_t>
 CsrAccResult CsrEnv::Write_TIME (Xlen_t data, PrivMode mode)
 {
   m_pe_thread->DebugPrint ("<Error: CSR TIME is not writable>\n");
   // time.time = data;
   return CsrAccResult::PrivError;
 }
 */

これでリビルドして再度実行した。

[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 516615
[    0.000000] Kernel command line:
[    0.000000] Dentry cache hash table entries: 262144 (order: 9, 2097152 bytes)
[    0.000000] Inode-cache hash table entries: 131072 (order: 8, 1048576 bytes)
[    0.000000] Sorting __ex_table...
[    0.000000] Memory: 1992040K/2095104K available (3210K kernel code, 214K rwdata, 939K rodata, 148K init, 762K bss, 103064K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns

今度はこの部分で止まる。トレースを確認してみる。

...
      <Return 69220154 mcall_trap>
      <FunctionCall 69220239 mcall_trap (0x0000000080001d34)>
      <Return 69220314 mcall_trap>
      <FunctionCall 69220399 mcall_trap (0x0000000080001d34)>
      <Return 69220474 mcall_trap>
      <FunctionCall 69220559 mcall_trap (0x0000000080001d34)>
      <Return 69220634 mcall_trap>
      <FunctionCall 69220719 mcall_trap (0x0000000080001d34)>
      <Return 69220794 mcall_trap>
      <FunctionCall 69220879 mcall_trap (0x0000000080001d34)>
      <Return 69220954 mcall_trap>
      <FunctionCall 69221039 mcall_trap (0x0000000080001d34)>
      <Return 69221114 mcall_trap>
      <FunctionCall 69221199 mcall_trap (0x0000000080001d34)>
      <Return 69221274 mcall_trap>
      <FunctionCall 69221359 mcall_trap (0x0000000080001d34)>
      <Return 69221434 mcall_trap>
      <FunctionCall 69221519 mcall_trap (0x0000000080001d34)>
      <Return 69221594 mcall_trap>
      <FunctionCall 69221679 mcall_trap (0x0000000080001d34)>
      <Return 69221754 mcall_trap>
...

またしてもmcall_trapで止まってしまった。次の例外も確認していく。