Le projet Megatron-Chisel est une reproduction du projet Megatron, mais non plus cette fois-ci avec Logisim, mais avec le langage Chisel. Le langage Chisel est un langage haut niveau de description hardware, Il a permis avec un peu plus de facilité la reproduction du projet Megatron, avec en support des outils de test beaucoup plus évolués que ceux présents sur Logisim.
Code source
Cette partie va illustrer le code source des composantes les plus importantes du projet Megatron-Chisel :
Megatron.scala : Le fichier principale qui fait appel aux autres fichiers.
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class Megatron extends Module
{ val io = IO(new Bundle{ val GamepadIn = Input(Bool())
val KeyboardIn = Input(Bool())
val output1 = Output(UInt(8.W)) })
val datapath = Module(new DataPath)
val controlUnit = Module(new CU)
// connecting the datapath to the CU
controlUnit.io.opCode := datapath.io.opCode
controlUnit.io.acc7 := datapath.io.acc7
controlUnit.io.carry := datapath.io.carry
// connecting the CU to the datapath
datapath.io.dBusAccess := controlUnit.io.dBusAccess
datapath.io.ramAddrSel := controlUnit.io.ramAddrSel
datapath.io.ramWrite := controlUnit.io.ramWrite
datapath.io.xWrite := controlUnit.io.xWrite
datapath.io.xInc := controlUnit.io.xInc
datapath.io.yWrite := controlUnit.io.yWrite
datapath.io.accWrite := controlUnit.io.accWrite
datapath.io.iocWrite := controlUnit.io.iocWrite
datapath.io.inputEnble := controlUnit.io.inputEnble
datapath.io.outputEnble := controlUnit.io.outputEnble
datapath.io.ioCtlEnble := controlUnit.io.ioCtlEnble
datapath.io.pcHighWrite := controlUnit.io.pcHighWrite
datapath.io.pcLowWrite := controlUnit.io.pcLowWrite
datapath.io.aluFuct := controlUnit.io.aluFuct
// connecting the datapath to megatron inputs/outputs
datapath.io.GamepadIn := io.GamepadIn
datapath.io.KeyboardIn := io.KeyboardIn
io.output1 := datapath.io.output1
}
DataPath.scala : Le fichier du DataPath.
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class DataPath extends Module
{
val io = IO(new Bundle{ // Control signals
val dBusAccess = Input(UInt(2.W))
val ramAddrSel = Input(UInt(2.W))
val ramWrite = Input(Bool())
val xWrite = Input(Bool())
val xInc = Input(Bool())
val yWrite = Input(Bool())
val accWrite = Input(Bool())
val iocWrite = Input(Bool())
val inputEnble = Input(Bool())
val outputEnble = Input(Bool())
val ioCtlEnble = Input(Bool())
val pcHighWrite = Input(Bool())
val pcLowWrite = Input(Bool())
val aluFuct = Input(UInt(3.W))
// Gamepad and Keyboard serial inputs
val GamepadIn = Input(Bool())
val KeyboardIn = Input(Bool())
// Megatron outputs
val acc7 = Output(Bool())
val carry = Output(Bool())
val opCode = Output(UInt(8.W))
val output1 = Output(UInt(8.W)) })
// all components cration
val pc = Module(new counter16bit)
val rom = Module(new ROM)
val mau = Module(new MAU)
val ram = Module(new RAM)
val alu = Module(new ALU)
val iou = Module(new IOU)
val x = Module(new counter8bit)
val y = Module(new Register8bit)
val acc = Module(new Register8bit)
val ioc = Module(new Register8bit)
val out = Module(new Register8bit)
val keyboard_in = Module(new Shifter8bit)
val gamepad_in = Module(new Shifter8bit)
// datapath interconnecting components
val dataBus = WireDefault(0.U(8.W)) // Data Bus creation
val resultBus = WireDefault(0.U(8.W)) // ALU Result Bus creation
val inputBus = WireDefault(0.U(8.W)) // Input Bus creation
rom.io.addr := pc.io.out // PC interconnexions
pc.io.lowerIn := dataBus
pc.io.upperIn := y.io.out
io.opCode := rom.io.ir // ROM interconnexions
mau.io.data := rom.io.data
mau.io.y := y.io.out // MAU interconnexions
mau.io.x := x.io.out
ram.io.addr := mau.io.memAddr
ram.io.in := dataBus // RAM interconnexions
y.io.in := resultBus // y register interconnexions
x.io.in := resultBus // x register interconnexions
resultBus := alu.io.sum.asUInt // ALU interconnexions
alu.io.b := dataBus.asSInt
alu.io.a := acc.io.out.asSInt
acc.io.in := resultBus // AC register interconnexions
ioc.io.in := resultBus // IOC register interconnexions
out.io.in := resultBus // OUT register interconnexions
io.output1 := out.io.out
iou.io.in := ioc.io.out // OUI register interconnexions
dataBus := 0.U
switch(io.dBusAccess) // Data Bus access multiplexer
{
is(0.U)
{
dataBus := rom.io.data
}
is(1.U)
{
dataBus := ram.io.out
}
is(2.U)
{
dataBus := acc.io.out
}
is(3.U)
{
dataBus := inputBus
}
}
inputBus := 0.U
switch(iou.io.inputEnable) // Inputs to Data Bus encoder
{
is("b0001".U)
{
inputBus := gamepad_in.io.out
}
is("b0010".U)
{
inputBus := keyboard_in.io.out
}
is("b0100".U)
{
inputBus := 0.U
}
is("b1000".U)
{
inputBus := 0.U
}
}
// connexion of control signals to components
pc.io.upperWrite := io.pcHighWrite // pc control signals connexion
pc.io.lowerWrite := io.pcLowWrite
mau.io.highAddr := io.ramAddrSel(1).asBool // pmau control signals connexion
mau.io.lowAddr := io.ramAddrSel(0).asBool
ram.io.write := io.ramWrite // ram control signals connexion
x.io.write := io.xWrite // x control signals connexion
x.io.inc := io.xInc
y.io.write := io.yWrite // y control signals connexion
acc.io.write := io.accWrite // acc control signals connexion
ioc.io.write := io.iocWrite // ioc control signals connexion
alu.io.func := io.aluFuct // alu functions signals connexion
iou.io.inputEnCtr := io.inputEnble // iou control signals connexion
iou.io.outputEnCtr := io.outputEnble
iou.io.ioEnCtr := io.ioCtlEnble
io.acc7 := acc.io.out(7).asBool // control signals yield to CU
io.carry := alu.io.carry
// gamepad and keyboard serial and parallel clocks should be outsoursed and not controlled by IOC and IOU
gamepad_in.io.pallelClock := iou.io.periphralCtr(0).asBool // connecting the iou control signals to peripherals controls
keyboard_in.io.pallelClock := iou.io.periphralCtr(1).asBool
out.io.write := iou.io.outputWrite(0).asBool // connecting the iou control signal to output peripherals
gamepad_in.io.in := io.GamepadIn
keyboard_in.io.in := io.KeyboardIn
}
CU.scala : Le fichier de l’Unité de Contrôle
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class CU extends Module
{ val io = IO(new Bundle{ val opCode = Input(UInt(8.W))
val acc7 = Input(Bool())
val carry = Input(Bool())
val dBusAccess = Output(UInt(2.W))
val ramAddrSel = Output(UInt(2.W))
val ramWrite = Output(Bool())
val xWrite = Output(Bool())
val xInc = Output(Bool())
val yWrite = Output(Bool())
val accWrite = Output(Bool())
val iocWrite = Output(Bool())
val inputEnble = Output(Bool())
val outputEnble = Output(Bool())
val ioCtlEnble = Output(Bool())
val pcHighWrite = Output(Bool())
val pcLowWrite = Output(Bool())
val aluFuct = Output(UInt(3.W)) })
val ioc_ce_instr = (io.opCode(7,5) === "b110".U) & (io.opCode(1,0) === "b01".U) // detect the IOU instruction
io.dBusAccess := Mux(ioc_ce_instr, MuxLookup(io.opCode(4,2), "b01".U)(Seq(0.U -> "b01".U, 1.U -> "b01".U, 2.U -> "b01".U, 3.U -> "b01".U, 4.U -> "b00".U, 5.U -> "b00".U, 6.U -> "b10".U, 7.U -> "b11".U)) , io.opCode(1,0))
io.ramAddrSel := Mux((io.opCode(7,5) === "b111".U), "b00".U, ((io.opCode(4,2) === 2.U(3.W)) | (io.opCode(4,2) === 3.U(3.W)) | (io.opCode(4,2) === 7.U(3.W))) ## ((io.opCode(4,2) === 1.U(3.W)) | (io.opCode(4,2) === 3.U(3.W)) | (io.opCode(4,2) === 7.U(3.W))))
io.ramWrite := (io.opCode(7,5) === "b110".U) & (io.opCode(1,0) =/= "b01".U)
io.xWrite := (io.opCode(4,2) === 4.U(3.W)) & ~(io.opCode(7,5) === "b111".U) & ~ioc_ce_instr
io.xInc := (io.opCode(4,2) === 7.U(3.W)) & ~(io.opCode(7,5) === "b111".U) & ~ioc_ce_instr
io.yWrite := (io.opCode(4,2) === 5.U(3.W)) & ~(io.opCode(7,5) === "b111".U) & ~ioc_ce_instr
io.accWrite := ~io.opCode(4) & ~(io.opCode(7,6) === "b11".U)
io.iocWrite := (io.opCode(7,5) === "b110".U) & (io.opCode(1,0) === "b01".U) & ~io.ioCtlEnble // Check the instruction set table
io.inputEnble := (io.opCode(1,0) === "b11".U)
io.outputEnble := (io.opCode(4,3) === "b11".U) & ~(io.opCode(7,6) === "b11".U)
io.ioCtlEnble := io.opCode === "b110_101_01".U // Check the instruction set table
io.pcHighWrite := (io.opCode(7,5) === "b111".U) & (io.opCode(4,2) === "b000".U)
io.pcLowWrite := io.pcHighWrite | ((io.opCode(7,5) === "b111".U) & MuxLookup((io.carry ## io.acc7), 0.U)(Seq(0.U -> io.opCode(2), 1.U -> io.opCode(3), 2.U -> io.opCode(4), 3.U -> 0.U)))
io.aluFuct := Mux(ioc_ce_instr, "b000".U, io.opCode(7,5))
}
ALU.scala : Le fichier de l’Unité Arithmétique et Logique
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class ALU extends Module
{
val io = IO(new Bundle{ val a = Input(SInt(8.W))
val b = Input(SInt(8.W))
val func = Input(UInt(3.W))
val sum = Output(SInt(8.W))
val carry = Output(Bool()) })
io.sum := 0.S
switch(io.func)
{
is(0.U(3.W))
{
io.sum := io.b
}
is(1.U(3.W))
{
io.sum := io.a & io.b
}
is(2.U(3.W))
{
io.sum := io.a | io.b
}
is(3.U(3.W))
{
io.sum := io.a ^ io.b
}
is(4.U(3.W))
{
io.sum := io.a + io.b
}
is(5.U(3.W))
{
io.sum := io.a - io.b
}
is(6.U(3.W))
{
io.sum := io.a
}
is(7.U(3.W))
{
io.sum := -io.a
}
}
io.carry := io.a === 0.S
}
MAU.scala : Le fichier de l’Unité d’Adressage Mémoire
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class MAU extends Module
{
val io = IO(new Bundle{ val data = Input(UInt(8.W))
val x = Input(UInt(8.W))
val y = Input(UInt(8.W))
val highAddr = Input(Bool())
val lowAddr = Input(Bool())
val memAddr = Output(UInt(16.W)) })
val highOutput = Wire(UInt(8.W))
val lowOutput = Wire(UInt(8.W))
when(io.lowAddr)
{
lowOutput := io.x
}
.otherwise
{
lowOutput := io.data
}
when(io.highAddr)
{
highOutput := io.y
}
.otherwise
{
highOutput := 0.U
}
io.memAddr := highOutput ## lowOutput
}
IOU.scala : Le fichier de l’Unité d’Entrées/Sorties
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class IOU extends Module
{
val io = IO(new Bundle{ val in = Input(UInt(8.W))
val inputEnCtr = Input(Bool())
val outputEnCtr = Input(Bool())
val ioEnCtr = Input(Bool())
val inputEnable = Output(UInt(4.W))
val outputWrite = Output(UInt(4.W))
val periphralCtr = Output(UInt(8.W)) })
when(io.inputEnCtr)
{
io.inputEnable := io.in(3,0)
}
.otherwise
{
io.inputEnable := "b0000".U
}
when(io.outputEnCtr)
{
io.outputWrite := io.in(7,4)
}
.otherwise
{
io.outputWrite := "b0000".U
}
when(io.ioEnCtr)
{
io.periphralCtr := io.in
}
.otherwise
{
io.periphralCtr := "b0000_0000".U
}
}
RAM.scala : Le fichier de la RAM
package megatron
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
import chisel3.util.experimental.loadMemoryFromFileInline
class RAM extends Module
{
val io = IO(new Bundle{ val in = Input(UInt(8.W))
val addr = Input(UInt(16.W))
val write = Input(Bool())
val out = Output(UInt(8.W)) })
val negClock = (~clock.asUInt).asBool.asClock
withClock(negClock)
{
val ram = Mem(65_536, UInt(8.W))
when(io.write)
{
ram.write(io.addr, io.in)
}
io.out := ram.read(io.addr)
// loadMemoryFromFileInline(ram, "/home/snakeas/Megatron-Chisel/src/main/resources/RAM.hex")
}
}
ROM.scala : Le fichier de la ROM
package megatron
import chisel3._
import chisel3.util._
import chisel3.util.experimental.loadMemoryFromFileInline
import _root_.circt.stage.ChiselStage
class ROM extends Module
{
val io = IO(new Bundle{ val addr = Input(UInt(16.W))
val ir = Output(UInt(8.W))
val data = Output(UInt(8.W)) })
val rom = Mem(65_536, UInt(16.W))
val output = rom.read(io.addr)
loadMemoryFromFileInline(rom, "/home/snakeas/Megatron-Chisel/src/main/resources/ROM.hex")
//loadMemoryFromFileInline(rom, "/home/snakeas/Megatron-Chisel/src/main/resources/Factorial.hex")
//loadMemoryFromFileInline(rom, "/home/snakeas/Megatron-Chisel/src/main/resources/Fibonacci.hex")
io.ir := output(7,0)
io.data := output(15,8)
}
Dépot GitHub
Le dépôt GitHub contient tout le projet avec la structure complète des répertoires et fichiers. La page du projet donne aussi une petite introduction pour comment installer et utiliser le compilateur de Chisel.
Le lien du dépôt : https://github.com/kara-abdelaziz/Megatron-Chisel