blob: 25c71164c667ad4357025ec5b639214316bbaaef [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 matcha
import chisel3._
import bus.KelvinMemIO
import java.nio.file.{Paths, Files, StandardOpenOption}
import java.nio.charset.{StandardCharsets}
import _root_.circt.stage.{ChiselStage}
object Kelvin {
def apply(p: kelvin.Parameters): Kelvin = {
return Module(new Kelvin(p, moduleName = "Kelvin"))
}
}
class Kelvin(p: kelvin.Parameters, moduleName: String) extends RawModule {
override val desiredName = moduleName
// IO ports. (RawModule removes default[clock, reset])
val clk_i = IO(Input(Clock()))
val rst_ni = IO(Input(AsyncReset()))
val mem = IO(new KelvinMemIO(p))
val clk_freeze = IO(Input(Bool()))
val ml_reset = IO(Input(Bool()))
val pc_start = IO(Input(UInt(32.W)))
val volt_sel = IO(Input(Bool()))
val debug_req = IO(Input(Bool()))
val finish = IO(Output(Bool()))
val host_req = IO(Output(Bool()))
val fault = IO(Output(Bool()))
val slog = IO(new kelvin.SLogIO(p))
// ---------------------------------------------------------------------------
// Gated Clock.
val cg = Module(new kelvin.ClockGate)
cg.io.clk_i := clk_i
cg.io.enable := !clk_freeze
cg.io.te := false.B
val clk_g = cg.io.clk_o
// ---------------------------------------------------------------------------
// Reset inverter and synchronizer.
//
// Most registers in the design are loaded by literals and it is safe to use
// rst_i directly. However some registers {fetch.instAddr} load from io ports
// or use "reset.asBool" to initialize state which infers synchronous resets.
// This hybrid design allows for interfaces to reset immediately on reset
// assertion while ensuring all internal state will eventually be reset
// correctly before usage.
val rst_i = (!rst_ni.asBool || ml_reset).asAsyncReset
val rst_core = Wire(Bool())
withClockAndReset(clk_i, rst_i) {
val rst_q1 = RegInit(true.B)
val rst_q2 = RegInit(true.B)
rst_q1 := false.B
rst_q2 := rst_q1
rst_core := rst_q2
}
// ---------------------------------------------------------------------------
// Connect clock and reset.
withClockAndReset(clk_g, rst_core.asAsyncReset) {
assert(p.vectorBits == 256)
val core = kelvin.Core(p)
val l1d = kelvin.L1DCache(p)
val l1i = kelvin.L1ICache(p)
val bus = Axi2Sram(p)
// -------------------------------------------------------------------------
// Control interface.
finish := core.io.halted
host_req := false.B
fault := core.io.fault
core.io.irq := false.B
// -------------------------------------------------------------------------
// Scalar Core logging.
slog := core.io.slog
// -------------------------------------------------------------------------
// Debug Interface.
core.io.debug_req := debug_req
// -------------------------------------------------------------------------
// L1Cache.
l1d.io.dbus <> core.io.dbus
l1d.io.flush <> core.io.dflush
l1d.io.volt_sel := volt_sel
l1i.io.ibus <> core.io.ibus
l1i.io.flush <> core.io.iflush
l1i.io.volt_sel := volt_sel
// core.io.ebus <> 0.U.asTypeOf(core.io.ebus)
core.io.ebus.dbus.ready := false.B
core.io.ebus.dbus.rdata := 0.U.asTypeOf(core.io.ebus.dbus.rdata)
core.io.ebus.fault.valid := false.B
core.io.ebus.fault.bits := 0.U.asTypeOf(core.io.ebus.fault.bits)
// -------------------------------------------------------------------------
// Bus Mux.
bus.io.l1d <> l1d.io.axi
bus.io.l1i.read <> l1i.io.axi.read
// -------------------------------------------------------------------------
// SRAM bridge.
mem.cvalid := bus.io.out.cvalid
bus.io.out.cready := mem.cready
mem.cwrite := bus.io.out.cwrite
mem.caddr := bus.io.out.caddr
mem.cid := bus.io.out.cid
mem.wdata := bus.io.out.wdata
mem.wmask := bus.io.out.wmask
bus.io.out.rvalid := mem.rvalid
bus.io.out.rid := mem.rid
bus.io.out.rdata := mem.rdata
// -------------------------------------------------------------------------
// Command interface.
for (i <- 0 until p.csrInCount) {
core.io.csr.in.value(i) := 0.U
}
core.io.csr.in.value(0) := pc_start
}
}
object EmitKelvin extends App {
val p = new kelvin.MatchaParameters
val core_p = new kelvin.Parameters
var moduleName = "Kelvin"
var chiselArgs = List[String]()
var targetDir: Option[String] = None
for (arg <- args) {
if (arg.startsWith("--enableFetchL0")) {
val argval = arg.split("=")(1).toBoolean
p.enableFetchL0 = argval
core_p.enableFetchL0 = argval
} else if (arg.startsWith("--moduleName")) {
moduleName = arg.split("=")(1)
} else if (arg.startsWith("--enableVector")) {
val argval = arg.split("=")(1).toBoolean
p.enableVector = argval
core_p.enableVector = argval
} else if (arg.startsWith("--fetchDataBits")) {
val argval = arg.split("=")(1).toInt
p.fetchDataBits = argval
core_p.fetchDataBits = argval
} else if (arg.startsWith("--lsuDataBits")) {
val argval = arg.split("=")(1).toInt
p.lsuDataBits = argval
core_p.lsuDataBits = argval
} 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 = new Kelvin(p, moduleName)
val firtoolOpts = Array(
"-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 header_str = kelvin.EmitParametersHeader(core_p)
targetDir match {
case Some(targetDir) => {
Files.write(
Paths.get(targetDir + "/V" + moduleName + "_parameters.h"),
header_str.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE)
Files.write(
Paths.get(targetDir + "/" + core.name + ".sv"),
strippedVerilogSource.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE)
()
}
case None => ()
}
}