FPGA開発日記

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

Chisel事始め(1)

今まではRubyのテーブルと自作のデコーダ生成スクリプトを使ってVerilog-HDLのコードを生成してきたが、もうちょっと既存のフレームワークを探してみよう。

まずはChiselだ。ChiselはScalaで記述された言語情報からハードウェアを自動生成するためのフレームワークだ。

Chisel: Constructing Hardware in an Scala Embedded Language

そのまま翻訳してみた(クオリティ低い...)

About Chisel:

実行環境はUbuntu-14.04LTSだ。

git clone https://github.com/ucb-bar/chisel.git
cd chisel
make clean test publish-local

早速ダウンロードしてみたけど、sbtが無いと言われて実行することができない。 インストールしてみよう。

Chiselを使うための準備 - めも

sudo aptitude install openjdk-7-jre
wget https://dl.bintray.com/sbt/debian/sbt-0.13.6.deb
sudo dpkg -i sbt-0.13.6.deb

改めてビルドしてみる。かなり時間がかかった。

make clean test publish-local

ついでに、チュートリアルのダウンロード:

git clone https://github.com/ucb-bar/chisel-tutorial.git
cd chisel-tutiroial/hello
make

これだけで何か出来た。とりあえず生成物を確認する。

$ ls -1
build.sbt
chisel-dependent.sbt
emulator_api.h
emulator.h
emulator_mod.h
Hello
Hello.cpp
Hello-emulator.cpp
Hello-emulator.o
Hello.h
Hello-harness.v
Hello.o
Hello.out
Hello.scala
Hello.v
Hello.vcd
Makefile
project
target

入力がHello.scala、.cppとか、.vとかが生成物だろう。とりあえず中を見てみよう。

package Hello

import Chisel._

class Hello extends Module {
  val io = new Bundle {
    val out = UInt(OUTPUT, 8)
  }
  io.out := UInt(42)
}

class HelloTests(c: Hello) extends Tester(c) {
  step(1)
  expect(c.io.out, 42)
}

object Hello {
  def main(args: Array[String]): Unit = {
    val tutArgs = args.slice(1, args.length)
    chiselMainTest(tutArgs, () => Module(new Hello())) {
      c => new HelloTests(c) }
  }
}

Moduleの継承?でHelloクラスが作られているあたり、Helloモジュールを生成している。 変数ioの中で出力ポート(out, 8ビット)を作成しており、それに定数Uint(42)をそのまま代入しているのかな?

HelloTestsは、Helloモジュールのテストをするためのモジュールかな。 c.io.out が 42であることを期待している。

  • Hello.v
module Hello(
    output[7:0] io_out
);



  assign io_out = 8'h2a;
endmodule

間違い無いけど、、、これだけではあまり作った感じがしないなあ。

  • Hello.cpp
#include "Hello.h"

void Hello_t::init ( val_t rand_init ) {
  this->__srand(rand_init);
}


int Hello_t::clock ( dat_t<1> reset ) {
  uint32_t min = ((uint32_t)1<<31)-1;
  if (clk_cnt < min) min = clk_cnt;
  clk_cnt-=min;
  if (clk_cnt == 0) clock_hi( reset );
  if (clk_cnt == 0) clock_lo( reset );
  if (clk_cnt == 0) clk_cnt = clk;
  return min;
}


mod_t* Hello_t::clone() {
  mod_t* cloned = new Hello_t(*this);
  return cloned;
}


bool Hello_t::set_circuit_from ( mod_t* src ) {
  Hello_t* mod_typed = dynamic_cast<Hello_t*>(src);
  assert(mod_typed);
  Hello__io_out = mod_typed->Hello__io_out;
  clk = mod_typed->clk;
  clk_cnt = mod_typed->clk_cnt;
  return true;
}


void Hello_t::print ( FILE* f ) {
}
void Hello_t::print ( std::ostream& s ) {
}


void Hello_t::dump_init ( FILE* f ) {
  fputs("$timescale 1ps $end\n", f);
  fputs("$scope module Hello $end\n", f);
  fputs("$var wire 8 \x21 io_out $end\n", f);
  fputs("$upscope $end\n", f);
  fputs("$enddefinitions $end\n", f);
  fputs("$dumpvars\n", f);
  fputs("$end\n", f);
  fputs("#0\n", f);
  dat_dump<1>(f, Hello__io_out, 0x21);
  Hello__io_out__prev = Hello__io_out;
}


void Hello_t::dump ( FILE* f, int t ) {
  if (t == 0) return dump_init(f);
  fprintf(f, "#%d\n", t);
  if (Hello__io_out != Hello__io_out__prev)
    goto L0;
K0:
  return;
L0:
  Hello__io_out__prev = Hello__io_out;
  dat_dump<1>(f, Hello__io_out, 0x21);
  goto K0;
}




void Hello_t::clock_lo ( dat_t<1> reset ) {
  { Hello__io_out.values[0] = 0x2aL;}
}


void Hello_t::clock_hi ( dat_t<1> reset ) {
}


void Hello_api_t::init_mapping_table (  ) {
  dat_table.clear();
  mem_table.clear();
  Hello_t* mod_typed = dynamic_cast<Hello_t*>(module);
  assert(mod_typed);
  dat_table["Hello.io_out"] = new dat_api<8>(&mod_typed->Hello__io_out, "Hello.io_out", "");
}

なが!これだけのScalaコードでこんなに長いコードが生成されるの?

次回は、もうちょっと実用的なものを試してみたい。