FPGA開発日記

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

Chiselでビットコインマイナーを設計してみる(1. SHA256コアの開発)

f:id:msyksphinz:20180702001454p:plain

Vivado-HLSで観察していたビットコインマイニング回路の開発、面白そうなのでChiselに移植してみることにした。

勘違いしがちだがChiselは高位合成システムではなく、基本的にRTLの記述レベルを維持しつつ、Scala言語の良いところを取り入れてよりソフトウェアっぽく書くという方法なので、まずはSystem Verilogで書かれたSHA256回路をChiselに移植しても何の問題もない。

まずは、System Verilogで書かれたSHA256回路をChiselに簡単に移植してみることにした。

github.com

参考にしたのは上記のリポジトリで、まずはSHA256の回路をChiselで書き直してみる。 どうもオリジナルの回路は256ビットとか512ビットとか、大きなベクトルを使って設計してある。 ただ、Chiselはこういうビット処理は苦手だし、どうせ32ビット単位で処理されるんだろうって分かっていたので、32ビットの配列を組み上げる形にインタフェースを変更した。

class sha256_pipe2_base (STAGES: Int = 64) extends Module {
  val io = IO (new Bundle {
    val i_state = Input(Vec(8, UInt(32.W)))
    val i_data = Input(Vec(16, UInt(32.W)))
    val out = Output(Vec(8, UInt(32.W)))
  })

SHA256の変換回路は、functionを使って書き直す。こっちの方がデバッグし易いと思ったからだ。

  def E0(x: UInt): UInt = {
    return Cat(x(1,0), x(31,2)) ^ Cat(x(12,0), x(31,13)) ^ Cat(x(21,0), x(31,22))
  }
  def E1(x: UInt): UInt = {
    return Cat(x(5,0), x(31,6)) ^ Cat(x(10,0), x(31,11)) ^ Cat(x(24,0), x(31,25))
  }

32ビット単位でのビットシフトも、以下のようにfor文を使って記述している。

      data_buf := data(i-1)
      for (j <- 0 until 15) {
        data(i)(j) := data_buf(j+1)
      }
      data15_p1 := S1(data(i-1)(15))                                          // 3
      data15_p2(i) := data15_p1     // 1

とりあえず、ざっくりとChiselに移植して、マイニング用(?、あまりマイニング用なSHA256回路が分かっていないので、もう少し勉強が必要だ)の回路を構成してみた。

class sha256_pipe130 extends Module {
  val io = IO(new Bundle {
    val state = Input(Vec(8, UInt(32.W)))
    val state2 = Input(Vec(8, UInt(32.W)))
    val data = Input(Vec(16, UInt(32.W)))
    val hash = Output(Vec(8, UInt(32.W)))
  })
  val out = Wire(Vec(8, UInt(32.W)))

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

  val P = Module(new sha256_pipe2_base (64))
  P.io.i_state := io.state
  P.io.i_data := io.data
  out := P.io.out

  for (i <- 0 until 8) {
    hash_r(i) := io.state2(i) + out(i)
    io.hash(i) := hash_r(i)
  }
}

これで、まずはVerilog回路を生成してみる。

sbt 'runMain sha_256.sha256_pipe130'

まずは生成できた。インタフェースは以下のように配列で用意される。解析はちょっと面倒だけど...

module sha256_pipe130( // @[:@8503.2]
  input         clock, // @[:@8504.4]
  input         reset, // @[:@8505.4]
  input  [31:0] io_state_0, // @[:@8506.4]
  input  [31:0] io_state_1, // @[:@8506.4]
  input  [31:0] io_state_2, // @[:@8506.4]
...
  input  [31:0] io_state2_0, // @[:@8506.4]
  input  [31:0] io_state2_1, // @[:@8506.4]
  input  [31:0] io_state2_2, // @[:@8506.4]
...
  input  [31:0] io_data_0, // @[:@8506.4]
  input  [31:0] io_data_1, // @[:@8506.4]
  input  [31:0] io_data_2, // @[:@8506.4]
...
  output [31:0] io_hash_0, // @[:@8506.4]
  output [31:0] io_hash_1, // @[:@8506.4]
  output [31:0] io_hash_2, // @[:@8506.4]
...