Merge "Add debug_req and matcha parameters"
diff --git a/doc/ChAI.md b/doc/ChAI.md
new file mode 100644
index 0000000..71115a3
--- /dev/null
+++ b/doc/ChAI.md
@@ -0,0 +1,117 @@
+# What is ChAI?
+
+ChAI is a reference implementation of an embedded system, build around the Kelvin CPU.
+It is intended as a starting point for either building out a more complete system, or integrating
+into an existing system
+
+## Building ChAI RTL
+
+The SystemVerilog for ChAI can be built with the following command:
+
+```
+bazel build //hdl/chisel/src/chai:chai_cc_library_emit_verilog
+```
+
+The generated file will be in `bazel-bin/hdl/chisel/src/chai/ChAI.sv`
+
+## Running a simulating ChAI
+
+You can run program on the ChAI verilator sim with:
+
+```
+bazel run //tests/verilator_sim:chai_sim -- \
+ --cycles 2000 \
+ /path/to/program.bin
+```
+
+## Components
+- Kelvin CPU
+- TileLink-UL crossbar
+- UART (from lowRISC)
+- SRAM (from Chisel, and using an adapter from lowRISC)
+
+## Limitations
+- Can only address memory at 32B / 256b offsets (existing peripherals may require modifications due to this)
+- No interrupt support
+
+## Adding a new peripheral
+In ChAI.scala, add a new entry to the memoryRegions table for your peripheral:
+```scala
+ val memoryRegions = Seq(
+ new kelvin.MemoryRegion(0, 4 * 1024 * 1024, true, 256), // SRAM
+ new kelvin.MemoryRegion(4 * 1024 * 1024, 4 * 1024 * 1024, false, 256) // UART
+ new kelvin.MemoryRegion(8 * 1024 * 1024, 1 * 1024 * 1024, false, 256) // My New Peripheral
+ )
+```
+A MemoryRegion consists of a starting address (in bytes), a size (in bytes), whether or not the region is cacheable (likely only to be true for a memory peripheral), and an access size (ignored today, all accesses are 256-bit)
+
+In ChAI.scala, connect your device's TileLink I/O to the crossbar:
+```scala
+val crossbar =
+ Module(new kelvin.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
+crossbar.io.devices_a(2) <> my_new_peripheral.io.tl_i
+crossbar.io.devices_d(2) <> my_new_peripheral.io.tl_o
+```
+
+Here's a skeleton of a peripheral that has a single register that can be
+read or written.
+```scala
+package chai
+
+import chisel3._
+import chisel3.util._
+
+object DummyPeripheral {
+ def apply(tlul_p: kelvin.TLULParameters): DummyPeripheral = {
+ return Module(new DummyPeripheral(tlul_p))
+ }
+}
+
+class DummyPeripheral(tlul_p: kelvin.TLULParameters) extends Module {
+ val io = IO(new Bundle {
+ val tl_i = Input(new kelvin.TileLinkULIO_H2D(tlul_p))
+ val tl_o = Output(new kelvin.TileLinkULIO_D2H(tlul_p))
+ })
+
+ io.tl_o := 0.U.asTypeOf(new kelvin.TileLinkULIO_D2H(tlul_p))
+ val saved_value = RegInit(0.U(32.W))
+
+ val tl_i_reg = Reg(new kelvin.TileLinkULIO_H2D(tlul_p))
+ tl_i_reg := io.tl_i
+
+ io.tl_o.a_ready := true.B
+ when (tl_i_reg.a_valid) {
+ io.tl_o.d_valid := true.B
+ switch (tl_i_reg.a_opcode) {
+ is (4.U /* kelvin.TLULOpcodesA.Get.asUInt */) {
+ io.tl_o.d_opcode := kelvin.TLULOpcodesD.AccessAckData.asUInt
+ io.tl_o.d_param := 0.U
+ io.tl_o.d_size := tl_i_reg.a_size
+ io.tl_o.d_source := tl_i_reg.a_source
+ io.tl_o.d_sink := 1.U
+ io.tl_o.d_data := saved_value
+ io.tl_o.d_error := false.B
+ }
+ is (0.U /* PutFullData */) {
+ saved_value := tl_i_reg.a_data
+ io.tl_o.d_opcode := kelvin.TLULOpcodesD.AccessAck.asUInt
+ io.tl_o.d_param := 0.U
+ io.tl_o.d_size := tl_i_reg.a_size
+ io.tl_o.d_source := tl_i_reg.a_source
+ io.tl_o.d_sink := 1.U
+ io.tl_o.d_data := 0.U
+ io.tl_o.d_error := false.B
+ }
+ is (1.U /* PutPartialData */) {
+ /* no-op */
+ }
+ }
+ }
+}
+```