シンプルな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では,論理回路を次のモデルに当てはめて設計します.
ピンバック: sbtでSynthesijer.Scala開発を始める | わさらぼ