From 41c5da1a648f032c92f550061893bf33c8b1cd99 Mon Sep 17 00:00:00 2001 From: supersurviveur Date: Tue, 4 Nov 2025 18:08:45 +0100 Subject: [PATCH] Formats code --- .scalafmt.conf | 5 + src/main/scala/common/EmitVerilog.scala | 92 +++---- src/main/scala/projet/BusInterconnect.scala | 137 +++++----- src/main/scala/projet/CLint.scala | 82 +++--- src/main/scala/projet/ClockGen.scala | 84 +++---- src/main/scala/projet/Compteur.scala | 28 ++- src/main/scala/projet/GeneSync.scala | 65 +++-- src/main/scala/projet/IDMem.scala | 264 +++++++++++--------- src/main/scala/projet/LedIp.scala | 59 +++-- src/main/scala/projet/Rv32i.scala | 79 +++--- src/main/scala/projet/VgaIp.scala | 60 ++--- src/main/scala/projet/ZzTop.scala | 124 ++++----- src/test/scala/projet/ZzTopSpec.scala | 178 +++++++------ 13 files changed, 664 insertions(+), 593 deletions(-) create mode 100644 .scalafmt.conf diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..d5bb456 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,5 @@ +version = "3.7.15" +runner.dialect = scala213 + +indent.main = 4 +indent.callSite = 4 diff --git a/src/main/scala/common/EmitVerilog.scala b/src/main/scala/common/EmitVerilog.scala index 7db48c7..8923e22 100644 --- a/src/main/scala/common/EmitVerilog.scala +++ b/src/main/scala/common/EmitVerilog.scala @@ -3,50 +3,54 @@ package common import chisel3._ import _root_.circt.stage.ChiselStage - object EmitModule { - def main(args: Array[String]): Unit = { - if (args.length < 1) { - println("Usage: mill ... common.EmitModule [constructor args...]") - sys.exit(1) + def main(args: Array[String]): Unit = { + if (args.length < 1) { + println( + "Usage: mill ... common.EmitModule [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") - ) - } } diff --git a/src/main/scala/projet/BusInterconnect.scala b/src/main/scala/projet/BusInterconnect.scala index cd6ac9c..29c1120 100644 --- a/src/main/scala/projet/BusInterconnect.scala +++ b/src/main/scala/projet/BusInterconnect.scala @@ -6,84 +6,91 @@ import chisel3.util._ // 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 ! -// 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 class BusInterface extends Bundle { - 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 rdata = Output(UInt(32.W)) // 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 + 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 rdata = Output( + UInt(32.W) + ) // 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 { - val io = IO(new Bundle { - val master = new BusInterface // Bus avec un unique maître - val dmem = Flipped(new BusInterface) // et plusieurs esclaves - val ldip = Flipped(new BusInterface) - val clnt = Flipped(new BusInterface) - val vmem = Flipped(new BusInterface) - }) + val io = IO(new Bundle { + val master = new BusInterface // Bus avec un unique maître + val dmem = Flipped(new BusInterface) // et plusieurs esclaves + val ldip = Flipped(new BusInterface) + val clnt = Flipped(new BusInterface) + val vmem = Flipped(new BusInterface) + }) - // On récupère l'adresse émise par le maître pour trouver à quel - // esclave elle fait référence - 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") + // On récupère l'adresse émise par le maître pour trouver à quel + // esclave elle fait référence + val addr = io.master.addr - val DMEM_BASE = 0x00000000L.U // Une mémoire unifiée code + données - val DMEM_SIZE = 0x00010000L.U + // 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 LDIP_BASE = 0x30000000L.U - val LDIP_SIZE = 0x00000004L.U + val DMEM_BASE = 0x00000000L.U // Une mémoire unifiée code + données + val DMEM_SIZE = 0x00010000L.U - val CLNT_BASE = 0x02000000L.U - val CLNT_SIZE = 0x0000C000L.U + val LDIP_BASE = 0x30000000L.U + val LDIP_SIZE = 0x00000004L.U - val VMEM_BASE = 0x80000000L.U // Mémoire vidéo dual port - val VMEM_SIZE = 0x00020000L.U + val CLNT_BASE = 0x02000000L.U + val CLNT_SIZE = 0x0000c000L.U - val is_dmem = addr >= DMEM_BASE && addr < (DMEM_BASE + DMEM_SIZE) - val is_ldip = addr >= LDIP_BASE && addr < (LDIP_BASE + LDIP_SIZE) - val is_clnt = addr >= CLNT_BASE && addr < (CLNT_BASE + CLNT_SIZE) - val is_vmem = addr >= VMEM_BASE && addr < (VMEM_BASE + VMEM_SIZE) + val VMEM_BASE = 0x80000000L.U // Mémoire vidéo dual port + val VMEM_SIZE = 0x00020000L.U - // On connecte directement ce qui doit l'être en faisant l'hypothèse que - io.dmem.en := io.master.en && is_dmem - io.ldip.en := io.master.en && is_ldip - io.clnt.en := io.master.en && is_clnt - 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 + val is_dmem = addr >= DMEM_BASE && addr < (DMEM_BASE + DMEM_SIZE) + val is_ldip = addr >= LDIP_BASE && addr < (LDIP_BASE + LDIP_SIZE) + val is_clnt = addr >= CLNT_BASE && addr < (CLNT_BASE + CLNT_SIZE) + val is_vmem = addr >= VMEM_BASE && addr < (VMEM_BASE + VMEM_SIZE) - // On doit décaler d'un cycle (latence de l'accès mémoire) - // les signaux qui multiplexent la donnée en sortie - val is_pdmem = RegNext(is_dmem) - val is_pldip = RegNext(is_ldip) - val is_pclnt = RegNext(is_clnt) - val is_pvmem = RegNext(is_vmem) + // On connecte directement ce qui doit l'être en faisant l'hypothèse que + io.dmem.en := io.master.en && is_dmem + io.ldip.en := io.master.en && is_ldip + io.clnt.en := io.master.en && is_clnt + 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 - 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 + // On doit décaler d'un cycle (latence de l'accès mémoire) + // les signaux qui multiplexent la donnée en sortie + val is_pdmem = RegNext(is_dmem) + val is_pldip = RegNext(is_ldip) + val is_pclnt = RegNext(is_clnt) + 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 + ) ) - ) } diff --git a/src/main/scala/projet/CLint.scala b/src/main/scala/projet/CLint.scala index 184ba14..c0a0999 100644 --- a/src/main/scala/projet/CLint.scala +++ b/src/main/scala/projet/CLint.scala @@ -5,52 +5,52 @@ import chisel3._ * Principalement le timer pour pouvoir faire tourner space invaders ! \*/ class CLint extends Module { - val io = IO(new Bundle { - val bus = new BusInterface - val tip = Output(Bool()) - }) + val io = IO(new Bundle { + val bus = new BusInterface + val tip = Output(Bool()) + }) - // Adresses des deux registres 64 bits du composant - val MTIMERCMP_ADDR = 0x4000 - val MTIMER_ADDR = 0xBFF8 + // Adresses des deux registres 64 bits du composant + val MTIMERCMP_ADDR = 0x4000 + val MTIMER_ADDR = 0xbff8 - // Sur le rv32i le timer est constitué de 2 registres pour faire 64 bits - val mtime_lo = RegInit(0.U(32.W)) - val mtime_hi = RegInit(0.U(32.W)) + // Sur le rv32i le timer est constitué de 2 registres pour faire 64 bits + val mtime_lo = RegInit(0.U(32.W)) + val mtime_hi = RegInit(0.U(32.W)) - // Free running, comme on dit chez nous ! - mtime_lo := mtime_lo + 1.U - mtime_hi := Mux(mtime_lo + 1.U === 0.U, mtime_hi + 1.U, mtime_hi) + // Free running, comme on dit chez nous ! + mtime_lo := mtime_lo + 1.U + mtime_hi := Mux(mtime_lo + 1.U === 0.U, mtime_hi + 1.U, mtime_hi) - // La comparaison nécessite également 2 registres, pour une - // comparaison sur 64 bits si nécessaire - val mtimecmp_lo = RegInit(0.U(32.W)) - val mtimecmp_hi = RegInit(0.U(32.W)) - // 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)) - // Valeur par défaut sur le bus de lecture - // avec toujours un cycle de latence - val clintRegValue = RegInit(0.U(32.W)) + // La comparaison nécessite également 2 registres, pour une + // comparaison sur 64 bits si nécessaire + val mtimecmp_lo = RegInit(0.U(32.W)) + val mtimecmp_hi = RegInit(0.U(32.W)) + // 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)) + // Valeur par défaut sur le bus de lecture + // avec toujours un cycle de latence + val clintRegValue = RegInit(0.U(32.W)) - io.bus.rdata := clintRegValue + io.bus.rdata := clintRegValue - when(io.bus.en === true.B) { - when (io.bus.be.asUInt.orR) { // Écriture - when(io.bus.addr === MTIMERCMP_ADDR.U) { - mtimecmp_lo := io.bus.wdata - } .elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) { - mtimecmp_hi := io.bus.wdata - } - } .otherwise { // Lecture - when(io.bus.addr === MTIMER_ADDR.U) { - clintRegValue := mtime_lo - } .elsewhen(io.bus.addr === (MTIMER_ADDR + 4).U) { - clintRegValue := mtime_hi - } .elsewhen(io.bus.addr === MTIMERCMP_ADDR.U) { - clintRegValue := mtimecmp_lo - } .elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) { - clintRegValue := mtimecmp_hi - } + when(io.bus.en === true.B) { + when(io.bus.be.asUInt.orR) { // Écriture + when(io.bus.addr === MTIMERCMP_ADDR.U) { + mtimecmp_lo := io.bus.wdata + }.elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) { + mtimecmp_hi := io.bus.wdata + } + }.otherwise { // Lecture + when(io.bus.addr === MTIMER_ADDR.U) { + clintRegValue := mtime_lo + }.elsewhen(io.bus.addr === (MTIMER_ADDR + 4).U) { + clintRegValue := mtime_hi + }.elsewhen(io.bus.addr === MTIMERCMP_ADDR.U) { + clintRegValue := mtimecmp_lo + }.elsewhen(io.bus.addr === (MTIMERCMP_ADDR + 4).U) { + clintRegValue := mtimecmp_hi + } + } } - } } diff --git a/src/main/scala/projet/ClockGen.scala b/src/main/scala/projet/ClockGen.scala index 5212088..491dee1 100644 --- a/src/main/scala/projet/ClockGen.scala +++ b/src/main/scala/projet/ClockGen.scala @@ -9,56 +9,56 @@ import chisel3.util._ * Le nom *doit* être celui qui est dans le systemverilog * (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 clk_in1 = Input(Clock()) - val reset = Input(Reset()) - val clk_out1 = Output(Clock()) // 10 MHz - val clk_out2 = Output(Clock()) // 125 MHz - val locked = Output(Bool()) - }) + val io = IO(new Bundle { + val clk_in1 = Input(Clock()) + val reset = Input(Reset()) + val clk_out1 = Output(Clock()) // 10 MHz + val clk_out2 = Output(Clock()) // 125 MHz + val locked = Output(Bool()) + }) } class ClockGen(val divBy: Int = 10, useSim: Boolean = false) extends Module { - val io = IO(new Bundle { - val clk_in1 = Input(Clock()) - val reset = Input(Reset()) - val clk_out1 = Output(Clock()) - val clk_out2 = Output(Clock()) - val locked = Output(Bool()) - }) + val io = IO(new Bundle { + val clk_in1 = Input(Clock()) + val reset = Input(Reset()) + val clk_out1 = Output(Clock()) + val clk_out2 = Output(Clock()) + val locked = Output(Bool()) + }) - val maxCnt = (divBy - 1).U - val midCnt = ((divBy / 2) - 1).U + val maxCnt = (divBy - 1).U + val midCnt = ((divBy / 2) - 1).U - if (useSim) { - val cntReg = RegInit(0.U(log2Ceil(divBy).W)) - cntReg := cntReg + 1.U - val outReg = RegInit(false.B) - val rstReg = RegInit(true.B) - - rstReg := Mux(io.reset.asBool, true.B, rstReg) + if (useSim) { + val cntReg = RegInit(0.U(log2Ceil(divBy).W)) + cntReg := cntReg + 1.U + val outReg = RegInit(false.B) + val rstReg = RegInit(true.B) - io.locked := !rstReg + rstReg := Mux(io.reset.asBool, true.B, rstReg) - when (cntReg === midCnt) { - outReg := true.B - } .elsewhen (cntReg === maxCnt) { - outReg := false.B - cntReg := 0.U - rstReg := false.B + io.locked := !rstReg + + when(cntReg === midCnt) { + outReg := true.B + }.elsewhen(cntReg === maxCnt) { + 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 - } } diff --git a/src/main/scala/projet/Compteur.scala b/src/main/scala/projet/Compteur.scala index 87bdd07..88fe0e2 100644 --- a/src/main/scala/projet/Compteur.scala +++ b/src/main/scala/projet/Compteur.scala @@ -3,26 +3,32 @@ import chisel3._ // Déclaration d'un classe Compteur très générique class 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. - 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. - // - une sortie optionnelle atMax, qui vaut 1 lorsque le compteur atteint la valeur max définie. - ) extends Module { + 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. + 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. + // - une sortie optionnelle atMax, qui vaut 1 lorsque le compteur atteint la valeur max définie. +) extends Module { val io = IO(new Bundle { // 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. // Some permet donc de wrapper notre type optionnel. - val en = if (hasEnable) Some(Input(Bool())) else None - val max = if (hasMax) Some(Input(UInt(n.W))) 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 en = if (hasEnable) Some(Input(Bool())) else None + val max = if (hasMax) Some(Input(UInt(n.W))) 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 compteur = RegInit(0.U(n.W)) // 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 val atMax = if (hasMax) compteur === io.max.get else false.B diff --git a/src/main/scala/projet/GeneSync.scala b/src/main/scala/projet/GeneSync.scala index 930bec8..94bf9eb 100644 --- a/src/main/scala/projet/GeneSync.scala +++ b/src/main/scala/projet/GeneSync.scala @@ -11,31 +11,46 @@ class GeneSync extends Module { }) // Paramètres de synchronisation horizontale et verticale - val synchroX = VecInit(Seq( - 95.U, // HS pulse width - 47.U, // horizontal back porch - 639.U, // horizontal display time - 15.U // horizontal front porch - )) + val synchroX = VecInit( + Seq( + 95.U, // HS pulse width + 47.U, // horizontal back porch + 639.U, // horizontal display time + 15.U // horizontal front porch + ) + ) - val synchroY = VecInit(Seq( - 1.U, // VS pulse width - 28.U, // vertical back porch - 479.U, // vertical display time - 9.U // vertical front porch - )) + val synchroY = VecInit( + Seq( + 1.U, // VS pulse width + 28.U, // vertical back porch + 479.U, // vertical display time + 9.U // vertical front porch + ) + ) // Instanciation des compteurs - val compteurCLK = Module(new Compteur(3, hasMax = true)) // Compteur horloge (3 bits) - val comptX = Module(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) + val compteurCLK = Module( + new Compteur(3, hasMax = true) + ) // Compteur horloge (3 bits) + val comptX = Module( + 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 - 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 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) compteurCLK.io.max.get := 4.U @@ -53,11 +68,11 @@ class GeneSync extends Module { phaseV.io.en.get := enY && comptY.io.atMax.get // Génération des signaux de sortie - 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.img := (phaseH.io.out === 2.U) && (phaseV.io.out === 2.U) // Zone d'affichage + 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.img := (phaseH.io.out === 2.U) && (phaseV.io.out === 2.U) // Zone d'affichage - // Coordonnées pixel - io.x := comptX.io.out - io.y := comptY.io.out + // Coordonnées pixel + io.x := comptX.io.out + io.y := comptY.io.out } diff --git a/src/main/scala/projet/IDMem.scala b/src/main/scala/projet/IDMem.scala index 95e435b..b5da30f 100644 --- a/src/main/scala/projet/IDMem.scala +++ b/src/main/scala/projet/IDMem.scala @@ -13,82 +13,93 @@ class dpram( // nom identique à celui du verilog val ADDR_WIDTH: Int, val DATA_WIDTH: Int, val INIT_FILE: String = "program.mem" -) extends BlackBox(Map( - "NUM_COL" -> IntParam(NUM_COL), - "COL_WIDTH" -> IntParam(COL_WIDTH), - "ADDR_WIDTH" -> IntParam(ADDR_WIDTH), - "DATA_WIDTH" -> IntParam(DATA_WIDTH), - "INIT_FILE" -> StringParam(if (INIT_FILE != "") INIT_FILE else "NONE") -)) with HasBlackBoxResource { - - require(DATA_WIDTH == NUM_COL * COL_WIDTH, "DATA_WIDTH must be equal to NUM_COL*COL_WIDTH.") - - addResource("/vsrc/dpram.v") +) extends BlackBox( + Map( + "NUM_COL" -> IntParam(NUM_COL), + "COL_WIDTH" -> IntParam(COL_WIDTH), + "ADDR_WIDTH" -> IntParam(ADDR_WIDTH), + "DATA_WIDTH" -> IntParam(DATA_WIDTH), + "INIT_FILE" -> StringParam( + if (INIT_FILE != "") INIT_FILE else "NONE" + ) + ) + ) + with HasBlackBoxResource { - val io = IO(new Bundle { - // Port A - val clkA = Input(Clock()) - val enaA = Input(Bool()) - val addrA = Input(UInt(ADDR_WIDTH.W)) - val doutA = Output(UInt(DATA_WIDTH.W)) - val dinA = Input(UInt(DATA_WIDTH.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)) - }) + require( + DATA_WIDTH == NUM_COL * COL_WIDTH, + "DATA_WIDTH must be equal to NUM_COL*COL_WIDTH." + ) + + addResource("/vsrc/dpram.v") + + val io = IO(new Bundle { + // Port A + val clkA = Input(Clock()) + val enaA = Input(Bool()) + val addrA = Input(UInt(ADDR_WIDTH.W)) + val doutA = Output(UInt(DATA_WIDTH.W)) + val dinA = Input(UInt(DATA_WIDTH.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 val ADDR_WIDTH: Int, - val INIT_FILE: String - ) extends BlackBox(Map( - "ADDR_WIDTH_A" -> IntParam(ADDR_WIDTH), - "ADDR_WIDTH_B" -> IntParam(ADDR_WIDTH), - "BYTE_WRITE_WIDTH_A" -> 8, - "MEMORY_INIT_FILE" -> StringParam(INIT_FILE), - "MEMORY_PRIMITIVE" -> "block", - "MEMORY_SIZE" -> 524288, - "READ_DATA_WIDTH_A" -> 32, - "READ_DATA_WIDTH_B" -> 32, - "READ_LATENCY_A" -> 1, - "READ_LATENCY_B" -> 1, - "WRITE_DATA_WIDTH_A" -> 32, - "WRITE_DATA_WIDTH_B" -> 32 - )) with HasBlackBoxResource { - val io = IO(new Bundle { - // Port A - val clka = Input(Clock()) - val rsta = Input(Bool()) - val ena = Input(Bool()) - val regcea = Input(Bool()) - val dina = Input(UInt(32.W)) - val addra = Input(UInt(ADDR_WIDTH.W)) - val wea = Input(UInt(4.W)) - val injectdbiterra = Input(Bool()) - val injectsbiterra = Input(Bool()) - val douta = Output(UInt(32.W)) + val INIT_FILE: String +) extends BlackBox( + Map( + "ADDR_WIDTH_A" -> IntParam(ADDR_WIDTH), + "ADDR_WIDTH_B" -> IntParam(ADDR_WIDTH), + "BYTE_WRITE_WIDTH_A" -> 8, + "MEMORY_INIT_FILE" -> StringParam(INIT_FILE), + "MEMORY_PRIMITIVE" -> "block", + "MEMORY_SIZE" -> 524288, + "READ_DATA_WIDTH_A" -> 32, + "READ_DATA_WIDTH_B" -> 32, + "READ_LATENCY_A" -> 1, + "READ_LATENCY_B" -> 1, + "WRITE_DATA_WIDTH_A" -> 32, + "WRITE_DATA_WIDTH_B" -> 32 + ) + ) + with HasBlackBoxResource { + val io = IO(new Bundle { + // Port A + val clka = Input(Clock()) + val rsta = Input(Bool()) + val ena = Input(Bool()) + val regcea = Input(Bool()) + val dina = Input(UInt(32.W)) + val addra = Input(UInt(ADDR_WIDTH.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 sbiterrA = Output(Bool()) - // Port B - val clkb = Input(Clock()) - val rstb = Input(Bool()) - val enb = Input(Bool()) - val regceb = Input(Bool()) - val dinb = Input(UInt(32.W)) - val addrb = Input(UInt(ADDR_WIDTH.W)) - val web = Input(UInt(4.W)) - val injectdbiterrb = Input(Bool()) - val injectsbiterrb = Input(Bool()) - val doutb = Output(UInt(32.W)) + // Port B + val clkb = Input(Clock()) + val rstb = Input(Bool()) + val enb = Input(Bool()) + val regceb = Input(Bool()) + val dinb = Input(UInt(32.W)) + val addrb = Input(UInt(ADDR_WIDTH.W)) + val web = Input(UInt(4.W)) + val injectdbiterrb = Input(Bool()) + val injectsbiterrb = Input(Bool()) + val doutb = Output(UInt(32.W)) // val dbiterrB = 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 @@ -99,66 +110,73 @@ class xpm_memory_tdpram( // nom identique à celui de Xilinx * pour faciliter le préchargement (on espère). * \*/ -class IDMem(sim: Boolean, addrWidth: Int, memFile: String ="") extends Module { - val io = IO(new Bundle { - val clki = Input(Clock()) - val ibus = new BusInterface - val clkd = Input(Clock()) - val dbus = new BusInterface - }) +class IDMem(sim: Boolean, addrWidth: Int, memFile: String = "") extends Module { + val io = IO(new Bundle { + val clki = Input(Clock()) + val ibus = new BusInterface + val clkd = Input(Clock()) + 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 - if (sim || memFile == "") { - // 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 - // poids faible de l'adresse + if (sim || memFile == "") { + // 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 + // poids faible de l'adresse - val unifiedRam = Module(new dpram( - NUM_COL = 4, - COL_WIDTH = 8, - ADDR_WIDTH = addrWidth - 2, - DATA_WIDTH = 32, - INIT_FILE = memFile - )) + val unifiedRam = Module( + new dpram( + NUM_COL = 4, + COL_WIDTH = 8, + ADDR_WIDTH = addrWidth - 2, + DATA_WIDTH = 32, + INIT_FILE = memFile + ) + ) - unifiedRam.io.clkA := io.clki - unifiedRam.io.clkB := io.clkd - // instructions sur le port a - unifiedRam.io.enaA := io.ibus.en - unifiedRam.io.addrA := io.ibus.addr(addrWidth - 1, 2) - io.ibus.rdata := unifiedRam.io.doutA - unifiedRam.io.weA := 0.U - unifiedRam.io.dinA := 0.U - // données sur le port b - unifiedRam.io.enaB := io.dbus.en - unifiedRam.io.addrB := io.dbus.addr(addrWidth - 1, 2) - io.dbus.rdata := unifiedRam.io.doutB - unifiedRam.io.weB := io.dbus.be.asUInt - unifiedRam.io.dinB := io.dbus.wdata - } else { - val dpram = Module(new xpm_memory_tdpram(ADDR_WIDTH = addrWidth - 2,INIT_FILE = memFile)) - dpram.io.clka := clock - dpram.io.rsta := 0.U - dpram.io.ena := io.dbus.en - dpram.io.regcea := false.B - 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 + unifiedRam.io.clkA := io.clki + unifiedRam.io.clkB := io.clkd + // instructions sur le port a + unifiedRam.io.enaA := io.ibus.en + unifiedRam.io.addrA := io.ibus.addr(addrWidth - 1, 2) + io.ibus.rdata := unifiedRam.io.doutA + unifiedRam.io.weA := 0.U + unifiedRam.io.dinA := 0.U + // données sur le port b + unifiedRam.io.enaB := io.dbus.en + unifiedRam.io.addrB := io.dbus.addr(addrWidth - 1, 2) + io.dbus.rdata := unifiedRam.io.doutB + unifiedRam.io.weB := io.dbus.be.asUInt + unifiedRam.io.dinB := io.dbus.wdata + } else { + val dpram = Module( + new xpm_memory_tdpram( + ADDR_WIDTH = addrWidth - 2, + INIT_FILE = memFile + ) + ) + dpram.io.clka := clock + dpram.io.rsta := 0.U + dpram.io.ena := io.dbus.en + dpram.io.regcea := false.B + 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.rstb := 0.U - dpram.io.enb := io.ibus.en - dpram.io.regceb := false.B - dpram.io.dinb := 0.U - dpram.io.addrb := io.ibus.addr(addrWidth - 1, 2) - dpram.io.web := 0.U - dpram.io.injectdbiterrb := false.B - dpram.io.injectsbiterrb := false.B - io.ibus.rdata := dpram.io.doutb + dpram.io.clkb := clock + dpram.io.rstb := 0.U + dpram.io.enb := io.ibus.en + dpram.io.regceb := false.B + dpram.io.dinb := 0.U + dpram.io.addrb := io.ibus.addr(addrWidth - 1, 2) + dpram.io.web := 0.U + dpram.io.injectdbiterrb := false.B + dpram.io.injectsbiterrb := false.B + io.ibus.rdata := dpram.io.doutb - dpram.io.sleep := false.B - } + dpram.io.sleep := false.B + } } diff --git a/src/main/scala/projet/LedIp.scala b/src/main/scala/projet/LedIp.scala index 21b85bb..4c20227 100644 --- a/src/main/scala/projet/LedIp.scala +++ b/src/main/scala/projet/LedIp.scala @@ -6,41 +6,40 @@ import chisel3.util._ * Fusion de IP_LED et IP_PIN du projet VHDL \*/ class LedIp extends Module { - val io = IO(new Bundle { - val x31 = Input(UInt(32.W)) - val switch = Input(UInt(4.W)) - val led = Output(UInt(4.W)) - val button = Input(UInt(3.W)) - val bus = new BusInterface - }) + val io = IO(new Bundle { + val x31 = Input(UInt(32.W)) + val switch = Input(UInt(4.W)) + val led = Output(UInt(4.W)) + val button = Input(UInt(3.W)) + 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 x31Reg = RegNext(io.x31) // On lit x31 en continue - val btnReg = Reg(UInt(32.W)) + val dinReg = RegInit(0.U(32.W)) + val x31Reg = RegNext(io.x31) // On lit x31 en continue + val btnReg = Reg(UInt(32.W)) - // Le cpu fait une requête - // On se moque de l'adresse ... - // L'écriture écrit dans le registre, la lecture retourne l'état des boutons - when (io.bus.en === true.B) { - when (io.bus.be.asUInt.orR) { // Écriture dans le registre des leds internes - dinReg := io.bus.wdata - } .otherwise { // Lecture des boutons poussoirs, pas de débounce car lus par le soft - btnReg := Fill(29, 0.U) ## io.button + // Le cpu fait une requête + // On se moque de l'adresse ... + // L'écriture écrit dans le registre, la lecture retourne l'état des boutons + when(io.bus.en === true.B) { + when(io.bus.be.asUInt.orR) { // Écriture dans le registre des leds internes + dinReg := io.bus.wdata + }.otherwise { // Lecture des boutons poussoirs, pas de débounce car lus par le soft + 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 - // 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 - val data = Mux(io.switch(3).asBool, x31Reg, dinReg) + // On échantillonne la donnée d'entrée en continue + // 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 + val data = Mux(io.switch(3).asBool, x31Reg, dinReg) - // On l'affiche - io.led := MuxLookup(io.switch(2, 0), 0.U(4.W))( - (0 until 8).map { i => - (i.U -> data((i * 4) + 3, i * 4)) - }) + // On l'affiche + io.led := MuxLookup(io.switch(2, 0), 0.U(4.W))((0 until 8).map { i => + (i.U -> data((i * 4) + 3, i * 4)) + }) } diff --git a/src/main/scala/projet/Rv32i.scala b/src/main/scala/projet/Rv32i.scala index ce2d6a4..e4df6d6 100644 --- a/src/main/scala/projet/Rv32i.scala +++ b/src/main/scala/projet/Rv32i.scala @@ -3,54 +3,53 @@ package projet import chisel3._ class Rv32i(sim: Boolean = true) extends Module { - val io = IO(new Bundle { - val ibus = Flipped(new BusInterface) - val dbus = Flipped(new BusInterface) - val x31 = Output(UInt(32.W)) - val valid_x31 = if (sim) Some(Output(Bool())) else None - }) + val io = IO(new Bundle { + val ibus = Flipped(new BusInterface) + val dbus = Flipped(new BusInterface) + val x31 = Output(UInt(32.W)) + val valid_x31 = if (sim) Some(Output(Bool())) else None + }) - val reg_pc = RegInit(0.U(32.W)); - val reg_file = Module(new RegFile(sim)); - reg_file.io.rd_data := 0.U; - reg_file.io.we := 0.U; + val reg_pc = RegInit(0.U(32.W)); + val reg_file = Module(new RegFile(sim)); + reg_file.io.rd_data := 0.U; + reg_file.io.we := 0.U; - // Increment PC each clock - reg_pc := reg_pc + 4.U; + // Increment PC each clock + reg_pc := reg_pc + 4.U; - // Fetch instruction - io.ibus.en := true.B; - io.ibus.addr := reg_pc; + // Fetch instruction + io.ibus.en := true.B; + io.ibus.addr := reg_pc; - val insn = io.ibus.rdata; + val insn = io.ibus.rdata; - // Decode rs1 index - val rs1 = insn(19, 15) - reg_file.io.rs1_addr := rs1 - // Decode rs2 index - val rs2 = insn(24, 20) - reg_file.io.rs2_addr := rs2 - // Decode rd index - val rd = insn(11, 7) - reg_file.io.rd_addr := rd + // Decode rs1 index + val rs1 = insn(19, 15) + reg_file.io.rs1_addr := rs1 + // Decode rs2 index + val rs2 = insn(24, 20) + reg_file.io.rs2_addr := rs2 + // Decode rd index + val rd = insn(11, 7) + reg_file.io.rd_addr := rd - // Decode imm-u - val imm_u = insn(31, 12) + // Decode imm-u + val imm_u = insn(31, 12) - // Debug instruction - io.x31 := reg_file.io.rs1_data; - if(sim) - { - io.valid_x31.get := true.B; - } + // Debug instruction + io.x31 := reg_file.io.rs1_data; + if (sim) { + io.valid_x31.get := true.B; + } - io.dbus.addr := 0.U; - io.dbus.wdata := 0.U; - io.dbus.be := VecInit(false.B,false.B,false.B,false.B) - io.dbus.en := false.B; + io.dbus.addr := 0.U; + io.dbus.wdata := 0.U; + io.dbus.be := VecInit(false.B, false.B, false.B, false.B) + io.dbus.en := false.B; - io.ibus.wdata := 0.U; - io.ibus.be := VecInit(false.B,false.B,false.B,false.B) + io.ibus.wdata := 0.U; + io.ibus.be := VecInit(false.B, false.B, false.B, false.B) - //locally(sim) + // locally(sim) } diff --git a/src/main/scala/projet/VgaIp.scala b/src/main/scala/projet/VgaIp.scala index 014e9dc..d461dbe 100644 --- a/src/main/scala/projet/VgaIp.scala +++ b/src/main/scala/projet/VgaIp.scala @@ -4,37 +4,39 @@ import chisel3._ import chisel3.util._ class VgaIp extends Module { - val io = IO(new Bundle { - val clk = Input(Clock()) - val bus = Flipped(new BusInterface) - val hs = Output(Bool()) - val vs = Output(Bool()) - val r = Output(UInt(5.W)) - val g = Output(UInt(6.W)) - val b = Output(UInt(5.W)) - }) + val io = IO(new Bundle { + val clk = Input(Clock()) + val bus = Flipped(new BusInterface) + val hs = Output(Bool()) + val vs = Output(Bool()) + val r = Output(UInt(5.W)) + val g = Output(UInt(6.W)) + val b = Output(UInt(5.W)) + }) - withClock(io.clk) { - val sync = Module(new GeneSync()) - io.hs := RegNext(sync.io.hsync) - io.vs := RegNext(sync.io.vsync) - val img_sync = RegNext(sync.io.img) - val index = RegNext(sync.io.x(2, 1)) + withClock(io.clk) { + val sync = Module(new GeneSync()) + io.hs := RegNext(sync.io.hsync) + io.vs := RegNext(sync.io.vsync) + val img_sync = RegNext(sync.io.img) + val index = RegNext(sync.io.x(2, 1)) - io.bus.en := true.B - io.bus.be := VecInit(false.B, false.B, false.B, false.B) - io.bus.wdata := 0.U - val addr = 80.U * sync.io.y(8, 1) + sync.io.x(9, 3) - // Adresse mot dans la ram vidéo - io.bus.addr := addr(14, 0) ## 0.U(2.W) + io.bus.en := true.B + io.bus.be := VecInit(false.B, false.B, false.B, false.B) + io.bus.wdata := 0.U + val addr = 80.U * sync.io.y(8, 1) + sync.io.x(9, 3) + // Adresse mot dans la ram vidéo + io.bus.addr := addr(14, 0) ## 0.U(2.W) - val pixel = MuxCase(0.U, - (0 until 4).map {i => - (index === i.U) -> io.bus.rdata(8 * (3 - i) + 7, 8 * (3 - i)) - }) + val pixel = MuxCase( + 0.U, + (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.g := Mux(img_sync, pixel(5, 3)<<3, 0.U) - io.b := Mux(img_sync, pixel(2, 0)<<2, 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.b := Mux(img_sync, pixel(2, 0) << 2, 0.U) + } } diff --git a/src/main/scala/projet/ZzTop.scala b/src/main/scala/projet/ZzTop.scala index 54fc500..ff946ea 100644 --- a/src/main/scala/projet/ZzTop.scala +++ b/src/main/scala/projet/ZzTop.scala @@ -2,73 +2,75 @@ package projet import chisel3._ -class ZzTop(file: String ="",sim: Boolean = true) extends Module { - val io = IO(new Bundle { - // Interface debug minimale - val switch = Input(UInt(4.W)) - val led = Output(UInt(4.W)) - val push = Input(UInt(3.W)) - // 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 valid_x31 = if (sim) Some(Output(Bool())) else None +class ZzTop(file: String = "", sim: Boolean = true) extends Module { + val io = IO(new Bundle { + // Interface debug minimale + val switch = Input(UInt(4.W)) + val led = Output(UInt(4.W)) + val push = Input(UInt(3.W)) + // 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 valid_x31 = if (sim) Some(Output(Bool())) else None - // Port vga - val hs = Output(Bool()) - val vs = Output(Bool()) - val r = Output(UInt(5.W)) - val g = Output(UInt(6.W)) - val b = Output(UInt(5.W)) - }) + // Port vga + val hs = Output(Bool()) + val vs = Output(Bool()) + val r = Output(UInt(5.W)) + val g = Output(UInt(6.W)) + val b = Output(UInt(5.W)) + }) - val clkg = Module(new ClockGen(12, sim)) // ~ 10 MHz - // clock et reset sont les signaux implicites - clkg.io.clk_in1 := clock - clkg.io.reset := reset - val rst = (reset.asBool || !clkg.io.locked).asAsyncReset + val clkg = Module(new ClockGen(12, sim)) // ~ 10 MHz + // clock et reset sont les signaux implicites + clkg.io.clk_in1 := clock + clkg.io.reset := reset + val rst = (reset.asBool || !clkg.io.locked).asAsyncReset - // Ces deux IPs doivent tourner à 125 MHz - val vmem = Module(new IDMem(sim,17)) - vmem.io.clki := clkg.io.clk_out2 // Clock du pixel - vmem.io.clkd := clkg.io.clk_out1 // Clock du reste du système - val vgad = Module(new VgaIp) - vgad.io.clk := clkg.io.clk_out2 // Clock pixel + // Ces deux IPs doivent tourner à 125 MHz + val vmem = Module(new IDMem(sim, 17)) + vmem.io.clki := clkg.io.clk_out2 // Clock du pixel + vmem.io.clkd := clkg.io.clk_out1 // Clock du reste du système + val vgad = Module(new VgaIp) + vgad.io.clk := clkg.io.clk_out2 // Clock pixel - withClockAndReset(clkg.io.clk_out1, rst) { - val interconnect = Module(new BusInterconnect) - val core = Module(new Rv32i(sim)) - val dmem = Module(new IDMem(sim,16,file)) - val ldip = Module(new LedIp) - val clnt = Module(new CLint) + // Expose core for testing usage + val core = withClockAndReset(clkg.io.clk_out1, rst) { + val interconnect = Module(new BusInterconnect) + val core = Module(new Rv32i(sim)) + val dmem = Module(new IDMem(sim, 16, file)) + val ldip = Module(new LedIp) + val clnt = Module(new CLint) - dmem.io.clki := clkg.io.clk_out1 // Ou clock, non ? - dmem.io.clkd := clkg.io.clk_out1 // + dmem.io.clki := clkg.io.clk_out1 // Ou clock, non ? + dmem.io.clkd := clkg.io.clk_out1 // - ldip.io.switch := io.switch - ldip.io.button := io.push - ldip.io.x31 := core.io.x31 - io.led := ldip.io.led + ldip.io.switch := io.switch + ldip.io.button := io.push + ldip.io.x31 := core.io.x31 + io.led := ldip.io.led - if (sim){ - io.x31.get := core.io.x31 - io.valid_x31.get := core.io.valid_x31.get + if (sim) { + io.x31.get := core.io.x31 + 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 - } } diff --git a/src/test/scala/projet/ZzTopSpec.scala b/src/test/scala/projet/ZzTopSpec.scala index 6cfa2ef..dcb61b0 100644 --- a/src/test/scala/projet/ZzTopSpec.scala +++ b/src/test/scala/projet/ZzTopSpec.scala @@ -11,91 +11,105 @@ import scala.collection.mutable.ListBuffer class ZzTopSpec extends AnyFreeSpec with Matchers with ChiselSim { -def expectedValuesFromAsm(path: String): Seq[BigInt] = { - val line = Source.fromFile(path).getLines().find(_.startsWith("# expected:")) - line match { - case Some(l) => - l.stripPrefix("# expected:") - .split(",") - .map(_.trim) - .filter(_.nonEmpty) - .map(str => BigInt(str, 16)) - .toSeq - 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) - } - } + def expectedValuesFromAsm(path: String): Seq[BigInt] = { + val line = + Source.fromFile(path).getLines().find(_.startsWith("# expected:")) + line match { + case Some(l) => + l.stripPrefix("# expected:") + .split(",") + .map(_.trim) + .filter(_.nonEmpty) + .map(str => BigInt(str, 16)) + .toSeq + case None => + Seq.empty + } } - } - 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) - } + 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) } - - 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) + 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/" + 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) + } } - } }