blob: 45b5766f334751f15869dd688e288513af11b53d [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kelvin
import chisel3._
import java.io.{File, FileOutputStream}
import java.util.zip._
import java.nio.file.{Paths, Files, StandardOpenOption}
import java.nio.charset.{StandardCharsets}
import kelvin.rvv.{RvvCore}
import _root_.circt.stage.ChiselStage
import scala.collection.mutable.Stack
object Core {
def apply(p: Parameters): Core = {
return Module(new Core(p, "Core"))
}
def apply(p: Parameters, moduleName: String): Core = {
return Module(new Core(p, moduleName))
}
}
class Core(p: Parameters, moduleName: String) extends Module with RequireAsyncReset {
override val desiredName = moduleName
val io = IO(new Bundle {
val csr = new CsrInOutIO(p)
val halted = Output(Bool())
val fault = Output(Bool())
val wfi = Output(Bool())
val irq = Input(Bool())
val debug_req = Input(Bool())
val dm = Option.when(p.useDebugModule)(new CoreDMIO(p))
// Bus between core and instruction memories.
val ibus = new IBusIO(p)
// Bus between core and data memories.
val dbus = new DBusIO(p)
// Bus between core and and external memories or peripherals.
val ebus = new EBusIO(p)
val iflush = new IFlushIO(p)
val dflush = new DFlushIO(p)
val slog = new SLogIO(p)
val debug = new DebugIO(p)
})
val score = SCore(p)
val vcore = Option.when(p.enableVector)(VCore(p))
val rvvCore = Option.when(p.enableRvv)(RvvCore(p))
if (p.enableRvv) {
rvvCore.get.io <> score.io.rvvcore.get
}
// ---------------------------------------------------------------------------
// Scalar Core outputs.
io.csr <> score.io.csr
io.ibus <> score.io.ibus
io.ebus <> score.io.ebus
io.halted := score.io.halted
io.fault := score.io.fault
io.wfi := score.io.wfi
score.io.irq := io.irq
if (p.useDebugModule) {
score.io.dm.get <> io.dm.get
}
io.iflush <> score.io.iflush
io.dflush <> score.io.dflush
io.slog := score.io.slog
io.debug <> score.io.debug
// ---------------------------------------------------------------------------
// Vector core.
if (p.enableVector) {
score.io.vcore.get <> vcore.get.io.score
}
// ---------------------------------------------------------------------------
// Local Data Bus Port
if (p.enableVector) {
val dbusmux = DBusMux(p)
dbusmux.io.vldst := score.io.vldst.get
dbusmux.io.vlast := vcore.get.io.last
dbusmux.io.vcore <> vcore.get.io.dbus
dbusmux.io.score <> score.io.dbus
io.dbus <> dbusmux.io.dbus
} else {
io.dbus <> score.io.dbus
}
}
object EmitCore extends App {
val p = new Parameters
var moduleName = "Core"
var chiselArgs = List[String]()
var targetDir: Option[String] = None
var useAxi = false
for (arg <- args) {
if (arg.startsWith("--enableFetchL0")) {
p.enableFetchL0 = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--moduleName")) {
moduleName = arg.split("=")(1)
} else if (arg.startsWith("--fetchDataBits")) {
p.fetchDataBits = arg.split("=")(1).toInt
} else if (arg.startsWith("--enableVector")) {
p.enableVector = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--enableRvv")) {
p.enableRvv = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--enableFloat")) {
p.enableFloat = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--enableVerification")) {
p.enableVerification = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--enableDebug")) {
p.enableDebug = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--enableDispatchV2")) {
p.enableDispatchV2 = arg.split("=")(1).toBoolean
} else if (arg.startsWith("--lsuDataBits")) {
p.lsuDataBits = arg.split("=")(1).toInt
} else if (arg.startsWith("--tcmHighmem")) {
p.tcmHighmem = true
} else if (arg.startsWith("--useAxi")) {
useAxi = true
} else if (arg.startsWith("--target-dir")) {
targetDir = Some(arg.split("=")(1))
} else {
chiselArgs = chiselArgs :+ arg
}
}
// The core module must be created in the ChiselStage context. Use lazy here
// so it's created in ChiselStage, but referencable afterwards.
lazy val core = if (useAxi) {
if (p.tcmHighmem == false) { // default case
val memoryRegions = Seq(
new MemoryRegion(0x0000, 0x2000, MemoryRegionType.IMEM), // ITCM
new MemoryRegion(0x10000, 0x8000, MemoryRegionType.DMEM), // DTCM
new MemoryRegion(0x30000, 0x2000, MemoryRegionType.Peripheral), // CSR
)
p.m = memoryRegions
}
else if (p.tcmHighmem == true) { // highmem case TODO: consider parameterizing this. Has its risks.
val memoryRegions = Seq(
new MemoryRegion(0x000000, 0x100000, MemoryRegionType.IMEM), // ITCM
new MemoryRegion(0x100000, 0x100000, MemoryRegionType.DMEM), // DTCM
new MemoryRegion(0x200000, 0x2000, MemoryRegionType.Peripheral), // CSR
)
p.m = memoryRegions
}
new CoreAxi(p, moduleName)
} else {
// "Matcha" memory layout
p.m = Seq(
new MemoryRegion(0x0, 0x400000, MemoryRegionType.DMEM),
)
new Core(p, moduleName)
}
val firtoolOpts = Array(
// Disable `automatic logic =`, Suppress location comments
"--lowering-options=disallowLocalVariables,locationInfoStyle=none",
"-enable-layers=Verification",
)
val systemVerilogSource = ChiselStage.emitSystemVerilog(
core, chiselArgs.toArray, firtoolOpts)
// CIRCT adds a little extra data to the sv file at the end. Remove it as we
// don't want it (it prevents the sv from being verilated).
val resourcesSeparator =
"// ----- 8< ----- FILE \"firrtl_black_box_resource_files.f\" ----- 8< -----"
val strippedVerilogSource = systemVerilogSource.split(resourcesSeparator)(0)
val coreName = core.name
val header_str = EmitParametersHeader(p)
targetDir match {
case Some(targetDir) => {
{
lazy val core2 = if (useAxi) {
new CoreAxi(p, moduleName)
} else {
new Core(p, moduleName)
}
ChiselStage.emitSystemVerilogFile(
core2, chiselArgs.toArray ++ Array(
"--split-verilog", "--target-dir", targetDir), firtoolOpts)
val zip = new ZipOutputStream(new FileOutputStream(
targetDir + "/" + coreName + ".zip"))
val dirStack = new Stack[File](1)
dirStack.push(new File(targetDir))
println(s"target: ${targetDir}")
while (!dirStack.isEmpty) {
val dir = dirStack.pop()
val files = dir.listFiles
files.foreach { name =>
if (name.isDirectory()) {
dirStack.push(name)
} else {
val zipName = name.getPath().replace(targetDir + "/", "")
zip.putNextEntry(new ZipEntry(zipName))
zip.write(Files.readAllBytes(Paths.get(name.getPath())))
zip.closeEntry()
}
}
}
zip.close()
}
Files.write(
Paths.get(targetDir + "/V" + core.name + "_parameters.h"),
header_str.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE)
Files.write(
Paths.get(targetDir + "/" + core.name + ".sv"),
strippedVerilogSource.replace("exclude_file", "exclude_module").getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE)
()
}
case None => ()
}
}