From e915f7837812e813b9986edf5ae843d9c40f24ff Mon Sep 17 00:00:00 2001 From: dokleina Date: Tue, 31 Oct 2023 21:49:30 -0400 Subject: [PATCH 01/45] Fixed bug in PackedBundle MappingBuilder MappingBuilder was not allocating the correct next range for untagged Data element, causing low bit overlaps. --- lib/src/main/scala/spinal/lib/PackedBundle.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/PackedBundle.scala b/lib/src/main/scala/spinal/lib/PackedBundle.scala index df77f16f56..cd8f2c34e0 100644 --- a/lib/src/main/scala/spinal/lib/PackedBundle.scala +++ b/lib/src/main/scala/spinal/lib/PackedBundle.scala @@ -29,7 +29,7 @@ class PackedBundle extends Bundle { * Does not check for overlap of elements. */ private class MappingBuilder { - var lastPos = 0 + var nextPos = 0 var highBit = 0 val mapping = ArrayBuffer[(Range, Data)]() @@ -55,9 +55,9 @@ class PackedBundle extends Bundle { case None => // Assume the full range of the data with the MSB as the highest bit - (lastPos + d.getBitsWidth - 1) downto (lastPos) + (nextPos + d.getBitsWidth - 1) downto (nextPos) } - lastPos = r.high + nextPos = r.high + 1 // Update the bit width highBit = highBit.max(r.high) From 88ce8ac91367b2f98a0b9cde5f6df4b1e97c9795 Mon Sep 17 00:00:00 2001 From: oletf <109208726+oletf@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:27:36 +0100 Subject: [PATCH 02/45] XSim: windows support through msys2 --- .../scala/spinal/core/sim/SimBootstraps.scala | 2 +- .../main/scala/spinal/sim/XSimBackend.scala | 63 ++++++++++++++----- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala index a77aa0daa1..13f177719e 100644 --- a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala +++ b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala @@ -453,13 +453,13 @@ object SpinalXSimBackend { vconfig.xciSourcesPaths = xciSourcesPaths vconfig.bdSourcesPaths = bdSourcesPaths vconfig.toplevelName = rtl.toplevelName - vconfig.wavePath = "test.wdb" vconfig.waveFormat = waveFormat match { case WaveFormat.DEFAULT => WaveFormat.WDB case _ => waveFormat } vconfig.workspaceName = workspaceName vconfig.workspacePath = workspacePath + vconfig.wavePath = s"${workspacePath}/${workspaceName}/${rtl.toplevelName}.wdb" vconfig.xilinxDevice = xilinxDevice vconfig.userSimulationScript = simScript vconfig.xelabFlags = simulatorFlags.toArray diff --git a/sim/src/main/scala/spinal/sim/XSimBackend.scala b/sim/src/main/scala/spinal/sim/XSimBackend.scala index 839eb499da..5e588b01e5 100644 --- a/sim/src/main/scala/spinal/sim/XSimBackend.scala +++ b/sim/src/main/scala/spinal/sim/XSimBackend.scala @@ -84,11 +84,23 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { ".sh" } } - val simulatePath = new File(s"${workPath}/${projectName}.sim/sim_1/behav/xsim").getAbsolutePath - val simulateKernelPath = new File(s"${simulatePath}/xsim.dir/${toplevelName}_behav").getAbsolutePath - val compilePath = s"${simulatePath}/compile.${scriptSuffix}" - val elaboratePath = s"${simulatePath}/elaborate.${scriptSuffix}" - + val simulateDir = s"${workPath}/${toplevelName}_xsim.sim/sim_1/behav/xsim" + val simulatePath = new File(simulateDir).getAbsolutePath + val compilePath = s"${simulatePath}/compile.${scriptSuffix}" + val elaboratePath = s"${simulatePath}/elaborate.${scriptSuffix}" + + val msys2Shell = sys.env.get("MSYS2_ROOT") match { + case Some(x) => s"${x}\\msys2_shell.cmd" + case None => { + val msysShellPath = "C:\\msys64\\msys2_shell.cmd" + if (new File(msysShellPath).exists){ + msysShellPath + } else { + assert(!isWindows, "MSYS2 Shell not found! Please Setup you MSYS2_ROOT Environment Variable.") + "" + } + } + } val vivadoInstallPath = sys.env.get("VIVADO_HOME") match { case Some(x) => x case None => { @@ -111,6 +123,15 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { throw new Exception(message) } } + def doCmdMys2(command: String, cwd: File, message : String) = { + val cmd_msys2 = s""" $msys2Shell -defterm -here -no-start -mingw64 -c "$command" """.trim + doCmd(cmd_msys2, cwd, message) + } + def doCmdMys2(command: String, cwd: File, extraEnv: Seq[(String, String)], message : String) = { + val cmd_msys2 = s""" $msys2Shell -defterm -here -no-start -mingw64 -c "$command" """.trim + doCmd(cmd_msys2, cwd, extraEnv, message) + } + class Logger extends ProcessLogger { val logs = new StringBuilder() @@ -208,9 +229,9 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { val vivadoScriptPath = scriptPath.replace("\\", "/") val command = { - val baseCommand = s"vivado -mode batch -source $vivadoScriptPath" + val baseCommand = s""" vivado -mode batch -source "$vivadoScriptPath" """.trim if (isWindows) { - s"sh -c \'${baseCommand}\'" + s"cmd /c $baseCommand" } else { baseCommand } @@ -222,7 +243,7 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { def getScriptCommand(cmd: String) = { if (isWindows) { - s"cmd /k ${cmd}.bat" + s"cmd /c ${cmd}.bat" } else { s"bash ${cmd}.sh" } @@ -235,8 +256,8 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { doCmd(getScriptCommand("elaborate"), new File(simulatePath), "run elaborate failed") - val simDesignDir = s"${workspacePath}/${workspaceName}/${toplevelName}_xsim.sim/sim_1/behav/xsim/xsim.dir" - FileUtils.copyDirectory(new File(simDesignDir), new File("xsim.dir")) + val simDesignDir = s"${simulatePath}/xsim.dir" + FileUtils.copyDirectory(new File(simDesignDir), new File("xsim.dir")) } def compileDriver() = { @@ -313,13 +334,20 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { ldFlags.mkString(" ") ) - doCmd( - Seq(CC, - cFlags.mkString(" ") - ).mkString(" "), - new File(s"${workspacePath}/${workspaceName}"), - "Compilation of driver failed" - ) + val gccCmd = Seq(CC, cFlags.mkString(" ")).mkString(" ") + if (isWindows) { + doCmdMys2( + gccCmd, + new File(s"${workspacePath}/${workspaceName}"), + "Compilation of driver failed" + ) + } else { + doCmd( + gccCmd, + new File(s"${workspacePath}/${workspaceName}"), + "Compilation of driver failed" + ) + } System.load(driverPath) } @@ -329,6 +357,7 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { } FileUtils.deleteQuietly(new File(s"${workPath}")) + FileUtils.deleteQuietly(new File("xsim.dir")) new File(workPath).mkdirs() generateScript() runScript() From 55952cce1e015d63aeb47abc7f0c74883f84b35e Mon Sep 17 00:00:00 2001 From: oletf <109208726+oletf@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:27:36 +0100 Subject: [PATCH 03/45] XSim: import xcix IP --- sim/src/main/scala/spinal/sim/XSimBackend.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim/src/main/scala/spinal/sim/XSimBackend.scala b/sim/src/main/scala/spinal/sim/XSimBackend.scala index 5e588b01e5..0ff79f60ee 100644 --- a/sim/src/main/scala/spinal/sim/XSimBackend.scala +++ b/sim/src/main/scala/spinal/sim/XSimBackend.scala @@ -178,7 +178,7 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { s"import_files -force $p" } val importXsi = xciAbsoluteSourcesPaths map { p => - s"import_files -force -quiet [findFiles $p *.xci]" + s"import_files -force -quiet [findFiles $p *.{xci,xcix}]" } val importBd = bdAbsoluteSourcesPaths map { p => s"import_files -force -quiet [findFiles $p *.bd]" From f8378857a2cd3c3cf5a506b15fe32765a5d34fda Mon Sep 17 00:00:00 2001 From: oletf <109208726+oletf@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:27:36 +0100 Subject: [PATCH 04/45] XSim: replace asserts by exceptions on `vivadoHome` and `msys2Shell` --- .../main/scala/spinal/sim/XSimBackend.scala | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/sim/src/main/scala/spinal/sim/XSimBackend.scala b/sim/src/main/scala/spinal/sim/XSimBackend.scala index 0ff79f60ee..ac6aaec2cf 100644 --- a/sim/src/main/scala/spinal/sim/XSimBackend.scala +++ b/sim/src/main/scala/spinal/sim/XSimBackend.scala @@ -88,26 +88,27 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { val simulatePath = new File(simulateDir).getAbsolutePath val compilePath = s"${simulatePath}/compile.${scriptSuffix}" val elaboratePath = s"${simulatePath}/elaborate.${scriptSuffix}" - - val msys2Shell = sys.env.get("MSYS2_ROOT") match { - case Some(x) => s"${x}\\msys2_shell.cmd" - case None => { - val msysShellPath = "C:\\msys64\\msys2_shell.cmd" - if (new File(msysShellPath).exists){ - msysShellPath - } else { - assert(!isWindows, "MSYS2 Shell not found! Please Setup you MSYS2_ROOT Environment Variable.") - "" - } + + val msys2Shell = { + if (isWindows) { + val msys2ShellPath = + sys.env.getOrElse("MSYS2_ROOT", "C:\\msys64") + "\\msys2_shell.cmd" + if (!new File(msys2ShellPath).exists) { + throw new Exception( + "MSYS2 Shell not found! Please Setup you MSYS2_ROOT Environment Variable." + ) } - } - val vivadoInstallPath = sys.env.get("VIVADO_HOME") match { - case Some(x) => x - case None => { - assert(assertion = false, "VIVADO_HOME not found! Setup your vivado environment first.") + msys2ShellPath + } else { "" } } + val vivadoHome = sys.env.getOrElse( + "VIVADO_HOME", + throw new Exception( + "VIVADO_HOME not found! Setup your vivado environment first." + ) + ) def doCmd(command: String, cwd: File, message : String) = { val logger = new Logger() @@ -271,7 +272,7 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { jdk + "/include" } - val xsim = Paths.get(vivadoInstallPath, "data", "xsim").toAbsolutePath.toString + val xsim = Paths.get(vivadoHome, "data", "xsim").toAbsolutePath.toString val xsimIncludes = if (isWindows) { FileUtils.copyDirectory(new File(s"$xsim\\include"), new File(s"${workspacePath}\\${workspaceName}\\xsimIncludes")) s"xsimIncludes" From 10280edc707ca66d184eeaca58f4186f4012cfbe Mon Sep 17 00:00:00 2001 From: oletf <109208726+oletf@users.noreply.github.com> Date: Mon, 27 Nov 2023 12:32:58 +0100 Subject: [PATCH 05/45] XSim: Windows : fix detection sbt shell detection - always use cmd to call Vivado related scripts - always using msys's g++ to build XSIIFace dll --- .../main/scala/spinal/sim/XSimBackend.scala | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/sim/src/main/scala/spinal/sim/XSimBackend.scala b/sim/src/main/scala/spinal/sim/XSimBackend.scala index ac6aaec2cf..37d528ed1d 100644 --- a/sim/src/main/scala/spinal/sim/XSimBackend.scala +++ b/sim/src/main/scala/spinal/sim/XSimBackend.scala @@ -89,48 +89,60 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { val compilePath = s"${simulatePath}/compile.${scriptSuffix}" val elaboratePath = s"${simulatePath}/elaborate.${scriptSuffix}" + val isMsys = sys.env.get("MSYSTEM").isDefined + val msys2Shell = { if (isWindows) { - val msys2ShellPath = - sys.env.getOrElse("MSYS2_ROOT", "C:\\msys64") + "\\msys2_shell.cmd" - if (!new File(msys2ShellPath).exists) { - throw new Exception( - "MSYS2 Shell not found! Please Setup you MSYS2_ROOT Environment Variable." - ) + if (isMsys) { // SBT Called from Msys + "sh" + } else { // SBT Called from CMD + val msys2ShellPath = + sys.env.getOrElse("MSYS2_ROOT", "C:\\msys64") + "\\msys2_shell.cmd" + if (!new File(msys2ShellPath).exists) { + throw new Exception( + "MSYS2 Shell not found! Please Setup you MSYS2_ROOT Environment Variable." + ) + } + msys2ShellPath + " -defterm -here -no-start -mingw64" } - msys2ShellPath } else { "" } } + val vivadoHome = sys.env.getOrElse( "VIVADO_HOME", throw new Exception( "VIVADO_HOME not found! Setup your vivado environment first." ) ) + val vivadoBinPath = new File(s"${vivadoHome}/bin").getAbsolutePath - def doCmd(command: String, cwd: File, message : String) = { - val logger = new Logger() - if(Process(command, cwd)! (logger) != 0){ - println(logger.logs) - throw new Exception(message) - } - } - def doCmd(command: String, cwd: File, extraEnv: Seq[(String, String)], message : String) = { + def doCmd(command: String, cwd: File, message : String, extraEnv: Seq[(String, String)] = Seq.empty) : Unit = { val logger = new Logger() if(Process(command, cwd, extraEnv :_*)! (logger) != 0){ println(logger.logs) throw new Exception(message) } } - def doCmdMys2(command: String, cwd: File, message : String) = { - val cmd_msys2 = s""" $msys2Shell -defterm -here -no-start -mingw64 -c "$command" """.trim - doCmd(cmd_msys2, cwd, message) + def doCmdVivado(command: String, cwd: File, message: String, extraEnv: Seq[(String, String)] = Seq.empty) = { + val cmdVivado = + if (isWindows) { + if (isMsys) { + // msys turns "/c" to "c:\" because of posix path conversion, needs escape + s"cmd //c ${vivadoBinPath}/setEnvAndRunCmd.bat ${command}" + } else { + s"cmd /c ${vivadoBinPath}/setEnvAndRunCmd.bat ${command}" + } + } else { + s"${vivadoBinPath}/setEnvAndRunCmd.sh ${command}" + } + + doCmd(cmdVivado, cwd, message, extraEnv) } - def doCmdMys2(command: String, cwd: File, extraEnv: Seq[(String, String)], message : String) = { - val cmd_msys2 = s""" $msys2Shell -defterm -here -no-start -mingw64 -c "$command" """.trim - doCmd(cmd_msys2, cwd, extraEnv, message) + def doCmdMys2(command: String, cwd: File, message: String, extraEnv: Seq[(String, String)] = Seq.empty) : Unit = { + val cmdMsys2 = s""" $msys2Shell -c "$command" """.trim + doCmd(cmdMsys2, cwd, message, extraEnv) } @@ -229,32 +241,25 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { outFile.close() val vivadoScriptPath = scriptPath.replace("\\", "/") - val command = { - val baseCommand = s""" vivado -mode batch -source "$vivadoScriptPath" """.trim - if (isWindows) { - s"cmd /c $baseCommand" - } else { - baseCommand - } - }; - doCmd(command, + val command = s""" vivado -mode batch -source "$vivadoScriptPath" """.trim + doCmdVivado(command, new File(workPath), "Generation of vivado script failed") } def getScriptCommand(cmd: String) = { if (isWindows) { - s"cmd /c ${cmd}.bat" + s"${cmd}.bat" } else { - s"bash ${cmd}.sh" + s"${cmd}.sh" } } def runScript() = { - doCmd(getScriptCommand("compile"), + doCmdVivado(getScriptCommand("compile"), new File(simulatePath), "run compile failed") - doCmd(getScriptCommand("elaborate"), + doCmdVivado(getScriptCommand("elaborate"), new File(simulatePath), "run elaborate failed") val simDesignDir = s"${simulatePath}/xsim.dir" From bba6e91d513cc7dbd81f5731b5634f770fd36865 Mon Sep 17 00:00:00 2001 From: Yindong Date: Mon, 11 Dec 2023 16:02:25 +0800 Subject: [PATCH 06/45] make the standalone MSYS2 runs. --- sim/src/main/scala/spinal/sim/XSimBackend.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sim/src/main/scala/spinal/sim/XSimBackend.scala b/sim/src/main/scala/spinal/sim/XSimBackend.scala index 37d528ed1d..797c2c3eb6 100644 --- a/sim/src/main/scala/spinal/sim/XSimBackend.scala +++ b/sim/src/main/scala/spinal/sim/XSimBackend.scala @@ -129,8 +129,7 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { val cmdVivado = if (isWindows) { if (isMsys) { - // msys turns "/c" to "c:\" because of posix path conversion, needs escape - s"cmd //c ${vivadoBinPath}/setEnvAndRunCmd.bat ${command}" + s"sh ${vivadoBinPath}/setEnvAndRunCmd.sh ${command}" } else { s"cmd /c ${vivadoBinPath}/setEnvAndRunCmd.bat ${command}" } @@ -249,7 +248,11 @@ class XSimBackend(config: XSimBackendConfig) extends Backend { def getScriptCommand(cmd: String) = { if (isWindows) { - s"${cmd}.bat" + if (isMsys){ + s"${simulatePath}\\${cmd}.bat" + } else { + s"${cmd}.bat" + } } else { s"${cmd}.sh" } From b2e031bdea703e9632bbf4f19949b20a18b2d208 Mon Sep 17 00:00:00 2001 From: dokleina Date: Sun, 7 Jan 2024 17:50:42 -0500 Subject: [PATCH 07/45] Added mixed spec pack and unpack tests in SpinalSimPackedBundleTester Added `pack mixed spec` and `unpack mixed spec` tests that target PackedBundles with mixed spec: those that use `packFrom`, `packTo` along with non-tagged Data. Cleaned up extra debug lines. Removed `withWave` from all sims. --- .../lib/SpinalSimPackedBundleTester.scala | 122 +++++++++++++++++- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/tester/src/test/scala/spinal/lib/SpinalSimPackedBundleTester.scala b/tester/src/test/scala/spinal/lib/SpinalSimPackedBundleTester.scala index 5d923c83b8..208a028013 100644 --- a/tester/src/test/scala/spinal/lib/SpinalSimPackedBundleTester.scala +++ b/tester/src/test/scala/spinal/lib/SpinalSimPackedBundleTester.scala @@ -6,7 +6,7 @@ import spinal.tester.SpinalAnyFunSuite class SpinalSimPackedBundleTester extends SpinalAnyFunSuite { test("pack legacy") { - SimConfig.withWave + SimConfig .compile(new Component { val packedBundle = new PackedBundle { val a = Bits(3 bit) // 0 to 2 @@ -71,7 +71,7 @@ class SpinalSimPackedBundleTester extends SpinalAnyFunSuite { dut.io.f.toBigInt, dut.io.g.toBigInt ) - // SpinalInfo(s"d = ${dut.io.d.toBigInt}, rev = ${bitReversed(dut.io.d.toBigInt, 6)}") + assert( dut.io.packed.toBigInt == packedCalc, s"0x${dut.io.packed.toBigInt.toString(16)} =!= 0x${packedCalc.toString(16)}\n" @@ -81,7 +81,7 @@ class SpinalSimPackedBundleTester extends SpinalAnyFunSuite { } test("unpack legacy") { - SimConfig.withWave + SimConfig .compile(new Component { val packedBundle = new PackedBundle { val a = Bits(3 bit) // 0 to 2 @@ -219,7 +219,7 @@ class SpinalSimPackedBundleTester extends SpinalAnyFunSuite { dut.io.f.toBigInt, dut.io.g.toBigInt ) - // SpinalInfo(s"d = ${dut.io.d.toBigInt}, rev = ${bitReversed(dut.io.d.toBigInt, 6)}") + assert( dut.io.packed.toBigInt == packedCalc, s"0x${dut.io.packed.toBigInt.toString(16)} =!= 0x${packedCalc.toString(16)}\n" @@ -300,4 +300,118 @@ class SpinalSimPackedBundleTester extends SpinalAnyFunSuite { } }) } + + test("pack mixed spec") { + SimConfig + .compile(new Component { + val packedBundle = new PackedBundle { + val a = Bits(4 bits) // 0 to 3 + val b = Bits(2 bits).packFrom(8) // 8 to 9 + val c = Bits(4 bits) // 10 to 13 + val d = Bits(2 bits).packTo(6) // 4 to 6 + } + + val io = new Bundle { + val a = in(Bits(4 bits)) + val b = in(Bits(2 bits)) + val c = in(Bits(4 bits)) + val d = in(Bits(2 bits)) + val packed = out(Bits(packedBundle.getPackedWidth bits)) + } + + io.packed := RegNext(packedBundle.packed) + packedBundle.a := io.a + packedBundle.b := io.b + packedBundle.c := io.c + packedBundle.d := io.d + }) + .doSim(dut => { + dut.clockDomain.forkStimulus(10) + + def pack(a: BigInt, b: BigInt, c: BigInt, d: BigInt): BigInt = { + var res = BigInt(0) + res += a + res += b << 8 + res += c << 10 + res += d << 5 + res + } + + for (i <- 0 to 13) { + val n = 1 << i + dut.io.a #= n & 0xF + dut.io.b #= (n >> 8) & 0x3 + dut.io.c #= (n >> 10) & 0xF + dut.io.d #= (n >> 5) & 0x3 + dut.clockDomain.waitFallingEdge() + val packedCalc = pack( + dut.io.a.toBigInt, + dut.io.b.toBigInt, + dut.io.c.toBigInt, + dut.io.d.toBigInt + ) + + assert( + dut.io.packed.toBigInt == packedCalc, + s"Bit ${i}: 0x${dut.io.packed.toBigInt.toString(16)} =!= 0x${packedCalc.toString(16)}\n" + ) + } + }) + } + + test("unpack mixed spec") { + SimConfig + .compile(new Component { + val packedBundle = new PackedBundle { + val a = Bits(4 bits) // 0 to 3 + val b = Bits(2 bits).packFrom(8) // 8 to 9 + val c = Bits(4 bits) // 10 to 13 + val d = Bits(2 bits).packTo(6) // 5 to 6 + } + + val io = new Bundle { + val a = out(Bits(4 bit)) + val b = out(Bits(2 bit)) + val c = out(Bits(4 bit)) + val d = out(Bits(2 bit)) + val packed = in(Bits(packedBundle.getPackedWidth bits)) + } + + packedBundle.unpack(RegNext(io.packed)) + io.a := packedBundle.a + io.b := packedBundle.b + io.c := packedBundle.c + io.d := packedBundle.d + }) + .doSim(dut => { + dut.clockDomain.forkStimulus(10) + + def pack(a: BigInt, b: BigInt, c: BigInt, d: BigInt): BigInt = { + var res = BigInt(0) + res += a + res += b << 8 + res += c << 10 + res += d << 5 + res + } + + for (i <- 0 to 13) { + dut.io.packed #= 1 << i + dut.clockDomain.waitFallingEdge() + var packedCalc = pack( + dut.io.a.toBigInt, + dut.io.b.toBigInt, + dut.io.c.toBigInt, + dut.io.d.toBigInt + ) + // Add the ignored bits back in + packedCalc |= (dut.io.packed.toBigInt & 1 << 4) + packedCalc |= (dut.io.packed.toBigInt & 1 << 7) + assert( + dut.io.packed.toBigInt == packedCalc, + s"Bit ${i}: 0x${dut.io.packed.toBigInt.toString(16)} =!= 0x${packedCalc.toString(16)}\n" + ) + } + }) + } } From e34c1cf2243c249a486178404bcb037b061b3c98 Mon Sep 17 00:00:00 2001 From: dokleina Date: Sun, 7 Jan 2024 18:08:51 -0500 Subject: [PATCH 08/45] Ran scalafmt --- lib/src/main/scala/spinal/lib/PackedBundle.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/PackedBundle.scala b/lib/src/main/scala/spinal/lib/PackedBundle.scala index cd8f2c34e0..abe864328b 100644 --- a/lib/src/main/scala/spinal/lib/PackedBundle.scala +++ b/lib/src/main/scala/spinal/lib/PackedBundle.scala @@ -83,14 +83,14 @@ class PackedBundle extends Bundle { // "Little endian" -- ascending range val subBits = data match { case subPacked: PackedBundle => subPacked.packed - case _ => data.asBits + case _ => data.asBits } packed(range) := subBits.takeLow(range.size.min(data.getBitsWidth)).resize(range.size) } else { // "Big endian" -- descending range val subBits = data match { case subPacked: PackedBundle => subPacked.packed - case _ => data.asBits + case _ => data.asBits } packed(range) := subBits.takeHigh(range.size.min(data.getBitsWidth)).resizeLeft(range.size) } @@ -111,21 +111,21 @@ class PackedBundle extends Bundle { val subBits = bits(elRange).resize(el.getBitsWidth) el match { case subPacked: PackedBundle => subPacked.unpack(subBits) - case _ => el.assignFromBits(subBits) + case _ => el.assignFromBits(subBits) } } else { // "Big endian" -- descending range val subBits = bits(elRange).resizeLeft(el.getBitsWidth) el match { case subPacked: PackedBundle => subPacked.unpack(subBits) - case _ => el.assignFromBits(subBits) + case _ => el.assignFromBits(subBits) } } } } } - def getPackedWidth: Int = mappings.map(_._1.high).max+1 + def getPackedWidth: Int = mappings.map(_._1.high).max + 1 implicit class DataPositionEnrich[T <: Data](t: T) { From 66b41da2c62829ca25001bc7b2ccaeddcf3cda19 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 8 Jan 2024 18:51:17 +0100 Subject: [PATCH 09/45] VerilatorBackend now cache the whole shared object instead of the .a, avoiding recompilation --- sim/src/main/scala/spinal/sim/Misc.scala | 2 +- .../scala/spinal/sim/VerilatorBackend.scala | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/sim/src/main/scala/spinal/sim/Misc.scala b/sim/src/main/scala/spinal/sim/Misc.scala index 4259221db6..a6b7699f76 100644 --- a/sim/src/main/scala/spinal/sim/Misc.scala +++ b/sim/src/main/scala/spinal/sim/Misc.scala @@ -50,7 +50,7 @@ sealed class WaveFormat(val ext : String = "???"){ trait Backend{ - val uniqueId = Backend.allocateUniqueId() + var uniqueId = Backend.allocateUniqueId() def isBufferedWrite : Boolean } diff --git a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala index f00c39c672..c954b7b949 100644 --- a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala +++ b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala @@ -590,6 +590,11 @@ JNIEXPORT void API JNICALL ${jniPrefix}disableWave_1${uniqueId} md.update(config.simulatorFlags.mkString(" ").getBytes()) md.update(0.toByte) md.update(verilatorVersion.getBytes()) + md.update(0.toByte) + md.update(workspacePath.getBytes()) + md.update(0.toByte) + md.update(workspaceName.getBytes()) + def hashFile(md: MessageDigest, file: File) = { val bis = new BufferedInputStream(new FileInputStream(file)) @@ -616,9 +621,11 @@ JNIEXPORT void API JNICALL ${jniPrefix}disableWave_1${uniqueId} md.update(0.toByte) } - val hash = md.digest().map(x => (x & 0xFF).toHexString.padTo(2, '0')).mkString("") + val digest = md.digest() + val hash = digest.map(x => (x & 0xFF).toHexString.padTo(2, '0')).mkString("") workspaceCacheDir = new File(s"${cachePath}/${hash}/${workspaceName}") hashCacheDir = new File(s"${cachePath}/${hash}") + uniqueId = BigInt(digest).toInt.abs cacheGlobalSynchronized { // remove old cache entries @@ -671,26 +678,20 @@ JNIEXPORT void API JNICALL ${jniPrefix}disableWave_1${uniqueId} verilatorScriptFile.close // invoke verilator or copy cached files depending on whether cache is not used or used + val libExt = if(isWindows) "dll" else (if(isMac) "dylib" else "so") if (!useCache) { val shCommand = if(isWindows) "sh.exe" else "sh" val logger = new Logger() assert(Process(Seq(shCommand, "verilatorScript.sh"), new File(workspacePath)).! (logger) == 0, "Verilator invocation failed\n" + logger.outStr.toString()) - } else { - FileUtils.copyDirectory(workspaceCacheDir, workspaceDir) - } - - genWrapperCpp(verilatorVersionDeci >= BigDecimal("4.034")) - val threadCount = SimManager.cpuCount - val logger = new Logger - if (!useCache) { + val threadCount = SimManager.cpuCount + genWrapperCpp(verilatorVersionDeci >= BigDecimal("4.034")) assert(s"${SpinalEnv.makeCmd} -j$threadCount VM_PARALLEL_BUILDS=1 -C ${workspacePath}/${workspaceName} -f V${config.toplevelName}.mk V${config.toplevelName} CURDIR=${workspacePath}/${workspaceName}".! (logger) == 0, "Verilator C++ model compilation failed\n" + logger.outStr.toString()) + FileUtils.copyFile(new File(s"${workspacePath}/${workspaceName}/V${config.toplevelName}${if(isWindows) ".exe" else ""}") , new File(s"${workspacePath}/${workspaceName}/${workspaceName}_$uniqueId.${libExt}")) } else { - // do not remake Vtoplevel__ALL.a - assert(s"${SpinalEnv.makeCmd} -j$threadCount VM_PARALLEL_BUILDS=1 -C ${workspacePath}/${workspaceName} -f V${config.toplevelName}.mk -o V${config.toplevelName}__ALL.a V${config.toplevelName} CURDIR=${workspacePath}/${workspaceName}".! (logger) == 0, "Verilator C++ model compilation failed\n" + logger.outStr.toString()) + FileUtils.copyDirectory(workspaceCacheDir, workspaceDir) } - FileUtils.copyFile(new File(s"${workspacePath}/${workspaceName}/V${config.toplevelName}${if(isWindows) ".exe" else ""}") , new File(s"${workspacePath}/${workspaceName}/${workspaceName}_$uniqueId.${if(isWindows) "dll" else (if(isMac) "dylib" else "so")}")) if (cacheEnabled) { // update cache @@ -700,7 +701,7 @@ JNIEXPORT void API JNICALL ${jniPrefix}disableWave_1${uniqueId} // copy only needed files to save disk space FileUtils.copyDirectory(workspaceDir, workspaceCacheDir, new FileFilter() { - def accept(file: File): Boolean = file.getName() == s"V${config.toplevelName}__ALL.a" || file.getName().endsWith(".mk") || file.getName().endsWith(".h") + def accept(file: File): Boolean = file.getName().endsWith("." + libExt) }) } From e0a5716b549d899a2503238b777bd17d5430606f Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 8 Jan 2024 19:00:50 +0100 Subject: [PATCH 10/45] Moar hash --- sim/src/main/scala/spinal/sim/Misc.scala | 2 +- sim/src/main/scala/spinal/sim/VerilatorBackend.scala | 2 +- .../scala/spinal/core/sim/VerilatorCacheTester.scala | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sim/src/main/scala/spinal/sim/Misc.scala b/sim/src/main/scala/spinal/sim/Misc.scala index a6b7699f76..5f74a06c6c 100644 --- a/sim/src/main/scala/spinal/sim/Misc.scala +++ b/sim/src/main/scala/spinal/sim/Misc.scala @@ -50,7 +50,7 @@ sealed class WaveFormat(val ext : String = "???"){ trait Backend{ - var uniqueId = Backend.allocateUniqueId() + var uniqueId = Backend.allocateUniqueId().toLong def isBufferedWrite : Boolean } diff --git a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala index c954b7b949..613fc5ebda 100644 --- a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala +++ b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala @@ -625,7 +625,7 @@ JNIEXPORT void API JNICALL ${jniPrefix}disableWave_1${uniqueId} val hash = digest.map(x => (x & 0xFF).toHexString.padTo(2, '0')).mkString("") workspaceCacheDir = new File(s"${cachePath}/${hash}/${workspaceName}") hashCacheDir = new File(s"${cachePath}/${hash}") - uniqueId = BigInt(digest).toInt.abs + uniqueId = BigInt(digest).toLong.abs cacheGlobalSynchronized { // remove old cache entries diff --git a/tester/src/test/scala/spinal/core/sim/VerilatorCacheTester.scala b/tester/src/test/scala/spinal/core/sim/VerilatorCacheTester.scala index 3bfaf49bc5..e8193143b3 100644 --- a/tester/src/test/scala/spinal/core/sim/VerilatorCacheTester.scala +++ b/tester/src/test/scala/spinal/core/sim/VerilatorCacheTester.scala @@ -98,6 +98,15 @@ class VerilatorCacheTester extends SpinalAnyFunSuite { duration } + +// test("large_design") { +// SimConfig.compile(new Component{ +// val i = in UInt(8 bits) +// val o = out UInt(8 bits) +// o := Delay(i, 10000) +// }) +// } + test("verilator cache") { deleteCache() From 65b8383bb7685fbaebe1852c117f151fe26801d8 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 8 Jan 2024 19:02:41 +0100 Subject: [PATCH 11/45] revert some hash --- sim/src/main/scala/spinal/sim/VerilatorBackend.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala index 613fc5ebda..8b7feb2b78 100644 --- a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala +++ b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala @@ -590,10 +590,6 @@ JNIEXPORT void API JNICALL ${jniPrefix}disableWave_1${uniqueId} md.update(config.simulatorFlags.mkString(" ").getBytes()) md.update(0.toByte) md.update(verilatorVersion.getBytes()) - md.update(0.toByte) - md.update(workspacePath.getBytes()) - md.update(0.toByte) - md.update(workspaceName.getBytes()) def hashFile(md: MessageDigest, file: File) = { From 0c69bde4a751c116479061f57c96c3ba78ed7cf4 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 11 Jan 2024 15:26:23 +0100 Subject: [PATCH 12/45] update build.sc dependencies --- .mill-version | 1 + build.sc | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 .mill-version diff --git a/.mill-version b/.mill-version new file mode 100644 index 0000000000..e5cbde33e6 --- /dev/null +++ b/.mill-version @@ -0,0 +1 @@ +0.11.6 diff --git a/build.sc b/build.sc index ecd3ba2da9..6d1651b39a 100644 --- a/build.sc +++ b/build.sc @@ -4,15 +4,17 @@ import $file.project.Version trait SpinalModule extends SbtModule { outer => def scalaVersion = Version.SpinalVersion.compilers(0) + def scalatestVersion = "3.2.14" def scalacOptions = super.scalacOptions() ++ Seq("-unchecked", "-target:jvm-1.8") def javacOptions = super.javacOptions() ++ Seq("-source", "1.8", "-target", "1.8") val IvyDeps = Agg( ivy"org.scala-lang:scala-library:${scalaVersion}", - ivy"net.java.dev.jna:jna:5.5.0", - ivy"net.java.dev.jna:jna-platform:5.5.0", - ivy"org.slf4j:slf4j-api:1.7.25", - ivy"org.scala-lang.modules::scala-xml:1.2.0" + ivy"org.scalactic:scalactic::3.2.10", + ivy"net.java.dev.jna:jna:5.12.1", + ivy"net.java.dev.jna:jna-platform:5.12.1", + ivy"org.slf4j:slf4j-api:2.0.5", + ivy"org.scala-lang.modules::scala-xml:1.3.0" ) } @@ -49,7 +51,7 @@ object idslplugin extends SpinalModule with SpinalPublishModule { object sim extends SpinalModule with SpinalPublishModule { def mainClass = Some("spinal.sim") def ivyDeps = super.ivyDeps() ++ Agg( - ivy"commons-io:commons-io:2.4", + ivy"commons-io:commons-io:2.11.0", ivy"net.openhft:affinity:3.21ea1.1", ivy"org.slf4j:slf4j-simple:1.7.25", ivy"com.github.oshi:oshi-core:5.2.0" @@ -61,7 +63,7 @@ object lib extends SpinalModule with SpinalPublishModule { def mainClass = Some("spinal.lib") def moduleDeps = Seq(core, sim) def scalacOptions = super.scalacOptions() ++ idslplugin.pluginOptions() - def ivyDeps = super.ivyDeps() ++ Agg(ivy"commons-io:commons-io:2.4", ivy"org.scalatest::scalatest:3.2.5") + def ivyDeps = super.ivyDeps() ++ Agg(ivy"commons-io:commons-io:2.11.0", ivy"org.scalatest::scalatest:${scalatestVersion}") def publishVersion = Version.SpinalVersion.lib } @@ -102,9 +104,9 @@ object tester extends SpinalModule with SpinalPublishModule { def mainClass = Some("spinal.tester") def moduleDeps = Seq(core, sim, lib) def scalacOptions = super.scalacOptions() - def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalatest::scalatest:3.2.5") + def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalatest::scalatest:${scalatestVersion}") - object test extends Tests with TestModule.ScalaTest { - def ivyDeps = Agg(ivy"org.scalatest::scalatest::3.2.5") + object test extends SbtModuleTests with TestModule.ScalaTest { + def ivyDeps = Agg(ivy"org.scalatest::scalatest::${scalatestVersion}") } } From ccc4a8174345f93974a55e74572a9ef4a2a6c660 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 11 Jan 2024 18:07:53 +0100 Subject: [PATCH 13/45] update to mill 0.11 --- build.sc | 33 +++++++++++++++++++-------------- project/Version.sc | 18 +----------------- 2 files changed, 20 insertions(+), 31 deletions(-) mode change 100644 => 120000 project/Version.sc diff --git a/build.sc b/build.sc index 6d1651b39a..31761acc8e 100644 --- a/build.sc +++ b/build.sc @@ -3,7 +3,6 @@ import mill._, scalalib._, publish._ import $file.project.Version trait SpinalModule extends SbtModule { outer => - def scalaVersion = Version.SpinalVersion.compilers(0) def scalatestVersion = "3.2.14" def scalacOptions = super.scalacOptions() ++ Seq("-unchecked", "-target:jvm-1.8") def javacOptions = super.javacOptions() ++ Seq("-source", "1.8", "-target", "1.8") @@ -34,21 +33,24 @@ trait SpinalPublishModule extends PublishModule { ) } -object idslpayload extends SpinalModule with SpinalPublishModule { +object idslpayload extends Cross[IdslPayload](Version.SpinalVersion.compilers) +trait IdslPayload extends SpinalModule with SpinalPublishModule with CrossSbtModule { def mainClass = Some("spinal.idslpayload") override def artifactName = "spinalhdl-idsl-payload" def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scala-lang:scala-reflect:${scalaVersion}") } -object idslplugin extends SpinalModule with SpinalPublishModule { +object idslplugin extends Cross[IdslPlugin](Version.SpinalVersion.compilers) +trait IdslPlugin extends SpinalModule with SpinalPublishModule with CrossSbtModule { def mainClass = Some("spinal.idslplugin") override def artifactName = "spinalhdl-idsl-plugin" - def moduleDeps = Seq(idslpayload) + def moduleDeps = Seq(idslpayload(crossScalaVersion)) def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scala-lang:scala-compiler:${scalaVersion}") def pluginOptions = T { Seq(s"-Xplugin:${assembly().path}") } } -object sim extends SpinalModule with SpinalPublishModule { +object sim extends Cross[Sim](Version.SpinalVersion.compilers) +trait Sim extends SpinalModule with SpinalPublishModule with CrossSbtModule { def mainClass = Some("spinal.sim") def ivyDeps = super.ivyDeps() ++ Agg( ivy"commons-io:commons-io:2.11.0", @@ -59,10 +61,11 @@ object sim extends SpinalModule with SpinalPublishModule { def publishVersion = Version.SpinalVersion.sim } -object lib extends SpinalModule with SpinalPublishModule { +object lib extends Cross[Lib](Version.SpinalVersion.compilers) +trait Lib extends SpinalModule with SpinalPublishModule with CrossSbtModule { def mainClass = Some("spinal.lib") - def moduleDeps = Seq(core, sim) - def scalacOptions = super.scalacOptions() ++ idslplugin.pluginOptions() + def moduleDeps = Seq(core(crossScalaVersion), sim(crossScalaVersion)) + def scalacOptions = super.scalacOptions() ++ idslplugin(crossScalaVersion).pluginOptions() def ivyDeps = super.ivyDeps() ++ Agg(ivy"commons-io:commons-io:2.11.0", ivy"org.scalatest::scalatest:${scalatestVersion}") def publishVersion = Version.SpinalVersion.lib } @@ -74,11 +77,12 @@ def gitHash(dir: os.Path) = (try { case e: java.io.IOException => "???" }).linesIterator.next() -object core extends SpinalModule with SpinalPublishModule { +object core extends Cross[Core](Version.SpinalVersion.compilers) +trait Core extends SpinalModule with SpinalPublishModule with CrossSbtModule { def mainClass = Some("spinal.core") - def moduleDeps = Seq(idslplugin, sim) + def moduleDeps = Seq(idslplugin(crossScalaVersion), sim(crossScalaVersion)) - def scalacOptions = super.scalacOptions() ++ idslplugin.pluginOptions() + def scalacOptions = super.scalacOptions() ++ idslplugin(crossScalaVersion).pluginOptions() def ivyDeps = super.ivyDeps() ++ Agg( ivy"org.scala-lang:scala-reflect:${scalaVersion}", ivy"com.github.scopt::scopt:3.7.1", @@ -100,13 +104,14 @@ object core extends SpinalModule with SpinalPublishModule { } } -object tester extends SpinalModule with SpinalPublishModule { +object tester extends Cross[Tester](Version.SpinalVersion.compilers) +trait Tester extends SpinalModule with SpinalPublishModule with CrossSbtModule { def mainClass = Some("spinal.tester") - def moduleDeps = Seq(core, sim, lib) + def moduleDeps = Seq(core(crossScalaVersion), sim(crossScalaVersion), lib(crossScalaVersion)) def scalacOptions = super.scalacOptions() def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalatest::scalatest:${scalatestVersion}") - object test extends SbtModuleTests with TestModule.ScalaTest { + object test extends CrossSbtModuleTests with TestModule.ScalaTest { def ivyDeps = Agg(ivy"org.scalatest::scalatest::${scalatestVersion}") } } diff --git a/project/Version.sc b/project/Version.sc deleted file mode 100644 index b69805183f..0000000000 --- a/project/Version.sc +++ /dev/null @@ -1,17 +0,0 @@ -object SpinalVersion { - val compilers = List("2.11.12", "2.12.13", "2.13.6") - val compilerIsRC = false - - val isDev = true - val isSnapshot = false - private def snapshot = if (isSnapshot) "-SNAPSHOT" else "" - private val major = "1.7.2" - val all = if(isDev) "dev" else s"$major$snapshot" - val sim = all - val core = all - val lib = all - val ip = all - val debugger = all - val demo = all - val tester = all -} diff --git a/project/Version.sc b/project/Version.sc new file mode 120000 index 0000000000..763c1f88ab --- /dev/null +++ b/project/Version.sc @@ -0,0 +1 @@ +Version.scala \ No newline at end of file From d29ce49828db4c98d83d0180408ffa796829d72c Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 12 Jan 2024 13:44:29 +0100 Subject: [PATCH 14/45] regression fix --- lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala index bb9935d70b..f1be8b8dc5 100644 --- a/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala @@ -120,6 +120,7 @@ class Axi4ReadOnlyToTilelink(config: Axi4Config, bytesMax : Int) extends Compone io.down.a.source := io.up.ar.id io.down.a.address := io.up.ar.addr io.down.a.size := (lenToSize + io.up.ar.size).resized + io.down.a.debugId := 0 } val d = new Area{ From 3f31f4a3f3b2e0f6f79617ee408030d2e368daf6 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 12 Jan 2024 13:44:51 +0100 Subject: [PATCH 15/45] fix #1277 catch non analog inout --- core/src/main/scala/spinal/core/Data.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/scala/spinal/core/Data.scala b/core/src/main/scala/spinal/core/Data.scala index da39ecf559..ca269bacab 100644 --- a/core/src/main/scala/spinal/core/Data.scala +++ b/core/src/main/scala/spinal/core/Data.scala @@ -318,6 +318,7 @@ trait Data extends ContextUser with NameableByComponent with Assignable with Spi /** set a data as inout */ def asInOut(): this.type = { + assert(this.isAnalog, "inout can only be used on Analog signal") if(this.component != Component.current) { LocatedPendingError(s"You should not set $this as output outside its own component." ) }else { From 021fbc533024341578f31d088b256ad4a14f168b Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 12 Jan 2024 14:29:19 +0100 Subject: [PATCH 16/45] fix #1274 Allow verilator sim to read simPublic clockdomain signal --- core/src/main/scala/spinal/core/sim/SimBootstraps.scala | 1 - core/src/main/scala/spinal/core/sim/package.scala | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala index e61fac530f..6db762d353 100644 --- a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala +++ b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala @@ -519,7 +519,6 @@ class SimVerilatorPhase extends PhaseNetlist { pc.walkDeclarations { d => d match { case x: SpinalTagReady if (x.hasTag(SimPublic)) => { - x.removeTag(SimPublic) x.addTag(Verilator.public) } case _ => diff --git a/core/src/main/scala/spinal/core/sim/package.scala b/core/src/main/scala/spinal/core/sim/package.scala index 4cb3ca0938..3c4d7f8c0d 100644 --- a/core/src/main/scala/spinal/core/sim/package.scala +++ b/core/src/main/scala/spinal/core/sim/package.scala @@ -659,7 +659,7 @@ package object sim { private def getBool(manager: SimManager, who: Bool): Bool = { val component = who.component - if((who.isInput || who.isOutput) && component != null && component.parent == null){ + if((who.isInput || who.isOutput) && component != null && component.parent == null || who.hasTag(SimPublic)){ who }else { manager.userData.asInstanceOf[Component].pulledDataCache.getOrElse(who, null).asInstanceOf[Bool] From c1d9f22121db6c2457cb16bc6e5e271e40723d43 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 12 Jan 2024 14:31:52 +0100 Subject: [PATCH 17/45] fix debug id --- lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala index f1be8b8dc5..c0fe9eb6cc 100644 --- a/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axi/Axi4ToTilelink.scala @@ -93,6 +93,7 @@ class Axi4WriteOnlyToTilelink(config: Axi4Config, bytesMax : Int) extends Compon io.down.a.mask := io.up.w.strb io.down.a.data := io.up.w.data io.down.a.corrupt := False + io.down.a.debugId := 0 } val d = new Area{ From b02a73289674463c6e0793a8e0bec1920283a1b2 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 12 Jan 2024 16:16:34 +0100 Subject: [PATCH 18/45] fix pipeline demo --- .../src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala b/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala index 4a07328202..464612df58 100644 --- a/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala +++ b/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala @@ -341,6 +341,7 @@ object PipelineDemo6 extends App { override def propagateDown(): Unit = { propagateDownAll() + down.valid } override def propagateUp(): Unit = { From 684fa0d88fc30b31ab04c424b199cd0844cb5cce Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 12 Jan 2024 18:02:56 +0100 Subject: [PATCH 19/45] SpinalSim doSim(testName) now have its own folder by default --- core/src/main/scala/spinal/core/sim/SimBootstraps.scala | 2 +- sim/src/main/scala/spinal/sim/VerilatorBackend.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala index 6db762d353..11e3979254 100644 --- a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala +++ b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala @@ -683,7 +683,7 @@ case class SpinalSimConfig( var _simScript : String = null, var _timePrecision : TimeNumber = null, var _timeScale : TimeNumber = null, - var _testPath : String = "$WORKSPACE/$COMPILED" + var _testPath : String = "$WORKSPACE/$COMPILED/$TEST" ){ diff --git a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala index 8b7feb2b78..644399f326 100644 --- a/sim/src/main/scala/spinal/sim/VerilatorBackend.scala +++ b/sim/src/main/scala/spinal/sim/VerilatorBackend.scala @@ -293,7 +293,7 @@ ${ val signalInits = for((signal, id) <- config.signals.zipWithIndex) yield { #ifdef TRACE Verilated::traceEverOn(true); top->trace(&tfp, 99); - tfp.open((std::string(wavePath) + name + ".${format.ext}").c_str()); + tfp.open((std::string(wavePath) + "wave" + ".${format.ext}").c_str()); #endif this->name = name; this->time_precision = ${if (useTimePrecision) "Verilated::timeprecision()" else "VL_TIME_PRECISION" }; From b21edc029012787d92c021f22bcae95ad5830f96 Mon Sep 17 00:00:00 2001 From: anton schulte Date: Sun, 14 Jan 2024 12:54:17 +0100 Subject: [PATCH 20/45] Fix comment for trait BitwiseOp --- core/src/main/scala/spinal/core/Trait.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/spinal/core/Trait.scala b/core/src/main/scala/spinal/core/Trait.scala index 9e1af0d25f..b2a35ab615 100644 --- a/core/src/main/scala/spinal/core/Trait.scala +++ b/core/src/main/scala/spinal/core/Trait.scala @@ -937,13 +937,13 @@ trait Num[T <: Data] { */ trait BitwiseOp[T <: Data]{ - /** Logical AND operator */ + /** Bitwise AND operator */ def &(right: T): T - /** Logical OR operator */ + /** Bitwise OR operator */ def |(right: T): T - /** Logical XOR operator */ + /** Bitwise XOR operator */ def ^(right: T): T /** Inverse bitwise operator */ From 7d45d9c3ff93d0c9e19b3c3eddafaf3e211231f6 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 15 Jan 2024 18:17:05 +0100 Subject: [PATCH 21/45] scala 2.12 is now the default --- project/Version.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Version.scala b/project/Version.scala index 84ac10b3ff..c834267502 100644 --- a/project/Version.scala +++ b/project/Version.scala @@ -1,5 +1,5 @@ object SpinalVersion { - val compilers = List("2.11.12", "2.12.18", "2.13.12") + val compilers = List("2.12.18", "2.11.12", "2.13.12") val compilerIsRC = false val isDev = true From 750d257ae62af3a15e83b9ae6bf0f1bdb18e4867 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 18 Jan 2024 10:42:24 +0100 Subject: [PATCH 22/45] literal conversions for byte arrays --- lib/src/main/scala/spinal/lib.scala | 11 +++++++++-- .../lib/tools/binarySystem/BytesToLiteral.scala | 15 +++++++++++++++ .../lib/tools/binarySystem/LiteralToBytes.scala | 13 +++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 lib/src/main/scala/spinal/lib/tools/binarySystem/BytesToLiteral.scala create mode 100644 lib/src/main/scala/spinal/lib/tools/binarySystem/LiteralToBytes.scala diff --git a/lib/src/main/scala/spinal/lib.scala b/lib/src/main/scala/spinal/lib.scala index 31b9587ae8..356d64f10a 100644 --- a/lib/src/main/scala/spinal/lib.scala +++ b/lib/src/main/scala/spinal/lib.scala @@ -119,6 +119,8 @@ package object lib { def toBinInts(num: Int): List[Int] = binarySystem.LiteralToBinInts.BigIntToBinInts(toBigInt, num) def toDecInts(num: Int): List[Int] = binarySystem.LiteralToBinInts.BigIntToDecInts(toBigInt, num) def toOctInts(num: Int): List[Int] = binarySystem.LiteralToBinInts.BigIntToOctInts(toBigInt, num) + + def toBytes: List[Byte] = binarySystem.LiteralToBytes.bigIntToBytes(toBigInt) } implicit class BigIntRicher(value: BigInt) extends LiteralRicher { @@ -139,6 +141,11 @@ package object lib { override val defaultAlignBit: Int = 8 } + implicit class BytesRicher(lb: List[Byte]) { + def bytesToHex: String = binarySystem.BytesToLiteral.bytesToHexString(lb) + def bytesToBigInt: BigInt = binarySystem.BytesToLiteral.bytesToBigInt(lb) + } + implicit class BinIntsRicher(li: List[Int]){ def binIntsToOctAlignHigh: String = binarySystem.BinIntsToLiteral.binIntsToOctString(li, true) def binIntsToHexAlignHigh: String = binarySystem.BinIntsToLiteral.binIntsToHexString(li, true) @@ -148,7 +155,7 @@ package object lib { def binIntsToInt: Int = binIntsToBigInt.toInt def binIntsToLong: Long = binIntsToBigInt.toLong } - - + + val OHMux = new MuxOHImpl } diff --git a/lib/src/main/scala/spinal/lib/tools/binarySystem/BytesToLiteral.scala b/lib/src/main/scala/spinal/lib/tools/binarySystem/BytesToLiteral.scala new file mode 100644 index 0000000000..bec659da20 --- /dev/null +++ b/lib/src/main/scala/spinal/lib/tools/binarySystem/BytesToLiteral.scala @@ -0,0 +1,15 @@ +package spinal.lib.tools.binarySystem + +import spinal.core._ + +object BytesToLiteral { + def bytesToHexString(bytes: List[Byte]): String = { + bytes.map(v => f"$v%02x").mkString + } + def bytesToBigInt(bytes: List[Byte], inputEndian: Endianness = LITTLE): BigInt = { + BigInt((inputEndian match { + case BIG => bytes + case LITTLE => bytes.reverse + }).toArray) + } +} diff --git a/lib/src/main/scala/spinal/lib/tools/binarySystem/LiteralToBytes.scala b/lib/src/main/scala/spinal/lib/tools/binarySystem/LiteralToBytes.scala new file mode 100644 index 0000000000..2e1c15effc --- /dev/null +++ b/lib/src/main/scala/spinal/lib/tools/binarySystem/LiteralToBytes.scala @@ -0,0 +1,13 @@ +package spinal.lib.tools.binarySystem + +import spinal.core._ + +object LiteralToBytes { + def bigIntToBytes(value: BigInt, outputEndian: Endianness = LITTLE) = { + val ba = value.toByteArray + (outputEndian match { + case LITTLE => ba.reverse + case BIG => ba + }).padTo(8, 0.toByte).toList + } +} From b68ae7fee825af2510e55ab0f0199f8ba3929831 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 18 Jan 2024 10:47:02 +0100 Subject: [PATCH 23/45] Axi4Master simulation driver --- .../lib/bus/amba4/axi/sim/Axi4Master.scala | 294 ++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala new file mode 100644 index 0000000000..6eff92c192 --- /dev/null +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala @@ -0,0 +1,294 @@ +package axi.sim + +import spinal.core._ +import spinal.core.sim._ +import spinal.lib._ +import spinal.lib.bus.amba4.axi._ +import spinal.lib.sim._ + +import scala.collection.mutable + +object Axi4Bursts extends Enumeration { + type Axi4Burst = Value + + val Fixed, Incr, Wrap, Reserved = Value +} + +object Axi4Resps extends Enumeration { + type Axi4Resp = Value + + val Okay, ExOkay, SlvErr, DecErr = Value +} + +import Axi4Bursts._ +import Axi4Resps._ + +// TODO: upstream +case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { + private val busConfig = axi.config + + val arQueue = mutable.Queue[Axi4Ar => Unit]() + val awQueue = mutable.Queue[Axi4Aw => Unit]() + + private val idCount = if (busConfig.useId) (1 << busConfig.idWidth) else 1 + val rQueue = Array.fill(idCount)(mutable.Queue[Axi4R => Unit]()) + val wQueue = mutable.Queue[Axi4W => Unit]() + val bQueue = Array.fill(idCount)(mutable.Queue[Axi4B => Unit]()) + + def readIdle = arQueue.isEmpty && rQueue.map(_.isEmpty).reduce(_ && _) + + def writeIdle = awQueue.isEmpty && wQueue.isEmpty && bQueue.map(_.isEmpty).reduce(_ && _) + + def idle = readIdle && writeIdle + + private val maxSize = log2Up(busConfig.bytePerWord) + + private def log(chan: String, msg: String): Unit = { + println(s"Axi4Master [$chan]\t: $msg") + } + + def read(address: BigInt, totalBytes: BigInt, id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize) = { + var result: List[Byte] = null + val mtx = SimMutex().lock() + readCB(address, totalBytes, id, burst, len, size) { data => + result = data + mtx.unlock() + } + mtx.await() + result + } + + def readCB(address: BigInt, totalBytes: BigInt, id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: List[Byte] => Unit) = { + val bytePerBeat = 1 << size + val bytes = (len + 1) * bytePerBeat // FIXME: 4K limitation? + val numTransactions = (totalBytes.toDouble / bytes).ceil.toInt + val builder = new mutable.ArrayBuilder.ofByte + + if (numTransactions > 1) { + log("..", f"read $address%#x in $numTransactions transactions") + } + + def run(addr: BigInt, totBytes: Int, numTransactions: Int) = { + val transactionSize = if (totBytes > bytes) bytes else totBytes + readSingle(addr, transactionSize, id, burst, len, size)(handleTransaction(addr, totBytes, numTransactions)) + } + + def handleTransaction(addr: BigInt, tot: Int, numTransactions: Int)(data: List[Byte]): Unit = { + builder ++= data + if (numTransactions == 1) { + // we are the last one + callback(builder.result().toList) + } else { + run(addr + data.length, tot - data.length, numTransactions - 1) + } + } + + run(address, totalBytes.toInt, numTransactions) + } + + def readSingle(address: BigInt, totalBytes: Int, id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: List[Byte] => Unit): Unit = { + assert(size <= maxSize, s"requested beat size too big: $size vs $maxSize") + if (burst != Incr) { + assert(len <= 15, s"max fixed/wrap burst in one transaction is 16") + } + assert(len <= 255, s"max burst in one transaction is 256") + val bytePerBeat = 1 << size + val bytes = (len + 1) * bytePerBeat // FIXME: 4K limitation? + assert(totalBytes <= bytes, s"requested length $totalBytes could not be completed in one transaction") + val bytePerBus = 1 << log2Up(busConfig.dataWidth / 8) + + val roundedAddress = address - (address & (busConfig.bytePerWord - 1)) + val dropFront = (address - roundedAddress).toInt + + arQueue += { ar => + ar.addr #= address + if (busConfig.useId) ar.id #= id + if (busConfig.useBurst) ar.burst #= burst.id + if (busConfig.useLen) ar.len #= len + if (busConfig.useSize) ar.size #= size + log("AR", f"addr $address%#x size $size len $len burst $burst") + + val builder = new mutable.ArrayBuilder.ofByte + + for (beat <- 0 to len) { + rQueue(id) += { r => + if (busConfig.useLast) assert(r.last.toBoolean == (beat == len), "bad last beat") + if (busConfig.useResp) assert(r.resp.toInt == Okay.id, s"bad resp ${r.resp.toInt}") + val data = r.data.toBigInt + val beatAddress = burst match { + case Fixed => address + case Incr => address + bytePerBeat * beat + case Wrap => + val base = address & ~BigInt(bytes - 1) + base + ((address + bytePerBeat * beat) & BigInt(bytes - 1)) + } + val accessAddress = beatAddress & ~BigInt(busConfig.bytePerWord - 1) + + val start = ((beatAddress & ~BigInt(bytePerBeat - 1)) - accessAddress).toInt + val end = start + bytePerBeat + for (i <- 0 until bytePerBus) { + val _byte = ((data >> (8 * i)).toInt & 0xFF).toByte + if (start <= i && i < end) { + builder += _byte + } + } + if (beat == len) { + val response = builder.result().slice(dropFront, dropFront + totalBytes).toList + log("R", f"got data ${response.bytesToHex}") + callback(response) + } + } + } + } + } + + private val arDriver = StreamDriver(axi.ar, clockDomain) { ar => + if (arQueue.isEmpty) false else { + arQueue.dequeue()(ar) + true + } + } + + StreamReadyRandomizer(axi.r, clockDomain) + StreamMonitor(axi.r, clockDomain) { r => + val id = if (busConfig.useId) r.id.toInt else 0 + if (rQueue(id).nonEmpty) { + rQueue(id).dequeue()(r) + } + } + + private def padData(address: BigInt, data: List[Byte]) = { + val roundedAddress = address - (address & (busConfig.bytePerWord - 1)) + val padFront = (address - roundedAddress).toInt + val totalLen = roundUp(padFront + data.length, busConfig.bytePerWord).toInt + val paddedData = (List.fill(padFront)(0.toByte) ++ data).padTo(totalLen, 0.toByte) + val padBack = totalLen - padFront - data.length + + (roundedAddress, padFront, padBack, paddedData) + } + + // FIXME: this can only handle one-transaction writes + def write(address: BigInt, data: List[Byte], id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize): Unit = { + val mtx = SimMutex().lock() + writeCB(address, data, id, burst, len, size) { + mtx.unlock() + } + mtx.await() + } + + def writeCB(address: BigInt, data: List[Byte], id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: => Unit): Unit = { + val bytePerBeat = 1 << size + val bytes = (len + 1) * bytePerBeat + + val (_, padFront, _, paddedData) = padData(address, data) + + val numTransactions = paddedData.length / bytes + if (numTransactions > 1) { + log("..", f"write $address%#x in $numTransactions transactions") + } + + def run(addr: BigInt, data: List[Byte], transactionId: Int): Unit = { + val slice = transactionId match { + case 0 => data.take(bytes - padFront) + case tid if tid == numTransactions - 1 => data + case _ => data.take(bytes) + } + val remaining = data.drop(slice.length) + writeSingle(addr, slice, id, burst, len, size)(handleTransaction(addr, transactionId, remaining)) + } + + def handleTransaction(addr: BigInt, transactionId: Int, remaining: List[Byte])() = { + if (transactionId == numTransactions - 1) { + // we are the last one + assert(remaining.isEmpty, s"left over ${remaining.length} bytes unsent!") + callback + } else { + val addrInc = if (transactionId == 0) bytes - padFront else bytes + run(addr + addrInc, remaining, transactionId + 1) + } + } + + run(address, data, 0) + } + + def writeSingle(address: BigInt, data: List[Byte], id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: => Unit): Unit = { + assert(size <= maxSize, s"requested beat size too big: $size vs $maxSize") + if (burst != Incr) { + assert(len <= 15, s"max fixed/wrap burst in one transaction is 16") + } + assert(len <= 255, s"max burst in one transaction is 256") + val bytePerBeat = 1 << size + val bytes = (len + 1) * bytePerBeat + val bytePerBus = 1 << log2Up(busConfig.dataWidth / 8) + + val (roundedAddress, padFront, padBack, paddedData) = padData(address, data) + val realLen = data.length + assert(paddedData.length <= bytes, s"requested length ${data.length} (${paddedData.length} with padding) could not be completed in one transaction") + + awQueue += { aw => + aw.addr #= roundedAddress + if (busConfig.useId) aw.id #= id + if (busConfig.useLen) aw.len #= len + if (busConfig.useSize) aw.size #= size + if (busConfig.useBurst) aw.burst #= burst.id + log("AW", f"addr $roundedAddress%#x size $size len $len burst $burst") + + for (beat <- 0 to len) { + wQueue += { w => + val data = paddedData.slice(beat * bytePerBeat, (beat + 1) * bytePerBeat) + w.data #= data.toArray + val strb = if (len == 0) { + ((BigInt(1) << realLen) - 1) << padFront + } else beat match { + case 0 => (BigInt(1) << (bytePerBeat - padFront)) - 1 + case `len` => ~((BigInt(1) << padBack) - 1) + case _ => BigInt(1) << busConfig.bytePerWord + } + if (busConfig.useStrb) w.strb #= strb + if (busConfig.useLast) w.last #= beat == len + log("W", f"data ${data.bytesToHex} strb $strb%#x last ${beat == len}") + } + } + + bQueue(id) += { b => + if (busConfig.useResp) assert(b.resp.toInt == Okay.id, s"bad resp ${b.resp.toInt}") + log("B", s"transaction finished resp ${b.resp.toInt}") + callback + } + } + } + + private val awDriver = StreamDriver(axi.aw, clockDomain) { aw => + if (awQueue.isEmpty) false else { + awQueue.dequeue()(aw) + true + } + } + + private val wDriver = StreamDriver(axi.w, clockDomain) { w => + if (wQueue.isEmpty) false else { + wQueue.dequeue()(w) + true + } + } + + StreamReadyRandomizer(axi.b, clockDomain) + StreamMonitor(axi.b, clockDomain) { b => + if (bQueue.nonEmpty) { + val id = if (busConfig.useId) b.id.toInt else 0 + bQueue(id).dequeue()(b) + } + } + + def reset(): Unit = { + arQueue.clear + rQueue.foreach(_.clear) + awQueue.clear + wQueue.clear + bQueue.foreach(_.clear) + + arDriver.reset + awDriver.reset + wDriver.reset + } +} From a623c870404cbb0aaf8af2b4a7c44ed098d2c614 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 18 Jan 2024 10:47:26 +0100 Subject: [PATCH 24/45] Axi4Stream simulation driver --- .../bus/amba4/axis/sim/Axi4StreamMaster.scala | 66 ++++++++++++++++++ .../bus/amba4/axis/sim/Axi4StreamSlave.scala | 67 +++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala create mode 100644 lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala new file mode 100644 index 0000000000..4879debcb4 --- /dev/null +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala @@ -0,0 +1,66 @@ +package axi.sim + +import spinal.core._ +import spinal.core.sim._ +import spinal.lib._ +import spinal.lib.bus.amba4.axis.Axi4Stream._ +import spinal.lib.sim._ + +import scala.collection.mutable + +case class Axi4StreamMaster(axis: Axi4Stream, clockDomain: ClockDomain) { + private val busConfig = axis.config + private val queue = mutable.Queue[Axi4StreamBundle => Unit]() + + private def log(msg: String): Unit = { + println(s"Axi4StreamMaster: $msg") + } + + def send(data: List[Byte]): Unit = { + val mtx = SimMutex().lock() + sendCB(data) { + mtx.unlock() + } + mtx.await() + } + + def sendCB(data: List[Byte])(callback: => Unit): Unit = { + val fullLength = roundUp(data.length, busConfig.dataWidth).toInt + if (fullLength != data.length && !busConfig.useStrb && !busConfig.useKeep) { + log(s"not using strb or keep but length not multiple of data width; data will be zero padded") + } + + val beats = (data.map { byte => (byte, 1) } padTo(fullLength, (0.toByte, 0)) grouped busConfig.dataWidth).toList + log(s"initiating send, ${beats.length} beats in total") + beats.zipWithIndex.foreach { case (dataWithStrb, idx) => + val (data, strbBinInts) = dataWithStrb.unzip + val strb = strbBinInts.binIntsToBigInt + queue += { bundle => + val isLast = idx + 1 == beats.length + + bundle.data #= data.toArray + if (busConfig.useId) bundle.id.randomize() + if (busConfig.useStrb) bundle.strb #= strb + if (busConfig.useLast) bundle.last #= isLast + if (busConfig.useKeep) bundle.keep #= strb + + log(f"beat #$idx: data ${data.bytesToHex} strb ${strb.hexString()} last $isLast") + + if (isLast) callback + } + } + } + + private val driver = StreamDriver(axis, clockDomain) { b => + if (queue.isEmpty) false else { + queue.dequeue()(b) + true + } + } + + def reset(): Unit = { + queue.clear + + driver.reset + } +} diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala new file mode 100644 index 0000000000..b2032138d7 --- /dev/null +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala @@ -0,0 +1,67 @@ +package axi.sim + +import spinal.core._ +import spinal.core.sim._ +import spinal.lib.sim._ +import spinal.lib.bus.amba4.axis.Axi4Stream._ + +import java.util.concurrent.atomic.AtomicReference +import scala.collection.mutable + +case class Axi4StreamSlave(axis: Axi4Stream, clockDomain: ClockDomain) { + private val busConfig = axis.config + private val queue = mutable.Queue[Axi4StreamBundle => Unit]() + + private def log(msg: String) = { + println(s"Axi4StreamSlave\t: $msg") + } + + def recv(): List[Byte] = { + var result: List[Byte] = null + val mtx = SimMutex().lock() + recvCB() { data => + result = data + mtx.unlock() + } + mtx.await() + result + } + + def recvCB()(callback: List[Byte] => Unit): Unit = { + val builder = new mutable.ArrayBuilder.ofByte + + log(s"initiating recv") + + def handleBeat(bundle: Axi4StreamBundle): Unit = { + // XXX: keep + strb has a special meaning, but we are not handling that + val strb = if (busConfig.useKeep) { + bundle.keep.toBooleans + } else if (busConfig.useStrb) { + bundle.strb.toBooleans + } else { + Array.fill(busConfig.dataWidth)(true) + } + + builder ++= bundle.data.toBytes.zip(strb).filter(_._2).map(_._1) + + if (bundle.last.toBoolean) { + callback(builder.result.toList) + } else { + queue += handleBeat + } + } + + queue += handleBeat + } + + StreamReadyRandomizer(axis, clockDomain) + StreamMonitor(axis, clockDomain) { b => + if (queue.nonEmpty) { + queue.dequeue()(b) + } + } + + def reset(): Unit = { + queue.clear + } +} From c3a986d47bdeca2ed0a51ef65e72ed2c2a06e6b6 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 18 Jan 2024 11:20:21 +0100 Subject: [PATCH 25/45] document Axi4Master --- .../lib/bus/amba4/axi/sim/Axi4Master.scala | 71 ++++++++++++++++--- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala index 6eff92c192..18f2edb3a0 100644 --- a/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axi/sim/Axi4Master.scala @@ -1,4 +1,4 @@ -package axi.sim +package spinal.lib.bus.amba4.axi.sim import spinal.core._ import spinal.core.sim._ @@ -23,22 +23,43 @@ object Axi4Resps extends Enumeration { import Axi4Bursts._ import Axi4Resps._ -// TODO: upstream +/** + * Simulation master for the Axi4 bus protocol [[spinal.lib.bus.amba4.axi.Axi4]]. + * + * @constructor create a new simulation master with the given bus instance and clock domain. + * @param axi bus master to drive + * @param clockDomain clock domain to sample data on + * @example + * {{{ + * SimConfig.compile(new Component { + * val io = new Bundle { + * val axiSlave = slave(Axi4(Axi4Config(32, 32))) + * } + * io.axiSlave.assignDontCare + * }).doSim("sample") { dut => + * val master = Axi4Master(dut.io.axiSlave, dut.clockDomain) + * val data = master.read(0x1000, 4) + * } + * }}} + */ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { private val busConfig = axi.config - val arQueue = mutable.Queue[Axi4Ar => Unit]() - val awQueue = mutable.Queue[Axi4Aw => Unit]() + private val arQueue = mutable.Queue[Axi4Ar => Unit]() + private val awQueue = mutable.Queue[Axi4Aw => Unit]() private val idCount = if (busConfig.useId) (1 << busConfig.idWidth) else 1 - val rQueue = Array.fill(idCount)(mutable.Queue[Axi4R => Unit]()) - val wQueue = mutable.Queue[Axi4W => Unit]() - val bQueue = Array.fill(idCount)(mutable.Queue[Axi4B => Unit]()) + private val rQueue = Array.fill(idCount)(mutable.Queue[Axi4R => Unit]()) + private val wQueue = mutable.Queue[Axi4W => Unit]() + private val bQueue = Array.fill(idCount)(mutable.Queue[Axi4B => Unit]()) + /** check if all read channels are idle (no read transactions active) */ def readIdle = arQueue.isEmpty && rQueue.map(_.isEmpty).reduce(_ && _) + /** check if all write channels are idle (no write transactions active) */ def writeIdle = awQueue.isEmpty && wQueue.isEmpty && bQueue.map(_.isEmpty).reduce(_ && _) + /** check if bus master is idle (readIdle && writeIdle) */ def idle = readIdle && writeIdle private val maxSize = log2Up(busConfig.bytePerWord) @@ -47,6 +68,18 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { println(s"Axi4Master [$chan]\t: $msg") } + /** + * Read synchronously multiple bytes from the specified address. + * + * @param address address to read from; does not need to be aligned (data will be truncated automatically) + * @param totalBytes total number of bytes in the result; + * if longer than a single transaction (as specified by `burst`, `len`, and `size`), + * the bus master will issue multiple transactions + * @param id AxID to use in the request; xID in the response will be checked against this (cf. AXI specification) + * @param burst burst mode to issue (cf. AXI specification) + * @param len number of beats in a single transaction minus one (cf. AXI specification) + * @param size number of bytes in one beat, log encoded (cf. AXI specification) + */ def read(address: BigInt, totalBytes: BigInt, id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize) = { var result: List[Byte] = null val mtx = SimMutex().lock() @@ -58,6 +91,10 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { result } + /** Read asynchronously; same as {@link read}, but result is delivered in a callback + * + * @param callback callback function on finish + */ def readCB(address: BigInt, totalBytes: BigInt, id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: List[Byte] => Unit) = { val bytePerBeat = 1 << size val bytes = (len + 1) * bytePerBeat // FIXME: 4K limitation? @@ -86,6 +123,7 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { run(address, totalBytes.toInt, numTransactions) } + /** Read asynchronously with only one transaction */ def readSingle(address: BigInt, totalBytes: Int, id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: List[Byte] => Unit): Unit = { assert(size <= maxSize, s"requested beat size too big: $size vs $maxSize") if (burst != Incr) { @@ -167,7 +205,18 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { (roundedAddress, padFront, padBack, paddedData) } - // FIXME: this can only handle one-transaction writes + /** + * Write synchronously multiple bytes to the specified address. + * + * @param address address to write to; does not need to be aligned (data will be truncated automatically) + * @param data list of bytes to write to address; + * if longer than a single transaction (as specified by `burst`, `len`, and `size`), + * the bus master will issue multiple transactions + * @param id AxID to use in the request; xID in the response will be checked against this (cf. AXI specification) + * @param burst burst mode to issue (cf. AXI specification) + * @param len number of beats in a single transaction minus one (cf. AXI specification) + * @param size number of bytes in one beat, log encoded (cf. AXI specification) + */ def write(address: BigInt, data: List[Byte], id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize): Unit = { val mtx = SimMutex().lock() writeCB(address, data, id, burst, len, size) { @@ -176,6 +225,10 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { mtx.await() } + /** Write asynchronously; same as {@link write}, but completion is delivered in a callback + * + * @param callback callback function on finish + */ def writeCB(address: BigInt, data: List[Byte], id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: => Unit): Unit = { val bytePerBeat = 1 << size val bytes = (len + 1) * bytePerBeat @@ -211,6 +264,7 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { run(address, data, 0) } + /** Write asynchronously with only one transaction */ def writeSingle(address: BigInt, data: List[Byte], id: Int = 0, burst: Axi4Burst = Incr, len: Int = 0, size: Int = maxSize)(callback: => Unit): Unit = { assert(size <= maxSize, s"requested beat size too big: $size vs $maxSize") if (burst != Incr) { @@ -280,6 +334,7 @@ case class Axi4Master(axi: Axi4, clockDomain: ClockDomain) { } } + /** Reset bus master (dropping all pending transactions) */ def reset(): Unit = { arQueue.clear rQueue.foreach(_.clear) From 16e79ae702ffc5383ee57fa90af722f8cd04227d Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 18 Jan 2024 13:16:58 +0100 Subject: [PATCH 26/45] document Axi4Stream master/slave --- .../bus/amba4/axis/sim/Axi4StreamMaster.scala | 24 ++++++++++++++- .../bus/amba4/axis/sim/Axi4StreamSlave.scala | 30 +++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala index 4879debcb4..5f6499de2c 100644 --- a/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamMaster.scala @@ -1,4 +1,4 @@ -package axi.sim +package spinal.lib.bus.amba4.axis.sim import spinal.core._ import spinal.core.sim._ @@ -8,6 +8,25 @@ import spinal.lib.sim._ import scala.collection.mutable +/** + * Simulation master for the Axi4Stream bus protocol [[Axi4Stream]]. + * + * @constructor create a simulation master with the given bus instance and clock domain + * @param axis bus master to drive + * @param clockDomain clock domain to sample data on + * @example + * {{{ + * SimConfig.compile(new Component { + * val io = new Bundle { + * val axisSlave = slave(Axi4Stream(Axi4StreamConfig(32))) + * } + * io.axisSlave.assignDontCare + * }).doSim("sample") { dut => + * val master = Axi4StreamMaster(dut.io.axisSlave, dut.clockDomain) + * master.send(Random.nextBytes(256).toList) + * } + * }}} + */ case class Axi4StreamMaster(axis: Axi4Stream, clockDomain: ClockDomain) { private val busConfig = axis.config private val queue = mutable.Queue[Axi4StreamBundle => Unit]() @@ -16,6 +35,7 @@ case class Axi4StreamMaster(axis: Axi4Stream, clockDomain: ClockDomain) { println(s"Axi4StreamMaster: $msg") } + /** Send synchronously a full transaction to the bus. */ def send(data: List[Byte]): Unit = { val mtx = SimMutex().lock() sendCB(data) { @@ -24,6 +44,7 @@ case class Axi4StreamMaster(axis: Axi4Stream, clockDomain: ClockDomain) { mtx.await() } + /** Send asynchronously; same as {@link send}, but completion is delivered in a callback */ def sendCB(data: List[Byte])(callback: => Unit): Unit = { val fullLength = roundUp(data.length, busConfig.dataWidth).toInt if (fullLength != data.length && !busConfig.useStrb && !busConfig.useKeep) { @@ -58,6 +79,7 @@ case class Axi4StreamMaster(axis: Axi4Stream, clockDomain: ClockDomain) { } } + /** Reset bus slave (dropping all pending transactions) */ def reset(): Unit = { queue.clear diff --git a/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala index b2032138d7..f66f55c120 100644 --- a/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala +++ b/lib/src/main/scala/spinal/lib/bus/amba4/axis/sim/Axi4StreamSlave.scala @@ -1,13 +1,36 @@ -package axi.sim +package spinal.lib.bus.amba4.axis.sim import spinal.core._ import spinal.core.sim._ import spinal.lib.sim._ import spinal.lib.bus.amba4.axis.Axi4Stream._ -import java.util.concurrent.atomic.AtomicReference import scala.collection.mutable +/** + * Simulation slave for the Axi4Stream bus protocol [[Axi4Stream]]. + * + * @constructor create a simulation slave with the given bus instance and clock domain + * @param axis bus slave to drive + * @param clockDomain clock domain to sample data on + * @example + * {{{ + * SimConfig.compile(new Component { + * val io = new Bundle { + * val axisMaster = master(Axi4Stream(Axi4StreamConfig(32))) + * } + * io.axisMaster.assignDontCare + * }).doSim("sample") { dut => + * val slave = Axi4StreamSlave(dut.io.axisMaster, dut.clockDomain) + * val data = slave.recv() + * } + * }}} + * + * @note The current implementation does not buffer unexpected transactions (i.e. bus activity when no + * {@link recv} has been issued). In some race conditions, this may result in incomplete captures due to the first + * beats being lost. Consider enqueuing a asynchronous request with the callback interface ({@link recvCB}) before + * issuing the triggering action. + */ case class Axi4StreamSlave(axis: Axi4Stream, clockDomain: ClockDomain) { private val busConfig = axis.config private val queue = mutable.Queue[Axi4StreamBundle => Unit]() @@ -16,6 +39,7 @@ case class Axi4StreamSlave(axis: Axi4Stream, clockDomain: ClockDomain) { println(s"Axi4StreamSlave\t: $msg") } + /** Receive synchronously a full transaction from the bus. */ def recv(): List[Byte] = { var result: List[Byte] = null val mtx = SimMutex().lock() @@ -27,6 +51,7 @@ case class Axi4StreamSlave(axis: Axi4Stream, clockDomain: ClockDomain) { result } + /** Receive asynchronously; same as {@link recv}, but result is delivered in a callback */ def recvCB()(callback: List[Byte] => Unit): Unit = { val builder = new mutable.ArrayBuilder.ofByte @@ -61,6 +86,7 @@ case class Axi4StreamSlave(axis: Axi4Stream, clockDomain: ClockDomain) { } } + /** Reset bus slave (dropping all pending transactions) */ def reset(): Unit = { queue.clear } From 2297642ff2977507c74e744b66fc102ca34f655a Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Thu, 18 Jan 2024 13:59:05 +0100 Subject: [PATCH 27/45] driveStream on BusSlaveFactory --- .../spinal/lib/bus/misc/BusSlaveFactory.scala | 71 ++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/bus/misc/BusSlaveFactory.scala b/lib/src/main/scala/spinal/lib/bus/misc/BusSlaveFactory.scala index 114a009183..2dfa6b0d44 100644 --- a/lib/src/main/scala/spinal/lib/bus/misc/BusSlaveFactory.scala +++ b/lib/src/main/scala/spinal/lib/bus/misc/BusSlaveFactory.scala @@ -406,12 +406,16 @@ trait BusSlaveFactory extends Area{ /** * Create a writable Flow register of type dataType at address and placed at bitOffset in the word + * + * @param checkByteEnable do not trigger flow if byte enable is all zero. See [[https://github.com/SpinalHDL/SpinalHDL/issues/1265]] + * for the discussion about this behaviour. */ - def createAndDriveFlow[T <: Data](dataType : T, - address : BigInt, - bitOffset : Int = 0): Flow[T] = { + def createAndDriveFlow[T <: Data](dataType : T, + address : BigInt, + bitOffset : Int = 0, + checkByteEnable: Boolean = false): Flow[T] = { val flow = Flow(dataType) - driveFlow(flow, address, bitOffset) + driveFlow(flow, address, bitOffset, checkByteEnable) flow } @@ -502,28 +506,56 @@ trait BusSlaveFactory extends Area{ /** * Emit on that a transaction when a write happen at address by using data placed at bitOffset in the word + * + * @param checkByteEnable do not trigger flow if byte enable is all zero. See [[https://github.com/SpinalHDL/SpinalHDL/issues/1265]] + * for the discussion about this behaviour. */ - def driveFlow[T <: Data](that : Flow[T], - address : BigInt, - bitOffset : Int = 0): Unit = { + def driveFlow[T <: Data](that : Flow[T], + address : BigInt, + bitOffset : Int = 0, + checkByteEnable: Boolean = false): Unit = { val wordCount = (bitOffset + widthOf(that.payload) - 1 ) / busDataWidth + 1 + val byteEnable = writeByteEnable() + def assignValidNext(valid: Bool): Unit = { + if (byteEnable != null && checkByteEnable) { + when(byteEnable =/= 0) { valid := True } + } else { + valid := True + } + } if (wordCount == 1){ that.valid := False - onWrite(address){ that.valid := True } + onWrite(address){ assignValidNext(that.valid) } nonStopWrite(that.payload, bitOffset) }else{ assert(bitOffset == 0, "BusSlaveFactory ERROR [driveFlow] : BitOffset must be equal to 0 if the payload of the Flow is bigger than the data bus width") val regValid = RegNext(False) init(False) - onWrite(address + ((wordCount - 1) * wordAddressInc)){ regValid := True } + onWrite(address + ((wordCount - 1) * wordAddressInc)){ assignValidNext(regValid) } driveMultiWord(that.payload, address) that.valid := regValid } } + /** + * Emit on that a transaction when a write happen at address, by using data placed at bitOffset in the word. + * Block the write transaction until the transaction succeeds (stream becomes ready). + */ + def driveStream[T <: Data](that: Stream[T], address: BigInt, bitOffset: Int = 0): Unit = { + val wordCount = (bitOffset + widthOf(that.payload) - 1) / busDataWidth + 1 + onWritePrimitive(SizeMapping(address, wordCount * wordAddressInc), haltSensitive = false, null) { + when(!that.ready) { + writeHalt() + } + } + val flow = Flow(that.payloadType()) + driveFlow(flow, address, bitOffset, checkByteEnable = true) + that << flow.toStream + } + /** * Read that (that is bigger than the busWidth) and consume the transaction when a read happen at address. * @note in order to avoid to read wrong data read first the address which contains the @@ -559,6 +591,27 @@ trait BusSlaveFactory extends Area{ } } + /** + * Same as {@link readStreamNonBlocking}, but block the bus for at most `blockCycles` before returning the NACK. + * @param that data to read over bus + * @param address address to map at + * @param blockCycles cycles to block read transaction before returning NACK + * @tparam T type of stream payload + */ + def readStreamBlockCycles[T <: Data](that: Stream[T], address: BigInt, blockCycles: UInt): Unit = { + val counter = Counter(blockCycles.getWidth bits) + val wordCount = (1 + that.payload.getBitsWidth - 1) / busDataWidth + 1 + onReadPrimitive(SizeMapping(address, wordCount * wordAddressInc), haltSensitive = false, null) { + counter.increment() + when(counter.value < blockCycles && !that.valid) { + readHalt() + } otherwise { + counter.clear() + } + } + + readStreamNonBlocking(that, address) + } /** * Read that and consume the transaction when a read happen at address. From f80c4dadebb1baf94da69817a35a4356fa14411f Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 18 Jan 2024 15:20:16 +0100 Subject: [PATCH 28/45] Add randomPick(Random) --- core/src/main/scala/spinal/core/sim/package.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/spinal/core/sim/package.scala b/core/src/main/scala/spinal/core/sim/package.scala index 3c4d7f8c0d..434d2075ee 100644 --- a/core/src/main/scala/spinal/core/sim/package.scala +++ b/core/src/main/scala/spinal/core/sim/package.scala @@ -29,6 +29,7 @@ import scala.collection.generic.Shrinkable import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.collection.Seq +import scala.util.Random /** * Simulation package @@ -284,7 +285,7 @@ package object sim { implicit class SimSeqPimper[T](pimped: Seq[T]){ - def randomPick(): T = pimped(simRandom.nextInt(pimped.length)) + def randomPick(rand : Random = simRandom): T = pimped(rand.nextInt(pimped.length)) def randomPickWithIndex(): (T, Int) = { val index = simRandom.nextInt(pimped.length) (pimped(index), index) From c2855b28ddee3b30f0b5b981953422b709039d2e Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 19 Jan 2024 12:53:10 +0100 Subject: [PATCH 29/45] Fix outsideCondScopeData when no statement is added XD --- core/src/main/scala/spinal/core/Misc.scala | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/spinal/core/Misc.scala b/core/src/main/scala/spinal/core/Misc.scala index 5f45d7f3e0..42789e204d 100644 --- a/core/src/main/scala/spinal/core/Misc.scala +++ b/core/src/main/scala/spinal/core/Misc.scala @@ -617,13 +617,16 @@ object ContextSwapper{ case cu: ContextUser => cu.parentScope = topBody case _ => } - topBody.head = addedHead - addedHead.lastScopeStatement = null.asInstanceOf[Statement] - addedLast.nextScopeStatement = oldHead - if(oldHead != null) oldHead.lastScopeStatement = addedLast - if(oldLast != null) oldLast.nextScopeStatement = null.asInstanceOf[Statement] - topBody.last = oldLast + if(addedHead != null) { + topBody.head = addedHead + addedHead.lastScopeStatement = null.asInstanceOf[Statement] + addedLast.nextScopeStatement = oldHead + if (oldHead != null) oldHead.lastScopeStatement = addedLast + if (oldLast != null) oldLast.nextScopeStatement = null.asInstanceOf[Statement] + topBody.last = oldLast + } + ret // return the value returned by that } } From 4da2715d9c9cabbac6d130d4ccfc4c219c5f7869 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 19 Jan 2024 14:05:31 +0100 Subject: [PATCH 30/45] Add SPINAL_BENCH_THREAD_COUNT --- lib/src/main/scala/spinal/lib/eda/bench/Bench.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/scala/spinal/lib/eda/bench/Bench.scala b/lib/src/main/scala/spinal/lib/eda/bench/Bench.scala index 8574872e1f..ebde9c23b7 100644 --- a/lib/src/main/scala/spinal/lib/eda/bench/Bench.scala +++ b/lib/src/main/scala/spinal/lib/eda/bench/Bench.scala @@ -71,7 +71,7 @@ object Bench { def apply(rtls : Seq[Rtl], targets : Seq[Target], workspacesRoot : String = sys.env.getOrElse("SPINAL_BENCH_WORKSPACE", null)): Unit ={ import scala.concurrent.ExecutionContext implicit val ec = ExecutionContext.fromExecutorService( - new ForkJoinPool(Math.max(1, SimManager.cpuCount / 2), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true) + new ForkJoinPool(Math.max(1, sys.env.getOrElse("SPINAL_BENCH_THREAD_COUNT", SimManager.cpuCount/4 toString).toInt), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true) ) val results = (for (rtl <- rtls) yield { From 9fc386dfbfb3a944540c1bed33601b2419fe5bf3 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 19 Jan 2024 14:05:57 +0100 Subject: [PATCH 31/45] Rework vivado tcl to generate a re-openable project --- .../spinal/lib/eda/xilinx/VivadoFlow.scala | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/eda/xilinx/VivadoFlow.scala b/lib/src/main/scala/spinal/lib/eda/xilinx/VivadoFlow.scala index be1dbe2923..68d44c074b 100644 --- a/lib/src/main/scala/spinal/lib/eda/xilinx/VivadoFlow.scala +++ b/lib/src/main/scala/spinal/lib/eda/xilinx/VivadoFlow.scala @@ -39,14 +39,26 @@ object VivadoFlow { // generate tcl script val tcl = new java.io.FileWriter(Paths.get(workspacePath, "doit.tcl").toFile) tcl.write( -s"""${readRtl} -read_xdc doit.xdc +s""" +create_project -force project_bft_batch ./project_bft_batch -part $device -synth_design -mode out_of_context -part $device -top ${rtl.getTopModuleName()} -opt_design -place_design -route_design +add_files {${rtl.getRtlPaths().mkString(" ")}} +add_files -fileset constrs_1 ./doit.xdc +import_files -force + +set_property -name {STEPS.SYNTH_DESIGN.ARGS.MORE OPTIONS} -value {-mode out_of_context} -objects [get_runs synth_1] +launch_runs synth_1 +wait_on_run synth_1 +open_run synth_1 -name netlist_1 + +report_timing_summary -delay_type max -report_unconstrained -check_timing_verbose -max_paths 10 -input_pins -file syn_timing.rpt +report_power -file syn_power.rpt + +launch_runs impl_1 +wait_on_run impl_1 + +open_run impl_1 report_utilization report_timing_summary -warn_on_violation report_pulse_width -warn_on_violation -all_violators From 59e6de724f562f5110c044adda6443ed470fc18b Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Mon, 22 Jan 2024 10:44:38 +0100 Subject: [PATCH 32/45] allow CounterFreeRun to take bitCount --- lib/src/main/scala/spinal/lib/Utils.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/Utils.scala b/lib/src/main/scala/spinal/lib/Utils.scala index a4612ad4a3..340d634c0f 100644 --- a/lib/src/main/scala/spinal/lib/Utils.scala +++ b/lib/src/main/scala/spinal/lib/Utils.scala @@ -534,12 +534,18 @@ class BitAggregator { * See [[https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Libraries/utils.html?highlight=counter#counter]] */ object CounterFreeRun { - def apply(stateCount: BigInt): Counter = { - val c = Counter(stateCount) + private def makeFreeRun(c: Counter): Counter = { c.willIncrement.removeAssignments() c.increment() c } + def apply(stateCount: BigInt): Counter = { + makeFreeRun(Counter(stateCount)) + } + + def apply(bitCount: BitCount): Counter = { + makeFreeRun(Counter(bitCount)) + } } /** Creates a counter From 9cce86b35bc70f131476eb3ab98a78c9a31093cc Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 22 Jan 2024 10:44:52 +0100 Subject: [PATCH 33/45] fix #1285 verilog emit ohhot test using x[y] instead of bit mask --- .../internals/ComponentEmitterVerilog.scala | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/spinal/core/internals/ComponentEmitterVerilog.scala b/core/src/main/scala/spinal/core/internals/ComponentEmitterVerilog.scala index 033aa92a77..3d350cbf11 100644 --- a/core/src/main/scala/spinal/core/internals/ComponentEmitterVerilog.scala +++ b/core/src/main/scala/spinal/core/internals/ComponentEmitterVerilog.scala @@ -598,8 +598,11 @@ class ComponentEmitterVerilog( def emitEnumParams(): Unit = { for((e,encoding) <- localEnums) { - for(element <- e.elements) { - localparams ++= s" localparam ${emitEnumLiteral(element, encoding,"")} = ${idToBits(element, encoding)};\n" + for (element <- e.elements) { + localparams ++= s" localparam ${emitEnumLiteral(element, encoding, "")} = ${idToBits(element, encoding)};\n" + } + if(encoding == binaryOneHot) for (element <- e.elements) { + localparams ++= s" localparam ${emitEnumLiteral(element, encoding, "")}_OH_ID = ${element.position};\n" } } } @@ -825,8 +828,13 @@ class ComponentEmitterVerilog( def emitIsCond(that: Expression): String = { that match { case lit: EnumLiteral[_] if (lit.encoding == binaryOneHot) => { - val expr = emitEnumLiteral(lit.senum, lit.encoding) - s"(((${emitExpression(switchStatement.value)}) & ${expr}) == ${expr})" + switchValue match { + case _ : SpinalEnumCraft[_] => s"(${emitExpression(switchStatement.value)}[${emitEnumLiteral(lit.senum, lit.encoding)}_OH_ID])" + case _ => { + val expr = emitEnumLiteral(lit.senum, lit.encoding) + s"(((${emitExpression(switchStatement.value)}) & ${expr}) == ${expr})" + } + } } } } @@ -1562,8 +1570,8 @@ end encoding match { case `binaryOneHot` => { (e.left, e.right) match { -// case (sig, lit : EnumLiteral[_]) => s"(${if (eguals) "" else "! "}${emitExpression(sig)}[${lit.senum.position}])" -// case (lit : EnumLiteral[_], sig) => s"(${if (eguals) "" else "! "}${emitExpression(sig)}[${lit.senum.position}])" + case (sig : SpinalEnumCraft[_], lit : EnumLiteral[_]) => s"(${if (eguals) "" else "! "}${emitExpression(sig)}[${emitEnumLiteral(lit.senum, lit.encoding)}_OH_ID])" + case (lit : EnumLiteral[_], sig : SpinalEnumCraft[_]) => s"(${if (eguals) "" else "! "}${emitExpression(sig)}[${emitEnumLiteral(lit.senum, lit.encoding)}_OH_ID])" case _ => s"((${emitExpression(e.left)} & ${emitExpression(e.right)}) ${if (eguals) "!=" else "=="} ${encoding.getWidth(enumDef)}'b${"0" * encoding.getWidth(enumDef)})" } } From ef0063da38424830a5b29f188e3887e2b043cb75 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 22 Jan 2024 14:35:28 +0100 Subject: [PATCH 34/45] Fix stageLink when no ready --- .../spinal/lib/misc/pipeline/StageLink.scala | 2 +- .../lib/misc/pipeline/PipelineDemo.scala | 88 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/lib/src/main/scala/spinal/lib/misc/pipeline/StageLink.scala b/lib/src/main/scala/spinal/lib/misc/pipeline/StageLink.scala index 85c29c96bb..e3f96f4da5 100644 --- a/lib/src/main/scala/spinal/lib/misc/pipeline/StageLink.scala +++ b/lib/src/main/scala/spinal/lib/misc/pipeline/StageLink.scala @@ -46,13 +46,13 @@ class StageLink(val up : Node, val down : Node) extends Link { if(down.ctrl.valid.nonEmpty) down.valid.setAsReg() init (False) matches.foreach(p => down(p).setAsReg()) - down.ctrl.forgetOne foreach { cond => down.valid clearWhen(cond) } up.ctrl.ready.isEmpty match { case true => if(down.ctrl.valid.nonEmpty) down.valid := up.isValid matches.foreach(p => down(p) := up(p)) case false => { + down.ctrl.forgetOne foreach { cond => down.valid clearWhen (cond) } if(down.ctrl.valid.nonEmpty) when(up.isReady) { down.valid := up.isValid } diff --git a/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala b/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala index 4a07328202..6668764951 100644 --- a/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala +++ b/tester/src/main/scala/spinal/lib/misc/pipeline/PipelineDemo.scala @@ -555,3 +555,91 @@ object PipelineDemo6 extends App { PixelSolverChecker(dut.io.cmd, dut.io.rsp, dut.clockDomain) } } + + + +object CpuDemo extends App{ + class Cpu extends Component { + val fetch, decode, execute = CtrlLink() + val f2d = StageLink(fetch.down, decode.up) + val d2e = StageLink(decode.down, execute.up) + + val PC = Payload(UInt(8 bits)) + val INSTRUCTION = Payload(Bits(16 bits)) + + val led = out(Reg(Bits(8 bits))) init(0) + + val fetcher = new fetch.Area{ + val pcReg = Reg(PC) init (0) + up(PC) := pcReg + up.valid := True + when(up.isFiring) { + pcReg := PC + 1 + } + + val mem = Mem.fill(256)(INSTRUCTION).simPublic + INSTRUCTION := mem.readAsync(PC) + } + + val decoder = new decode.Area{ + val opcode = INSTRUCTION(7 downto 0) + val IS_ADD = insert(opcode === 0x1) + val IS_JUMP = insert(opcode === 0x2) + val IS_LED = insert(opcode === 0x3) + val IS_DELAY = insert(opcode === 0x4) + } + + + val alu = new execute.Area{ + val regfile = Reg(UInt(8 bits)) init(0) + + val flush = False + for (stage <- List(fetch, decode)) { + stage.throwWhen(flush, usingReady = true) + } + + val delayCounter = Reg(UInt(8 bits)) init (0) + + when(isValid) { + when(decoder.IS_ADD) { + regfile := regfile + U(INSTRUCTION(15 downto 8)) + } + when(decoder.IS_JUMP) { + flush := True + fetcher.pcReg := U(INSTRUCTION(15 downto 8)) + } + when(decoder.IS_LED) { + led := B(regfile) + } + when(decoder.IS_DELAY) { + delayCounter := delayCounter + 1 + when(delayCounter === U(INSTRUCTION(15 downto 8))) { + delayCounter := 0 + } otherwise { + execute.haltIt() + } + } + } + } + + Builder(fetch, decode, execute, f2d, d2e) + } + + SimConfig.withFstWave.compile(new Cpu).doSim(seed = 2){ dut => + def nop() = BigInt(0) + def add(value: Int) = BigInt(1 | (value << 8)) + def jump(target: Int) = BigInt(2 | (target << 8)) + def led() = BigInt(3) + def delay(cycles: Int) = BigInt(4 | (cycles << 8)) + val mem = dut.fetcher.mem + mem.setBigInt(0, nop()) + mem.setBigInt(1, nop()) + mem.setBigInt(2, add(0x1)) + mem.setBigInt(3, led()) + mem.setBigInt(4, delay(16)) + mem.setBigInt(5, jump(0x2)) + + dut.clockDomain.forkStimulus(10) + dut.clockDomain.waitSampling(100) + } +} \ No newline at end of file From 33c0a5bc009d3ee92b0a787ff747a1a3bb299968 Mon Sep 17 00:00:00 2001 From: Pengcheng Xu Date: Tue, 23 Jan 2024 11:09:12 +0100 Subject: [PATCH 35/45] add setOutputAsReg --- core/src/main/scala/spinal/core/Data.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/scala/spinal/core/Data.scala b/core/src/main/scala/spinal/core/Data.scala index ca269bacab..ef55386961 100644 --- a/core/src/main/scala/spinal/core/Data.scala +++ b/core/src/main/scala/spinal/core/Data.scala @@ -347,6 +347,11 @@ trait Data extends ContextUser with NameableByComponent with Assignable with Spi /** Set baseType to reg */ def setAsReg(): this.type + /** Recursively set baseType to reg only for output */ + def setOutputAsReg(): this.type = { + flatten.filter(_.dir == out).foreach(_.setAsReg()) + this + } /** Set baseType to Combinatorial */ def setAsComb(): this.type From f0367264443a0c87df4a5b231dc047b7e69b4f6f Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 25 Jan 2024 12:18:23 +0100 Subject: [PATCH 36/45] tilelink sim memory agent can now take mem as arg --- .../main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala b/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala index d9449d1268..a2c29e6a0a 100644 --- a/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala +++ b/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala @@ -14,11 +14,12 @@ class MemoryAgent(bus: Bus, seed : Long = simRandom.nextInt(), blockSize : Int = 64, var randomProberFactor : Float = 0.0f, - var randomProberDelayMax : Int = 1000 + var randomProberDelayMax : Int = 1000, + memArg : Option[SparseMemory] = None )(implicit idCallback : IdCallback) extends MonitorSubscriber{ implicit val _ = sm - val mem = SparseMemory(seed) + val mem = memArg.getOrElse(SparseMemory(seed)) val monitor = new Monitor(bus, cd).add(this) val driver = new SlaveDriver(bus, cd) From 940c8aa134e1b2bf1fd6e73db45869adccd6ee0e Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 25 Jan 2024 19:10:55 +0100 Subject: [PATCH 37/45] tilelink memory agent delay function --- .../scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala b/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala index a2c29e6a0a..ec58140264 100644 --- a/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala +++ b/lib/src/main/scala/spinal/lib/bus/tilelink/sim/MemoryAgent.scala @@ -62,6 +62,11 @@ class MemoryAgent(bus: Bus, capMap(m2s)(address) = cap } + def delayOnA(a : TransactionA): Unit = { + val r = simRandom.nextFloat() + cd.waitSampling((r * r * 20).toInt) //Will enable out of order handeling + } + override def onA(a: TransactionA) = { if(bus.p.withBCE && simRandom.nextFloat() < randomProberFactor) fork { cd.waitSampling(simRandom.nextInt(randomProberDelayMax)) @@ -78,8 +83,7 @@ class MemoryAgent(bus: Bus, } fork{ - val r = simRandom.nextFloat() - cd.waitSampling((r*r*20).toInt) //Will enable out of order handeling + delayOnA(a) val blockAddress = a.address.toLong & ~(blockSize-1) reserve(blockAddress) a.opcode match { From a2fdad88eefbeccfae6587f17b415ed5572b1f48 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 29 Jan 2024 11:21:12 +0100 Subject: [PATCH 38/45] #1254 Move hardmap to SpinalHDL --- core/src/main/scala/spinal/core/HardMap.scala | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 core/src/main/scala/spinal/core/HardMap.scala diff --git a/core/src/main/scala/spinal/core/HardMap.scala b/core/src/main/scala/spinal/core/HardMap.scala new file mode 100644 index 0000000000..8b9231022a --- /dev/null +++ b/core/src/main/scala/spinal/core/HardMap.scala @@ -0,0 +1,71 @@ +package spinal.core + +import spinal.idslplugin.Location + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + +object HardMap{ + def apply(content : Seq[NamedType[_ <: Data]]) : HardMap = { + val ret = new HardMap() + content.foreach(e => ret.add(e)) + ret + } +} + +class HardMap extends MultiData { + val storage = mutable.LinkedHashMap[NamedType[Data], Data]() + var elementsCache : ArrayBuffer[(String, Data)] = null + + def keyToName(key : Any) = key match { + case n: Nameable if n.isNamed => n.getName() + } + + def update[T <: Data](key : NamedType[T], value : T): Unit = { + assert(elementsCache == null) + assert(!storage.contains(key.asInstanceOf[NamedType[Data]])) + storage(key.asInstanceOf[NamedType[Data]]) = value + if(OwnableRef.proposal(value, this)) value.setPartialName(keyToName(key), Nameable.DATAMODEL_WEAK) + } + + def add[T <: Data](key: NamedType[T]) : Unit = { + this(key) = key() + } + + def apply[T <: Data](key: NamedType[T]): T = { + storage(key.asInstanceOf[NamedType[Data]]).asInstanceOf[T] + } + + override def elements: ArrayBuffer[(String, Data)] = { + if(elementsCache == null) { + elementsCache = ArrayBuffer[(String, Data)]() + for ((k, d) <- storage) { + val name = keyToName(k) + elementsCache += name -> d + } + } + elementsCache + } + + def hardMapAssign(that: HardMap)(f: (Data, Data) => Unit): Unit = { + for ((name, element) <- elements) { + val other = that.find(name) + if (other == null) { + LocatedPendingError(s"Bundle assignment is not complete. $this need '$name' but $that doesn't provide it.") + } + else { + f(element, other) + } + } + } + + protected override def assignFromImpl(that: AnyRef, target: AnyRef, kind: AnyRef)(implicit loc: Location): Unit = { + that match { + case that: HardMap => + if (!this.getClass.isAssignableFrom(that.getClass)) SpinalError("HardMap must have the same final class to" + + " be assigned. Either use assignByName or assignSomeByName at \n" + ScalaLocated.long) + hardMapAssign(that)((to, from) => to.compositAssignFrom(from, to, kind)) + case _ => throw new Exception("Undefined assignment") + } + } +} From cebff7089ee47013a0b076db1987fffb8054d683 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 29 Jan 2024 11:21:12 +0100 Subject: [PATCH 39/45] #1254 Move hardmap to SpinalHDL --- core/src/main/scala/spinal/core/HardMap.scala | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 core/src/main/scala/spinal/core/HardMap.scala diff --git a/core/src/main/scala/spinal/core/HardMap.scala b/core/src/main/scala/spinal/core/HardMap.scala new file mode 100644 index 0000000000..8b9231022a --- /dev/null +++ b/core/src/main/scala/spinal/core/HardMap.scala @@ -0,0 +1,71 @@ +package spinal.core + +import spinal.idslplugin.Location + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + +object HardMap{ + def apply(content : Seq[NamedType[_ <: Data]]) : HardMap = { + val ret = new HardMap() + content.foreach(e => ret.add(e)) + ret + } +} + +class HardMap extends MultiData { + val storage = mutable.LinkedHashMap[NamedType[Data], Data]() + var elementsCache : ArrayBuffer[(String, Data)] = null + + def keyToName(key : Any) = key match { + case n: Nameable if n.isNamed => n.getName() + } + + def update[T <: Data](key : NamedType[T], value : T): Unit = { + assert(elementsCache == null) + assert(!storage.contains(key.asInstanceOf[NamedType[Data]])) + storage(key.asInstanceOf[NamedType[Data]]) = value + if(OwnableRef.proposal(value, this)) value.setPartialName(keyToName(key), Nameable.DATAMODEL_WEAK) + } + + def add[T <: Data](key: NamedType[T]) : Unit = { + this(key) = key() + } + + def apply[T <: Data](key: NamedType[T]): T = { + storage(key.asInstanceOf[NamedType[Data]]).asInstanceOf[T] + } + + override def elements: ArrayBuffer[(String, Data)] = { + if(elementsCache == null) { + elementsCache = ArrayBuffer[(String, Data)]() + for ((k, d) <- storage) { + val name = keyToName(k) + elementsCache += name -> d + } + } + elementsCache + } + + def hardMapAssign(that: HardMap)(f: (Data, Data) => Unit): Unit = { + for ((name, element) <- elements) { + val other = that.find(name) + if (other == null) { + LocatedPendingError(s"Bundle assignment is not complete. $this need '$name' but $that doesn't provide it.") + } + else { + f(element, other) + } + } + } + + protected override def assignFromImpl(that: AnyRef, target: AnyRef, kind: AnyRef)(implicit loc: Location): Unit = { + that match { + case that: HardMap => + if (!this.getClass.isAssignableFrom(that.getClass)) SpinalError("HardMap must have the same final class to" + + " be assigned. Either use assignByName or assignSomeByName at \n" + ScalaLocated.long) + hardMapAssign(that)((to, from) => to.compositAssignFrom(from, to, kind)) + case _ => throw new Exception("Undefined assignment") + } + } +} From 1e5f5b8ae9fef54c7ccf121f756d89c7f270af87 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 31 Jan 2024 10:26:42 +0100 Subject: [PATCH 40/45] fix #1298 Fix CtrlLink ignore ready priority --- .../main/scala/spinal/lib/misc/pipeline/CtrlLink.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala b/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala index 0fb15accf4..61f3629fbd 100644 --- a/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala +++ b/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala @@ -147,9 +147,6 @@ class CtrlLink(override val up : Node, override val down : Node) extends Link wi if(down.ctrl.valid.nonEmpty) down.valid := up.valid if(up.ctrl.ready.nonEmpty) { up.ready := down.isReady - if(requests.ignoresReady.nonEmpty) when(requests.ignoresReady.orR){ - up.ready := True - } } if(requests.halts.nonEmpty) when(requests.halts.orR){ down.valid := False @@ -161,6 +158,11 @@ class CtrlLink(override val up : Node, override val down : Node) extends Link wi if(requests.terminates.nonEmpty) when(requests.terminates.orR){ down.valid := False } + if (up.ctrl.ready.nonEmpty) { + if (requests.ignoresReady.nonEmpty) when(requests.ignoresReady.orR) { + up.ready := True + } + } val matches = down.fromUp.payload.intersect(up.fromDown.payload) for (m <- matches) { bypasses.get(m) match { From c38792a9b20e5eb625fd8e71ff58d934518fb032 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 31 Jan 2024 10:26:42 +0100 Subject: [PATCH 41/45] fix #1298 Fix CtrlLink ignore ready priority --- .../main/scala/spinal/lib/misc/pipeline/CtrlLink.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala b/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala index 0fb15accf4..61f3629fbd 100644 --- a/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala +++ b/lib/src/main/scala/spinal/lib/misc/pipeline/CtrlLink.scala @@ -147,9 +147,6 @@ class CtrlLink(override val up : Node, override val down : Node) extends Link wi if(down.ctrl.valid.nonEmpty) down.valid := up.valid if(up.ctrl.ready.nonEmpty) { up.ready := down.isReady - if(requests.ignoresReady.nonEmpty) when(requests.ignoresReady.orR){ - up.ready := True - } } if(requests.halts.nonEmpty) when(requests.halts.orR){ down.valid := False @@ -161,6 +158,11 @@ class CtrlLink(override val up : Node, override val down : Node) extends Link wi if(requests.terminates.nonEmpty) when(requests.terminates.orR){ down.valid := False } + if (up.ctrl.ready.nonEmpty) { + if (requests.ignoresReady.nonEmpty) when(requests.ignoresReady.orR) { + up.ready := True + } + } val matches = down.fromUp.payload.intersect(up.fromDown.payload) for (m <- matches) { bypasses.get(m) match { From 7e864de12239785d9105578c7e2536d2427c8fde Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 31 Jan 2024 17:12:29 +0100 Subject: [PATCH 42/45] Add tilelinkuart --- .../lib/com/uart/TilelinkUartCtrl.scala | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 lib/src/main/scala/spinal/lib/com/uart/TilelinkUartCtrl.scala diff --git a/lib/src/main/scala/spinal/lib/com/uart/TilelinkUartCtrl.scala b/lib/src/main/scala/spinal/lib/com/uart/TilelinkUartCtrl.scala new file mode 100644 index 0000000000..50a11f9cdf --- /dev/null +++ b/lib/src/main/scala/spinal/lib/com/uart/TilelinkUartCtrl.scala @@ -0,0 +1,59 @@ +package spinal.lib.com.uart + +import spinal.core._ +import spinal.core.fiber._ +import spinal.lib._ +import spinal.lib.bus.tilelink._ +import spinal.lib.misc.InterruptNode + +object TilelinkUartCtrl{ + def getTilelinkSupport(proposed: bus.tilelink.M2sSupport) = bus.tilelink.SlaveFactory.getSupported( + addressWidth = addressWidth, + dataWidth = 32, + allowBurst = true, + proposed + ) + def addressWidth = 6 +} + +case class TilelinkUartCtrl(config : UartCtrlMemoryMappedConfig, tilelinkParameter: BusParameter) extends Component{ + val io = new Bundle{ + val bus = slave(Bus(tilelinkParameter)) + val uart = master(Uart(ctsGen = config.uartCtrlConfig.ctsGen, rtsGen = config.uartCtrlConfig.rtsGen)) + val interrupt = out Bool() + } + + val uartCtrl = new UartCtrl(config.uartCtrlConfig) + io.uart <> uartCtrl.io.uart + + val busCtrl = new SlaveFactory(io.bus, false) + val bridge = uartCtrl.driveFrom32(busCtrl,config) + io.interrupt := bridge.interruptCtrl.interrupt +} + + +case class TilelinkUartFiber() extends Area{ + val node = bus.tilelink.fabric.Node.slave() + val interrupt = InterruptNode.master() + + var config = UartCtrlMemoryMappedConfig( + uartCtrlConfig = UartCtrlGenerics(), + initConfig = UartCtrlInitConfig( + baudrate = 115200, + dataLength = 7, // 8 bits + parity = UartParityType.NONE, + stop = UartStopType.ONE + ) + ) + + val logic = Fiber build new Area{ + node.m2s.supported.load(TilelinkUartCtrl.getTilelinkSupport(node.m2s.proposed)) + node.s2m.none() + + val core = TilelinkUartCtrl(config, node.bus.p) + core.io.bus <> node.bus + interrupt.flag := core.io.interrupt + + val uart = core.io.uart.toIo() + } +} \ No newline at end of file From 5d60063afa9e28cff8285d9e8a32ba69ba7cb434 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 31 Jan 2024 18:11:45 +0100 Subject: [PATCH 43/45] Allow FiberPlugin host to be set in setup phase --- lib/src/main/scala/spinal/lib/misc/plugin/Fiber.scala | 4 ++++ .../main/scala/spinal/lib/misc/plugin/ServiceDemo.scala | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/src/main/scala/spinal/lib/misc/plugin/Fiber.scala b/lib/src/main/scala/spinal/lib/misc/plugin/Fiber.scala index 899188d080..7f899c206d 100644 --- a/lib/src/main/scala/spinal/lib/misc/plugin/Fiber.scala +++ b/lib/src/main/scala/spinal/lib/misc/plugin/Fiber.scala @@ -19,6 +19,7 @@ class FiberPlugin extends Area with Hostable { var pluginEnabled = true var host : PluginHost = null + val hostLock = Lock().retain() val subservices = ArrayBuffer[Any]() def addService[T](that : T) : T = { @@ -55,11 +56,13 @@ class FiberPlugin extends Area with Hostable { h.addService(this) subservices.foreach(h.addService) host = h + hostLock.release() } def during = new { def setup[T: ClassTag](body: => T): Handle[T] = spinal.core.fiber.Fiber setup { pluginEnabled generate { + hostLock.await() host.rework(body) } } @@ -68,6 +71,7 @@ class FiberPlugin extends Area with Hostable { buildCount += 1 spinal.core.fiber.Fiber build { pluginEnabled generate { + hostLock.await() val ret = host.rework(body) buildCount -= 1 if (buildCount == 0) { diff --git a/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala b/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala index 96f04090be..32c0b904f3 100644 --- a/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala +++ b/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala @@ -57,12 +57,12 @@ object Example2 extends App{ class DriverPlugin extends FiberPlugin { lazy val sp = host[StatePlugin].logic.get - val lock = Lock() + val hostLock = Lock() // incrementBy will be set by others plugin at elaboration time var incrementBy = 0 val logic = during build new Area { - lock.await() + hostLock.await() // Generate the incrementer hardware sp.signal := sp.signal + incrementBy } @@ -74,14 +74,14 @@ object Example2 extends App{ // during setup { body } will run the body of code in the Fiber setup phase (it is before the Fiber build phase) during setup { // Prevent the DriverPlugin from executing its build's body (until release() is called) - dp.lock.retain() + dp.hostLock.retain() } val logic = during build new Area { // Let's mutate DriverPlugin.incrementBy dp.incrementBy += 1 // Allows the DriverPlugin to execute its build's body - dp.lock.release() + dp.hostLock.release() } } From 12453e6903327dd85f83245d628e957fa5964beb Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 1 Feb 2024 09:24:38 +0100 Subject: [PATCH 44/45] Fix HexTools int being negative --- lib/src/main/scala/spinal/lib/misc/HexTools.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/scala/spinal/lib/misc/HexTools.scala b/lib/src/main/scala/spinal/lib/misc/HexTools.scala index 9152937095..e402d1f27b 100644 --- a/lib/src/main/scala/spinal/lib/misc/HexTools.scala +++ b/lib/src/main/scala/spinal/lib/misc/HexTools.scala @@ -49,7 +49,7 @@ object HexTools{ val wordSize = ram.wordType.getBitsWidth/8 val initContent = Array.fill[BigInt](ram.wordCount)(0) HexTools.readHexFile(onChipRamHexFile, 0,(address,data) => { - val addressWithoutOffset = (address - hexOffset).toLong + val addressWithoutOffset = ((address.toLong & 0xFFFFFFFFl) - hexOffset).toLong val addressWord = addressWithoutOffset/wordSize if(addressWord < 0 || addressWord >= initContent.size){ assert(allowOverflow) From a1c826b046333c5f55016fb3c0098bac90c323da Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 1 Feb 2024 09:34:46 +0100 Subject: [PATCH 45/45] Fix compilation --- .../src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala b/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala index 32c0b904f3..5e0465e118 100644 --- a/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala +++ b/tester/src/main/scala/spinal/lib/misc/plugin/ServiceDemo.scala @@ -57,12 +57,12 @@ object Example2 extends App{ class DriverPlugin extends FiberPlugin { lazy val sp = host[StatePlugin].logic.get - val hostLock = Lock() + val hostLockX = Lock() // incrementBy will be set by others plugin at elaboration time var incrementBy = 0 val logic = during build new Area { - hostLock.await() + hostLockX.await() // Generate the incrementer hardware sp.signal := sp.signal + incrementBy }