Formats code
This commit is contained in:
5
.scalafmt.conf
Normal file
5
.scalafmt.conf
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
version = "3.7.15"
|
||||||
|
runner.dialect = scala213
|
||||||
|
|
||||||
|
indent.main = 4
|
||||||
|
indent.callSite = 4
|
||||||
@@ -3,50 +3,54 @@ package common
|
|||||||
import chisel3._
|
import chisel3._
|
||||||
import _root_.circt.stage.ChiselStage
|
import _root_.circt.stage.ChiselStage
|
||||||
|
|
||||||
|
|
||||||
object EmitModule {
|
object EmitModule {
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
println("Usage: mill ... common.EmitModule <ModuleName> [constructor args...]")
|
println(
|
||||||
sys.exit(1)
|
"Usage: mill ... common.EmitModule <ModuleName> [constructor args...]"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val moduleName = args(0)
|
||||||
|
val ctorArgs = args.drop(1) // reste des arguments (strings)
|
||||||
|
|
||||||
|
// Reflection scala : charger dynamiquement un module
|
||||||
|
val cls = Class.forName(moduleName)
|
||||||
|
val ctor = cls.getConstructors.head
|
||||||
|
|
||||||
|
// Fonction de conversion string -> objet typé
|
||||||
|
def parseArg(s: String): AnyRef = {
|
||||||
|
// essai en Int
|
||||||
|
if (s.matches("^-?\\d+$")) {
|
||||||
|
Integer.valueOf(s.toInt)
|
||||||
|
}
|
||||||
|
// essai en Bool
|
||||||
|
else if (
|
||||||
|
s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")
|
||||||
|
) {
|
||||||
|
java.lang.Boolean.valueOf(s.toBoolean)
|
||||||
|
}
|
||||||
|
// sinon, on laisse en String
|
||||||
|
else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val generator = () => {
|
||||||
|
if (ctorArgs.isEmpty) {
|
||||||
|
ctor.newInstance().asInstanceOf[RawModule]
|
||||||
|
} else {
|
||||||
|
val argsObjects: Array[Object] = ctorArgs.map(parseArg).toArray
|
||||||
|
ctor.newInstance(argsObjects: _*).asInstanceOf[RawModule]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChiselStage.emitSystemVerilogFile(
|
||||||
|
gen = generator(),
|
||||||
|
firtoolOpts =
|
||||||
|
Array("-disable-all-randomization", "-strip-debug-info"),
|
||||||
|
args = Array("--target-dir", s"RTL/$moduleName")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val moduleName = args(0)
|
|
||||||
val ctorArgs = args.drop(1) // reste des arguments (strings)
|
|
||||||
|
|
||||||
// Reflection scala : charger dynamiquement un module
|
|
||||||
val cls = Class.forName(moduleName)
|
|
||||||
val ctor = cls.getConstructors.head
|
|
||||||
|
|
||||||
// Fonction de conversion string -> objet typé
|
|
||||||
def parseArg(s: String): AnyRef = {
|
|
||||||
// essai en Int
|
|
||||||
if (s.matches("^-?\\d+$")) {
|
|
||||||
Integer.valueOf(s.toInt)
|
|
||||||
}
|
|
||||||
// essai en Bool
|
|
||||||
else if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")) {
|
|
||||||
java.lang.Boolean.valueOf(s.toBoolean)
|
|
||||||
}
|
|
||||||
// sinon, on laisse en String
|
|
||||||
else {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val generator = () => {
|
|
||||||
if (ctorArgs.isEmpty) {
|
|
||||||
ctor.newInstance().asInstanceOf[RawModule]
|
|
||||||
} else {
|
|
||||||
val argsObjects: Array[Object] = ctorArgs.map(parseArg).toArray
|
|
||||||
ctor.newInstance(argsObjects: _*).asInstanceOf[RawModule]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ChiselStage.emitSystemVerilogFile(
|
|
||||||
gen = generator(),
|
|
||||||
firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info"),
|
|
||||||
args = Array("--target-dir", s"RTL/$moduleName")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,84 +6,91 @@ import chisel3.util._
|
|||||||
// Bus très simple : au temps t le maître fait une requête
|
// Bus très simple : au temps t le maître fait une requête
|
||||||
// au temps t+1, il a la réponse, sinon c'est mort !
|
// au temps t+1, il a la réponse, sinon c'est mort !
|
||||||
|
|
||||||
// Signaux utilisés par le bus, coté esclave, on fait un
|
// Signaux utilisés par le bus, coté esclave, on fait un
|
||||||
// Flip pour l'avoir coté maître ensuite
|
// Flip pour l'avoir coté maître ensuite
|
||||||
class BusInterface extends Bundle {
|
class BusInterface extends Bundle {
|
||||||
val addr = Input(UInt(32.W)) // adresse cible de la transaction
|
val addr = Input(UInt(32.W)) // adresse cible de la transaction
|
||||||
val wdata = Input(UInt(32.W)) // donnée en écriture (max 32 bits)
|
val wdata = Input(UInt(32.W)) // donnée en écriture (max 32 bits)
|
||||||
val rdata = Output(UInt(32.W)) // donnée lue de l'esclave, avec 1 cycle de retard
|
val rdata = Output(
|
||||||
val be = Input(Vec(4, Bool())) // byte-enable pour les accès écriture mémoire
|
UInt(32.W)
|
||||||
val en = Input(Bool()) // enable de la mémoire, read si be.orR est égal à 0, écriture sinon
|
) // donnée lue de l'esclave, avec 1 cycle de retard
|
||||||
|
val be = Input(
|
||||||
|
Vec(4, Bool())
|
||||||
|
) // byte-enable pour les accès écriture mémoire
|
||||||
|
val en = Input(
|
||||||
|
Bool()
|
||||||
|
) // enable de la mémoire, read si be.orR est égal à 0, écriture sinon
|
||||||
}
|
}
|
||||||
|
|
||||||
class BusInterconnect extends Module {
|
class BusInterconnect extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val master = new BusInterface // Bus avec un unique maître
|
val master = new BusInterface // Bus avec un unique maître
|
||||||
val dmem = Flipped(new BusInterface) // et plusieurs esclaves
|
val dmem = Flipped(new BusInterface) // et plusieurs esclaves
|
||||||
val ldip = Flipped(new BusInterface)
|
val ldip = Flipped(new BusInterface)
|
||||||
val clnt = Flipped(new BusInterface)
|
val clnt = Flipped(new BusInterface)
|
||||||
val vmem = Flipped(new BusInterface)
|
val vmem = Flipped(new BusInterface)
|
||||||
})
|
})
|
||||||
|
|
||||||
// On récupère l'adresse émise par le maître pour trouver à quel
|
// On récupère l'adresse émise par le maître pour trouver à quel
|
||||||
// esclave elle fait référence
|
// esclave elle fait référence
|
||||||
val addr = io.master.addr
|
val addr = io.master.addr
|
||||||
|
|
||||||
// On reprend nos bonnes vielles adresses, pas très efficaces, mais ça fait le job
|
|
||||||
// -- Slave 0 1 2 3 4 5
|
|
||||||
// -- Name RAM prog | IP led | IP pin | IP PLIC | IP_CLINT | DDR
|
|
||||||
// BASE => ( X"0000_1000", X"3000_0000", X"3000_0008", X"0C00_0000", X"0200_0000", X"8000_0000"),
|
|
||||||
// HIGH => ( X"0000_8FFF", X"3000_0004", X"3000_0008", X"1000_0000", X"0200_C000", X"8FFF_FFFF")
|
|
||||||
|
|
||||||
val DMEM_BASE = 0x00000000L.U // Une mémoire unifiée code + données
|
// On reprend nos bonnes vielles adresses, pas très efficaces, mais ça fait le job
|
||||||
val DMEM_SIZE = 0x00010000L.U
|
// -- Slave 0 1 2 3 4 5
|
||||||
|
// -- Name RAM prog | IP led | IP pin | IP PLIC | IP_CLINT | DDR
|
||||||
|
// BASE => ( X"0000_1000", X"3000_0000", X"3000_0008", X"0C00_0000", X"0200_0000", X"8000_0000"),
|
||||||
|
// HIGH => ( X"0000_8FFF", X"3000_0004", X"3000_0008", X"1000_0000", X"0200_C000", X"8FFF_FFFF")
|
||||||
|
|
||||||
val LDIP_BASE = 0x30000000L.U
|
val DMEM_BASE = 0x00000000L.U // Une mémoire unifiée code + données
|
||||||
val LDIP_SIZE = 0x00000004L.U
|
val DMEM_SIZE = 0x00010000L.U
|
||||||
|
|
||||||
val CLNT_BASE = 0x02000000L.U
|
val LDIP_BASE = 0x30000000L.U
|
||||||
val CLNT_SIZE = 0x0000C000L.U
|
val LDIP_SIZE = 0x00000004L.U
|
||||||
|
|
||||||
val VMEM_BASE = 0x80000000L.U // Mémoire vidéo dual port
|
val CLNT_BASE = 0x02000000L.U
|
||||||
val VMEM_SIZE = 0x00020000L.U
|
val CLNT_SIZE = 0x0000c000L.U
|
||||||
|
|
||||||
val is_dmem = addr >= DMEM_BASE && addr < (DMEM_BASE + DMEM_SIZE)
|
val VMEM_BASE = 0x80000000L.U // Mémoire vidéo dual port
|
||||||
val is_ldip = addr >= LDIP_BASE && addr < (LDIP_BASE + LDIP_SIZE)
|
val VMEM_SIZE = 0x00020000L.U
|
||||||
val is_clnt = addr >= CLNT_BASE && addr < (CLNT_BASE + CLNT_SIZE)
|
|
||||||
val is_vmem = addr >= VMEM_BASE && addr < (VMEM_BASE + VMEM_SIZE)
|
|
||||||
|
|
||||||
// On connecte directement ce qui doit l'être en faisant l'hypothèse que
|
val is_dmem = addr >= DMEM_BASE && addr < (DMEM_BASE + DMEM_SIZE)
|
||||||
io.dmem.en := io.master.en && is_dmem
|
val is_ldip = addr >= LDIP_BASE && addr < (LDIP_BASE + LDIP_SIZE)
|
||||||
io.ldip.en := io.master.en && is_ldip
|
val is_clnt = addr >= CLNT_BASE && addr < (CLNT_BASE + CLNT_SIZE)
|
||||||
io.clnt.en := io.master.en && is_clnt
|
val is_vmem = addr >= VMEM_BASE && addr < (VMEM_BASE + VMEM_SIZE)
|
||||||
io.vmem.en := io.master.en && is_vmem
|
|
||||||
// la chose connectée à ses adresses qui commencent à zéro
|
|
||||||
io.dmem.addr := io.master.addr - DMEM_BASE
|
|
||||||
io.ldip.addr := io.master.addr - LDIP_BASE
|
|
||||||
io.clnt.addr := io.master.addr - CLNT_BASE
|
|
||||||
io.vmem.addr := io.master.addr - VMEM_BASE
|
|
||||||
io.dmem.wdata := io.master.wdata
|
|
||||||
io.ldip.wdata := io.master.wdata
|
|
||||||
io.clnt.wdata := io.master.wdata
|
|
||||||
io.vmem.wdata := io.master.wdata
|
|
||||||
io.dmem.be := io.master.be
|
|
||||||
io.ldip.be := io.master.be
|
|
||||||
io.clnt.be := io.master.be
|
|
||||||
io.vmem.be := io.master.be
|
|
||||||
|
|
||||||
// On doit décaler d'un cycle (latence de l'accès mémoire)
|
// On connecte directement ce qui doit l'être en faisant l'hypothèse que
|
||||||
// les signaux qui multiplexent la donnée en sortie
|
io.dmem.en := io.master.en && is_dmem
|
||||||
val is_pdmem = RegNext(is_dmem)
|
io.ldip.en := io.master.en && is_ldip
|
||||||
val is_pldip = RegNext(is_ldip)
|
io.clnt.en := io.master.en && is_clnt
|
||||||
val is_pclnt = RegNext(is_clnt)
|
io.vmem.en := io.master.en && is_vmem
|
||||||
val is_pvmem = RegNext(is_vmem)
|
// la chose connectée à ses adresses qui commencent à zéro
|
||||||
|
io.dmem.addr := io.master.addr - DMEM_BASE
|
||||||
|
io.ldip.addr := io.master.addr - LDIP_BASE
|
||||||
|
io.clnt.addr := io.master.addr - CLNT_BASE
|
||||||
|
io.vmem.addr := io.master.addr - VMEM_BASE
|
||||||
|
io.dmem.wdata := io.master.wdata
|
||||||
|
io.ldip.wdata := io.master.wdata
|
||||||
|
io.clnt.wdata := io.master.wdata
|
||||||
|
io.vmem.wdata := io.master.wdata
|
||||||
|
io.dmem.be := io.master.be
|
||||||
|
io.ldip.be := io.master.be
|
||||||
|
io.clnt.be := io.master.be
|
||||||
|
io.vmem.be := io.master.be
|
||||||
|
|
||||||
io.master.rdata := MuxCase(0x0BADADDL.U(32.W),
|
// On doit décaler d'un cycle (latence de l'accès mémoire)
|
||||||
Seq(
|
// les signaux qui multiplexent la donnée en sortie
|
||||||
is_pdmem -> io.dmem.rdata,
|
val is_pdmem = RegNext(is_dmem)
|
||||||
is_pldip -> io.ldip.rdata,
|
val is_pldip = RegNext(is_ldip)
|
||||||
is_pclnt -> io.clnt.rdata,
|
val is_pclnt = RegNext(is_clnt)
|
||||||
is_pvmem -> io.vmem.rdata
|
val is_pvmem = RegNext(is_vmem)
|
||||||
|
|
||||||
|
io.master.rdata := MuxCase(
|
||||||
|
0x0badaddL.U(32.W),
|
||||||
|
Seq(
|
||||||
|
is_pdmem -> io.dmem.rdata,
|
||||||
|
is_pldip -> io.ldip.rdata,
|
||||||
|
is_pclnt -> io.clnt.rdata,
|
||||||
|
is_pvmem -> io.vmem.rdata
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,52 +5,52 @@ import chisel3._
|
|||||||
* Principalement le timer pour pouvoir faire tourner space invaders !
|
* Principalement le timer pour pouvoir faire tourner space invaders !
|
||||||
\*/
|
\*/
|
||||||
class CLint extends Module {
|
class CLint extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val bus = new BusInterface
|
val bus = new BusInterface
|
||||||
val tip = Output(Bool())
|
val tip = Output(Bool())
|
||||||
})
|
})
|
||||||
|
|
||||||
// Adresses des deux registres 64 bits du composant
|
// Adresses des deux registres 64 bits du composant
|
||||||
val MTIMERCMP_ADDR = 0x4000
|
val MTIMERCMP_ADDR = 0x4000
|
||||||
val MTIMER_ADDR = 0xBFF8
|
val MTIMER_ADDR = 0xbff8
|
||||||
|
|
||||||
// Sur le rv32i le timer est constitué de 2 registres pour faire 64 bits
|
// Sur le rv32i le timer est constitué de 2 registres pour faire 64 bits
|
||||||
val mtime_lo = RegInit(0.U(32.W))
|
val mtime_lo = RegInit(0.U(32.W))
|
||||||
val mtime_hi = RegInit(0.U(32.W))
|
val mtime_hi = RegInit(0.U(32.W))
|
||||||
|
|
||||||
// Free running, comme on dit chez nous !
|
// Free running, comme on dit chez nous !
|
||||||
mtime_lo := mtime_lo + 1.U
|
mtime_lo := mtime_lo + 1.U
|
||||||
mtime_hi := Mux(mtime_lo + 1.U === 0.U, mtime_hi + 1.U, mtime_hi)
|
mtime_hi := Mux(mtime_lo + 1.U === 0.U, mtime_hi + 1.U, mtime_hi)
|
||||||
|
|
||||||
// La comparaison nécessite également 2 registres, pour une
|
// La comparaison nécessite également 2 registres, pour une
|
||||||
// comparaison sur 64 bits si nécessaire
|
// comparaison sur 64 bits si nécessaire
|
||||||
val mtimecmp_lo = RegInit(0.U(32.W))
|
val mtimecmp_lo = RegInit(0.U(32.W))
|
||||||
val mtimecmp_hi = RegInit(0.U(32.W))
|
val mtimecmp_hi = RegInit(0.U(32.W))
|
||||||
// Sortie mtimer interrupt pending dont on se moque pour l'instant
|
// Sortie mtimer interrupt pending dont on se moque pour l'instant
|
||||||
io.tip := (mtime_hi > mtimecmp_hi) || ((mtime_hi === mtimecmp_hi) && (mtime_lo >= mtimecmp_lo))
|
io.tip := (mtime_hi > mtimecmp_hi) || ((mtime_hi === mtimecmp_hi) && (mtime_lo >= mtimecmp_lo))
|
||||||
// Valeur par défaut sur le bus de lecture
|
// Valeur par défaut sur le bus de lecture
|
||||||
// avec toujours un cycle de latence
|
// avec toujours un cycle de latence
|
||||||
val clintRegValue = RegInit(0.U(32.W))
|
val clintRegValue = RegInit(0.U(32.W))
|
||||||
|
|
||||||
io.bus.rdata := clintRegValue
|
io.bus.rdata := clintRegValue
|
||||||
|
|
||||||
when(io.bus.en === true.B) {
|
when(io.bus.en === true.B) {
|
||||||
when (io.bus.be.asUInt.orR) { // Écriture
|
when(io.bus.be.asUInt.orR) { // Écriture
|
||||||
when(io.bus.addr === MTIMERCMP_ADDR.U) {
|
when(io.bus.addr === MTIMERCMP_ADDR.U) {
|
||||||
mtimecmp_lo := io.bus.wdata
|
mtimecmp_lo := io.bus.wdata
|
||||||
} .elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) {
|
}.elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) {
|
||||||
mtimecmp_hi := io.bus.wdata
|
mtimecmp_hi := io.bus.wdata
|
||||||
}
|
}
|
||||||
} .otherwise { // Lecture
|
}.otherwise { // Lecture
|
||||||
when(io.bus.addr === MTIMER_ADDR.U) {
|
when(io.bus.addr === MTIMER_ADDR.U) {
|
||||||
clintRegValue := mtime_lo
|
clintRegValue := mtime_lo
|
||||||
} .elsewhen(io.bus.addr === (MTIMER_ADDR + 4).U) {
|
}.elsewhen(io.bus.addr === (MTIMER_ADDR + 4).U) {
|
||||||
clintRegValue := mtime_hi
|
clintRegValue := mtime_hi
|
||||||
} .elsewhen(io.bus.addr === MTIMERCMP_ADDR.U) {
|
}.elsewhen(io.bus.addr === MTIMERCMP_ADDR.U) {
|
||||||
clintRegValue := mtimecmp_lo
|
clintRegValue := mtimecmp_lo
|
||||||
} .elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) {
|
}.elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) {
|
||||||
clintRegValue := mtimecmp_hi
|
clintRegValue := mtimecmp_hi
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,56 +9,56 @@ import chisel3.util._
|
|||||||
* Le nom *doit* être celui qui est dans le systemverilog
|
* Le nom *doit* être celui qui est dans le systemverilog
|
||||||
* (qui est dans TP/common/clk_wiz_0_clk_wiz.sv)
|
* (qui est dans TP/common/clk_wiz_0_clk_wiz.sv)
|
||||||
\*/
|
\*/
|
||||||
class clk_wiz extends BlackBox with HasBlackBoxResource {
|
class clk_wiz extends BlackBox with HasBlackBoxResource {
|
||||||
|
|
||||||
addResource("/vsrc/clk_wiz.v")
|
addResource("/vsrc/clk_wiz.v")
|
||||||
|
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val clk_in1 = Input(Clock())
|
val clk_in1 = Input(Clock())
|
||||||
val reset = Input(Reset())
|
val reset = Input(Reset())
|
||||||
val clk_out1 = Output(Clock()) // 10 MHz
|
val clk_out1 = Output(Clock()) // 10 MHz
|
||||||
val clk_out2 = Output(Clock()) // 125 MHz
|
val clk_out2 = Output(Clock()) // 125 MHz
|
||||||
val locked = Output(Bool())
|
val locked = Output(Bool())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClockGen(val divBy: Int = 10, useSim: Boolean = false) extends Module {
|
class ClockGen(val divBy: Int = 10, useSim: Boolean = false) extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val clk_in1 = Input(Clock())
|
val clk_in1 = Input(Clock())
|
||||||
val reset = Input(Reset())
|
val reset = Input(Reset())
|
||||||
val clk_out1 = Output(Clock())
|
val clk_out1 = Output(Clock())
|
||||||
val clk_out2 = Output(Clock())
|
val clk_out2 = Output(Clock())
|
||||||
val locked = Output(Bool())
|
val locked = Output(Bool())
|
||||||
})
|
})
|
||||||
|
|
||||||
val maxCnt = (divBy - 1).U
|
val maxCnt = (divBy - 1).U
|
||||||
val midCnt = ((divBy / 2) - 1).U
|
val midCnt = ((divBy / 2) - 1).U
|
||||||
|
|
||||||
if (useSim) {
|
if (useSim) {
|
||||||
val cntReg = RegInit(0.U(log2Ceil(divBy).W))
|
val cntReg = RegInit(0.U(log2Ceil(divBy).W))
|
||||||
cntReg := cntReg + 1.U
|
cntReg := cntReg + 1.U
|
||||||
val outReg = RegInit(false.B)
|
val outReg = RegInit(false.B)
|
||||||
val rstReg = RegInit(true.B)
|
val rstReg = RegInit(true.B)
|
||||||
|
|
||||||
rstReg := Mux(io.reset.asBool, true.B, rstReg)
|
|
||||||
|
|
||||||
io.locked := !rstReg
|
rstReg := Mux(io.reset.asBool, true.B, rstReg)
|
||||||
|
|
||||||
when (cntReg === midCnt) {
|
io.locked := !rstReg
|
||||||
outReg := true.B
|
|
||||||
} .elsewhen (cntReg === maxCnt) {
|
when(cntReg === midCnt) {
|
||||||
outReg := false.B
|
outReg := true.B
|
||||||
cntReg := 0.U
|
}.elsewhen(cntReg === maxCnt) {
|
||||||
rstReg := false.B
|
outReg := false.B
|
||||||
|
cntReg := 0.U
|
||||||
|
rstReg := false.B
|
||||||
|
}
|
||||||
|
io.clk_out1 := outReg.asClock
|
||||||
|
io.clk_out2 := io.clk_in1
|
||||||
|
} else {
|
||||||
|
val ckgen = Module(new clk_wiz)
|
||||||
|
ckgen.io.clk_in1 := io.clk_in1
|
||||||
|
ckgen.io.reset := io.reset
|
||||||
|
io.clk_out1 := ckgen.io.clk_out1
|
||||||
|
io.clk_out2 := ckgen.io.clk_out2
|
||||||
|
io.locked := ckgen.io.locked
|
||||||
}
|
}
|
||||||
io.clk_out1 := outReg.asClock
|
|
||||||
io.clk_out2 := io.clk_in1
|
|
||||||
} else {
|
|
||||||
val ckgen = Module(new clk_wiz)
|
|
||||||
ckgen.io.clk_in1 := io.clk_in1
|
|
||||||
ckgen.io.reset := io.reset
|
|
||||||
io.clk_out1 := ckgen.io.clk_out1
|
|
||||||
io.clk_out2 := ckgen.io.clk_out2
|
|
||||||
io.locked := ckgen.io.locked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,26 +3,32 @@ import chisel3._
|
|||||||
|
|
||||||
// Déclaration d'un classe Compteur très générique
|
// Déclaration d'un classe Compteur très générique
|
||||||
class Compteur(
|
class Compteur(
|
||||||
n: Int, // Le paramètre n permet de fixer le nombre de bits du compteur.
|
n: Int, // Le paramètre n permet de fixer le nombre de bits du compteur.
|
||||||
hasEnable: Boolean = false, // Le paramètre hasEnable permet d'ajouter une entrée optionnelle pour autoriser l'incrémentation.
|
hasEnable: Boolean =
|
||||||
hasMax: Boolean = false // Le paramètre hasMax permet d'ajouter : - une entrée optionnelle max, qui fait reboucler le compteur après qu'il est atteint la valeur fournie.
|
false, // Le paramètre hasEnable permet d'ajouter une entrée optionnelle pour autoriser l'incrémentation.
|
||||||
// - une sortie optionnelle atMax, qui vaut 1 lorsque le compteur atteint la valeur max définie.
|
hasMax: Boolean =
|
||||||
) extends Module {
|
false // Le paramètre hasMax permet d'ajouter : - une entrée optionnelle max, qui fait reboucler le compteur après qu'il est atteint la valeur fournie.
|
||||||
|
// - une sortie optionnelle atMax, qui vaut 1 lorsque le compteur atteint la valeur max définie.
|
||||||
|
) extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
// Grâce au parametre hasEnable, on peut activer optionnellement le port d'entrée en.
|
// Grâce au parametre hasEnable, on peut activer optionnellement le port d'entrée en.
|
||||||
// En scala, la classe option a 2 classes dérivées : Some et None.
|
// En scala, la classe option a 2 classes dérivées : Some et None.
|
||||||
// Some permet donc de wrapper notre type optionnel.
|
// Some permet donc de wrapper notre type optionnel.
|
||||||
val en = if (hasEnable) Some(Input(Bool())) else None
|
val en = if (hasEnable) Some(Input(Bool())) else None
|
||||||
val max = if (hasMax) Some(Input(UInt(n.W))) else None
|
val max = if (hasMax) Some(Input(UInt(n.W))) else None
|
||||||
val atMax = if (hasMax) Some(Output(Bool())) else None
|
val atMax = if (hasMax) Some(Output(Bool())) else None
|
||||||
val out = Output(UInt(n.W)) // On utilise le paramètre n pour remplacer la constante de l'exercice prédédent.
|
val out = Output(
|
||||||
|
UInt(n.W)
|
||||||
|
) // On utilise le paramètre n pour remplacer la constante de l'exercice prédédent.
|
||||||
})
|
})
|
||||||
|
|
||||||
val compteur = RegInit(0.U(n.W))
|
val compteur = RegInit(0.U(n.W))
|
||||||
|
|
||||||
// condition d'incrément
|
// condition d'incrément
|
||||||
val doInc = if (hasEnable) io.en.get else true.B // Attention, notez qu'il faut utiliser l'opérateur get pour récupérer l'option wrappée par le Some.
|
val doInc =
|
||||||
|
if (hasEnable) io.en.get
|
||||||
|
else
|
||||||
|
true.B // Attention, notez qu'il faut utiliser l'opérateur get pour récupérer l'option wrappée par le Some.
|
||||||
|
|
||||||
// condition d'égalité au maximum
|
// condition d'égalité au maximum
|
||||||
val atMax = if (hasMax) compteur === io.max.get else false.B
|
val atMax = if (hasMax) compteur === io.max.get else false.B
|
||||||
|
|||||||
@@ -11,31 +11,46 @@ class GeneSync extends Module {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Paramètres de synchronisation horizontale et verticale
|
// Paramètres de synchronisation horizontale et verticale
|
||||||
val synchroX = VecInit(Seq(
|
val synchroX = VecInit(
|
||||||
95.U, // HS pulse width
|
Seq(
|
||||||
47.U, // horizontal back porch
|
95.U, // HS pulse width
|
||||||
639.U, // horizontal display time
|
47.U, // horizontal back porch
|
||||||
15.U // horizontal front porch
|
639.U, // horizontal display time
|
||||||
))
|
15.U // horizontal front porch
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val synchroY = VecInit(Seq(
|
val synchroY = VecInit(
|
||||||
1.U, // VS pulse width
|
Seq(
|
||||||
28.U, // vertical back porch
|
1.U, // VS pulse width
|
||||||
479.U, // vertical display time
|
28.U, // vertical back porch
|
||||||
9.U // vertical front porch
|
479.U, // vertical display time
|
||||||
))
|
9.U // vertical front porch
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Instanciation des compteurs
|
// Instanciation des compteurs
|
||||||
val compteurCLK = Module(new Compteur(3, hasMax = true)) // Compteur horloge (3 bits)
|
val compteurCLK = Module(
|
||||||
val comptX = Module(new Compteur(10, hasEnable = true, hasMax = true)) // Compteur horizontal (10 bits)
|
new Compteur(3, hasMax = true)
|
||||||
val comptY = Module(new Compteur(9, hasEnable = true, hasMax = true)) // Compteur vertical (9 bits)
|
) // Compteur horloge (3 bits)
|
||||||
val phaseH = Module(new Compteur(2, hasEnable = true)) // Phases horizontales (2 bits)
|
val comptX = Module(
|
||||||
val phaseV = Module(new Compteur(2, hasEnable = true)) // Phases verticales (2 bits)
|
new Compteur(10, hasEnable = true, hasMax = true)
|
||||||
|
) // Compteur horizontal (10 bits)
|
||||||
|
val comptY = Module(
|
||||||
|
new Compteur(9, hasEnable = true, hasMax = true)
|
||||||
|
) // Compteur vertical (9 bits)
|
||||||
|
val phaseH = Module(
|
||||||
|
new Compteur(2, hasEnable = true)
|
||||||
|
) // Phases horizontales (2 bits)
|
||||||
|
val phaseV = Module(
|
||||||
|
new Compteur(2, hasEnable = true)
|
||||||
|
) // Phases verticales (2 bits)
|
||||||
|
|
||||||
// Signaux d'activation
|
// Signaux d'activation
|
||||||
val en25 = compteurCLK.io.atMax.get // Activation à 25MHz quand compteurCLK atteint 4
|
val en25 =
|
||||||
|
compteurCLK.io.atMax.get // Activation à 25MHz quand compteurCLK atteint 4
|
||||||
val enPhaseH = en25 && comptX.io.atMax.get
|
val enPhaseH = en25 && comptX.io.atMax.get
|
||||||
val enY = enPhaseH && phaseH.io.out === 3.U // Nouvelle ligne
|
val enY = enPhaseH && phaseH.io.out === 3.U // Nouvelle ligne
|
||||||
|
|
||||||
// Configuration compteur CLK (divise 125MHz par 5 pour avoir 25MHz)
|
// Configuration compteur CLK (divise 125MHz par 5 pour avoir 25MHz)
|
||||||
compteurCLK.io.max.get := 4.U
|
compteurCLK.io.max.get := 4.U
|
||||||
@@ -53,11 +68,11 @@ class GeneSync extends Module {
|
|||||||
phaseV.io.en.get := enY && comptY.io.atMax.get
|
phaseV.io.en.get := enY && comptY.io.atMax.get
|
||||||
|
|
||||||
// Génération des signaux de sortie
|
// Génération des signaux de sortie
|
||||||
io.hsync := phaseH.io.out.orR // true si phaseH != 0 (pas en phase pulse)
|
io.hsync := phaseH.io.out.orR // true si phaseH != 0 (pas en phase pulse)
|
||||||
io.vsync := phaseV.io.out.orR // true si phaseV != 0 (pas en phase pulse)
|
io.vsync := phaseV.io.out.orR // true si phaseV != 0 (pas en phase pulse)
|
||||||
io.img := (phaseH.io.out === 2.U) && (phaseV.io.out === 2.U) // Zone d'affichage
|
io.img := (phaseH.io.out === 2.U) && (phaseV.io.out === 2.U) // Zone d'affichage
|
||||||
|
|
||||||
// Coordonnées pixel
|
// Coordonnées pixel
|
||||||
io.x := comptX.io.out
|
io.x := comptX.io.out
|
||||||
io.y := comptY.io.out
|
io.y := comptY.io.out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,82 +13,93 @@ class dpram( // nom identique à celui du verilog
|
|||||||
val ADDR_WIDTH: Int,
|
val ADDR_WIDTH: Int,
|
||||||
val DATA_WIDTH: Int,
|
val DATA_WIDTH: Int,
|
||||||
val INIT_FILE: String = "program.mem"
|
val INIT_FILE: String = "program.mem"
|
||||||
) extends BlackBox(Map(
|
) extends BlackBox(
|
||||||
"NUM_COL" -> IntParam(NUM_COL),
|
Map(
|
||||||
"COL_WIDTH" -> IntParam(COL_WIDTH),
|
"NUM_COL" -> IntParam(NUM_COL),
|
||||||
"ADDR_WIDTH" -> IntParam(ADDR_WIDTH),
|
"COL_WIDTH" -> IntParam(COL_WIDTH),
|
||||||
"DATA_WIDTH" -> IntParam(DATA_WIDTH),
|
"ADDR_WIDTH" -> IntParam(ADDR_WIDTH),
|
||||||
"INIT_FILE" -> StringParam(if (INIT_FILE != "") INIT_FILE else "NONE")
|
"DATA_WIDTH" -> IntParam(DATA_WIDTH),
|
||||||
)) with HasBlackBoxResource {
|
"INIT_FILE" -> StringParam(
|
||||||
|
if (INIT_FILE != "") INIT_FILE else "NONE"
|
||||||
require(DATA_WIDTH == NUM_COL * COL_WIDTH, "DATA_WIDTH must be equal to NUM_COL*COL_WIDTH.")
|
)
|
||||||
|
)
|
||||||
addResource("/vsrc/dpram.v")
|
)
|
||||||
|
with HasBlackBoxResource {
|
||||||
|
|
||||||
val io = IO(new Bundle {
|
require(
|
||||||
// Port A
|
DATA_WIDTH == NUM_COL * COL_WIDTH,
|
||||||
val clkA = Input(Clock())
|
"DATA_WIDTH must be equal to NUM_COL*COL_WIDTH."
|
||||||
val enaA = Input(Bool())
|
)
|
||||||
val addrA = Input(UInt(ADDR_WIDTH.W))
|
|
||||||
val doutA = Output(UInt(DATA_WIDTH.W))
|
addResource("/vsrc/dpram.v")
|
||||||
val dinA = Input(UInt(DATA_WIDTH.W))
|
|
||||||
val weA = Input(UInt(NUM_COL.W))
|
val io = IO(new Bundle {
|
||||||
// Port B
|
// Port A
|
||||||
val clkB = Input(Clock())
|
val clkA = Input(Clock())
|
||||||
val enaB = Input(Bool())
|
val enaA = Input(Bool())
|
||||||
val addrB = Input(UInt(ADDR_WIDTH.W))
|
val addrA = Input(UInt(ADDR_WIDTH.W))
|
||||||
val doutB = Output(UInt(DATA_WIDTH.W))
|
val doutA = Output(UInt(DATA_WIDTH.W))
|
||||||
val dinB = Input(UInt(DATA_WIDTH.W))
|
val dinA = Input(UInt(DATA_WIDTH.W))
|
||||||
val weB = Input(UInt(NUM_COL.W))
|
val weA = Input(UInt(NUM_COL.W))
|
||||||
})
|
// Port B
|
||||||
|
val clkB = Input(Clock())
|
||||||
|
val enaB = Input(Bool())
|
||||||
|
val addrB = Input(UInt(ADDR_WIDTH.W))
|
||||||
|
val doutB = Output(UInt(DATA_WIDTH.W))
|
||||||
|
val dinB = Input(UInt(DATA_WIDTH.W))
|
||||||
|
val weB = Input(UInt(NUM_COL.W))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
class xpm_memory_tdpram( // nom identique à celui de Xilinx
|
class xpm_memory_tdpram( // nom identique à celui de Xilinx
|
||||||
val ADDR_WIDTH: Int,
|
val ADDR_WIDTH: Int,
|
||||||
val INIT_FILE: String
|
val INIT_FILE: String
|
||||||
) extends BlackBox(Map(
|
) extends BlackBox(
|
||||||
"ADDR_WIDTH_A" -> IntParam(ADDR_WIDTH),
|
Map(
|
||||||
"ADDR_WIDTH_B" -> IntParam(ADDR_WIDTH),
|
"ADDR_WIDTH_A" -> IntParam(ADDR_WIDTH),
|
||||||
"BYTE_WRITE_WIDTH_A" -> 8,
|
"ADDR_WIDTH_B" -> IntParam(ADDR_WIDTH),
|
||||||
"MEMORY_INIT_FILE" -> StringParam(INIT_FILE),
|
"BYTE_WRITE_WIDTH_A" -> 8,
|
||||||
"MEMORY_PRIMITIVE" -> "block",
|
"MEMORY_INIT_FILE" -> StringParam(INIT_FILE),
|
||||||
"MEMORY_SIZE" -> 524288,
|
"MEMORY_PRIMITIVE" -> "block",
|
||||||
"READ_DATA_WIDTH_A" -> 32,
|
"MEMORY_SIZE" -> 524288,
|
||||||
"READ_DATA_WIDTH_B" -> 32,
|
"READ_DATA_WIDTH_A" -> 32,
|
||||||
"READ_LATENCY_A" -> 1,
|
"READ_DATA_WIDTH_B" -> 32,
|
||||||
"READ_LATENCY_B" -> 1,
|
"READ_LATENCY_A" -> 1,
|
||||||
"WRITE_DATA_WIDTH_A" -> 32,
|
"READ_LATENCY_B" -> 1,
|
||||||
"WRITE_DATA_WIDTH_B" -> 32
|
"WRITE_DATA_WIDTH_A" -> 32,
|
||||||
)) with HasBlackBoxResource {
|
"WRITE_DATA_WIDTH_B" -> 32
|
||||||
val io = IO(new Bundle {
|
)
|
||||||
// Port A
|
)
|
||||||
val clka = Input(Clock())
|
with HasBlackBoxResource {
|
||||||
val rsta = Input(Bool())
|
val io = IO(new Bundle {
|
||||||
val ena = Input(Bool())
|
// Port A
|
||||||
val regcea = Input(Bool())
|
val clka = Input(Clock())
|
||||||
val dina = Input(UInt(32.W))
|
val rsta = Input(Bool())
|
||||||
val addra = Input(UInt(ADDR_WIDTH.W))
|
val ena = Input(Bool())
|
||||||
val wea = Input(UInt(4.W))
|
val regcea = Input(Bool())
|
||||||
val injectdbiterra = Input(Bool())
|
val dina = Input(UInt(32.W))
|
||||||
val injectsbiterra = Input(Bool())
|
val addra = Input(UInt(ADDR_WIDTH.W))
|
||||||
val douta = Output(UInt(32.W))
|
val wea = Input(UInt(4.W))
|
||||||
|
val injectdbiterra = Input(Bool())
|
||||||
|
val injectsbiterra = Input(Bool())
|
||||||
|
val douta = Output(UInt(32.W))
|
||||||
// val dbiterrA = Output(Bool())
|
// val dbiterrA = Output(Bool())
|
||||||
// val sbiterrA = Output(Bool())
|
// val sbiterrA = Output(Bool())
|
||||||
// Port B
|
// Port B
|
||||||
val clkb = Input(Clock())
|
val clkb = Input(Clock())
|
||||||
val rstb = Input(Bool())
|
val rstb = Input(Bool())
|
||||||
val enb = Input(Bool())
|
val enb = Input(Bool())
|
||||||
val regceb = Input(Bool())
|
val regceb = Input(Bool())
|
||||||
val dinb = Input(UInt(32.W))
|
val dinb = Input(UInt(32.W))
|
||||||
val addrb = Input(UInt(ADDR_WIDTH.W))
|
val addrb = Input(UInt(ADDR_WIDTH.W))
|
||||||
val web = Input(UInt(4.W))
|
val web = Input(UInt(4.W))
|
||||||
val injectdbiterrb = Input(Bool())
|
val injectdbiterrb = Input(Bool())
|
||||||
val injectsbiterrb = Input(Bool())
|
val injectsbiterrb = Input(Bool())
|
||||||
val doutb = Output(UInt(32.W))
|
val doutb = Output(UInt(32.W))
|
||||||
// val dbiterrB = Output(Bool())
|
// val dbiterrB = Output(Bool())
|
||||||
// val sbiterrB = Output(Bool())
|
// val sbiterrB = Output(Bool())
|
||||||
val sleep = Input(Bool())
|
val sleep = Input(Bool())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/*\
|
/*\
|
||||||
* Attention, les BRAM sont adressables en mots, et les adresses émises
|
* Attention, les BRAM sont adressables en mots, et les adresses émises
|
||||||
@@ -99,66 +110,73 @@ class xpm_memory_tdpram( // nom identique à celui de Xilinx
|
|||||||
* pour faciliter le préchargement (on espère).
|
* pour faciliter le préchargement (on espère).
|
||||||
*
|
*
|
||||||
\*/
|
\*/
|
||||||
class IDMem(sim: Boolean, addrWidth: Int, memFile: String ="") extends Module {
|
class IDMem(sim: Boolean, addrWidth: Int, memFile: String = "") extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val clki = Input(Clock())
|
val clki = Input(Clock())
|
||||||
val ibus = new BusInterface
|
val ibus = new BusInterface
|
||||||
val clkd = Input(Clock())
|
val clkd = Input(Clock())
|
||||||
val dbus = new BusInterface
|
val dbus = new BusInterface
|
||||||
})
|
})
|
||||||
|
|
||||||
// Pour la simulation ou la synthèse ne nécessitant pas de repérer les BRAM, on blackboxe avec le verilog Xilinx
|
// Pour la simulation ou la synthèse ne nécessitant pas de repérer les BRAM, on blackboxe avec le verilog Xilinx
|
||||||
if (sim || memFile == "") {
|
if (sim || memFile == "") {
|
||||||
// Mémoire 32 bits : 4 colonnes de 8 bit
|
// Mémoire 32 bits : 4 colonnes de 8 bit
|
||||||
// l'accès BRAM est un accès mot, et non octet, on ignore les 2 bits de
|
// l'accès BRAM est un accès mot, et non octet, on ignore les 2 bits de
|
||||||
// poids faible de l'adresse
|
// poids faible de l'adresse
|
||||||
|
|
||||||
val unifiedRam = Module(new dpram(
|
val unifiedRam = Module(
|
||||||
NUM_COL = 4,
|
new dpram(
|
||||||
COL_WIDTH = 8,
|
NUM_COL = 4,
|
||||||
ADDR_WIDTH = addrWidth - 2,
|
COL_WIDTH = 8,
|
||||||
DATA_WIDTH = 32,
|
ADDR_WIDTH = addrWidth - 2,
|
||||||
INIT_FILE = memFile
|
DATA_WIDTH = 32,
|
||||||
))
|
INIT_FILE = memFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
unifiedRam.io.clkA := io.clki
|
unifiedRam.io.clkA := io.clki
|
||||||
unifiedRam.io.clkB := io.clkd
|
unifiedRam.io.clkB := io.clkd
|
||||||
// instructions sur le port a
|
// instructions sur le port a
|
||||||
unifiedRam.io.enaA := io.ibus.en
|
unifiedRam.io.enaA := io.ibus.en
|
||||||
unifiedRam.io.addrA := io.ibus.addr(addrWidth - 1, 2)
|
unifiedRam.io.addrA := io.ibus.addr(addrWidth - 1, 2)
|
||||||
io.ibus.rdata := unifiedRam.io.doutA
|
io.ibus.rdata := unifiedRam.io.doutA
|
||||||
unifiedRam.io.weA := 0.U
|
unifiedRam.io.weA := 0.U
|
||||||
unifiedRam.io.dinA := 0.U
|
unifiedRam.io.dinA := 0.U
|
||||||
// données sur le port b
|
// données sur le port b
|
||||||
unifiedRam.io.enaB := io.dbus.en
|
unifiedRam.io.enaB := io.dbus.en
|
||||||
unifiedRam.io.addrB := io.dbus.addr(addrWidth - 1, 2)
|
unifiedRam.io.addrB := io.dbus.addr(addrWidth - 1, 2)
|
||||||
io.dbus.rdata := unifiedRam.io.doutB
|
io.dbus.rdata := unifiedRam.io.doutB
|
||||||
unifiedRam.io.weB := io.dbus.be.asUInt
|
unifiedRam.io.weB := io.dbus.be.asUInt
|
||||||
unifiedRam.io.dinB := io.dbus.wdata
|
unifiedRam.io.dinB := io.dbus.wdata
|
||||||
} else {
|
} else {
|
||||||
val dpram = Module(new xpm_memory_tdpram(ADDR_WIDTH = addrWidth - 2,INIT_FILE = memFile))
|
val dpram = Module(
|
||||||
dpram.io.clka := clock
|
new xpm_memory_tdpram(
|
||||||
dpram.io.rsta := 0.U
|
ADDR_WIDTH = addrWidth - 2,
|
||||||
dpram.io.ena := io.dbus.en
|
INIT_FILE = memFile
|
||||||
dpram.io.regcea := false.B
|
)
|
||||||
dpram.io.dina := io.dbus.wdata
|
)
|
||||||
dpram.io.addra := io.dbus.addr(addrWidth - 1, 2)
|
dpram.io.clka := clock
|
||||||
dpram.io.wea := io.dbus.be.asUInt
|
dpram.io.rsta := 0.U
|
||||||
dpram.io.injectdbiterra := false.B
|
dpram.io.ena := io.dbus.en
|
||||||
dpram.io.injectsbiterra := false.B
|
dpram.io.regcea := false.B
|
||||||
io.dbus.rdata := dpram.io.douta
|
dpram.io.dina := io.dbus.wdata
|
||||||
|
dpram.io.addra := io.dbus.addr(addrWidth - 1, 2)
|
||||||
|
dpram.io.wea := io.dbus.be.asUInt
|
||||||
|
dpram.io.injectdbiterra := false.B
|
||||||
|
dpram.io.injectsbiterra := false.B
|
||||||
|
io.dbus.rdata := dpram.io.douta
|
||||||
|
|
||||||
dpram.io.clkb := clock
|
dpram.io.clkb := clock
|
||||||
dpram.io.rstb := 0.U
|
dpram.io.rstb := 0.U
|
||||||
dpram.io.enb := io.ibus.en
|
dpram.io.enb := io.ibus.en
|
||||||
dpram.io.regceb := false.B
|
dpram.io.regceb := false.B
|
||||||
dpram.io.dinb := 0.U
|
dpram.io.dinb := 0.U
|
||||||
dpram.io.addrb := io.ibus.addr(addrWidth - 1, 2)
|
dpram.io.addrb := io.ibus.addr(addrWidth - 1, 2)
|
||||||
dpram.io.web := 0.U
|
dpram.io.web := 0.U
|
||||||
dpram.io.injectdbiterrb := false.B
|
dpram.io.injectdbiterrb := false.B
|
||||||
dpram.io.injectsbiterrb := false.B
|
dpram.io.injectsbiterrb := false.B
|
||||||
io.ibus.rdata := dpram.io.doutb
|
io.ibus.rdata := dpram.io.doutb
|
||||||
|
|
||||||
dpram.io.sleep := false.B
|
dpram.io.sleep := false.B
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,41 +6,40 @@ import chisel3.util._
|
|||||||
* Fusion de IP_LED et IP_PIN du projet VHDL
|
* Fusion de IP_LED et IP_PIN du projet VHDL
|
||||||
\*/
|
\*/
|
||||||
class LedIp extends Module {
|
class LedIp extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val x31 = Input(UInt(32.W))
|
val x31 = Input(UInt(32.W))
|
||||||
val switch = Input(UInt(4.W))
|
val switch = Input(UInt(4.W))
|
||||||
val led = Output(UInt(4.W))
|
val led = Output(UInt(4.W))
|
||||||
val button = Input(UInt(3.W))
|
val button = Input(UInt(3.W))
|
||||||
val bus = new BusInterface
|
val bus = new BusInterface
|
||||||
})
|
})
|
||||||
|
|
||||||
io.bus.rdata := 0x01ed01ed.U // On lira ça si on lit !
|
io.bus.rdata := 0x01ed01ed.U // On lira ça si on lit !
|
||||||
|
|
||||||
val dinReg = RegInit(0.U(32.W))
|
val dinReg = RegInit(0.U(32.W))
|
||||||
val x31Reg = RegNext(io.x31) // On lit x31 en continue
|
val x31Reg = RegNext(io.x31) // On lit x31 en continue
|
||||||
val btnReg = Reg(UInt(32.W))
|
val btnReg = Reg(UInt(32.W))
|
||||||
|
|
||||||
// Le cpu fait une requête
|
// Le cpu fait une requête
|
||||||
// On se moque de l'adresse ...
|
// On se moque de l'adresse ...
|
||||||
// L'écriture écrit dans le registre, la lecture retourne l'état des boutons
|
// L'écriture écrit dans le registre, la lecture retourne l'état des boutons
|
||||||
when (io.bus.en === true.B) {
|
when(io.bus.en === true.B) {
|
||||||
when (io.bus.be.asUInt.orR) { // Écriture dans le registre des leds internes
|
when(io.bus.be.asUInt.orR) { // Écriture dans le registre des leds internes
|
||||||
dinReg := io.bus.wdata
|
dinReg := io.bus.wdata
|
||||||
} .otherwise { // Lecture des boutons poussoirs, pas de débounce car lus par le soft
|
}.otherwise { // Lecture des boutons poussoirs, pas de débounce car lus par le soft
|
||||||
btnReg := Fill(29, 0.U) ## io.button
|
btnReg := Fill(29, 0.U) ## io.button
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
io.bus.rdata := btnReg
|
io.bus.rdata := btnReg
|
||||||
|
|
||||||
// On échantillonne la donnée d'entrée en continue
|
// On échantillonne la donnée d'entrée en continue
|
||||||
// C'est soit une donnée venant du registre x31 du
|
// C'est soit une donnée venant du registre x31 du
|
||||||
// proc, si le dernier switch est à 1, soit le registre écrit par le soft
|
// proc, si le dernier switch est à 1, soit le registre écrit par le soft
|
||||||
val data = Mux(io.switch(3).asBool, x31Reg, dinReg)
|
val data = Mux(io.switch(3).asBool, x31Reg, dinReg)
|
||||||
|
|
||||||
// On l'affiche
|
// On l'affiche
|
||||||
io.led := MuxLookup(io.switch(2, 0), 0.U(4.W))(
|
io.led := MuxLookup(io.switch(2, 0), 0.U(4.W))((0 until 8).map { i =>
|
||||||
(0 until 8).map { i =>
|
(i.U -> data((i * 4) + 3, i * 4))
|
||||||
(i.U -> data((i * 4) + 3, i * 4))
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,54 +3,53 @@ package projet
|
|||||||
import chisel3._
|
import chisel3._
|
||||||
|
|
||||||
class Rv32i(sim: Boolean = true) extends Module {
|
class Rv32i(sim: Boolean = true) extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val ibus = Flipped(new BusInterface)
|
val ibus = Flipped(new BusInterface)
|
||||||
val dbus = Flipped(new BusInterface)
|
val dbus = Flipped(new BusInterface)
|
||||||
val x31 = Output(UInt(32.W))
|
val x31 = Output(UInt(32.W))
|
||||||
val valid_x31 = if (sim) Some(Output(Bool())) else None
|
val valid_x31 = if (sim) Some(Output(Bool())) else None
|
||||||
})
|
})
|
||||||
|
|
||||||
val reg_pc = RegInit(0.U(32.W));
|
val reg_pc = RegInit(0.U(32.W));
|
||||||
val reg_file = Module(new RegFile(sim));
|
val reg_file = Module(new RegFile(sim));
|
||||||
reg_file.io.rd_data := 0.U;
|
reg_file.io.rd_data := 0.U;
|
||||||
reg_file.io.we := 0.U;
|
reg_file.io.we := 0.U;
|
||||||
|
|
||||||
// Increment PC each clock
|
// Increment PC each clock
|
||||||
reg_pc := reg_pc + 4.U;
|
reg_pc := reg_pc + 4.U;
|
||||||
|
|
||||||
// Fetch instruction
|
// Fetch instruction
|
||||||
io.ibus.en := true.B;
|
io.ibus.en := true.B;
|
||||||
io.ibus.addr := reg_pc;
|
io.ibus.addr := reg_pc;
|
||||||
|
|
||||||
val insn = io.ibus.rdata;
|
val insn = io.ibus.rdata;
|
||||||
|
|
||||||
// Decode rs1 index
|
// Decode rs1 index
|
||||||
val rs1 = insn(19, 15)
|
val rs1 = insn(19, 15)
|
||||||
reg_file.io.rs1_addr := rs1
|
reg_file.io.rs1_addr := rs1
|
||||||
// Decode rs2 index
|
// Decode rs2 index
|
||||||
val rs2 = insn(24, 20)
|
val rs2 = insn(24, 20)
|
||||||
reg_file.io.rs2_addr := rs2
|
reg_file.io.rs2_addr := rs2
|
||||||
// Decode rd index
|
// Decode rd index
|
||||||
val rd = insn(11, 7)
|
val rd = insn(11, 7)
|
||||||
reg_file.io.rd_addr := rd
|
reg_file.io.rd_addr := rd
|
||||||
|
|
||||||
// Decode imm-u
|
// Decode imm-u
|
||||||
val imm_u = insn(31, 12)
|
val imm_u = insn(31, 12)
|
||||||
|
|
||||||
// Debug instruction
|
// Debug instruction
|
||||||
io.x31 := reg_file.io.rs1_data;
|
io.x31 := reg_file.io.rs1_data;
|
||||||
if(sim)
|
if (sim) {
|
||||||
{
|
io.valid_x31.get := true.B;
|
||||||
io.valid_x31.get := true.B;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
io.dbus.addr := 0.U;
|
io.dbus.addr := 0.U;
|
||||||
io.dbus.wdata := 0.U;
|
io.dbus.wdata := 0.U;
|
||||||
io.dbus.be := VecInit(false.B,false.B,false.B,false.B)
|
io.dbus.be := VecInit(false.B, false.B, false.B, false.B)
|
||||||
io.dbus.en := false.B;
|
io.dbus.en := false.B;
|
||||||
|
|
||||||
io.ibus.wdata := 0.U;
|
io.ibus.wdata := 0.U;
|
||||||
io.ibus.be := VecInit(false.B,false.B,false.B,false.B)
|
io.ibus.be := VecInit(false.B, false.B, false.B, false.B)
|
||||||
|
|
||||||
//locally(sim)
|
// locally(sim)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,37 +4,39 @@ import chisel3._
|
|||||||
import chisel3.util._
|
import chisel3.util._
|
||||||
|
|
||||||
class VgaIp extends Module {
|
class VgaIp extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val clk = Input(Clock())
|
val clk = Input(Clock())
|
||||||
val bus = Flipped(new BusInterface)
|
val bus = Flipped(new BusInterface)
|
||||||
val hs = Output(Bool())
|
val hs = Output(Bool())
|
||||||
val vs = Output(Bool())
|
val vs = Output(Bool())
|
||||||
val r = Output(UInt(5.W))
|
val r = Output(UInt(5.W))
|
||||||
val g = Output(UInt(6.W))
|
val g = Output(UInt(6.W))
|
||||||
val b = Output(UInt(5.W))
|
val b = Output(UInt(5.W))
|
||||||
})
|
})
|
||||||
|
|
||||||
withClock(io.clk) {
|
withClock(io.clk) {
|
||||||
val sync = Module(new GeneSync())
|
val sync = Module(new GeneSync())
|
||||||
io.hs := RegNext(sync.io.hsync)
|
io.hs := RegNext(sync.io.hsync)
|
||||||
io.vs := RegNext(sync.io.vsync)
|
io.vs := RegNext(sync.io.vsync)
|
||||||
val img_sync = RegNext(sync.io.img)
|
val img_sync = RegNext(sync.io.img)
|
||||||
val index = RegNext(sync.io.x(2, 1))
|
val index = RegNext(sync.io.x(2, 1))
|
||||||
|
|
||||||
io.bus.en := true.B
|
io.bus.en := true.B
|
||||||
io.bus.be := VecInit(false.B, false.B, false.B, false.B)
|
io.bus.be := VecInit(false.B, false.B, false.B, false.B)
|
||||||
io.bus.wdata := 0.U
|
io.bus.wdata := 0.U
|
||||||
val addr = 80.U * sync.io.y(8, 1) + sync.io.x(9, 3)
|
val addr = 80.U * sync.io.y(8, 1) + sync.io.x(9, 3)
|
||||||
// Adresse mot dans la ram vidéo
|
// Adresse mot dans la ram vidéo
|
||||||
io.bus.addr := addr(14, 0) ## 0.U(2.W)
|
io.bus.addr := addr(14, 0) ## 0.U(2.W)
|
||||||
|
|
||||||
val pixel = MuxCase(0.U,
|
val pixel = MuxCase(
|
||||||
(0 until 4).map {i =>
|
0.U,
|
||||||
(index === i.U) -> io.bus.rdata(8 * (3 - i) + 7, 8 * (3 - i))
|
(0 until 4).map { i =>
|
||||||
})
|
(index === i.U) -> io.bus.rdata(8 * (3 - i) + 7, 8 * (3 - i))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
io.r := Mux(img_sync, pixel(7, 6)<<3, 0.U)
|
io.r := Mux(img_sync, pixel(7, 6) << 3, 0.U)
|
||||||
io.g := Mux(img_sync, pixel(5, 3)<<3, 0.U)
|
io.g := Mux(img_sync, pixel(5, 3) << 3, 0.U)
|
||||||
io.b := Mux(img_sync, pixel(2, 0)<<2, 0.U)
|
io.b := Mux(img_sync, pixel(2, 0) << 2, 0.U)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,73 +2,75 @@ package projet
|
|||||||
|
|
||||||
import chisel3._
|
import chisel3._
|
||||||
|
|
||||||
class ZzTop(file: String ="",sim: Boolean = true) extends Module {
|
class ZzTop(file: String = "", sim: Boolean = true) extends Module {
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
// Interface debug minimale
|
// Interface debug minimale
|
||||||
val switch = Input(UInt(4.W))
|
val switch = Input(UInt(4.W))
|
||||||
val led = Output(UInt(4.W))
|
val led = Output(UInt(4.W))
|
||||||
val push = Input(UInt(3.W))
|
val push = Input(UInt(3.W))
|
||||||
// Debug simu pour ne pas s'embeter avec le switch dans le testbench
|
// Debug simu pour ne pas s'embeter avec le switch dans le testbench
|
||||||
val x31 = if (sim) Some(Output(UInt(32.W))) else None
|
val x31 = if (sim) Some(Output(UInt(32.W))) else None
|
||||||
val valid_x31 = if (sim) Some(Output(Bool())) else None
|
val valid_x31 = if (sim) Some(Output(Bool())) else None
|
||||||
|
|
||||||
// Port vga
|
// Port vga
|
||||||
val hs = Output(Bool())
|
val hs = Output(Bool())
|
||||||
val vs = Output(Bool())
|
val vs = Output(Bool())
|
||||||
val r = Output(UInt(5.W))
|
val r = Output(UInt(5.W))
|
||||||
val g = Output(UInt(6.W))
|
val g = Output(UInt(6.W))
|
||||||
val b = Output(UInt(5.W))
|
val b = Output(UInt(5.W))
|
||||||
})
|
})
|
||||||
|
|
||||||
val clkg = Module(new ClockGen(12, sim)) // ~ 10 MHz
|
val clkg = Module(new ClockGen(12, sim)) // ~ 10 MHz
|
||||||
// clock et reset sont les signaux implicites
|
// clock et reset sont les signaux implicites
|
||||||
clkg.io.clk_in1 := clock
|
clkg.io.clk_in1 := clock
|
||||||
clkg.io.reset := reset
|
clkg.io.reset := reset
|
||||||
val rst = (reset.asBool || !clkg.io.locked).asAsyncReset
|
val rst = (reset.asBool || !clkg.io.locked).asAsyncReset
|
||||||
|
|
||||||
// Ces deux IPs doivent tourner à 125 MHz
|
// Ces deux IPs doivent tourner à 125 MHz
|
||||||
val vmem = Module(new IDMem(sim,17))
|
val vmem = Module(new IDMem(sim, 17))
|
||||||
vmem.io.clki := clkg.io.clk_out2 // Clock du pixel
|
vmem.io.clki := clkg.io.clk_out2 // Clock du pixel
|
||||||
vmem.io.clkd := clkg.io.clk_out1 // Clock du reste du système
|
vmem.io.clkd := clkg.io.clk_out1 // Clock du reste du système
|
||||||
val vgad = Module(new VgaIp)
|
val vgad = Module(new VgaIp)
|
||||||
vgad.io.clk := clkg.io.clk_out2 // Clock pixel
|
vgad.io.clk := clkg.io.clk_out2 // Clock pixel
|
||||||
|
|
||||||
withClockAndReset(clkg.io.clk_out1, rst) {
|
// Expose core for testing usage
|
||||||
val interconnect = Module(new BusInterconnect)
|
val core = withClockAndReset(clkg.io.clk_out1, rst) {
|
||||||
val core = Module(new Rv32i(sim))
|
val interconnect = Module(new BusInterconnect)
|
||||||
val dmem = Module(new IDMem(sim,16,file))
|
val core = Module(new Rv32i(sim))
|
||||||
val ldip = Module(new LedIp)
|
val dmem = Module(new IDMem(sim, 16, file))
|
||||||
val clnt = Module(new CLint)
|
val ldip = Module(new LedIp)
|
||||||
|
val clnt = Module(new CLint)
|
||||||
|
|
||||||
dmem.io.clki := clkg.io.clk_out1 // Ou clock, non ?
|
dmem.io.clki := clkg.io.clk_out1 // Ou clock, non ?
|
||||||
dmem.io.clkd := clkg.io.clk_out1 //
|
dmem.io.clkd := clkg.io.clk_out1 //
|
||||||
|
|
||||||
ldip.io.switch := io.switch
|
ldip.io.switch := io.switch
|
||||||
ldip.io.button := io.push
|
ldip.io.button := io.push
|
||||||
ldip.io.x31 := core.io.x31
|
ldip.io.x31 := core.io.x31
|
||||||
io.led := ldip.io.led
|
io.led := ldip.io.led
|
||||||
|
|
||||||
if (sim){
|
if (sim) {
|
||||||
io.x31.get := core.io.x31
|
io.x31.get := core.io.x31
|
||||||
io.valid_x31.get := core.io.valid_x31.get
|
io.valid_x31.get := core.io.valid_x31.get
|
||||||
|
}
|
||||||
|
|
||||||
|
interconnect.io.master <> core.io.dbus
|
||||||
|
|
||||||
|
interconnect.io.dmem <> dmem.io.dbus
|
||||||
|
interconnect.io.ldip <> ldip.io.bus
|
||||||
|
interconnect.io.clnt <> clnt.io.bus
|
||||||
|
interconnect.io.vmem <> vmem.io.dbus
|
||||||
|
|
||||||
|
// connexion ad-hoc pour le bus d'instructions
|
||||||
|
dmem.io.ibus <> core.io.ibus
|
||||||
|
// connexion ad-hoc pour l'IP Vga
|
||||||
|
vmem.io.ibus <> vgad.io.bus
|
||||||
|
io.hs := vgad.io.hs
|
||||||
|
io.vs := vgad.io.vs
|
||||||
|
io.r := vgad.io.r
|
||||||
|
io.g := vgad.io.g
|
||||||
|
io.b := vgad.io.b
|
||||||
|
|
||||||
|
core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interconnect.io.master <> core.io.dbus
|
|
||||||
|
|
||||||
interconnect.io.dmem <> dmem.io.dbus
|
|
||||||
interconnect.io.ldip <> ldip.io.bus
|
|
||||||
interconnect.io.clnt <> clnt.io.bus
|
|
||||||
interconnect.io.vmem <> vmem.io.dbus
|
|
||||||
|
|
||||||
// connexion ad-hoc pour le bus d'instructions
|
|
||||||
dmem.io.ibus <> core.io.ibus
|
|
||||||
// connexion ad-hoc pour l'IP Vga
|
|
||||||
vmem.io.ibus <> vgad.io.bus
|
|
||||||
io.hs := vgad.io.hs
|
|
||||||
io.vs := vgad.io.vs
|
|
||||||
io.r := vgad.io.r
|
|
||||||
io.g := vgad.io.g
|
|
||||||
io.b := vgad.io.b
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,91 +11,105 @@ import scala.collection.mutable.ListBuffer
|
|||||||
|
|
||||||
class ZzTopSpec extends AnyFreeSpec with Matchers with ChiselSim {
|
class ZzTopSpec extends AnyFreeSpec with Matchers with ChiselSim {
|
||||||
|
|
||||||
def expectedValuesFromAsm(path: String): Seq[BigInt] = {
|
def expectedValuesFromAsm(path: String): Seq[BigInt] = {
|
||||||
val line = Source.fromFile(path).getLines().find(_.startsWith("# expected:"))
|
val line =
|
||||||
line match {
|
Source.fromFile(path).getLines().find(_.startsWith("# expected:"))
|
||||||
case Some(l) =>
|
line match {
|
||||||
l.stripPrefix("# expected:")
|
case Some(l) =>
|
||||||
.split(",")
|
l.stripPrefix("# expected:")
|
||||||
.map(_.trim)
|
.split(",")
|
||||||
.filter(_.nonEmpty)
|
.map(_.trim)
|
||||||
.map(str => BigInt(str, 16))
|
.filter(_.nonEmpty)
|
||||||
.toSeq
|
.map(str => BigInt(str, 16))
|
||||||
case None =>
|
.toSeq
|
||||||
Seq.empty
|
case None =>
|
||||||
}
|
Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
def failTrace(msg: String = "Erreur",trace: ListBuffer[String]): Unit = {
|
|
||||||
val prefix = s"🛑 $msg\n"
|
|
||||||
val traceStr =
|
|
||||||
if (trace.nonEmpty) "Trace:\n" + trace.map(" - " + _).mkString("\n") + "\n"
|
|
||||||
else ""
|
|
||||||
fail(prefix + traceStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
def expectNextValue(dut: ZzTop, expected: BigInt, timeout: Int, trace: ListBuffer[String]): Unit = {
|
|
||||||
var cycles = 0
|
|
||||||
dut.clock.step(12) // Attente d'un cycle proc (12 cycles systeme)
|
|
||||||
// Attendre une écriture dans x31
|
|
||||||
while (!dut.io.valid_x31.get.peek().litToBoolean && cycles < timeout) {
|
|
||||||
dut.clock.step(12) // Attente d'un cycle proc
|
|
||||||
cycles += 1
|
|
||||||
trace += f"Attente $cycles cycles"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cycles < timeout){
|
|
||||||
// Vérifier la nouvelle valeur
|
|
||||||
val got = dut.io.x31.get.peek().litValue
|
|
||||||
trace += f"Valeur attendue = 0x$expected%08X, reçue = 0x$got%08X"
|
|
||||||
if ( got != expected ) failTrace("Mauvaise sortie",trace)
|
|
||||||
} else {
|
|
||||||
trace += f"Valeur attendue = 0x$expected%08X, reçue = ⏰"
|
|
||||||
failTrace("Simulation bloquée",trace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Fonction de test commune
|
|
||||||
def testWith(file: String): Unit = {
|
|
||||||
val path = file.replace("/mem/","/").replace(".mem",".s")
|
|
||||||
if (Files.exists(Paths.get(path))){
|
|
||||||
val expected = expectedValuesFromAsm(path)
|
|
||||||
simulate(new ZzTop(file,true)) { dut =>
|
|
||||||
val trace = ListBuffer[String]()
|
|
||||||
expected.foreach { value =>
|
|
||||||
expectNextValue(dut, value, timeout = 100,trace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val dir = sys.env("PROOT")+"/bench/mem/"
|
def failTrace(msg: String = "Erreur", trace: ListBuffer[String]): Unit = {
|
||||||
val suffix = ".mem"
|
val prefix = s"🛑 $msg\n"
|
||||||
val programFile = sys.env.get("PROG")
|
val traceStr =
|
||||||
if (programFile.isDefined) {
|
if (trace.nonEmpty)
|
||||||
val name = programFile.get
|
"Trace:\n" + trace.map(" - " + _).mkString("\n") + "\n"
|
||||||
val path = dir+name+suffix
|
else ""
|
||||||
|
fail(prefix + traceStr)
|
||||||
"Simulation pour gtkwave" in {
|
|
||||||
simulate(new ZzTop(path,true)) { dut =>
|
|
||||||
dut.io.switch.poke(8) // Paramétrage de l'affichage
|
|
||||||
dut.io.led.expect(dut.io.led.peek()) // test dummy
|
|
||||||
dut.clock.step(sys.env("CYCLES").toInt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s"Test unitaire $name" in testWith(path)
|
|
||||||
|
|
||||||
} else {
|
def expectNextValue(
|
||||||
// Récupération des fichiers .mem et génération de la table de programmes
|
dut: ZzTop,
|
||||||
val programs = Files.list(Paths.get(dir)).iterator().asScala.filter(_.toString.endsWith(suffix))
|
expected: BigInt,
|
||||||
val p2 = programs.map(path => (path.getFileName.toString.stripSuffix(suffix), path.toString)).toSeq
|
timeout: Int,
|
||||||
val table = Table(("name", "path"), p2: _*)
|
trace: ListBuffer[String]
|
||||||
|
): Unit = {
|
||||||
// Déclaration des tests à partir de la table
|
var cycles = 0
|
||||||
forAll(table) { (name, path) =>
|
dut.clock.step(12) // Attente d'un cycle proc (12 cycles systeme)
|
||||||
s"Programme $name" in testWith(path)
|
// Attendre une écriture dans x31
|
||||||
|
while (!dut.io.valid_x31.get.peek().litToBoolean && cycles < timeout) {
|
||||||
|
dut.clock.step(12) // Attente d'un cycle proc
|
||||||
|
cycles += 1
|
||||||
|
trace += f"Attente $cycles cycles"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cycles < timeout) {
|
||||||
|
// Vérifier la nouvelle valeur
|
||||||
|
val got = dut.io.x31.get.peek().litValue
|
||||||
|
trace += f"Valeur attendue = 0x$expected%08X, reçue = 0x$got%08X"
|
||||||
|
if (got != expected) failTrace("Mauvaise sortie", trace)
|
||||||
|
} else {
|
||||||
|
trace += f"Valeur attendue = 0x$expected%08X, reçue = ⏰"
|
||||||
|
failTrace("Simulation bloquée", trace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction de test commune
|
||||||
|
def testWith(file: String): Unit = {
|
||||||
|
val path = file.replace("/mem/", "/").replace(".mem", ".s")
|
||||||
|
if (Files.exists(Paths.get(path))) {
|
||||||
|
val expected = expectedValuesFromAsm(path)
|
||||||
|
simulate(new ZzTop(file, true)) { dut =>
|
||||||
|
val trace = ListBuffer[String]()
|
||||||
|
expected.foreach { value =>
|
||||||
|
expectNextValue(dut, value, timeout = 100, trace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dir = sys.env("PROOT") + "/bench/mem/"
|
||||||
|
val suffix = ".mem"
|
||||||
|
val programFile = sys.env.get("PROG")
|
||||||
|
if (programFile.isDefined) {
|
||||||
|
val name = programFile.get
|
||||||
|
val path = dir + name + suffix
|
||||||
|
|
||||||
|
"Simulation pour gtkwave" in {
|
||||||
|
simulate(new ZzTop(path, true)) { dut =>
|
||||||
|
dut.io.switch.poke(8) // Paramétrage de l'affichage
|
||||||
|
dut.io.led.expect(dut.io.led.peek()) // test dummy
|
||||||
|
dut.clock.step(sys.env("CYCLES").toInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s"Test unitaire $name" in testWith(path)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Récupération des fichiers .mem et génération de la table de programmes
|
||||||
|
val programs = Files
|
||||||
|
.list(Paths.get(dir))
|
||||||
|
.iterator()
|
||||||
|
.asScala
|
||||||
|
.filter(_.toString.endsWith(suffix))
|
||||||
|
val p2 = programs
|
||||||
|
.map(path =>
|
||||||
|
(path.getFileName.toString.stripSuffix(suffix), path.toString)
|
||||||
|
)
|
||||||
|
.toSeq
|
||||||
|
val table = Table(("name", "path"), p2: _*)
|
||||||
|
|
||||||
|
// Déclaration des tests à partir de la table
|
||||||
|
forAll(table) { (name, path) =>
|
||||||
|
s"Programme $name" in testWith(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user