blob: 23745ae916949bc370a47ec74392044d414c6ae6 [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 chisel3.util._
import bus.KelvinMemIO
import common._
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
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
// -------------------------------------------------------------------------
// 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
// -------------------------------------------------------------------------
// Bus Mux.
if (p.enableVector) {
bus.io.in0.get <> core.io.axi0.get
}
bus.io.in1 <> core.io.axi1
bus.io.in2 <> l1d.io.axi
bus.io.in3.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 12) {
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
var nextIsTargetDir = false
for (arg <- args) {
if (nextIsTargetDir) {
nextIsTargetDir = false
chiselArgs = chiselArgs :+ arg
targetDir = Some(arg)
}
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")) {
nextIsTargetDir = true
chiselArgs = chiselArgs :+ arg
} else {
chiselArgs = chiselArgs :+ arg
}
}
ChiselStage.emitSystemVerilogFile(
new Kelvin(p, moduleName), chiselArgs.toArray)
val header_str = kelvin.EmitParametersHeader(core_p)
targetDir match {
case Some(targetDir) => {
var ret = Files.write(Paths.get(targetDir + "/V" + moduleName + "_parameters.h"), header_str.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE)
()
}
case None => ()
}
}