シンプルなSynthesijer.Scalaの始め方(2)

Pocket

シンプルなSynthesijer.Scalaの始め方(1)では,環境の構築方法を紹介しました.ここでは,Lチカを例題に,Synthesijer.Scalaの使い方を説明します.

Lチカ

早速コードを見てみましょう.ScalaのREPLで入力することもできますが,ここではファイルに保存して実行することにします.次の内容のプログラムを”プロジェクトディレクトリ/src/main/scala/mkLed.scala”に記述しましょう.プロジェクトディレクトリはactivatorで作成したものを想定しています.

import synthesijer.scala._
object mkLed{
  def main(args:Array[String]) = {
    val m = new Module("blink_led", "clk", "reset") // 名前"blink_led"のモジュールを用意.
    val q = m.outP("q") // 出力用のポート"q"をmに追加.
    val counter = m.signal(32) // 32bitのレジスタをmに追加.
    val seq = m.sequencer("main") // 名前"main"の状態遷移機械をmに追加.
    // 状態遷移機械のアイドル状態で毎クロックcounterをインクリメント
    counter <= (counter + 1) * seq.idle // 式と状態の積で,特定の状態下での代入を意味
    q := counter.ref(23) // 32bitレジスタcounterの23bit目を出力ポートqに接続
    m.genVHDL()
  }
}

mkLed.mainメソッドの中でblink_ledモジュールを組み立てて,最後にVHDLファイルとして出力しています.

activator compile
activator run

として実行するとblink_led.vhdが生成されます.たとえば,Zyboの上で実行したい場合には,

set_property PACKAGE_PIN L16 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN G15 [get_ports reset]
set_property IOSTANDARD LVCMOS33 [get_ports reset]
set_property PACKAGE_PIN M14 [get_ports q]
set_property IOSTANDARD LVCMOS33 [get_ports q]

のような制約ファイル(たとえばzybo.xdcという名前で保存)を用意してVivadoなどで合成,bitファイルをダウンロードすれば,DIPスイッチG15を’0’にセットすることでLEDが点滅することを確認できます.

Lチカ(2)

先の例ではModuleのインスタンスを作成して追加していきましたが,Moduleを継承したクラスを作成することもできます.この場合,明示的にModuleのインスタンスを持ち回る事なく論理回路を設計できます.

import synthesijer.scala._
class mkLed2(n:Int) extends Module("blink_led2", "clk", "reset"){ // "blink_led2"という名前にする
  val q = outP("q") // 出力用のポート"q"をこのモジュールに追加.
  val counter = signal(32) // 32bitのレジスタを追加.
  val seq = sequencer("main")
  // 状態遷移機械のアイドル状態で毎クロックcounterをインクリメント
  counter <= (counter + 1) * seq.idle
  q := counter.ref(n) // 32bitレジスタcounterのn-bit目を出力ポートqに接続
}
object mkLed2{
  def main(args:Array[String]) = {
    val m = new mkLed2(23) // コンストラクタで出力bitを決める
    m.genVHDL()
  }
}

Lチカ(3)

今度はDIPスイッチの状態によって,速いLED点灯と遅いLED点灯の状態を選択できるようにしてみることにします.

import synthesijer.scala._
class mkLed3(n:Int) extends Module("blink_led3", "clk", "reset"){ // "blink_led3"という名前にする
  val q = outP("q") // 出力用のポート"q"をこのモジュールに追加.
  val sw = inP("sw") // 入力用のポート"sw"をこのモジュールに追加.

  val counter = signal(32) // 32bitのレジスタを追加.

  val seq = sequencer("main")
  val s0 = seq.add() // 状態遷移機械に新しい状態を追加
  seq.idle -> s0 // アイドル状態から無条件でs0に遷移
                 // 上の行とまとめて"val s0 = seq.idle -> seq.add()"と書いてもいい
  val s1 = s0 * (sw == HIGH) -> seq.add() // 状態s0のときswがHIGHなら新しい状態に遷移.
                                          // 新しい状態はs1に束縛.
  s1 * (sw == LOW) -> s0 // 状態s0のときswがLOWならs0に遷移.

  // 状態s0では毎クロックcounterをインクリメント
  counter <= (counter + 1) * s0
  // 状態s1では毎クロックcounterを4ずつ増やす
  counter <= (counter + 4) * s1

  q := counter.ref(n) // 32bitレジスタcounterのn-bit目を出力ポートqに接続
}
object mkLed3{
  def main(args:Array[String]) = {
    val m = new mkLed3(24) // コンストラクタで出力bitを決める
    m.genVHDL()
  }
}

新しい信号swを追加したので,制約ファイルの修正も必要です.たとえばZyboの場合であれば,

set_property PACKAGE_PIN P15 [get_ports sw]
set_property IOSTANDARD LVCMOS33 [get_ports sw]

などと追加します.合成して実行すると,SWをON/OFFすることでLEDの点滅の速さが変化することが確認できます.

モデル

Synthesijer.Scalaでは,論理回路を次のモデルに当てはめて設計します.