Megatron-Chisel

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