| // Copyright 2024 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 chai |
| |
| import chisel3._ |
| import chisel3.util._ |
| |
| import bus._ |
| import java.nio.file.{Paths, Files, StandardOpenOption} |
| import java.nio.charset.{StandardCharsets} |
| import _root_.circt.stage.{ChiselStage} |
| |
| case class Parameters() { |
| val sramReadPorts = 1 |
| val sramWritePorts = 1 |
| val sramReadWritePorts = 0 |
| val sramDataBits = 256 |
| val sramBytes = 4 * 1024 * 1024 |
| def sramDataEntries(): Int = { |
| ((sramBytes * 8) / sramDataBits) |
| } |
| def sramAddrBits(): Int = { |
| log2Ceil(sramDataEntries()) |
| } |
| } |
| |
| object ChAI { |
| def apply(p: Parameters): ChAI = { |
| return Module(new ChAI(p)) |
| } |
| } |
| |
| class ChAI(p: Parameters) extends RawModule { |
| val io = IO(new Bundle { |
| val clk_i = Input(Clock()) |
| val rst_ni = Input(AsyncReset()) |
| val sram = new Bundle { |
| val write_address = Input(UInt(p.sramAddrBits().W)) |
| val write_enable = Input(Bool()) |
| val write_data = Input(UInt(p.sramDataBits.W)) |
| } |
| val finish = Output(Bool()) |
| val fault = Output(Bool()) |
| val freeze = Input(Bool()) |
| |
| val uart_rx = Input(Bool()) |
| val uart_tx = Output(Bool()) |
| }) |
| |
| // TODO(atv): Compute that we don't have any overlaps in regions. |
| val memoryRegions = Seq( |
| new kelvin.MemoryRegion(0, 4 * 1024 * 1024, kelvin.MemoryRegionType.DMEM), // SRAM |
| new kelvin.MemoryRegion(4 * 1024 * 1024, 4 * 1024 * 1024, kelvin.MemoryRegionType.Peripheral) // UART |
| ) |
| val kelvin_p = kelvin.Parameters(memoryRegions) |
| val rst_i = (!io.rst_ni.asBool).asAsyncReset |
| |
| val u_kelvin = matcha.Kelvin(kelvin_p) |
| u_kelvin.clk_i := io.clk_i |
| u_kelvin.rst_ni := io.rst_ni |
| u_kelvin.clk_freeze := io.freeze |
| u_kelvin.ml_reset := 0.U |
| u_kelvin.pc_start := 0.U |
| u_kelvin.volt_sel := 0.U |
| u_kelvin.debug_req := 0.U |
| |
| io.finish := u_kelvin.finish |
| io.fault := u_kelvin.fault |
| |
| withClockAndReset(io.clk_i, rst_i) { |
| val tlul_p = new TLULParameters(kelvin_p) |
| val kelvin_to_tlul = KelvinToTlul(tlul_p, kelvin_p) |
| kelvin_to_tlul.io.kelvin <> u_kelvin.mem |
| |
| val tlul_sram = |
| SRAM(p.sramDataEntries(), UInt(p.sramDataBits.W), p.sramReadPorts, p.sramWritePorts, p.sramReadWritePorts) |
| val tlul_adapter_sram = Module(new chai.TlulAdapterSram(tlul_p)) |
| tlul_adapter_sram.io.clk_i := io.clk_i |
| tlul_adapter_sram.io.rst_ni := io.rst_ni |
| tlul_adapter_sram.io.en_ifetch_i := 9.U // MuBi4False |
| tlul_sram.readPorts(0).enable := tlul_adapter_sram.io.req_o |
| tlul_sram.readPorts(0).address := tlul_adapter_sram.io.addr_o |
| tlul_sram.writePorts(0).enable := Mux(io.freeze, io.sram.write_enable, tlul_adapter_sram.io.we_o) |
| tlul_sram.writePorts(0).address := Mux(io.freeze, io.sram.write_address, tlul_adapter_sram.io.addr_o) |
| tlul_sram.writePorts(0).data := Mux(io.freeze, io.sram.write_data, tlul_adapter_sram.io.wdata_o) |
| tlul_adapter_sram.io.gnt_i := 1.U |
| tlul_adapter_sram.io.rdata_i := tlul_sram.readPorts(0).data |
| tlul_adapter_sram.io.rvalid_i := 1.U |
| tlul_adapter_sram.io.rerror_i := 0.U |
| |
| val uart = Module(new chai.Uart(tlul_p)) |
| uart.io.clk_i := io.clk_i |
| uart.io.rst_ni := io.rst_ni |
| uart.io.alert_rx_i := 0.U |
| uart.io.cio_rx_i := io.uart_rx |
| io.uart_tx := uart.io.cio_tx_o |
| |
| val crossbar = |
| Module(new TileLinkUL(tlul_p, kelvin_p.m, /* hosts= */ 1)) |
| crossbar.io.hosts_a(0) <> kelvin_to_tlul.io.tl_o |
| crossbar.io.hosts_d(0) <> kelvin_to_tlul.io.tl_i |
| crossbar.io.devices_a(0) <> tlul_adapter_sram.io.tl_i |
| crossbar.io.devices_d(0) <> tlul_adapter_sram.io.tl_o |
| crossbar.io.devices_a(1) <> uart.io.tl_i |
| crossbar.io.devices_d(1) <> uart.io.tl_o |
| } |
| } |
| |
| object EmitChAI extends App { |
| val p = new Parameters() |
| var chiselArgs = List[String]() |
| var targetDir: Option[String] = None |
| for (arg <- args) { |
| if (arg.startsWith("--target-dir")) { |
| targetDir = Some(arg.split("=")(1)) |
| } else { |
| chiselArgs = chiselArgs :+ arg |
| } |
| } |
| |
| lazy val core = new ChAI(p) |
| 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) |
| |
| targetDir match { |
| case Some(targetDir) => { |
| Files.write( |
| Paths.get(targetDir + "/" + core.name + ".sv"), |
| strippedVerilogSource.getBytes(StandardCharsets.UTF_8), |
| StandardOpenOption.CREATE) |
| () |
| } |
| case None => () |
| } |
| } |