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,11 +3,12 @@ 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 <ModuleName> [constructor args...]")
|
||||
println(
|
||||
"Usage: mill ... common.EmitModule <ModuleName> [constructor args...]"
|
||||
)
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
@@ -25,7 +26,9 @@ object EmitModule {
|
||||
Integer.valueOf(s.toInt)
|
||||
}
|
||||
// essai en Bool
|
||||
else if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")) {
|
||||
else if (
|
||||
s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")
|
||||
) {
|
||||
java.lang.Boolean.valueOf(s.toBoolean)
|
||||
}
|
||||
// sinon, on laisse en String
|
||||
@@ -45,7 +48,8 @@ object EmitModule {
|
||||
|
||||
ChiselStage.emitSystemVerilogFile(
|
||||
gen = generator(),
|
||||
firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info"),
|
||||
firtoolOpts =
|
||||
Array("-disable-all-randomization", "-strip-debug-info"),
|
||||
args = Array("--target-dir", s"RTL/$moduleName")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,9 +11,15 @@ import chisel3.util._
|
||||
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 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 {
|
||||
@@ -42,7 +48,7 @@ class BusInterconnect extends Module {
|
||||
val LDIP_SIZE = 0x00000004L.U
|
||||
|
||||
val CLNT_BASE = 0x02000000L.U
|
||||
val CLNT_SIZE = 0x0000C000L.U
|
||||
val CLNT_SIZE = 0x0000c000L.U
|
||||
|
||||
val VMEM_BASE = 0x80000000L.U // Mémoire vidéo dual port
|
||||
val VMEM_SIZE = 0x00020000L.U
|
||||
@@ -78,7 +84,8 @@ class BusInterconnect extends Module {
|
||||
val is_pclnt = RegNext(is_clnt)
|
||||
val is_pvmem = RegNext(is_vmem)
|
||||
|
||||
io.master.rdata := MuxCase(0x0BADADDL.U(32.W),
|
||||
io.master.rdata := MuxCase(
|
||||
0x0badaddL.U(32.W),
|
||||
Seq(
|
||||
is_pdmem -> io.dmem.rdata,
|
||||
is_pldip -> io.ldip.rdata,
|
||||
|
||||
@@ -12,7 +12,7 @@ class CLint extends Module {
|
||||
|
||||
// Adresses des deux registres 64 bits du composant
|
||||
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
|
||||
val mtime_lo = RegInit(0.U(32.W))
|
||||
|
||||
@@ -4,8 +4,10 @@ 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.
|
||||
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 {
|
||||
@@ -15,14 +17,18 @@ class Compteur(
|
||||
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 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
|
||||
|
||||
@@ -11,29 +11,44 @@ class GeneSync extends Module {
|
||||
})
|
||||
|
||||
// Paramètres de synchronisation horizontale et verticale
|
||||
val synchroX = VecInit(Seq(
|
||||
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(
|
||||
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
|
||||
|
||||
|
||||
@@ -13,15 +13,23 @@ class dpram( // nom identique à celui du verilog
|
||||
val ADDR_WIDTH: Int,
|
||||
val DATA_WIDTH: Int,
|
||||
val INIT_FILE: String = "program.mem"
|
||||
) extends BlackBox(Map(
|
||||
) 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 {
|
||||
"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.")
|
||||
require(
|
||||
DATA_WIDTH == NUM_COL * COL_WIDTH,
|
||||
"DATA_WIDTH must be equal to NUM_COL*COL_WIDTH."
|
||||
)
|
||||
|
||||
addResource("/vsrc/dpram.v")
|
||||
|
||||
@@ -46,7 +54,8 @@ class dpram( // nom identique à celui du verilog
|
||||
class xpm_memory_tdpram( // nom identique à celui de Xilinx
|
||||
val ADDR_WIDTH: Int,
|
||||
val INIT_FILE: String
|
||||
) extends BlackBox(Map(
|
||||
) extends BlackBox(
|
||||
Map(
|
||||
"ADDR_WIDTH_A" -> IntParam(ADDR_WIDTH),
|
||||
"ADDR_WIDTH_B" -> IntParam(ADDR_WIDTH),
|
||||
"BYTE_WRITE_WIDTH_A" -> 8,
|
||||
@@ -59,7 +68,9 @@ class xpm_memory_tdpram( // nom identique à celui de Xilinx
|
||||
"READ_LATENCY_B" -> 1,
|
||||
"WRITE_DATA_WIDTH_A" -> 32,
|
||||
"WRITE_DATA_WIDTH_B" -> 32
|
||||
)) with HasBlackBoxResource {
|
||||
)
|
||||
)
|
||||
with HasBlackBoxResource {
|
||||
val io = IO(new Bundle {
|
||||
// Port A
|
||||
val clka = Input(Clock())
|
||||
@@ -113,13 +124,15 @@ class IDMem(sim: Boolean, addrWidth: Int, memFile: String ="") extends Module {
|
||||
// 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(
|
||||
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
|
||||
@@ -136,7 +149,12 @@ class IDMem(sim: Boolean, addrWidth: Int, memFile: String ="") extends Module {
|
||||
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))
|
||||
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
|
||||
|
||||
@@ -39,8 +39,7 @@ class LedIp extends Module {
|
||||
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 =>
|
||||
io.led := MuxLookup(io.switch(2, 0), 0.U(4.W))((0 until 8).map { i =>
|
||||
(i.U -> data((i * 4) + 3, i * 4))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,8 +39,7 @@ class Rv32i(sim: Boolean = true) extends Module {
|
||||
|
||||
// Debug instruction
|
||||
io.x31 := reg_file.io.rs1_data;
|
||||
if(sim)
|
||||
{
|
||||
if (sim) {
|
||||
io.valid_x31.get := true.B;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,12 @@ class VgaIp extends Module {
|
||||
// Adresse mot dans la ram vidéo
|
||||
io.bus.addr := addr(14, 0) ## 0.U(2.W)
|
||||
|
||||
val pixel = MuxCase(0.U,
|
||||
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)
|
||||
|
||||
@@ -33,7 +33,8 @@ class ZzTop(file: String ="",sim: Boolean = true) extends Module {
|
||||
val vgad = Module(new VgaIp)
|
||||
vgad.io.clk := clkg.io.clk_out2 // Clock pixel
|
||||
|
||||
withClockAndReset(clkg.io.clk_out1, rst) {
|
||||
// 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))
|
||||
@@ -53,7 +54,6 @@ class ZzTop(file: String ="",sim: Boolean = true) extends Module {
|
||||
io.valid_x31.get := core.io.valid_x31.get
|
||||
}
|
||||
|
||||
|
||||
interconnect.io.master <> core.io.dbus
|
||||
|
||||
interconnect.io.dmem <> dmem.io.dbus
|
||||
@@ -70,5 +70,7 @@ class ZzTop(file: String ="",sim: Boolean = true) extends Module {
|
||||
io.r := vgad.io.r
|
||||
io.g := vgad.io.g
|
||||
io.b := vgad.io.b
|
||||
|
||||
core
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ 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:"))
|
||||
val line =
|
||||
Source.fromFile(path).getLines().find(_.startsWith("# expected:"))
|
||||
line match {
|
||||
case Some(l) =>
|
||||
l.stripPrefix("# expected:")
|
||||
@@ -29,12 +30,18 @@ def expectedValuesFromAsm(path: String): Seq[BigInt] = {
|
||||
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"
|
||||
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 = {
|
||||
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
|
||||
@@ -55,7 +62,6 @@ def expectNextValue(dut: ZzTop, expected: BigInt, timeout: Int, trace: ListBuffe
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fonction de test commune
|
||||
def testWith(file: String): Unit = {
|
||||
val path = file.replace("/mem/", "/").replace(".mem", ".s")
|
||||
@@ -89,8 +95,16 @@ def expectNextValue(dut: ZzTop, expected: BigInt, timeout: Int, trace: ListBuffe
|
||||
|
||||
} 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 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
|
||||
|
||||
Reference in New Issue
Block a user