FPGA開発日記

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

FIRRTLに入門する (20. 多次元配列及び構造体に関する処理の検討(2))

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

前回、多次元配列をそのままChiselで扱うためにはどうすればよいか考えたが、さらにまだ考えないといけないことがある。さらに、以下のような構文について考えた。

circuit VecBundle2 :
  module VecBundle2 :
    input in: { a : { b : UInt<8>[2] } [3] }[4]
    input sel: UInt<2>
    output out : { b : UInt<8>[2] }
    out.b <= in[0].a[1].b

BundleTypeが多重構造になっているタイプだ。さらに、配列で覆いかぶせている。今回はとりあえずWSubIndexで固定値のインデックスを取っている。これを扱うためには、最終的に以下のようになってほしい。

circuit VecBundle2 :
  module VecBundle2 :
    input in_a_b : UInt<8>[2][3][4]
    input sel : UInt<2>
    output out_b : UInt<8>[2]

    out_b <= in_a_b[0][1]

このような、in[0].a[1].bをどのように変換するかという問題だが、これはSubFieldが登場するたびにそのメンバ変数の名前を覚えておき、それを順繰りに並べてin_a_b[0][1]とすれば良い。まあこれを作るのにもかなり苦労したけど。

  def lowerTypesExp(memDataTypeMap: MemDataTypeMap,
    info: Info, mname: String, subfield_name: String = "")(e: Expression): Expression = {
    e match {
      case e: WRef => e.copy(name = subfield_name)
...
      case e: WSubField => {
        kind(e) match {
          case InstanceKind =>
...
          case _ => {
            val new_subfield_name = subfield_name match {
              case "" => e.name
              case x => e.name + x
            }
            val exps = lowerTypesExp(memDataTypeMap, info, mname, new_subfield_name)(e.expr)
            exps match {
              case ex: WSubAccess => WSubAccess(WRef(loweredExpName(e), e.tpe, kind(e), flow(e)), ex.index, ex.tpe, flow(ex))
              case ex: WSubIndex  => ex
...

new_subfield_nameという変数を新たに定義した。これはSubFieldの名前が来るたびに順繰りに文字列として繋げていく。最終的には_a_bという文字列を作り、WRefの参照が来たときに、

      case e: WRef => e.copy(name = subfield_name)

で名前を更新する。そしてWSubIndexのときに変数名の最初にため込んだ値を埋め込む、という訳だ。

  def lowerTypesExp(memDataTypeMap: MemDataTypeMap,
...
      case (e: WSubIndex) => kind(e) match {
...
        case _ => {
          val exps = lowerTypesExp(memDataTypeMap, info, mname, subfield_name)(e.expr)
          exps match {
            case ex: WSubAccess => WSubIndex(WSubAccess(WRef(loweredExpName(e), e.tpe, kind(e), flow(e)), ex.index, ex.tpe, flow(ex)), e.value, e.tpe, flow(e))
            case ex: WSubIndex  => WSubIndex(WSubIndex(WRef(loweredExpName(e) + "_" + subfield_name, e.tpe, kind(e), flow(e)), ex.value, ex.tpe, flow(ex)), e.value, e.tpe, flow(e))

これで上記の回路の変換を試行した。

circuit VecBundle2 :
  module VecBundle2 :
    input in_a_b : UInt<8>[2][3][4]
    input sel : UInt<2>
    output out_b : UInt<8>[2]

    out_b <= in_a_b[0][1]

一応想定通りの形にはなったようだ。

FIRRTLに入門する (19. 多次元配列及び構造体に関する処理の検討)

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

前回、多次元配列をそのままChiselで扱うためにはどうすればよいか考えたが、さらにまだ考えないといけないことがある。これに、構造体が入ってきた場合はどうするか、ということだ。つまり、以下のようなコードを考える。

circuit VecBundle2 :
  module VecBundle2 :
    input in: { a : UInt<32>[3] }[4]
    input sel: UInt<2>
    output out : { a : UInt<32>[3] }
    out.a <= in[1].a

このようなコードの場合、まずはin: { a : UInt<32>[3] }[4]を扱う必要があるが、これを、

in_a : UInt<32>[3][4]

に置き換える必要がある。このために、create_expsを改造する。キモとしては、BundleTypeが来た場合、先に構造体の名前を出していって、構造体を分解していく。

  def create_exps(e: Expression): Seq[Expression] = e match {
    case ex: Mux =>
      val e1s = create_exps(ex.tval)
...
    case ex: WRef => {
      ex.tpe match {
        case (_: GroundType) => Seq(ex)
        case t: BundleType => {
          val ret = t.fields.flatMap(f => create_exps(WRef(loweredExpName(ex, f.name), f.tpe, ExpKind, flow(ex))))
          ret
        }

各構造体のメンバに対してcreate_expsを適用していくのだが、新しい名前はloweredExpNameで生成し、その型は直下の型とする。これがcreate_exps(WRef(loweredExpName(ex, f.name), f.tpe, ExpKind, flow(ex))の意味するところだ。

これにより、上記のコードはいかのように変換される。

circuit VecBundle2 :
  module VecBundle2 :
    input in_a : UInt<32>[3][4]
    input sel : UInt<2>
    output out_a : UInt<32>[3]

    skip
    out_a <= in_a[1]

WSubIndexでなくても、WSubAccessでも同様に変換できることが確認できた。

circuit VecBundle2 :
  module VecBundle2 :
    input in_a : UInt<32>[3][4]
    input sel : UInt<2>
    output out_a : UInt<32>[3]

    skip
    out_a <= in_a[sel]

FIRRTLのSystemVerilog変換までのフローを調査する。

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

FIRRTLの変換フローについて調査した。ソースコードを読みながら各Passが何を実行しているのかを確認していく。

実行方法としては、

./utils/bin/firrtl -td regress -i ./regress/simple_test.fir -X sverilog -ll trace 2>&1 | tee simple_test.log

として、出力されるログを見ながらPassを確認していく。

  • ChirrtlToHighFirrtl
    • CheckChirrtl
      • CheckHighFormLikeに実体が存在する。Circuit内のTopModuleを探す。
    • CInferTypes
      • src/main/scala/firrtl/passes/InferTypes.scala
      • 上位から順に伝搬できる型を下位に伝搬させる。
    • CInferMDir$
      • src/main/scala/firrtl/passes/Resolves.scala
      • ポートの方向を伝搬させる?
    • RemoveCHIRRTL
      • src/main/scala/firrtl/passes/RemoveCHIRRTL.scala
      • CHIRRTL と呼ばれるChiselのHigh Level Immediate Representationを変換する。
  • IRToWorkingIR
    • ToWorkingIR
      • src/main/scala/firrtl/passes/Passes.scala
      • IRのノードからFirrtlNodeの形式に変換する。
  • ResolveAndCheck
    • CheckHighForm$
      • src/main/scala/firrtl/passes/Checks.scala
      • ルール違反の制約を探す?
    • ResolveKinds$
      • src/main/scala/firrtl/passes/Resolves.scala
      • ポートの名前から型に対するテーブルを作っていく。
    • InferTypes
      • src/main/scala/firrtl/passes/InferTypes.scala
      • CInferTypeと同様だが、FirrtlNodeに対して処理を実行する。
    • CheckTypes
      • src/main/scala/firrtl/passes/Checks.scala
      • 各構文に対して、有効な型が使われているかをチェックする。例えば演算に対して有効な型のノードが適用されているか。
    • Uniquify
      • src/main/scala/firrtl/passes/Uniquify.scala
      • 変数名の変換により別の変数と衝突してしまうことを防ぐ。例えば、a[0]a_0に変換すると元々a_0だったものにぶつかってしまう。なのでこの場合はa__0などに変換する?
    • ResolveKinds
      • src/main/scala/firrtl/passes/Resolves.scala
      • ポートの名前から型に対するテーブルを作っていく。
    • InferTypes
      • `src/main/scala/firrtl/passes/InferTypes.scala``
      • `CInferTypeと同様だが、FirrtlNodeに対して処理を実行する。
    • ResolveFlows
      • src/main/scala/firrtl/passes/Resolves.scala
      • 配線同士のフローを構築する。例えばFlipなどは方向を逆転させて本来の方向へ戻す。
    • CheckFlows
      • src/main/scala/firrtl/passes/Checks.scala
      • ResolveFlowsの解決後のチェックを行う。
    • InferWidths
      • src/main/scala/firrtl/passes/InferWidths.scala
      • トップ階層から順に、配線のビット幅の推論を行っていく。
    • CheckWidths
      • src/main/scala/firrtl/passes/CheckWidths.scala
      • ビット幅の矛盾をチェックする。例えば、代入時のビット幅に矛盾がないか、など。
    • InferResets
      • src/main/scala/firrtl/transforms/InferResets.scala
      • リセット信号を推論させる。
  • DedupModules
    • src/main/scala/firrtl/transforms/Dedup.scala
    • モジュールの再帰的なインスタンスについてチェックを行う?
  • HighFirrtlToMiddleFirrtl
    • PullMuxes
      • src/main/scala/firrtl/passes/Passes.scala
      • SubFieldSubIndexなどの型に対して、MuxやValidIfが使用されていると、この順序を変える。
    • ReplaceAccesses
      • src/main/scala/firrtl/passes/ReplaceAccesses.scala
      • WSubAccess(index)で、indexが定数の場合はWSubIndexに変換する。
    • ExpandConnects
      • src/main/scala/firrtl/passes/Passes.scala
      • 配列や構造体について、Connect文を各要素に展開する。
    • Uniquify
      • src/main/scala/firrtl/passes/Uniquify.scala
      • 変数名の変換により別の変数と衝突してしまうことを防ぐ。例えば、a[0]a_0に変換すると元々a_0だったものにぶつかってしまう。なのでこの場合はa__0などに変換する?
    • ExpandWhens
      • src/main/scala/firrtl/passes/ExpandWhens.scala
      • Whenを分解してMuxに変換する。
    • CheckInitialization
      • src/main/scala/firrtl/passes/CheckInitialization.scala
      • 完全に初期化されていない変数を探してエラーを出力する。
    • ResolveKinds
    • InferTypes
    • CheckTypes
    • CheckResets
    • ResolveFlows
    • InferWidths
    • CheckWidths
    • ConvertFixedToSInt
      • src/main/scala/firrtl/passes/ConvertFixedToSInt.scala
      • FixedInt型をSInt型に変換する。
    • ZeroWidth
      • src/main/scala/firrtl/passes/ZeroWidth.scala
      • ビット幅がゼロのメモリを除去する。なんでそんなものが生成されるのか謎だが...
    • InferTypes
  • MiddleFirrtlToLowFirrtl
    • LowerTypes
    • ResolveKinds
    • InferTypes
    • ResolveFlows
    • InferWidths
    • Legalize
      • src/main/scala/firrtl/passes/Passes.scala
      • 演算に際し型のLegalizeなどを行う。
    • RemoveReset
      • src/main/scala/firrtl/transforms/RemoveReset.scala
      • Registerの定義されていないモジュールに対してResetの除去を行う。
    • CheckCombLoops
      • src/main/scala/firrtl/transforms/CheckCombLoops.scala
      • Combinational Loopの検出を行う。
    • RemoveWires
      • src/main/scala/firrtl/transforms/RemoveWires.scala
      • 不要な信号を除去する。
  • LowFirrtlOptimization
    • RemoveValidIf
      • src/main/scala/firrtl/passes/RemoveValidIf.scala
      • ValidIfを除去して分解する。
    • ConstantPropagation
      • src/main/scala/firrtl/transforms/ConstantPropagation.scala
      • 定数の伝搬を行う。
    • PadWidths
      • src/main/scala/firrtl/passes/PadWidths.scala
      • 自動的なビット拡張に対する処理を行う。
    • ConstantPropagation
    • Legalize
    • VerilogMemDelays
      • src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
      • メモリモジュールに対する遅延の挿入?
    • ConstantPropagation
    • SplitExpressions
      • src/main/scala/firrtl/passes/SplitExpressions.scala
      • 複数の式などをそれぞれの式に分解する。
    • CombineCats
      • src/main/scala/firrtl/transforms/CombineCats.scala
      • 複数のCatsをマージする。
    • CommonSubexpressionElimination
      • src/main/scala/firrtl/passes/CommonSubexpressionElimination.scala
      • 共通の式を除去する?
  • SystemVerilogEmitter
    • BlackBoxSourceHelper
    • ReplaceTruncatingArithmetic
    • FlattenRegUpdate
    • VerilogModulusCleanup$
    • VerilogRename
    • VerilogPrep$
    • AddDescriptionNodes

FIRRTLのFIRノード種類一覧

FIRRTLのLowerTypeで使われるExpression, Statementの種類について纏めておく。

  • src/main/scala/firrtl/passes/LowerTypes.scala
  • src/main/scala/firrtl/ir/IR.scala

github.com

特に迷ったのは、WSubIndexWSubAccessの違い。WSubIndexはインデックスが固定だが、WSubAccessはインデックスが固定ではない。

何故このように違いがあるのかというと、

  • WSubIndexの場合 : signal[0]signal_0に変換される。
  • WSubAcessの場合 : signal[idx]Mux(idx == 0, signal_0, Mux(idx == 1, signal_1, Mux(idx == 2, signal_2, Mux(idx == 3, signal_3))))に変換される。

という違いがある。

  • Expressionの種類
Expression型 パラメータ1 パラメータ2 パラメータ3 パラメータ4 パラメータ5
WRef 標準的な変数 name: String(変数名) tpe:Type(型) kind: Kind flow: Flow(信号の向き)
WSubField 構造体の参照(a.b) expr: Expression (構造体自体の型) name: String (メンバ名) tpe: Type (メンバ名の型) flow: Flow (信号の向き)
WSubIndex 配列アクセス(a[1]) expr: Expression(配列自体の型) value: Int(インデックス(固定値)) tpe: Type(出力の型) flow: Flow(信号の向き)
WSubAccess 配列アクセス(a[b]) expr: Expression(配列自体の型) index: Expression(インデックス) tpe: Type(出力の型) flow: Flow(信号の向き)
WVoid
WInvalid
EmptyExpression
  • Statementの種類
Statements型 パラメータ1 パラメータ2 パラメータ3 パラメータ4 パラメータ5 パラメータ6
DefWire info: Info name: String tpe: Type
DefRegister info: Info name: String tpe: Type clock: Expression reset: Expression init: Expression
DefInstance info: Info name: String module: String
DefMemory info: Info name: String dataType: Type
DefNode info: Info name: String value: Expression
Conditionally info: Info pred: Expression conseq: Statement alt: Statement
Block stmts: Seq[Statement]
PartialConnect info: Info loc: Expression expr: Expression
Connect info: Info loc: Expression expr: Expression
IsInvalid info: Info expr: Expression
Attach info: Info exprs: Seq[Expression]
Stop info: Info `ret: Int clk: Expression en: Expression
Print info: Info `string: StringLit args: Seq[Expression] clk: Expression en: Expression
EmptyStmt
  • Typeの種類
ベースの型 パラメータ1 パラメータ2
GroundType
AggregateType Type
UIntType GroundType width: Width
SIntType GroundType width: Width
FixedType GroundType width: Width point: Width
BundleType AggregateType fields: Seq[Field]
VectorType AggregateType tpe: Type size: Int
ClockType GroundType
ResetType GroundType
AsyncResetType GroundType
AnalogType GroundType width: Width
UnknownType Type

Scalaのvar型を使用したChiselの記述量削減テクニック

Scalaにはval型とvar型という2種類の型の種類が存在する。var型は再割り当て可能, val型は再割り当て不可能な型というもので、要するに、

  • var型 : 処理の中で何度も書き換えができる。
  • val型 : 処理の中で一度しか書き換えることができない。

というものだ。Chiselのチュートリアルやサンプルコードを見ていると、大体の場合はval型が使用されている。これはハードウェアは一度だけassignするのが基本なので自然の流れではあるのだが、var型を使うことで場合によってはコード量を削減できる。このようなケースについて調査する。

例えば、テーブル探索において、IDと値のペアで格納されているハッシュのような構造を考え、その中のハッシュ値と一致するアドレスの値を探索する。 もしテーブルの中に同じIDが複数存在している場合、テーブルのアドレスの大きい側の値を優先して取り出す。このような回路をChiselで設計する場合、以下のようなコードを記述することになる。

f:id:msyksphinz:20191210233029p:plain
SearchTableの実装の概要
  val entry_id = Reg(Vec(8, UInt(8.W)))
  val entry_value = Reg(Vec(8, UInt(32.W)))

  val is_hit = Wire(Vec(8, Bool()))
  val hit_value = Wire(Vec(8, UInt(32.W)))

  for (i <- 0 until 8) {
    is_hit(i) := (entry_id(i) === io.addr)
    hit_value(i) := Mux(is_hit(i), entry_value(i), if(i==0) { 0.U} else { hit_value(i-1) })
  }
  io.id_out := hit_value(7)
  io.id_hit := is_hit(7)

要点としては、

  • is_hit(i)により、各エントリがIDの値と一致しているかをチェックする。
  • hit_value(i)は、もしエントリiがヒットしていればその値を選択し、そうでなければ一つ前のエントリの結果を引き継ぐ。

というわけで、一つ前のエントリを保持するために、hit_valueをエントリの数だけ用意したり、インデックスが0の時だけ特殊な処理を追加したりと若干面倒くさい。そこでどうするかというと、手続き型のように記述できる(つまり上書きが可能な)var型を使用するという訳である。

  val entry_id = Reg(Vec(8, UInt(8.W)))
  val entry_value = Reg(Vec(8, UInt(32.W)))

  var is_hit = false.B
  var hit_value = 0.U
  for (i <- 0 until 8) {
    is_hit = (entry_id(i) === io.addr)
    hit_value = Mux(is_hit, entry_value(i), hit_value)
  }
  io.id_out := hit_value
  io.id_hit := is_hit

まず、is_hithit_valueを複数エントリ分持たなくても良くなる。当然ハードウェアに生成するときは(valを使用した場合と等価なので)ハードウェア量としては変わらないのでが、ソースコード量の削減になる。

生成されるハードウェア量はどちらも一緒が、Chiselの記述量の削減としてこのような記法も可能、ということだ。


付録 : 全ソースコード

class search_table_var extends Module
{
  val io = IO(new Bundle {
    val addr = Input(UInt(8.W))
    val id_out = Output(UInt(32.W))
    val id_hit = Output(Bool())

    val w_en    = Input(Bool())
    val w_addr  = Input(UInt(3.W))
    val w_id    = Input(UInt(8.W))
    val w_value = Input(UInt(32.W))
  })

  val entry_id = Reg(Vec(8, UInt(8.W)))
  val entry_value = Reg(Vec(8, UInt(32.W)))

  var is_hit = false.B
  var hit_id = 0.U
  for (i <- 0 until 8) {
    is_hit = (entry_id(i) === io.addr)
    hit_id = Mux(is_hit, entry_value(i), hit_id)
  }
  io.id_out := hit_id
  io.id_hit := is_hit


  when (io.w_en) {
    entry_id(io.w_addr) := io.w_id
    entry_value(io.w_addr) := io.w_value
  }
}

あけましておめでとうございます2020

あけましておめでとうございます。今年も、FPGA開発日記をよろしくお願いします。

去年の成果としては、以下のような感じだと思う。自分自身、今年は意外とハードウェアよりも、ソフトウェアよりに移っていると思う。

  • 引き続きRISC-Vをベースとしたエコシステムの勉強。RISC-VをベースとしたLLVMへの理解は進んだ。
  • Rustを使ってRISC-Vのシミュレータを作って公開した。でもRustの知識はあまり強化されていない...
  • RISC-Vに関連して2件の発表を行った。CQ出版インタフェースに特集の寄稿を行った。
  • LLVMの勉強を進めた。MYRISCVXオリジナルターゲットアーキテクチャを作ってLLVMのバックエンドに対する知識を深めた。
  • Chiselに関する知識を深めた。
    • Chisel3のエンジンについて学んだ。Chisel3のデータ構造について勉強して改造に取り組んだ。
    • FIRRTLのエンジンについて学んだ。FIRRTLの改造に取り組んでいる。

今年の目標は以下のように設定したいと思う。

  • 引き続きRISC-Vをベースとしたエコシステムの勉強。自分でOSSへの貢献をしていきたい。
    • これはRISC-Vの実装も含む。ソフトウェアエコシステム・ハードウェアエコシステムを含めて、RISC-V界隈への貢献を続けたい。
  • コンパイラの知識も付いたので、さらに昇華させたい。
    • 特にLLVMに関する知識はかなり詳しくなった。これを何らかの形でまとめ上げたい。
  • ハードウェアと言語処理実装について興味がある。
    • FIRRTLのPassの勉強はかなり進んだ。FIRRTLを題材に、自分で何か取り組みたい。

と、なんだか「FPGA開発日記」の名前を根本的に覆すような、ソフトウェアやら言語実装などばかりやっているが、引き続きハードウェアも一生懸命勉強していきたい。Chisel/SystemVerilogなども含め、CPUから始まるハードウェアの勉強、引き続き一生懸命やっていこうと思います。

今年も、よろしくお願い致します。

年末なので今年のまとめをしよう2019

f:id:msyksphinz:20191227233139p:plain

このブログも始めてから5年が経った。 年末年始なので、今年一年はどうだったかな、ということでまとめてみよう。

今年の元旦には、こんな目標を立てていたのだった。

msyksphinz.hatenablog.com

2019年始に立てた目標

  • 引き続きRISC-Vをベースとしたエコシステムの勉強。自分でOSSへの貢献をしていきたい。
  • Vivado-HLSを使って、いろんなシステムを作っていきたい。
    • これは、2017年からの継続目標。何か目標となるアプリケーション(ディープラーニングなども)を、Vivado HLSを使って勉強していきたい。
  • ディープラーニングなどの理解を深め、RISC-Vエコシステムとの連携にも挑戦していきたい。
  • なにか一つ新しい言語を覚えたい。今のブームだとRustかな。Rust, LLVM, RISC-Vなどの関連性も見ていきたい。

なんか全然できていないぞ...

目標達成状況

  • 引き続きRISC-Vをベースとしたエコシステムの勉強。自分でOSSへの貢献をしていきたい。
    • RISC-VをベースとしたLLVMへの理解は進んだ。コンパイラの知識も付いたので、これを来年はさらに昇華させたい。
  • Vivado-HLSを使って、いろんなシステムを作っていきたい。 → 結局Vivado-HLSは殆ど触っていない。高位合成については、いったんお休みかな?
  • ディープラーニングなどの理解を深め、RISC-Vエコシステムとの連携にも挑戦していきたい→ ディープラーニングについては殆ど触っていない。いったんお休みかな?
  • なにか一つ新しい言語を覚えたい。今のブームだとRustかな。Rust, LLVM, RISC-Vなどの関連性も見ていきたい。
    • Rustを使ってRISC-Vのシミュレータを作って公開した。でもRustの知識はあまり強化されていない...

これ以外にも、いろいろ達成できたかな。

  • RISC-Vに関連して2件の発表を行った。CQ出版インタフェースに特集の寄稿を行った。
  • LLVMの勉強を進めた。MYRISCVXオリジナルターゲットアーキテクチャを作ってLLVMのバックエンドに対する知識を深めた。
  • Chiselに関する知識を深めた。
    • Chisel3のエンジンについて学んだ。Chisel3のデータ構造について勉強して改造に取り組んだ。
    • FIRRTLのエンジンについて学んだ。FIRRTLの改造に取り組んでいる。
  • Rubyを使ったDSLの勉強を行った。ビルドシステムの勉強をして独自DSLでビルドツールを作成した。

RISC-Vが注目を浴びてきたこと

私がこのブログを開始して調査を始めたときには全くと言っていいほど知名度のなかったRISC-Vが、現在ここまで知名度を得たことには正直驚いている。 今年に入っても有志による勉強会や、技術系の記事にも普通にRISC-Vという言葉が登場してきており、5年前には思いもしなかった状況になっている。

おそらく私がハードウェア界隈に住んでいる人間だからか、RISC-Vと言えばCPU、そんな話を聞くことが多い。 ただし、最近ではソフトウェア界隈でもRISC-Vの注目度が上がっている。 RISC-Vというのはやはりソフトウェアエコシステムを含めないとその価値を見出せないのではないか?昔から思っている私の持論だ。

2017年に「Design Solution Forum」に初めて呼ばれたとき、日本の技術者にとって「RISC-Vのメリット」をどのように説明するか、かなり悩んだ。 その時に作った資料が以下だったりする。

つまり、RISC-VをCPUという共通部品としてとらえるならば、RISC-Vそのものに注目する必要は必ずしもなくて、この共通部品をどのようにうまく活用して、本当に価値のあるIPを早く作り込むか、という方がよっぽど大事なのだ。 これができるのはRISC-Vのエコシステムが順調に展開されているからだ。 ソフトウェアが充実しないとCPUに価値はない。

f:id:msyksphinz:20191227230616p:plain
f:id:msyksphinz:20191227230521p:plain

今見るとめっちゃゴチャゴチャしてるね。

だから、例えばRISC-Vと言っただけで「CPUだ!」と飛びついていきなりフルスクラッチでCPUを作り始めて、しかもテープアウトするまで数年もかかっているなんて話を聞くと、何のためにRISC-V使ってるの?という気持ちにされられる。

AIチップでもなんでもそうだが、市場投入のタイミングが遅れてしまうのが一番問題なのだ。 急速にアルゴリズムが発展しているAIの分野で1年もテープアウトにかけていると、すぐに古臭くなってしまう。

私がこの1年で感じたのは、半導体の開発技術も、ここらでもう一度考え直さなければならない時代に来ているのではなかろうか。 半導体にかかわる人間として、RISC-Vという単なるISAだけでなく、それを取り巻く半導体の開発手法、開発フローまで全体的に見直していければいいと思っている。

私がそのように考え始めたのは、ある人に「なんでUCB(University of California, Berkeley)の人たちはあんなに早く大量にテープアウトできてんの?」という質問を受けたからだった。

その答えの一つ(すべてではないと思うが)にChiselという新しい開発手法や、HAMMERなどの新しい手法の導入などが挙げられるが、確かに今の時代ソフトウェアの開発に比べてハードウェアの開発はなんだか時間が止まっているようだ。

これでは新しいアイデアがあってもすぐに実現できない。半導体の開発コストは値上がりする一方で、EDAツールの寡占の状況も変わらない。 こんな中で、アイデアはあってもお金がない人たちは、どうやって自分のアイデアを実現すればいいのか? アイデアを持っている人たちがもっと気軽に参入できるための裾野を広げていかないと、半導体だけ時代の流れから置いて行かれそうな気がする。


RISC-VというISA自体に焦点を当てると、6月にRISC-V Workshopの最新情報を確認したとき、正直「RISC-V大丈夫か?」という気持ちになった。

それは、例えばOpenHWグループの登場、そして出てきたBit Manipulationの仕様がメチャクチャであったこと、これらを含めて実は空中分解しているのではないと感じたからだ。

しかし12月のRISC-V Summit、これを見て、少し統制が取れてきているのではないかという気持ちになっている。 それは、各ISAのリリーススケジュール、レビューに関するプロセスなどがきちんと定まりつつあり、それに伴って全体的な動きも統制が取れてきているように見える。 ただし本部がスイスに移ったことも含めてまだ情勢的には不安定なように見えるので、もう少し注目していたい。

ブログの記事数

今年は、この記事を含めて358記事を書いた。これまでに比べて最高記録を更新。っていうか365日で考えると7日(1週間)しか休んでいないってこと?

自分で言うのもアレだけど、あまり無理をしすぎないように。

Jan Feb Mar Apr Mar Jun Jul Aug Sep Oct Nov Dec Total
2019 29 28 28 31 29 29 31 31 30 31 29 32 358
2018 33 29 29 29 30 31 27 26 27 30 28 29 348
2017 27 21 30 24 33 30 30 29 29 31 33 32 349
2016 32 31 22 28 25 20 23 19 23 30 29 26 308
2015 20 27 17 6 28 29 35 31 44 31 34 27 329

このブログ自身のこと

おかげさまで多くの人にこのブログを読んでもらっている。記事もたくさん書いたし、連載もたくさんさせてもらった。 自分自身、ちょっとだけHW界隈のインフルエンサーとして、正確かつ公平な記事ということにより注意しなければならない。

半面、あくまでこれは自分自身の趣味のブログだということ。 基本方針は、人に見られるための記事を書くのではなく、「自分のためになる記事」を書くことだ。その方針はずっと崩さずにおきたい。

ツイッターのフォロワー数も、この1年でぐっと増えた。ありがたいことだ。

  • 2016/01/01のフォロワー数: 5
  • 2016/12/31のフォロワー数: 86
  • 2017/12/31のフォロワー数: 292
  • 2018/12/31のフォロワー数: 768
  • 2019/12/31のフォロワー数: 1,092

Twitterのフォロワー数はあくまで参考値でしかないが、これほどまでに増えたのは自分でもびっくりしている。

さらに、このブログのアクセス数も、記録を取っている4年前からのグラフを作ってみた。

f:id:msyksphinz:20191230225108p:plain
FPGA開発日記の月別アクセス数

という訳で、来年もよろしくお願いします。来年も自分の好きなことを、好き勝手にやっていこうと思います。