2018.02.12
Chisel(チズル:彫刻刀、Constructing Hardware in a Scala Embedded Language)とは、オブジェクト指向言語と関数型言語を統合したScalaをホストとしたDSL(Domain Specific Language)で実装されたハードウェア構成言語(Hardware Construction Language)です。RTLレベルの論理記述をVerilog HDLよりも少ない量で記述できます。このページでは、MAX10本:実践編(MAX 10実験キットで学ぶFPGA&コンピュータ)のp336で紹介した簡易型8ビットCPU「simple_cpu」をChiselで記述してVerilog HDL記述への変換と論理シミュレーションを行ってみます。
2018.02.12
2018.05.04 インストール方法を更新
Windows 10 (Version 1803)上のWindows Subsystem for Linux(WSL)へのインストール方法を以下に示します。
$sudo apt-get install -y openjdk-8-jdk
$sudo apt-get install -y openjdk-8-jre
$curl -L https://gist.githubusercontent.com/DanielGGordon/160c32f42f233a4025ce33ee5f4a40f2/raw/3f85a9f5dcbfe5d86552f3ca495b420bc7e5e6d3/updateScalaVersion | sudo bash
$echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list
$sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
$sudo apt-get update
$sudo apt-get install sbt
Verilatorもインストールしておきます。
$sudo apt-get install verilator
2018.02.12
2018.02.17 テスト記述を更新(test.scala)
2018.03.04
test.scala テストシーケンスにdefを活用
top.scala モジュール間信号結線の記述量を削減
cpu_control.scala when()での信号比較でDon't CareをBitPatで記述
2018.03.10 シミュレーション実行方法追記
ここからchisel_cpu.tar.gzをダウンロードして解凍してそのディレクトリの下に移動してください。
$ tar xvfz chisel_cpu.tar.gz
$ cd chisel_cpu
シミュレーションを実行するには以下のようにします。test.scala内に4箇所あるチェッカー(Chk_GPIO_OUT())による確認結果が表示されます。sbtの初回起動時は時間がかかります。
$ sbt
> test:runMain mcu.TOP_Main
> exit
Verilog HDLのコードと論理シミュレーション波形ファイルを生成するにはVerilatorを起動します。
$ sbt
> test:runMain mcu.TOP_Main --backend-name verilator
> exit
ディレクトリtest_run_dir以下に結果が入ります。変換後のVerilog HDL記述TOP.vとシミュレーション波形ファイルTOP.vcdが生成されています。波形はgtkwaveをインストールすれば覗けます。
2018.02.12
以下にchisel_cpu.tar.gzに格納したソース・コードを示します。chisel初心者によるコードなのでふざけた部分が多いと思いますが、とりあえず動作しているのでご参考用としてご容赦ください。
オリジナルのsimple_cpuから以下を変更しています。
(1) RAMをROMとRAMに分離した。オリジナルではテストベンチでRAMを初期化してプログラムをRAMにロードしていたが、プログラムをROM上に固定化した。
(2) PORTの入力ポート(8ビット)と出力ポート(8ビット)それぞれにアドレスをアサイン(2バイト)した。入力ポートをリードするとポートのレベルを読め、出力ポートをリードすると出力ポートにライトした値を読めるようにした。
(3) メモリ空間について、0x00〜0x17をROM、0x18〜0x1DをRAM、0x1E〜0x1FをPORTにアサインした。
src/test/scala/mcu/main.scala:テストベンチのメイン
package mcu
import chisel3._
object TOP_Main extends App
{
iotesters.Driver.execute(args, () => new TOP)
{
c => new TOP_Unit_Tester(c)
}
}
src/test/scala/mcu/test.scala:テストベンチのテスタ(入力波形生成)
package mcu
import chisel3._
import chisel3.iotesters
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
//---------------------------
// TOP Tester
//---------------------------
class TOP_Unit_Tester(c: TOP) extends PeekPokeTester(c)
{
private val top = c
//
// Define Tasks
def Set_GPIO_IN(in: Bits) =
{
poke(top.io.gpio.port_in, in)
}
def Chk_GPIO_OUT(out: Bits) =
{
expect(top.io.gpio.port_out, out)
}
//
// Test Sequence
Set_GPIO_IN("h00".U(8.W))
step(500)
Chk_GPIO_OUT("h00".U(8.W))
//
Set_GPIO_IN("h80".U(8.W))
step(1000)
//
Set_GPIO_IN("h00".U(8.W))
step(500)
Chk_GPIO_OUT("h06".U(8.W))
//
Set_GPIO_IN("h80".U(8.W))
step(2000)
//
Set_GPIO_IN("h00".U(8.W))
step(500)
Chk_GPIO_OUT("h12".U(8.W))
//
Set_GPIO_IN("h80".U(8.W))
step(3000)
//
Set_GPIO_IN("h00".U(8.W))
step(500)
Chk_GPIO_OUT("h24".U(8.W))
}
//---------------------------
// ROM Tester
//---------------------------
class ROM_Unit_Tester(c: ROM) extends PeekPokeTester(c)
{
private val rom = c
// Idle
poke(rom.io.cs, 0)
poke(rom.io.bus.re, 0)
poke(rom.io.bus.we, 0)
poke(rom.io.bus.addr , "h00".U(5.W))
poke(rom.io.bus.wdata, "h00".U(8.W))
//
for(i <- 0 to 30 by 2)
{
// Read
step(1)
poke(rom.io.cs, 1)
poke(rom.io.bus.re, 1)
poke(rom.io.bus.we, 0)
poke(rom.io.bus.addr , (i+0).U(5.W))
poke(rom.io.bus.wdata, "h00".U(8.W))
// Read
step(1)
poke(rom.io.cs, 1)
poke(rom.io.bus.re, 1)
poke(rom.io.bus.we, 0)
poke(rom.io.bus.addr , (i+1).U(5.W))
poke(rom.io.bus.wdata, "h00".U(8.W))
// Idle
step(1)
poke(rom.io.cs, 0)
poke(rom.io.bus.re, 0)
poke(rom.io.bus.we, 0)
poke(rom.io.bus.addr , "h00".U(5.W))
poke(rom.io.bus.wdata, "h00".U(8.W))
}
}
//---------------------------
// RAM Tester
//---------------------------
class RAM_Unit_Tester(c: RAM) extends PeekPokeTester(c)
{
private val ram = c
// Idle
poke(ram.io.cs, 0)
poke(ram.io.bus.re, 0)
poke(ram.io.bus.we, 0)
poke(ram.io.bus.addr , "h00".U(5.W))
poke(ram.io.bus.wdata, "h00".U(8.W))
// Write "haa" to @"h00"
step(1)
poke(ram.io.cs, 1)
poke(ram.io.bus.re, 0)
poke(ram.io.bus.we, 1)
poke(ram.io.bus.addr , "h00".U(5.W))
poke(ram.io.bus.wdata, "haa".U(8.W))
// Write "h55" to @"h1f"
step(1)
poke(ram.io.cs, 1)
poke(ram.io.bus.re, 0)
poke(ram.io.bus.we, 1)
poke(ram.io.bus.addr , "h1f".U(5.W))
poke(ram.io.bus.wdata, "h55".U(8.W))
// Read @"h00"
step(1)
poke(ram.io.cs, 1)
poke(ram.io.bus.re, 1)
poke(ram.io.bus.we, 0)
poke(ram.io.bus.addr , "h00".U(5.W))
poke(ram.io.bus.wdata, "h00".U(8.W))
// Read @"h1f"
step(1)
poke(ram.io.cs, 1)
poke(ram.io.bus.re, 1)
poke(ram.io.bus.we, 0)
poke(ram.io.bus.addr , "h1f".U(5.W))
poke(ram.io.bus.wdata, "h00".U(8.W))
// Idle
step(1)
poke(ram.io.cs, 0)
poke(ram.io.bus.re, 0)
poke(ram.io.bus.we, 0)
poke(ram.io.bus.addr , "h00".U(5.W))
poke(ram.io.bus.wdata, "h00".U(8.W))
step(4)
}
//---------------------------
// PORT Tester
//---------------------------
class PORT_Unit_Tester(c: PORT) extends PeekPokeTester(c)
{
private val port = c
// Idle
poke(port.io.gpio.port_in, "h00".U(8.W))
poke(port.io.bus_cs.cs, 0)
poke(port.io.bus_cs.bus.re, 0)
poke(port.io.bus_cs.bus.we, 0)
poke(port.io.bus_cs.bus.addr , "h00".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h00".U(8.W))
// Read Port Input
step(1)
poke(port.io.gpio.port_in, "hab".U(8.W))
poke(port.io.bus_cs.cs, 1)
poke(port.io.bus_cs.bus.re, 1)
poke(port.io.bus_cs.bus.we, 0)
poke(port.io.bus_cs.bus.addr , "h00".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h00".U(8.W))
// Read Port Input
step(1)
poke(port.io.gpio.port_in, "hcd".U(8.W))
poke(port.io.bus_cs.cs, 1)
poke(port.io.bus_cs.bus.re, 1)
poke(port.io.bus_cs.bus.we, 0)
poke(port.io.bus_cs.bus.addr , "h00".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h00".U(8.W))
// Write Port Output
step(1)
poke(port.io.gpio.port_in, "h00".U(8.W))
poke(port.io.bus_cs.cs, 1)
poke(port.io.bus_cs.bus.re, 0)
poke(port.io.bus_cs.bus.we, 1)
poke(port.io.bus_cs.bus.addr , "h01".U(5.W))
poke(port.io.bus_cs.bus.wdata, "haa".U(8.W))
// Read Port Output
step(1)
poke(port.io.gpio.port_in, "h00".U(8.W))
poke(port.io.bus_cs.cs, 1)
poke(port.io.bus_cs.bus.re, 1)
poke(port.io.bus_cs.bus.we, 0)
poke(port.io.bus_cs.bus.addr , "h01".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h00".U(8.W))
// Write Port Output
step(1)
poke(port.io.gpio.port_in, "h00".U(8.W))
poke(port.io.bus_cs.cs, 1)
poke(port.io.bus_cs.bus.re, 0)
poke(port.io.bus_cs.bus.we, 1)
poke(port.io.bus_cs.bus.addr , "h01".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h55".U(8.W))
// Read Port Output
step(1)
poke(port.io.gpio.port_in, "h00".U(8.W))
poke(port.io.bus_cs.cs, 1)
poke(port.io.bus_cs.bus.re, 1)
poke(port.io.bus_cs.bus.we, 0)
poke(port.io.bus_cs.bus.addr , "h01".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h00".U(8.W))
// Idle
step(1)
poke(port.io.gpio.port_in, "h00".U(8.W))
poke(port.io.bus_cs.cs, 0)
poke(port.io.bus_cs.bus.re, 0)
poke(port.io.bus_cs.bus.we, 0)
poke(port.io.bus_cs.bus.addr , "h00".U(5.W))
poke(port.io.bus_cs.bus.wdata, "h00".U(8.W))
step(4)
}
// This is a trivial example of how to run this Specification
// From within sbt use:
// testOnly mcu.ROM_Tester
//
// From a terminal shell use:
// sbt 'testOnly mcu.ROM_Tester'
// }}}
class ROM_Tester extends ChiselFlatSpec
{
private val backendNames = if(firrtl.FileUtils.isCommandAvailable("verilator"))
{
Array("firrtl", "verilator")
}
else
{
Array("firrtl")
}
//
for ( backendName <- backendNames )
{
"ROM" should s"read fixed contents data from its memory mat (with $backendName)" in
{
Driver(() => new ROM, backendName)
{
c => new ROM_Unit_Tester(c)
} should be (true)
}
}
//
"Basic test using Driver.execute" should "be used as an alternative way to run specification" in
{
iotesters.Driver.execute(Array(), () => new ROM)
{
c => new ROM_Unit_Tester(c)
} should be (true)
}
//
"using --backend-name verilator" should "be an alternative way to run using verilator" in
{
if(backendNames.contains("verilator"))
{
iotesters.Driver.execute(Array("--backend-name", "verilator"), () => new ROM)
{
c => new ROM_Unit_Tester(c)
} should be(true)
}
}
//
"running with --is-verbose" should "show more about what's going on in your tester" in
{
iotesters.Driver.execute(Array("--is-verbose"), () => new ROM)
{
c => new ROM_Unit_Tester(c)
} should be(true)
}
//
"running with --fint-write-vcd" should "create a vcd file from your test" in
{
iotesters.Driver.execute(Array("--fint-write-vcd"), () => new ROM)
{
c => new ROM_Unit_Tester(c)
} should be(true)
}
//
"using --help" should s"show the many options available" in
{
iotesters.Driver.execute(Array("--help"), () => new ROM)
{
c => new ROM_Unit_Tester(c)
} should be (true)
}
}
src/main/scala/mcu/top.v:最上位記述
package mcu
import chisel3._
//------------------
// LSI Top Signals
//------------------
class IO_PIN extends Bundle
{
val gpio = new IO_GPIO()
}
//-------------------
// Bus Signals
//-------------------
class IO_BUS extends Bundle
{
val re = Output(Bool())
val we = Output(Bool())
val addr = Output(UInt(5.W))
val wdata = Output(UInt(8.W))
val rdata = Input(UInt(8.W))
}
//
class IO_BUS_CS extends Bundle
{
val bus = new IO_BUS()
val cs = Output(Bool())
}
//-------------------
// TOP
//-------------------
class TOP extends Module
{
// Port
val io = IO(
new IO_PIN()
)
// ROM
val rom = Module(new ROM)
// RAM
val ram = Module(new RAM)
// PORT
val port = Module(new PORT)
// CPU Core
val cpu = Module(new CPU)
// Access Decoder
val cs_port = (cpu.io.addr(4, 1) === "b1111".U(4.W)) // 0x1E - 0x1F
val cs_ram = (cpu.io.addr(4) === "b1".U(1.W)) && !cs_port // 0x18 - 0x1D
val cs_rom = !(cs_ram || cs_port) // 0x00 - 0x0F
// Connection among Modules
io.gpio <> port.io.gpio
//
rom.io.cs := cs_rom
ram.io.cs := cs_ram
port.io.bus_cs.cs := cs_port
//
rom.io.bus <> cpu.io
ram.io.bus <> cpu.io
port.io.bus_cs.bus <> cpu.io
cpu.io.rdata := rom.io.bus.rdata | ram.io.bus.rdata | port.io.bus_cs.bus.rdata
}
src/main/scala/mcu/rom.scala:ROM記述
package mcu
import chisel3._
//---------------------
// ROM 8bits x 32words
//---------------------
class ROM extends Module
{
// Port
val io = IO(
new IO_BUS_CS().flip
)
// ROM Contents
val rom_data = Vec(Array
(
// Application : Increment port_out[7:0] during port_in[7]=1.
// 18 DATA RES 6 // ram
// 1E PORTIN RES 1 // port_in[7:0]
// 1F PORTOUT RES 1 // port_out[7:0]
//
// 00 START ORG 0x00
"h7e".U(8.W), // 00 CHKPORTIN MOV R, @PORTIN
"he7".U(8.W), // 01 BIT R, #7 // get port_in[7]
"hc0".U(8.W), // 02 JNZ CHKPORTIN
"h7f".U(8.W), // 03 INCPORTOUT MOV R, @PORTOUT
"h98".U(8.W), // 04 MOV @DATA, R // to verify
"h21".U(8.W), // 05 MOV R, #1 // to verify
"h58".U(8.W), // 06 ADD R, @DATA // to verify
"h98".U(8.W), // 07 MOV @DATA, R // to verify
"h9f".U(8.W), // 08 MOV @PORTOUT, R
"h2a".U(8.W), // 09 WAIT MOV R, #10
"h1f".U(8.W), // 0A WAIT_1 ADD R, #-1
"hca".U(8.W), // 0B JNZ WAIT_1
"ha0".U(8.W), // 0C JMP START
"h00".U(8.W), // 0D
"h00".U(8.W), // 0E
"h00".U(8.W), // 0F
"h00".U(8.W), // 10
"h00".U(8.W), // 11
"h00".U(8.W), // 12
"h00".U(8.W), // 13
"h00".U(8.W), // 14
"h00".U(8.W), // 15
"h00".U(8.W), // 16
"h00".U(8.W), // 17
"h00".U(8.W), // 18
"h00".U(8.W), // 19
"h00".U(8.W), // 1A
"h00".U(8.W), // 1B
"h00".U(8.W), // 1C
"h00".U(8.W), // 1D
"h00".U(8.W), // 1E
"h00".U(8.W) // 1F
))
// Read ROM Data
val ff_rdata = RegInit("h00".U(8.W))
//
when (io.cs & io.bus.re)
{
ff_rdata := rom_data(io.bus.addr)
}
.otherwise
{
ff_rdata := "h00".U(8.W)
}
// Read Data
io.bus.rdata := ff_rdata
}
src/main/scala/mcu/ram.v:RAM記述
package mcu
import chisel3._
//---------------------
// RAM 8bits x 32words
//---------------------
class RAM extends Module
{
// Port
val io = IO(
new IO_BUS_CS().flip
)
// RAM Body
val ram = Mem(32, UInt(8.W))
val ff_rdata = RegInit(0.U(8.W))
// Read/Write Operation
when (io.cs & io.bus.we)
{
ram(io.bus.addr) := io.bus.wdata
}
.elsewhen (io.cs & io.bus.re)
{
ff_rdata := ram(io.bus.addr)
}
// Finish Read Data
val dphase = RegInit(0.U(1.W))
dphase := (io.cs & io.bus.re).toUInt
when(dphase.toBool)
{
io.bus.rdata := ff_rdata
}
.otherwise
{
io.bus.rdata := "h00".U(8.W)
}
}
src/main/scala/mcu/port.scala:入出力ポート
package mcu
import chisel3._
//----------------
// GPIO Signals
//----------------
class IO_GPIO extends Bundle
{
val port_in = Input(UInt(8.W))
val port_out = Output(UInt(8.W))
}
//---------------------
// PORT Module
//---------------------
class PORT extends Module
{
// Port
val io = IO(
new Bundle
{
val gpio = new IO_GPIO()
val bus_cs = new IO_BUS_CS().flip
}
)
// Port Input
val port_in_sel = io.bus_cs.cs && (io.bus_cs.bus.addr(0) === 0.U(1.W))
val port_in_re = port_in_sel && io.bus_cs.bus.re
val ff_port_in = RegInit(0.U(8.W))
val ff_port_in_dphase = RegInit(0.U(1.W))
//
when (port_in_re)
{
ff_port_in := io.gpio.port_in
}
ff_port_in_dphase := port_in_re.toUInt
// Port Output
val port_out_sel = io.bus_cs.cs && (io.bus_cs.bus.addr(0) === 1.U(1.W))
val port_out_re = port_out_sel && io.bus_cs.bus.re
val port_out_we = port_out_sel && io.bus_cs.bus.we
val ff_port_out = RegInit(0.U(8.W))
val ff_port_out_dphase = RegInit(0.U(1.W))
//
when (port_out_we)
{
ff_port_out := io.bus_cs.bus.wdata
}
io.gpio.port_out := ff_port_out
ff_port_out_dphase := port_out_re.toUInt
// Read Data
when (ff_port_in_dphase.toBool)
{
io.bus_cs.bus.rdata := ff_port_in
}
.elsewhen (ff_port_out_dphase.toBool)
{
io.bus_cs.bus.rdata := ff_port_out
}
.otherwise
{
io.bus_cs.bus.rdata := 0.U(8.W)
}
}
src/main/scala/mcu/cpu_defines/scala:CPU記述内定数の定義
package mcu
import chisel3._
//-----------------------------------
// Data Select Signal for BUSX, BUSY
//-----------------------------------
object CPU_BUS_SEL
{
val NOP = "b00000".U(5.W)
val MAR = "b00001".U(5.W)
val MWD = "b00010".U(5.W)
val MRD = "b00100".U(5.W)
val PC = "b01000".U(5.W)
val R = "b10000".U(5.W)
}
//----------------
// ALU Operation
//----------------
object CPU_ALU_OUT
{
val NOP = "b00000000".U(8.W)
val BUSX = "b00000001".U(8.W)
val BUSY = "b00000010".U(8.W)
val BUSYU5 = "b00000100".U(8.W)
val BUSYS5 = "b00001000".U(8.W)
val ADD = "b00010000".U(8.W)
val ADDYS5 = "b00100000".U(8.W)
val INC = "b01000000".U(8.W)
}
//
object CPU_ALU_BIT
{
val NOP = "b0000".U(4.W)
val ZERO = "b0001".U(4.W)
val LOAD = "b0010".U(4.W)
}
//------------------------------
// Define State in CPU_CONTROL
//------------------------------
object CPU_STATE
{
val INIT = "h00".U(8.W)
val IF_0 = "h01".U(8.W)
val IF_1 = "h02".U(8.W)
val IF_2 = "h03".U(8.W)
val ID = "h04".U(8.W)
//
val EX_ADD_R_IMM_0 = "h80".U(8.W)
val EX_MOV_R_IMM_0 = "h90".U(8.W)
//
val EX_ADD_R_ADR_0 = "ha0".U(8.W)
val EX_ADD_R_ADR_1 = "ha1".U(8.W)
val EX_ADD_R_ADR_2 = "ha2".U(8.W)
val EX_ADD_R_ADR_3 = "ha3".U(8.W)
//
val EX_MOV_R_ADR_0 = "hb0".U(8.W)
val EX_MOV_R_ADR_1 = "hb1".U(8.W)
val EX_MOV_R_ADR_2 = "hb2".U(8.W)
val EX_MOV_R_ADR_3 = "hb3".U(8.W)
//
val EX_MOV_ADR_R_0 = "hc0".U(8.W)
val EX_MOV_ADR_R_1 = "hc1".U(8.W)
val EX_MOV_ADR_R_2 = "hc2".U(8.W)
//
val EX_JMP_ADR_0 = "hd0".U(8.W)
val EX_JNZ_ADR_0 = "he0".U(8.W)
val EX_BIT_R_BIT_0 = "hf0".U(8.W)
}
src/main/scala/mcu/cpu.scala:CPUの最上位記述
package mcu
import chisel3._
//---------------------
// CPU
//---------------------
class CPU extends Module
{
// Port
val io = IO(
new IO_BUS()
)
// Datapath
val dpth = Module(new CPU_DATAPATH)
// Control
val ctrl = Module(new CPU_CONTROL)
// Signal Connection
dpth.io.ctrl <> ctrl.io.ctrl
io.re := ctrl.io.strb.mem_rd
io.we := ctrl.io.strb.mem_wr
io.addr := dpth.io.addt.mem_addr
io.wdata := dpth.io.addt.mem_wdata
dpth.io.addt.mem_rdata := io.rdata
}
src/main/scala/mcu/cpu_datapath.scala:CPUのデータパス記述
package mcu
import chisel3._
import chisel3.util._ // need it for Cat statement
//-------------------------------
// CPU Internal Control Signals
//-------------------------------
class IO_CPU_BUS_ADDT extends Bundle
{
val mem_addr = Output(UInt(5.W))
val mem_wdata = Output(UInt(8.W))
val mem_rdata = Input(UInt(8.W))
}
//---------------------
// CPU
//---------------------
class CPU_DATAPATH extends Module
{
// Port
val io = IO(
new Bundle
{
val ctrl = new IO_CPU_CTRL().flip
val addt = new IO_CPU_BUS_ADDT()
}
)
// Internal Bus
val bus_x = Wire(UInt(8.W))
val bus_y = Wire(UInt(8.W))
val bus_z = Wire(UInt(8.W))
// Memory Address
val ff_reg_mar = RegInit("h00".U(8.W))
when (io.ctrl.reg_mar_wr) {ff_reg_mar := Cat("b000".U(3.W), bus_z(4, 0))}
io.addt.mem_addr := ff_reg_mar
// Memory Write Data
val ff_reg_mwd = RegInit("h00".U(8.W))
when (io.ctrl.reg_mwd_wr) {ff_reg_mwd := bus_z}
io.addt.mem_wdata := ff_reg_mwd
// Memory Read Data
val ff_reg_mrd = RegInit("h00".U(8.W))
when (io.ctrl.reg_mrd_wr) {ff_reg_mrd := io.addt.mem_rdata}
io.ctrl.opcode := ff_reg_mrd
// Program Counter
val ff_reg_pc = RegInit("h00".U(8.W))
when (io.ctrl.reg_pc_wr) {ff_reg_pc := Cat("b000".U(3.W), bus_z(4, 0))}
// CPU Register
val ff_reg_r = RegInit("h00".U(8.W))
when (io.ctrl.reg_r_wr) {ff_reg_r := bus_z}
// bus_x Data Selector
bus_x := "h00".U(8.W) // default
switch (io.ctrl.bus_x_sel)
{
is (CPU_BUS_SEL.MAR) {bus_x := ff_reg_mar}
is (CPU_BUS_SEL.MWD) {bus_x := ff_reg_mwd}
is (CPU_BUS_SEL.MRD) {bus_x := ff_reg_mrd}
is (CPU_BUS_SEL.PC ) {bus_x := ff_reg_pc }
is (CPU_BUS_SEL.R ) {bus_x := ff_reg_r }
}
// bus_y Data Selector
bus_y := "h00".U(8.W) // default
switch (io.ctrl.bus_y_sel)
{
is (CPU_BUS_SEL.MAR) {bus_y := ff_reg_mar}
is (CPU_BUS_SEL.MWD) {bus_y := ff_reg_mwd}
is (CPU_BUS_SEL.MRD) {bus_y := ff_reg_mrd}
is (CPU_BUS_SEL.PC ) {bus_y := ff_reg_pc }
is (CPU_BUS_SEL.R ) {bus_y := ff_reg_r }
}
// ALU Operation
val alu_out = Wire(UInt(8.W))
val alu_bit = Wire(Bool())
val bus_yu5 = Wire(UInt(8.W))
val bus_ys5 = Wire(UInt(8.W))
bus_yu5 := Cat("b000".U(3.W), bus_y(4, 0))
bus_ys5 := Cat(Fill(3, bus_y(4)), bus_y(4, 0))
//
alu_out := "h00".U(8.W) // default
switch (io.ctrl.alu_out_op)
{
is (CPU_ALU_OUT.BUSX ) {alu_out := bus_x }
is (CPU_ALU_OUT.BUSY ) {alu_out := bus_y }
is (CPU_ALU_OUT.BUSYU5) {alu_out := bus_yu5}
is (CPU_ALU_OUT.BUSYS5) {alu_out := bus_ys5}
is (CPU_ALU_OUT.ADD ) {alu_out := bus_x + bus_y }
is (CPU_ALU_OUT.ADDYS5) {alu_out := bus_x + bus_ys5};
is (CPU_ALU_OUT.INC ) {alu_out := bus_x + "h01".U(8.W)}
}
bus_z := alu_out
//
alu_bit := false.B // default
switch (io.ctrl.alu_bit_op)
{
is (CPU_ALU_BIT.ZERO) {alu_bit := (alu_out === "h00".U(8.W))}
is (CPU_ALU_BIT.LOAD)
{
switch (bus_y(2, 0))
{
is ("b000".U(3.W)) {alu_bit := bus_x(0)}
is ("b001".U(3.W)) {alu_bit := bus_x(1)}
is ("b010".U(3.W)) {alu_bit := bus_x(2)}
is ("b011".U(3.W)) {alu_bit := bus_x(3)}
is ("b100".U(3.W)) {alu_bit := bus_x(4)}
is ("b101".U(3.W)) {alu_bit := bus_x(5)}
is ("b110".U(3.W)) {alu_bit := bus_x(6)}
is ("b111".U(3.W)) {alu_bit := bus_x(7)}
}
}
}
// Zero Bit Flag
val ff_bit_z = RegInit("b0".U(1.W))
when (io.ctrl.bit_z_wr) {ff_bit_z := alu_bit}
io.ctrl.bit_z := ff_bit_z
}
src/main/scala/mcu/cpu_control.scala:CPUの制御部記述
package mcu
import chisel3._
import chisel3.util._ // need it for switch statement
//-------------------------------
// CPU Internal Control Signals
//-------------------------------
class IO_CPU_CTRL extends Bundle
{
val reg_mar_wr = Output(Bool())
val reg_mwd_wr = Output(Bool())
val reg_mrd_wr = Output(Bool())
//
val opcode = Input(UInt(8.W))
val reg_pc_wr = Output(Bool())
val reg_r_wr = Output(Bool())
//
val bit_z = Input(Bool())
val bit_z_wr = Output(Bool())
//
val alu_out_op = Output(UInt(8.W))
val alu_bit_op = Output(UInt(4.W))
val bus_x_sel = Output(UInt(5.W))
val bus_y_sel = Output(UInt(5.W))
}
//
class IO_CPU_BUS_STRB extends Bundle
{
val mem_rd = Output(Bool())
val mem_wr = Output(Bool())
}
//---------------------
// CPU Control
//---------------------
class CPU_CONTROL extends Module
{
// Port
val io = IO(
new Bundle
{
val ctrl = new IO_CPU_CTRL()
val strb = new IO_CPU_BUS_STRB()
}
)
// State Machine
val state = Reg(init = CPU_STATE.INIT)
val state_next = Wire(UInt(8.W))
state := state_next
// Set Default Values of Control Signals
io.strb.mem_rd := false.B
io.strb.mem_wr := false.B
io.ctrl.reg_mar_wr := false.B
io.ctrl.reg_mrd_wr := false.B
io.ctrl.reg_mwd_wr := false.B
io.ctrl.reg_pc_wr := false.B
io.ctrl.reg_r_wr := false.B
io.ctrl.bit_z_wr := false.B
io.ctrl.alu_out_op := CPU_ALU_OUT.NOP;
io.ctrl.alu_bit_op := CPU_ALU_BIT.NOP;
io.ctrl.bus_x_sel := CPU_BUS_SEL.NOP;
io.ctrl.bus_y_sel := CPU_BUS_SEL.NOP;
state_next := CPU_STATE.INIT;
// Generate Control Signals and Next State
switch (state)
{
//---------------------------------------------------
// Initial State
is (CPU_STATE.INIT)
{
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// Instruction Fetch
is (CPU_STATE.IF_0)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.PC
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSX
io.ctrl.reg_mar_wr := true.B
state_next := CPU_STATE.IF_1
}
is (CPU_STATE.IF_1)
{
io.strb.mem_rd := true.B
io.ctrl.bus_x_sel := CPU_BUS_SEL.PC
io.ctrl.alu_out_op := CPU_ALU_OUT.INC
io.ctrl.reg_pc_wr := true.B
state_next := CPU_STATE.IF_2
}
is (CPU_STATE.IF_2)
{
io.ctrl.reg_mrd_wr := true.B
state_next := CPU_STATE.ID;
}
//---------------------------------------------------
// Instruction Decode
is (CPU_STATE.ID)
{
state_next := CPU_STATE.IF_0 // default
when (io.ctrl.opcode === BitPat("b000?????")) {state_next := CPU_STATE.EX_ADD_R_IMM_0}
when (io.ctrl.opcode === BitPat("b001?????")) {state_next := CPU_STATE.EX_MOV_R_IMM_0}
when (io.ctrl.opcode === BitPat("b010?????")) {state_next := CPU_STATE.EX_ADD_R_ADR_0}
when (io.ctrl.opcode === BitPat("b011?????")) {state_next := CPU_STATE.EX_MOV_R_ADR_0}
when (io.ctrl.opcode === BitPat("b100?????")) {state_next := CPU_STATE.EX_MOV_ADR_R_0}
when (io.ctrl.opcode === BitPat("b101?????")) {state_next := CPU_STATE.EX_JMP_ADR_0 }
when (io.ctrl.opcode === BitPat("b110?????")) {state_next := CPU_STATE.EX_JNZ_ADR_0 }
when (io.ctrl.opcode === BitPat("b11100???")) {state_next := CPU_STATE.EX_BIT_R_BIT_0}
}
//---------------------------------------------------
// ADD R, #imm5 000iiiii
is (CPU_STATE.EX_ADD_R_IMM_0)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.R
io.ctrl.bus_y_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.ADDYS5
io.ctrl.reg_r_wr := true.B
io.ctrl.alu_bit_op := CPU_ALU_BIT.ZERO
io.ctrl.bit_z_wr := true.B
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// MOV R, #imm5 001iiiii
is (CPU_STATE.EX_MOV_R_IMM_0)
{
io.ctrl.bus_y_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSYS5
io.ctrl.reg_r_wr := true.B
io.ctrl.alu_bit_op := CPU_ALU_BIT.ZERO
io.ctrl.bit_z_wr := true.B
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// ADD R, @adr5 010aaaaa
is (CPU_STATE.EX_ADD_R_ADR_0)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSX
io.ctrl.reg_mar_wr := true.B
state_next := CPU_STATE.EX_ADD_R_ADR_1
}
is (CPU_STATE.EX_ADD_R_ADR_1)
{
io.strb.mem_rd := true.B
state_next := CPU_STATE.EX_ADD_R_ADR_2
}
is (CPU_STATE.EX_ADD_R_ADR_2)
{
io.ctrl.reg_mrd_wr := true.B
state_next := CPU_STATE.EX_ADD_R_ADR_3
}
is (CPU_STATE.EX_ADD_R_ADR_3)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.R
io.ctrl.bus_y_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.ADD
io.ctrl.reg_r_wr := true.B
io.ctrl.alu_bit_op := CPU_ALU_BIT.ZERO
io.ctrl.bit_z_wr := true.B
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// MOV R, @adr5 010aaaaa
is (CPU_STATE.EX_MOV_R_ADR_0)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSX
io.ctrl.reg_mar_wr := true.B
state_next := CPU_STATE.EX_MOV_R_ADR_1
}
is (CPU_STATE.EX_MOV_R_ADR_1)
{
io.strb.mem_rd := true.B
state_next := CPU_STATE.EX_MOV_R_ADR_2
}
is (CPU_STATE.EX_MOV_R_ADR_2)
{
io.ctrl.reg_mrd_wr := true.B
state_next := CPU_STATE.EX_MOV_R_ADR_3
}
is (CPU_STATE.EX_MOV_R_ADR_3)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSX
io.ctrl.reg_r_wr := true.B
io.ctrl.alu_bit_op := CPU_ALU_BIT.ZERO
io.ctrl.bit_z_wr := true.B
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// MOV @adr5, R 100aaaaa
is (CPU_STATE.EX_MOV_ADR_R_0)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSX
io.ctrl.reg_mar_wr := true.B
state_next := CPU_STATE.EX_MOV_ADR_R_1
}
is (CPU_STATE.EX_MOV_ADR_R_1)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.R
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSX
io.ctrl.reg_mwd_wr := true.B
state_next := CPU_STATE.EX_MOV_ADR_R_2
}
is (CPU_STATE.EX_MOV_ADR_R_2)
{
io.strb.mem_wr := true.B
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// JMP addr5 101aaaaa
is (CPU_STATE.EX_JMP_ADR_0)
{
io.ctrl.bus_y_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSYU5
io.ctrl.reg_pc_wr := true.B
state_next := CPU_STATE.IF_0
}
//---------------------------------------------------
// JNZ addr5 101aaaaa
is (CPU_STATE.EX_JNZ_ADR_0)
{
when(io.ctrl.bit_z)
{
state_next := CPU_STATE.IF_0
}
.otherwise
{
io.ctrl.bus_y_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_out_op := CPU_ALU_OUT.BUSYU5
io.ctrl.reg_pc_wr := true.B
state_next := CPU_STATE.IF_0
}
}
//---------------------------------------------------
// BIT R, #bit3 11100bbb
is (CPU_STATE.EX_BIT_R_BIT_0)
{
io.ctrl.bus_x_sel := CPU_BUS_SEL.R
io.ctrl.bus_y_sel := CPU_BUS_SEL.MRD
io.ctrl.alu_bit_op := CPU_ALU_BIT.LOAD
io.ctrl.bit_z_wr := true.B
state_next := CPU_STATE.IF_0
}
}
}