This commit is contained in:
peteraa 2019-06-07 17:43:33 +02:00
commit 932413bb3d
61 changed files with 7249 additions and 0 deletions

57
src/main/scala/CPU.scala Normal file
View file

@ -0,0 +1,57 @@
package FiveStage
import chisel3._
import chisel3.core.Input
import chisel3.experimental.MultiIOModule
import chisel3.experimental._
class CPU extends MultiIOModule {
val testHarness = IO(
new Bundle {
val setupSignals = Input(new SetupSignals)
val testReadouts = Output(new TestReadouts)
val regUpdates = Output(new RegisterUpdates)
val memUpdates = Output(new MemUpdates)
val currentPC = Output(UInt(32.W))
}
)
/**
You need to create the classes for these yourself
*/
// val IFBarrier = Module(new IFBarrier).io
// val IDBarrier = Module(new IDBarrier).io
// val EXBarrier = Module(new EXBarrier).io
// val MEMBarrier = Module(new MEMBarrier).io
val ID = Module(new InstructionDecode)
val IF = Module(new InstructionFetch)
// val EX = Module(new Execute)
val MEM = Module(new MemoryFetch)
// val WB = Module(new Execute) (You may not need this one?)
/**
* Setup. You should not change this code
*/
IF.testHarness.IMEMsetup := testHarness.setupSignals.IMEMsignals
ID.testHarness.registerSetup := testHarness.setupSignals.registerSignals
MEM.testHarness.DMEMsetup := testHarness.setupSignals.DMEMsignals
testHarness.testReadouts.registerRead := ID.testHarness.registerPeek
testHarness.testReadouts.DMEMread := MEM.testHarness.DMEMpeek
/**
spying stuff
*/
testHarness.regUpdates := ID.testHarness.testUpdates
testHarness.memUpdates := MEM.testHarness.testUpdates
testHarness.currentPC := IF.testHarness.PC
/**
TODO: Your code here
*/
}

View file

@ -0,0 +1,60 @@
package FiveStage
import chisel3._
import chisel3.util._
import chisel3.core.Input
import chisel3.iotesters.PeekPokeTester
// From RISC-V reference card
object ALUOps {
val ADD = 0.U(4.W)
val SUB = 1.U(4.W)
val AND = 2.U(4.W)
val OR = 3.U(4.W)
val XOR = 4.U(4.W)
val SLT = 5.U(4.W)
val SLL = 6.U(4.W)
val SLTU = 7.U(4.W)
val SRL = 8.U(4.W)
val SRA = 9.U(4.W)
val COPY_A = 10.U(4.W)
val COPY_B = 11.U(4.W)
val DC = 15.U(4.W)
}
object lookup {
def BEQ = BitPat("b?????????????????000?????1100011")
def BNE = BitPat("b?????????????????001?????1100011")
def BLT = BitPat("b?????????????????100?????1100011")
def BGE = BitPat("b?????????????????101?????1100011")
def BLTU = BitPat("b?????????????????110?????1100011")
def BGEU = BitPat("b?????????????????111?????1100011")
def JALR = BitPat("b?????????????????000?????1100111")
def JAL = BitPat("b?????????????????????????1101111")
def LUI = BitPat("b?????????????????????????0110111")
def AUIPC = BitPat("b?????????????????????????0010111")
def ADDI = BitPat("b?????????????????000?????0010011")
def SLLI = BitPat("b000000???????????001?????0010011")
def SLTI = BitPat("b?????????????????010?????0010011")
def SLTIU = BitPat("b?????????????????011?????0010011")
def XORI = BitPat("b?????????????????100?????0010011")
def SRLI = BitPat("b000000???????????101?????0010011")
def SRAI = BitPat("b010000???????????101?????0010011")
def ORI = BitPat("b?????????????????110?????0010011")
def ANDI = BitPat("b?????????????????111?????0010011")
def ADD = BitPat("b0000000??????????000?????0110011")
def SUB = BitPat("b0100000??????????000?????0110011")
def SLL = BitPat("b0000000??????????001?????0110011")
def SLT = BitPat("b0000000??????????010?????0110011")
def SLTU = BitPat("b0000000??????????011?????0110011")
def XOR = BitPat("b0000000??????????100?????0110011")
def SRL = BitPat("b0000000??????????101?????0110011")
def SRA = BitPat("b0100000??????????101?????0110011")
def OR = BitPat("b0000000??????????110?????0110011")
def AND = BitPat("b0000000??????????111?????0110011")
def LW = BitPat("b?????????????????010?????0000011")
def SW = BitPat("b?????????????????010?????0100011")
}

54
src/main/scala/DMem.scala Normal file
View file

@ -0,0 +1,54 @@
package FiveStage
import chisel3._
import chisel3.experimental.MultiIOModule
/**
* This module is already done. Have one on me
*/
class DMEM extends MultiIOModule {
// Don't touch the test harness
val testHarness = IO(
new Bundle {
val setup = Input(new DMEMsetupSignals)
val testUpdates = Output(new MemUpdates)
})
val io = IO(
new Bundle {
val writeEnable = Input(Bool())
val dataIn = Input(UInt(32.W))
val dataAddress = Input(UInt(12.W))
val dataOut = Output(UInt(32.W))
})
val data = SyncReadMem(4096, UInt(32.W))
val addressSource = Wire(UInt(32.W))
val dataSource = Wire(UInt(32.W))
val writeEnableSource = Wire(Bool())
// For loading data
when(testHarness.setup.setup){
addressSource := testHarness.setup.dataAddress
dataSource := testHarness.setup.dataIn
writeEnableSource := testHarness.setup.writeEnable
}.otherwise {
addressSource := io.dataAddress
dataSource := io.dataIn
writeEnableSource := io.writeEnable
}
testHarness.testUpdates.writeEnable := writeEnableSource
testHarness.testUpdates.writeData := dataSource
testHarness.testUpdates.writeAddress := addressSource
io.dataOut := data(addressSource)
when(writeEnableSource){
data(addressSource) := dataSource
}
}

View file

@ -0,0 +1,82 @@
package FiveStage
import chisel3._
import chisel3.util.BitPat
import chisel3.util.ListLookup
/**
* This module is mostly done, but you will have to fill in the blanks in opcodeMap.
* You may want to add more signals to be decoded in this module depending on your
* design if you so desire.
*
* In the "classic" 5 stage decoder signals such as op1select and immType
* are not included, however I have added them to my design, and similarily you might
* find it useful to add more
*/
class Decoder() extends Module {
val io = IO(new Bundle {
val instruction = Input(new Instruction)
val controlSignals = Output(new ControlSignals)
val branchType = Output(UInt(3.W))
val op1Select = Output(UInt(1.W))
val op2Select = Output(UInt(1.W))
val immType = Output(UInt(3.W))
val ALUop = Output(UInt(4.W))
})
import lookup._
import Op1Select._
import Op2Select._
import branchType._
import ImmFormat._
val N = 0.asUInt(1.W)
val Y = 1.asUInt(1.W)
/**
* In scala we sometimes (ab)use the `->` operator to create tuples.
* The reason for this is that it serves as convenient sugar to make maps.
*
* This doesn't matter to you, just fill in the blanks in the style currently
* used, I just want to demystify some of the magic.
*
* `a -> b` == `(a, b)` == `Tuple2(a, b)`
*/
val opcodeMap: Array[(BitPat, List[UInt])] = Array(
// signal memToReg, regWrite, memRead, memWrite, branch, jump, branchType, Op1Select, Op2Select, ImmSelect, ALUOp
LW -> List(Y, Y, Y, N, N, N, branchType.DC, rs1, imm, ITYPE, ALUOps.ADD),
SW -> List(N, N, N, Y, N, N, branchType.DC, rs1, imm, STYPE, ALUOps.ADD),
ADD -> List(N, Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.ADD),
SUB -> List(N, Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.SUB),
/**
TODO: Fill in the blanks
*/
)
val NOP = List(N, N, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.DC)
val decodedControlSignals = ListLookup(
io.instruction.asUInt(),
NOP,
opcodeMap)
io.controlSignals.memToReg := decodedControlSignals(0)
io.controlSignals.regWrite := decodedControlSignals(1)
io.controlSignals.memRead := decodedControlSignals(2)
io.controlSignals.memWrite := decodedControlSignals(3)
io.controlSignals.branch := decodedControlSignals(4)
io.controlSignals.jump := decodedControlSignals(5)
io.branchType := decodedControlSignals(6)
io.op1Select := decodedControlSignals(7)
io.op2Select := decodedControlSignals(8)
io.immType := decodedControlSignals(9)
io.ALUop := decodedControlSignals(10)
}

49
src/main/scala/ID.scala Normal file
View file

@ -0,0 +1,49 @@
package FiveStage
import chisel3._
import chisel3.util.{ BitPat, MuxCase }
import chisel3.experimental.MultiIOModule
class InstructionDecode extends MultiIOModule {
// Don't touch the test harness
val testHarness = IO(
new Bundle {
val registerSetup = Input(new RegisterSetupSignals)
val registerPeek = Output(UInt(32.W))
val testUpdates = Output(new RegisterUpdates)
})
val io = IO(
new Bundle {
/**
* TODO: Your code here.
*/
}
)
val registers = Module(new Registers)
val decoder = Module(new Decoder).io
/**
* Setup. You should not change this code
*/
registers.testHarness.setup := testHarness.registerSetup
testHarness.registerPeek := registers.io.readData1
testHarness.testUpdates := registers.testHarness.testUpdates
/**
* TODO: Your code here.
*/
registers.io.readAddress1 := 0.U
registers.io.readAddress2 := 0.U
registers.io.writeEnable := false.B
registers.io.writeAddress := 0.U
registers.io.writeData := 0.U
decoder.instruction := 0.U.asTypeOf(new Instruction)
}

59
src/main/scala/IF.scala Normal file
View file

@ -0,0 +1,59 @@
package FiveStage
import chisel3._
import chisel3.experimental.MultiIOModule
class InstructionFetch extends MultiIOModule {
// Don't touch
val testHarness = IO(
new Bundle {
val IMEMsetup = Input(new IMEMsetupSignals)
val PC = Output(UInt())
}
)
/**
* TODO: Add signals for handling events such as jumps
*/
val io = IO(
new Bundle {
val PC = Output(UInt())
})
val IMEM = Module(new IMEM)
val PC = RegInit(UInt(32.W), 0.U)
/**
* Setup. You should not change this code
*/
IMEM.testHarness.setupSignals := testHarness.IMEMsetup
testHarness.PC := IMEM.testHarness.requestedAddress
/**
* TODO: Your code here.
*
* You should expand on or rewrite the code below.
*/
io.PC := PC
IMEM.io.instructionAddress := PC
PC := PC + 4.U
val instruction = Wire(new Instruction)
instruction := IMEM.io.instruction.asTypeOf(new Instruction)
/**
* Setup. You should not change this code
*/
when(testHarness.IMEMsetup.setup) {
PC := 0.U
// TODO: You must set the instruction to Instruction.NOP here.
// throw new Exception("Just making sure you're seeing the line above")
}
}

52
src/main/scala/IMem.scala Normal file
View file

@ -0,0 +1,52 @@
package FiveStage
import chisel3._
import chisel3.experimental.MultiIOModule
/**
* This module is finished and does not need to be modified to complete your fivestage.
*
* When setup is enabled data is written to the instruction memory.
* In normal operation this memory is write only (no self modifying code)
*/
class IMEM() extends MultiIOModule {
// Don't touch
val testHarness = IO(
new Bundle {
val setupSignals = Input(new IMEMsetupSignals)
val requestedAddress = Output(UInt())
}
)
val io = IO(
new Bundle {
val instructionAddress = Input(UInt(32.W))
val instruction = Output(UInt(32.W))
})
/**
SyncReadMem will output the value of the address signal set in the previous cycle.
*/
val instructions = SyncReadMem(4096, UInt(32.W))
// The address we want to read at during operation. During setup it acts as a write address
// leading to the somewhat uninformative name shown here.
val addressSource = Wire(UInt(32.W))
testHarness.requestedAddress := io.instructionAddress
when(testHarness.setupSignals.setup){
addressSource := testHarness.setupSignals.address
}.otherwise {
addressSource := io.instructionAddress
}
// For loading data
when(testHarness.setupSignals.setup){
instructions(addressSource) := testHarness.setupSignals.instruction
}
io.instruction := instructions(addressSource)
}

41
src/main/scala/MEM.scala Normal file
View file

@ -0,0 +1,41 @@
package FiveStage
import chisel3._
import chisel3.util._
import chisel3.experimental.MultiIOModule
class MemoryFetch() extends MultiIOModule {
// Don't touch the test harness
val testHarness = IO(
new Bundle {
val DMEMsetup = Input(new DMEMsetupSignals)
val DMEMpeek = Output(UInt(32.W))
val testUpdates = Output(new MemUpdates)
})
val io = IO(
new Bundle {
})
val DMEM = Module(new DMEM)
/**
* Setup. You should not change this code
*/
DMEM.testHarness.setup := testHarness.DMEMsetup
testHarness.DMEMpeek := DMEM.io.dataOut
testHarness.testUpdates := DMEM.testHarness.testUpdates
/**
* Your code here.
*/
DMEM.io.dataIn := 0.U
DMEM.io.dataAddress := 0.U
DMEM.io.writeEnable := false.B
}

View file

@ -0,0 +1,81 @@
package FiveStage
import chisel3._
import chisel3.experimental.MultiIOModule
/**
* This module is already done. Have one on me
*
* When a write and read conflicts this might result in stale data.
* This caveat must be handled using a bypass.
*/
class Registers() extends MultiIOModule {
// Don't touch the test harness
val testHarness = IO(
new Bundle {
val setup = Input(new RegisterSetupSignals)
val testUpdates = Output(new RegisterUpdates)
}
)
// You shouldn't really touch these either
val io = IO(
new Bundle {
val readAddress1 = Input(UInt(5.W))
val readAddress2 = Input(UInt(5.W))
val writeEnable = Input(Bool())
val writeAddress = Input(UInt(5.W))
val writeData = Input(UInt(32.W))
val readData1 = Output(UInt(32.W))
val readData2 = Output(UInt(32.W))
})
/**
* Mem creates asynchronous read, synchronous write.
* In other words, reading is combinatory.
*/
val registerFile = Mem(32, UInt(32.W))
val readAddress1 = Wire(UInt(5.W))
val readAddress2 = Wire(UInt(5.W))
val writeAddress = Wire(UInt(5.W))
val writeData = Wire(UInt(32.W))
val writeEnable = Wire(Bool())
when(testHarness.setup.setup){
readAddress1 := testHarness.setup.readAddress
readAddress2 := io.readAddress2
writeData := testHarness.setup.writeData
writeEnable := testHarness.setup.writeEnable
writeAddress := testHarness.setup.readAddress
}.otherwise{
readAddress1 := io.readAddress1
readAddress2 := io.readAddress2
writeData := io.writeData
writeEnable := io.writeEnable
writeAddress := io.writeAddress
}
testHarness.testUpdates.writeData := writeData
testHarness.testUpdates.writeEnable := writeEnable
testHarness.testUpdates.writeAddress := writeAddress
when(writeEnable){
when(writeAddress =/= 0.U){
registerFile(writeAddress) := writeData
}
}
io.readData1 := 0.U
io.readData2 := 0.U
when(readAddress1 =/= 0.U){ io.readData1 := registerFile(readAddress1) }
when(readAddress2 =/= 0.U){ io.readData2 := registerFile(readAddress2) }
}

View file

@ -0,0 +1,51 @@
package FiveStage
import chisel3._
import chisel3.core.Wire
import chisel3.util.{ BitPat, Cat }
/**
* Don't touch these
*/
class SetupSignals extends Bundle {
val IMEMsignals = new IMEMsetupSignals
val DMEMsignals = new DMEMsetupSignals
val registerSignals = new RegisterSetupSignals
}
class IMEMsetupSignals extends Bundle {
val setup = Bool()
val address = UInt(32.W)
val instruction = UInt(32.W)
}
class DMEMsetupSignals extends Bundle {
val setup = Bool()
val writeEnable = Bool()
val dataIn = UInt(32.W)
val dataAddress = UInt(32.W)
}
class RegisterSetupSignals extends Bundle {
val setup = Bool()
val readAddress = UInt(5.W)
val writeEnable = Bool()
val writeAddress = UInt(5.W)
val writeData = UInt(32.W)
}
class TestReadouts extends Bundle {
val registerRead = UInt(32.W)
val DMEMread = UInt(32.W)
}
class RegisterUpdates extends Bundle {
val writeEnable = Bool()
val writeData = UInt(32.W)
val writeAddress = UInt(5.W)
}
class MemUpdates extends Bundle {
val writeEnable = Bool()
val writeData = UInt(32.W)
val writeAddress = UInt(32.W)
}

75
src/main/scala/Tile.scala Normal file
View file

@ -0,0 +1,75 @@
package FiveStage
import chisel3._
import chisel3.util._
import chisel3.core.Input
import chisel3.iotesters.PeekPokeTester
/**
* The top level module. You do not have to change anything here,
* however you are free to route out signals as you see fit for debugging.
*/
class Tile() extends Module{
val io = IO(
new Bundle {
val DMEMWriteData = Input(UInt(32.W))
val DMEMAddress = Input(UInt(32.W))
val DMEMWriteEnable = Input(Bool())
val DMEMReadData = Output(UInt(32.W))
val regsWriteData = Input(UInt(32.W))
val regsAddress = Input(UInt(5.W))
val regsWriteEnable = Input(Bool())
val regsReadData = Output(UInt(32.W))
val regsDeviceWriteEnable = Output(Bool())
val regsDeviceWriteData = Output(UInt(32.W))
val regsDeviceWriteAddress = Output(UInt(5.W))
val memDeviceWriteEnable = Output(Bool())
val memDeviceWriteData = Output(UInt(32.W))
val memDeviceWriteAddress = Output(UInt(32.W))
val IMEMWriteData = Input(UInt(32.W))
val IMEMAddress = Input(UInt(32.W))
val setup = Input(Bool())
val currentPC = Output(UInt())
})
val CPU = Module(new CPU).testHarness
CPU.setupSignals.IMEMsignals.address := io.IMEMAddress
CPU.setupSignals.IMEMsignals.instruction := io.IMEMWriteData
CPU.setupSignals.IMEMsignals.setup := io.setup
CPU.setupSignals.DMEMsignals.writeEnable := io.DMEMWriteEnable
CPU.setupSignals.DMEMsignals.dataAddress := io.DMEMAddress
CPU.setupSignals.DMEMsignals.dataIn := io.DMEMWriteData
CPU.setupSignals.DMEMsignals.setup := io.setup
CPU.setupSignals.registerSignals.readAddress := io.regsAddress
CPU.setupSignals.registerSignals.writeEnable := io.regsWriteEnable
CPU.setupSignals.registerSignals.writeAddress := io.regsAddress
CPU.setupSignals.registerSignals.writeData := io.regsWriteData
CPU.setupSignals.registerSignals.setup := io.setup
io.DMEMReadData := CPU.testReadouts.DMEMread
io.regsReadData := CPU.testReadouts.registerRead
io.regsDeviceWriteAddress := CPU.regUpdates.writeAddress
io.regsDeviceWriteEnable := CPU.regUpdates.writeEnable
io.regsDeviceWriteData := CPU.regUpdates.writeData
io.memDeviceWriteAddress := CPU.memUpdates.writeAddress
io.memDeviceWriteEnable := CPU.memUpdates.writeEnable
io.memDeviceWriteData := CPU.memUpdates.writeData
io.currentPC := CPU.currentPC
}

View file

@ -0,0 +1,105 @@
package FiveStage
import chisel3._
import chisel3.core.Wire
import chisel3.util.{ BitPat, Cat }
class Instruction extends Bundle(){
val instruction = UInt(32.W)
def opcode = instruction(6, 0)
def registerRd = instruction(11, 7)
def funct3 = instruction(14, 12)
def registerRs1 = instruction(19, 15)
def registerRs2 = instruction(24, 20)
def funct7 = instruction(31, 25)
def funct6 = instruction(26, 31)
def immediateIType = instruction(31, 20).asSInt
def immediateSType = Cat(instruction(31, 25), instruction(11,7)).asSInt
def immediateBType = Cat(instruction(31), instruction(7), instruction(30, 25), instruction(11, 8), 0.U(1.W)).asSInt
def immediateUType = Cat(instruction(31, 12), 0.U(12.W)).asSInt
def immediateJType = Cat(instruction(31), instruction(19, 12), instruction(20), instruction(30, 25), instruction(24, 21), 0.U(1.W)).asSInt
def immediateZType = instruction(19, 15).zext
}
object Instruction {
def bubble(i: Instruction) =
i.opcode := BitPat.bitPatToUInt(BitPat("b0010011"))
def default: Instruction = {
val w = Wire(new Instruction)
w.instruction := 0.U
w
}
}
class ControlSignals extends Bundle(){
val memToReg = Bool()
val regWrite = Bool()
val memRead = Bool()
val memWrite = Bool()
val branch = Bool()
val jump = Bool()
}
object ControlSignals {
def nop: ControlSignals = {
val b = Wire(new ControlSignals)
b.memToReg := false.B
b.regWrite := false.B
b.memRead := false.B
b.memWrite := false.B
b.branch := false.B
b.jump := false.B
b
}
}
object branchType {
val beq = 0.asUInt(3.W)
val neq = 1.asUInt(3.W)
val gte = 2.asUInt(3.W)
val lt = 3.asUInt(3.W)
val gteu = 4.asUInt(3.W)
val ltu = 5.asUInt(3.W)
val jump = 6.asUInt(3.W)
val DC = 0.asUInt(3.W)
}
/**
these take the role of the alu source signal.
Used in the decoder.
In the solution manual I use these to select signals at the decode stage.
You can choose to instead do this in the execute stage, and you may forego
using them altogether.
*/
object Op1Select {
val rs1 = 0.asUInt(1.W)
val PC = 1.asUInt(1.W)
val DC = 0.asUInt(1.W)
}
object Op2Select {
val rs2 = 0.asUInt(1.W)
val imm = 1.asUInt(1.W)
val DC = 0.asUInt(1.W)
}
/**
Used in the decoder
*/
object ImmFormat {
val ITYPE = 0.asUInt(3.W)
val STYPE = 1.asUInt(3.W)
val BTYPE = 2.asUInt(3.W)
val UTYPE = 3.asUInt(3.W)
val JTYPE = 4.asUInt(3.W)
val SHAMT = 5.asUInt(3.W)
val DC = 0.asUInt(3.W)
}