| // 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 => () |
| } |
| } |