Chiselを使って4bit CPUのTD4をFPGAボード上に実装する

# Chiselとは？

[Chisel](https://www.chisel-lang.org/)とは、VHDLやVerilog等と同じく、ディジタル回路設計用のハードウェア記述言語(HDL)の一種です(ただし、ハードウェアの設計をアジャイルに行う事を主目的としているので、シミュレーション等の機能は弱い)。Chiselで書かれたコードはVerilogのコードに変換され、そのVerilogコードを使って、FPGAをプログラムしたり、ASICの製造に用います。

有名な用途としては、オープンソースのCPU ISAの[RISC-V](https://riscv.org/)の実装でもっとも有名なものの一つの[Rocket Chip](https://github.com/chipsalliance/rocket-chip)や、Googleの[Edge TPU](https://cloud.google.com/edge-tpu/?hl=ja)があります(講演の動画は[こちら](https://www.youtube.com/watch?v=x85342Cny8c))

Chiselの文法について知りたい方は、[Chisel入門書「Digital Design with Chisel」1章の勉強記録](https://qiita.com/Kosuke_Matsui/items/80dde1219e3ce02c4b66)から始まる記事を参照してください。

# TD4とは？

TD4とは、[CPUの創りかた](https://www.amazon.co.jp/dp/4839909865/)にて、設計されている4bit CPUです。実用的なCPUではありません(ラーメンタイマーを作るのが精一杯…)ですが、CPUの基本的な構成要素は実装されているので、CPUの設計の学習には十分なものとなっています。

書籍では、手を動かして、ユニバーサル基板にICをハンダ付けしていきながら、電子工作自体も学習する事も目的としています。FPGAを使って実装するのは書籍の趣旨に反するのですが、HDLで簡単なCPUを実装するのはHDLの学習にも適しているので、Chiselで実装してみました。

本記事ではTD4自体の解説は行いません。詳細を知りたい場合はぜひ書籍を購入してみてください。

# TD4の実装

書籍での解説順に実装していきます。
(最終の実装結果を見たい場合は、[GitHubのリポジトリ](https://github.com/horie-t/TD4-with-Chisel)を参照してください)

## リセットとクロック

Chiselでは、Moduleクラスを継承したクラスを定義していく事で、回路設計を行っていきます。Moduleクラスを継承したクラスはio(I/O)のメンバーを実装する必要があり、IOメソッドを呼び出して入出力を定義します。

```scala
package td4

import chisel3._
import chisel3.util._
import chisel3.core.withClockAndReset

/**
  * TD4のトップモジュール
  */
class TD4Top extends Module {
  val io = IO(new Bundle {
    // ここに、モジュールへの入出力が定義される
  })
}
```

また、Chiselのコードから、Verilogのコードを出力するために以下のオブジェクトを定義します。

```scala
/**
  * TD4のVerilogファイルを生成するためのオブジェクト
  */
object TD4Top extends App {
  chisel3.Driver.execute(args, () => new TD4Top)
}
```

リセットやクロックの定義は以下のようにします。

```scala
class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。
  })

  // 1Hz, 10Hzのパルスを生成してクロック信号の代わりにする
  val clockFrequency = 100000000    // 使用するFPGAボードの周波数(Hz)
  val (clockCount, hz10Pulse) = Counter(true.B, clockFrequency / 10 / 2)
  val hz10Clock = RegInit(true.B)
  hz10Clock := Mux(hz10Pulse, ~hz10Clock, hz10Clock)
  val (helz10Count, hz1Pulse) = Counter(hz10Pulse, 10)
  val hz1Clock = RegInit(true.B)
  hz1Clock := Mux(hz1Pulse, ~hz1Clock, hz1Clock)

  // マニュアル・クロック用のボタンのチャタリングを除去
  val manualClock = Debounce(io.manualClock, clockFrequency)
  val td4Clock = Mux(io.isManualClock,
    io.manualClock,
    Mux(io.isHz10, hz10Clock, hz1Clock)).asClock

  // CPU RESETボタンは負論理なので反転する。
  withClockAndReset(td4Clock, ~io.cpuReset) {
    // TODO: 実装する
  }
}

/**
  *  プッシュボタン用デバンウンス
  */
class Debounce(hz: Int) extends Module {
  val io = IO(new Bundle{
    val in = Input(Bool())
    val out = Output(Bool())
  })

  val (count, enable) = Counter(true.B, hz / 10) // 0.1秒間隔で値を取り込む

  val reg0 = RegEnable(io.in, false.B, enable)
  val reg1 = RegEnable(reg0,  false.B, enable)

  io.out := reg0 && !reg1 && enable // enableの時だけ変化を見るようにして、1クロックのパルスにする
}

/**
  * プッシュボタン用デバンウンスのコンパニオン・オブジェクト
  */
object Debounce {
  def apply(in: Bool, hz: Int): Bool = {
    val debounce = Module(new Debounce(hz))
    debounce.io.in := in
    debounce.io.out
  }
}
```

## ROMを作る

書籍で解説されている内容で、実際に基板を使って作成するのが一番大変なのがROMですが、HDLで実装すると簡単です。
ここでは、プログラムの内容は決まっていないので、ROMの中身は全て0になっています。ROMは、しばらく使わないのでどこにも接続していません。

```scala
/**
  * ROM
  */
class ROM extends Module {
  val io = IO(new Bundle() {
    val addr = Input(UInt(4.W)) // アドレス
    val data = Output(UInt(8.W)) // データ
  })

   // ROMの中身
  val rom = VecInit(List(
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  ).map(_.asUInt(8.W)))

   io.data := rom(io.addr)
}
```

## 演算できるようにする(NOT Aを実装)

書籍で最初に解説されている命令は、MOV A, Aであり、「迷ったらココに戻って来るコト」などと言われていますが、変化が全然なくて退屈なので、NOT Aから実装していきます。
CPUのコアの部分は独自のクラスに実装しています。

```scala
/**
  * TD4 CPUコア
  */
class TD4 extends Module {
  val io = IO(new Bundle() {
    val out = Output(UInt(1.W))
  })

   // 1bitのAレジスタ(0で初期化)
  val regA = RegInit(1.U(1.W))

   // NOT A, A
  regA := ~regA

   // Aレジスタの内容を出力しておく
  io.out := regA
}

class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。

    val out           = Output(UInt(1.W)) // LEDへの出力
  })

// 中略

  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    io.out := core.io.out
  }
}
```

FPGAボードの制約ファイルを作成すれば(ここでは、Digilent社のNexys4 DDRの制約ファイル)、Aレジスタの変化をLEDの点滅として見る事ができます。

```
## Clock signal
set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { clock }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {clock}];

##Switches

set_property -dict { PACKAGE_PIN J15   IOSTANDARD LVCMOS33 } [get_ports { io_isManualClock }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { io_isHz10 }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]

## LEDs

set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { io_out }]; #IO_L18P_T2_A24_15 Sch=led[0]

##Buttons

set_property -dict { PACKAGE_PIN C12   IOSTANDARD LVCMOS33 } [get_ports { io_cpuReset }]; #IO_L3P_T0_DQS_AD1P_15 Sch=cpu_resetn

set_property -dict { PACKAGE_PIN N17   IOSTANDARD LVCMOS33 } [get_ports { io_manualClock }]; #IO_L9P_T1_DQS_14 Sch=btnc
```

## 命令にしたがって処理を変更する(MOV A, AとNOT Aを切り替える)

CPUコアを以下のように変更します。

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val inst = Input(Bool())    // 命令(現在は、真ならMOV A, A、偽ならNOT A)
    val out = Output(UInt(1.W)) // Aレジスタの内容
  })

  // 1bitのAレジスタ(0で初期化)
  val regA = RegInit(1.U(1.W))
  
  when (io.inst) {
    regA := regA    // MOV A, A
  } .otherwise {
    regA := ~regA  // NOT A
  }

  // Aレジスタの内容を出力しておく
  io.out := regA
}
```

TOPモジュールも以下のように変更します。(instをFPGAボードのスイッチに接続すれば、命令の切り替えを外部から入力出来ます)

```scala
class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。
    val inst          = Input(Bool()) // 命令(現在は、真ならMOV A, A、偽ならNOT A)

    val out           = Output(UInt(1.W)) // LEDへの出力
  })

// 中略

  // CPU RESETボタンは負論理なので反転する。
  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    core.io.inst := io.inst
    io.out := core.io.out
  }
}
```

## レジスタの数を増やす(MOV A, B)

レジスタ間のデータ転送を実装します。

```scala

class TD4 extends Module {
  val io = IO(new Bundle() {
    val select = Input(UInt(2.W)) // レジスタ・セレクタ
    val load   = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする
    val out    = Output(UInt(4.W)) // Aレジスタの内容
  })

  // 4bitのレジスタを4つ作成。レジスタの番号のビット位置のビットを立てる
  val regs = RegInit(Vec(1.U(4.W), 2.U(4.W), 4.U(4.W), 8.U(4.W)))

  // MOV X, X
  val selectedVal = regs(io.select)
  for (i <- 0 until regs.size) {
    regs(i) := Mux(io.load(i), selectedVal, regs(i))
  }

  // Aレジスタの内容を出力しておく
  io.out := regs(0)
}

class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。
    val select        = Input(UInt(2.W)) // レジスタ・セレクタ
    val load          = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする

    val out           = Output(UInt(4.W)) // LEDへの出力
  })

// 中略

  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    core.io.select := io.select
    core.io.load := io.load
    io.out := core.io.out
  }
}
```

## 加算回路を追加(ADD X,Im)

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val select = Input(UInt(2.W)) // レジスタ・セレクタ
    val load   = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする
    val imData = Input(UInt(4.W)) // 即値データ
    val out    = Output(UInt(4.W)) // Aレジスタの内容
  })

  // 4bitのレジスタを4つ作成。レジスタの番号のビット位置のビットを立てる
  val regs = RegInit(Vec(1.U(4.W), 2.U(4.W), 4.U(4.W), 8.U(4.W)))

  val selectedVal = regs(io.select)
  val addedVal = selectedVal + io.imData
  for (i <- 0 until regs.size) {
    regs(i) := Mux(io.load(i), addedVal, regs(i))
  }

  // Aレジスタの内容を出力しておく
  io.out := regs(0)
}

class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。
    val select        = Input(UInt(2.W)) // レジスタ・セレクタ
    val load          = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする
    val imData        = Input(UInt(4.W)) // 即値データ

    val out           = Output(UInt(4.W)) // LEDへの出力
  })

// 中略

  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    core.io.select := io.select
    core.io.load := io.load
    core.io.imData := io.imData
    io.out := core.io.out
  }
}
```

## MOV X,Im

以下のように変更します。

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val select = Input(UInt(2.W)) // レジスタ・セレクタ
    val load   = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする
    val imData = Input(UInt(4.W)) // 即値データ
    val out    = Output(UInt(4.W)) // Aレジスタの内容
  })

  // 4bitのレジスタを4つ作成。レジスタの番号のビット位置のビットを立てる
  val regs = RegInit(Vec(1.U(4.W), 2.U(4.W), 4.U(4.W), 8.U(4.W)))

  val selectedVal = MuxLookup(io.select, 0.U(4.W), Seq(
    (0.U(4.W) -> regs(0)),
    (1.U(4.W) -> regs(1)),
    (2.U(4.W) -> regs(2)) // 3.Uの分はデフォルト値で代用
  ))
  val addedVal = selectedVal + io.imData
  for (i <- 0 until regs.size) {
    regs(i) := Mux(io.load(i), addedVal, regs(i))
  }

  // Aレジスタの内容を出力しておく
  io.out := regs(0)
}
```

### キャリーフラグを実装

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val select = Input(UInt(2.W)) // レジスタ・セレクタ
    val load   = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする
    val imData = Input(UInt(4.W)) // 即値データ
    val out    = Output(UInt(4.W)) // Aレジスタの内容
  })

  // 4bitのレジスタを4つ作成。レジスタの番号のビット位置のビットを立てる
  val regs = RegInit(Vec(1.U(4.W), 2.U(4.W), 4.U(4.W), 8.U(4.W)))
  val carryFlag = RegInit(false.B)

  val selectedVal = MuxLookup(io.select, 0.U(4.W), Seq(
    (0.U(4.W) -> regs(0)),
    (1.U(4.W) -> regs(1)),
    (2.U(4.W) -> regs(2)) // 3.Uの分はデフォルト値で代用
  ))
  val addedVal = selectedVal +& io.imData
  carryFlag := addedVal(4)
  for (i <- 0 until regs.size) {
    regs(i) := Mux(io.load(i), addedVal(3, 0), regs(i))
  }

  // Aレジスタの内容を出力しておく
  io.out := regs(0)
}
```

### プログラム・カウンタを追加

プログラムカウンタを追加して、ROMから命令データを読み込んで行きます。ただし、命令を解釈する部分(デコーダ)がないので、意味のある処理はできません。

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val iAddr  = Output(UInt(4.W)) // 命令アドレス
    val iData  = Input(UInt(8.W))  // 命令データ
    val select = Input(UInt(2.W)) // レジスタ・セレクタ
    val load   = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする

    val out    = Output(UInt(4.W)) // テスト用出力値
  })

  // 汎用レジスタ
  val regA = RegInit(1.U(4.W)) // Aレジスタ
  val regB = RegInit(2.U(4.W)) // Bレジスタ
  val regC = RegInit(4.U(4.W)) // (Cレジスタ。最終的には別のものになる)
  val programCounter = RegInit(0.U(4.W)) // プログラム・カウンター

  val carryFlag = RegInit(false.B)

  val selectedVal = MuxLookup(io.select, 0.U(4.W), Seq(
    (0.U(4.W) -> regA),
    (1.U(4.W) -> regB),
    (2.U(4.W) -> regC) // 3.Uの分はデフォルト値で代用
  ))
  val addedVal = selectedVal +& io.iData(3, 0)
  carryFlag := addedVal(4)
  when (io.load(0)) {
    regA := addedVal(3, 0)
  }
  when (io.load(1)) {
    regB := addedVal(3, 0)
  }
  when (io.load(2)) {
    regC := addedVal(3, 0)
  }
  when (io.load(3)) {
    programCounter := addedVal
  } .otherwise {
    programCounter := programCounter + 1.U
  }

  io.iAddr := programCounter
  // 即値を出しておく
  io.out := io.iData(3, 0)
}

class ROM extends Module {
  val io = IO(new Bundle() {
    val addr = Input(UInt(4.W)) // アドレス
    val data = Output(UInt(8.W)) // データ
  })

  // ROMの中身
  val rom = VecInit(List(
    0x00, 0x01, 0x02, 0x03,
    0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0A, 0x0B,
    0x0C, 0x0D, 0x0E, 0x0F
  ).map(_.asUInt(8.W)))

  io.data := rom(io.addr)
}

class TD4Top extends Module {

// 中略

  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    core.io.select := io.select
    core.io.load := io.load
    io.out := core.io.out

    val rom = Module(new ROM())
    rom.io.addr := core.io.iAddr
    core.io.iData := rom.io.data
  }
}
```

## I/Oポート

以下のように、入出力ポートを追加します。

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val iAddr  = Output(UInt(4.W)) // 命令アドレス
    val iData  = Input(UInt(8.W))  // 命令データ
    val select = Input(UInt(2.W)) // レジスタ・セレクタ
    val load   = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする

    val in     = Input(UInt(4.W))  // 入力ポートへ
    val out    = Output(UInt(4.W)) // 出力ポートへ
  })

  // 汎用レジスタ
  val regA = RegInit(1.U(4.W)) // Aレジスタ
  val regB = RegInit(2.U(4.W)) // Bレジスタ
  val regOut = RegInit(4.U(4.W)) // 出力ポート用レジスタ
  val programCounter = RegInit(0.U(4.W)) // プログラム・カウンター

  val carryFlag = RegInit(false.B)

  val selectedVal = MuxLookup(io.select, 0.U(4.W), Seq(
    (0.U(4.W) -> regA),
    (1.U(4.W) -> regB),
    (2.U(4.W) -> io.in) // 3.Uの分はデフォルト値で代用
  ))
  val addedVal = selectedVal +& io.iData(3, 0)
  carryFlag := addedVal(4)
  when (io.load(0)) {
    regA := addedVal(3, 0)
  }
  when (io.load(1)) {
    regB := addedVal(3, 0)
  }
  when (io.load(2)) {
    regOut := addedVal(3, 0)
  }
  when (io.load(3)) {
    programCounter := addedVal
  } .otherwise {
    programCounter := programCounter + 1.U
  }

  // 出力
  io.iAddr := programCounter
  io.out := regOut
}

class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。
    val select        = Input(UInt(2.W)) // レジスタ・セレクタ
    val load          = Input(Vec(4, Bool())) // 真の位置のレジスタに値をロードする

    val in            = Input(UInt(4.W))  // 入力ポート
    val out           = Output(UInt(4.W)) // 出力ポート
  })

// 中略

  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    core.io.select := io.select
    core.io.load := io.load
    core.io.in := io.in
    io.out := core.io.out

    val rom = Module(new ROM())
    rom.io.addr := core.io.iAddr
    core.io.iData := rom.io.data
  }
}
```

## 命令デコーダを実装し、Lチカのプログラムを実行する

```scala
class TD4 extends Module {
  val io = IO(new Bundle() {
    val iAddr  = Output(UInt(4.W)) // 命令アドレス
    val iData  = Input(UInt(8.W))  // 命令データ
    val in     = Input(UInt(4.W))  // 入力ポートへ
    val out    = Output(UInt(4.W)) // 出力ポートへ
  })

  /*
   * レジスタ定義
   */
  // 汎用レジスタ
  val regA = RegInit(0.U(4.W)) // Aレジスタ
  val regB = RegInit(0.U(4.W)) // Bレジスタ

  // 出力ポート用レジスタ
  val regOut = RegInit(0.U(4.W))

  // プログラム・カウンタ
  val programCounter = RegInit(0.U(4.W))

  // キャリーフラグ
  val carryFlag = RegInit(false.B)

  /*
   * 命令デコーダ
   */
  val opData = Cat(io.iData(7, 4), carryFlag)
  val ctrlSig = MuxCase(0.U(6.W), Seq(
    // 本のデコーダの設計の最初の真理値表
    ((opData === BitPat("b0000?")) -> "b000111".U), // ADD A,Im
    ((opData === BitPat("b0001?")) -> "b010111".U), // MOV A,B
    ((opData === BitPat("b0010?")) -> "b100111".U), // IN  A
    ((opData === BitPat("b0011?")) -> "b110111".U), // MOV A,Im
    ((opData === BitPat("b0100?")) -> "b001011".U), // MOV B,A
    ((opData === BitPat("b0101?")) -> "b011011".U), // ADD B,Im
    ((opData === BitPat("b0110?")) -> "b101011".U), // IN  B
    ((opData === BitPat("b0111?")) -> "b111011".U), // MOV B,Im
    ((opData === BitPat("b1001?")) -> "b011101".U), // OUT B
    ((opData === BitPat("b1011?")) -> "b111101".U), // OUT Im
    ((opData === BitPat("b11100")) -> "b111110".U), // JNC(C=0)
    ((opData === BitPat("b11101")) -> "b111111".U), // JNC(C=1)"b??1111"
    ((opData === BitPat("b1111?")) -> "b111110".U)  // JMP
  ))
  val select = ctrlSig(5, 4)
  // 本はloadの値は、負論理なので否定して、順序も逆なのでReverseする
  val load   = Reverse(~ctrlSig(3, 0))

  /*
   * レジスタ読み込み
   */
  val selectedVal = MuxLookup(select, 0.U(4.W), Seq(
    (0.U(4.W) -> regA),
    (1.U(4.W) -> regB),
    (2.U(4.W) -> io.in) // 3.Uの分はデフォルト値で代用
  ))

  /*
   * 演算処理(ALU)
   */
  val addedVal = selectedVal +& io.iData(3, 0)
  carryFlag := addedVal(4)

  /*
   * レジスタ書き戻し
   */
  when (load(0)) {
    regA := addedVal(3, 0)
  }
  when (load(1)) {
    regB := addedVal(3, 0)
  }
  when (load(2)) {
    regOut := addedVal(3, 0)
  }
  when (load(3)) {
    programCounter := addedVal
  } .otherwise {
    programCounter := programCounter + 1.U
  }

  // 出力
  io.iAddr := programCounter
  io.out := regOut
}

class ROM extends Module {
  val io = IO(new Bundle() {
    val addr = Input(UInt(4.W)) // アドレス
    val data = Output(UInt(8.W)) // データ
  })

  // ROMの中身
  val rom = VecInit(List(
    // Lチカプログラム
    0xB3, 0xB6, 0xBC, 0xB8,
    0xB8, 0xBC, 0xB6, 0xB3,
    0xB1, 0xF0, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
  ).map(_.asUInt(8.W)))

  io.data := rom(io.addr)
}

class TD4Top extends Module {
  val io = IO(new Bundle {
    val cpuReset      = Input(Bool()) // CPU RESETボタンに接続
    val isManualClock = Input(Bool()) // クロック信号をマニュアル操作するか
    val manualClock   = Input(Bool()) // マニュアル・クロック信号
    val isHz10        = Input(Bool()) // 10Hzのクロックで動作するか？ 偽の場合は1Hzで動作します。
    val in            = Input(UInt(4.W))  // 入力ポート
    val out           = Output(UInt(4.W)) // 出力ポート
  })

// 中略

  withClockAndReset(td4Clock, ~io.cpuReset) {
    val core = Module(new TD4())
    core.io.in := io.in
    io.out := core.io.out

    val rom = Module(new ROM())
    rom.io.addr := core.io.iAddr
    core.io.iData := rom.io.data
  }
}
```

全体としてのコードを見たい場合は、[GitHubのリポジトリ](https://github.com/horie-t/TD4-with-Chisel)を参照してください