diff --git a/src/main/scala/lltriscv/bus/AXI.scala b/src/main/scala/lltriscv/bus/AXI.scala index af5302c..19d654e 100644 --- a/src/main/scala/lltriscv/bus/AXI.scala +++ b/src/main/scala/lltriscv/bus/AXI.scala @@ -8,28 +8,33 @@ object AXISpec { val dataWidth = 32 } -class AXIManagerIO extends Bundle { +class AXIMasterIO extends Bundle { // Write request channel (AW) val AWVALID = Output(Bool()) val AWREADY = Input(Bool()) val AWADDR = Output(UInt(AXISpec.addressWidth.W)) + val AWPORT = Output(UInt(3.W)) // Write data channel (W) val WVALID = Output(Bool()) val WREADY = Input(Bool()) val WDATA = Output(UInt(AXISpec.dataWidth.W)) + val WSTRB = Output(UInt((AXISpec.dataWidth / 8).W)) // Write response channel (B) val BVALID = Input(Bool()) val BREADY = Output(Bool()) + val BRESP = Input(UInt(2.W)) // Read request channel (AR) val ARVALID = Output(Bool()) val ARREADY = Input(Bool()) val ARADDR = Output(UInt(AXISpec.addressWidth.W)) + val ARPORT = Output(UInt(3.W)) // Read data channel (R) val RVALID = Input(Bool()) val RREADY = Output(Bool()) val RDATA = Input(UInt(AXISpec.dataWidth.W)) + val RRESP = Input(UInt(2.W)) } diff --git a/src/main/scala/lltriscv/bus/AXIMaster.scala b/src/main/scala/lltriscv/bus/AXIMaster.scala new file mode 100644 index 0000000..1718044 --- /dev/null +++ b/src/main/scala/lltriscv/bus/AXIMaster.scala @@ -0,0 +1,141 @@ +package lltriscv.bus + +import chisel3._ +import chisel3.util._ + +import lltriscv.core.execute.MemoryAccessLength + +/** Core M/MMIO aggregation interface + * + * Provide conversion from a set of SMA buses to AXI bus + * + * Misaligned address access not supported + */ +class AXIMaster extends Module { + val io = IO(new Bundle { + val smaReader = Flipped(new SMAReaderIO()) + val smaWriter = Flipped(new SMAWriterIO()) + + val axi = new AXIMasterIO() + }) + + private object AXIStatus extends ChiselEnum { + val idle, address, data, response = Value; + } + + // Reader logic + private val readerStatusReg = RegInit(AXIStatus.idle) + + when(readerStatusReg === AXIStatus.idle) { + when(io.smaReader.valid) { + readerStatusReg := AXIStatus.address + } + } + + when(readerStatusReg === AXIStatus.address) { + // 32-bit address alignment + io.axi.ARADDR := io.smaReader.address(31, 2) ## 0.U(2.W) + io.axi.ARPORT := 0.U + io.axi.ARVALID := true.B + when(io.axi.ARREADY) { + readerStatusReg := AXIStatus.response + } + } + + when(readerStatusReg === AXIStatus.response) { + io.axi.RREADY := true.B + + when(io.axi.RVALID) { + switch(io.smaReader.readType) { + is(MemoryAccessLength.byte) { + io.smaReader.data := MuxLookup(io.smaReader.address(1, 0), 0.U)( + Seq( + "b00".U -> io.axi.RDATA(7, 0), + "b01".U -> io.axi.RDATA(15, 8), + "b10".U -> io.axi.RDATA(23, 16), + "b11".U -> io.axi.RDATA(31, 24) + ) + ) + } + is(MemoryAccessLength.half) { + io.smaReader.data := Mux(io.smaReader.address(1), io.axi.RDATA(31, 16), io.axi.RDATA(15, 0)) + } + is(MemoryAccessLength.word) { + io.smaReader.data := io.axi.RDATA + } + } + + io.smaReader.error := false.B + io.smaReader.ready := true.B + + readerStatusReg := AXIStatus.idle + } + } + + // Writer logic + private val writerStatusReg = RegInit(AXIStatus.idle) + + when(writerStatusReg === AXIStatus.idle) { + when(io.smaWriter.valid) { + writerStatusReg := AXIStatus.address + } + } + + when(writerStatusReg === AXIStatus.address) { + // 32-bit address alignment + io.axi.AWADDR := io.smaWriter.address(31, 2) ## 0.U(2.W) + io.axi.AWPORT := 0.U + io.axi.AWVALID := true.B + when(io.axi.AWREADY) { + writerStatusReg := AXIStatus.data + } + } + + when(writerStatusReg === AXIStatus.data) { + switch(io.smaWriter.writeType) { + is(MemoryAccessLength.byte) { + io.axi.WDATA := MuxLookup(io.smaWriter.address(1, 0), 0.U)( + Seq( + "b00".U -> io.smaWriter.data(7, 0), + "b01".U -> io.smaWriter.data(7, 0) ## 0.U(8.W), + "b10".U -> io.smaWriter.data(7, 0) ## 0.U(16.W), + "b11".U -> io.smaWriter.data(7, 0) ## 0.U(24.W) + ) + ) + + io.axi.WSTRB := MuxLookup(io.smaWriter.address(1, 0), 0.U)( + Seq( + "b00".U -> "b0001".U, + "b01".U -> "b0010".U, + "b10".U -> "b0100".U, + "b11".U -> "b1000".U + ) + ) + } + + is(MemoryAccessLength.half) { + io.axi.WDATA := Mux(io.smaWriter.address(1), io.smaWriter.data(15, 0) ## 0.U(16.W), io.smaWriter.data(15, 0)) + io.axi.WSTRB := Mux(io.smaWriter.address(1), "b1100".U, "b0011".U) + } + + is(MemoryAccessLength.word) { + io.axi.WDATA := io.smaWriter.data + io.axi.WSTRB := "b1111".U + } + } + + io.axi.WVALID := true.B + when(io.axi.WREADY) { + writerStatusReg := AXIStatus.response + } + } + + when(writerStatusReg === AXIStatus.response) { + io.axi.BREADY := true.B + when(io.axi.BVALID) { + io.smaWriter.ready := true.B + io.smaWriter.error := 0.U + writerStatusReg := AXIStatus.idle + } + } +} diff --git a/src/main/scala/lltriscv/cache/Cache.scala b/src/main/scala/lltriscv/cache/Cache.scala index dfa97f2..fd81cbc 100644 --- a/src/main/scala/lltriscv/cache/Cache.scala +++ b/src/main/scala/lltriscv/cache/Cache.scala @@ -34,21 +34,26 @@ class TrivialDCache extends Module { io.flush.empty := true.B } -//! TestCode -class TrivialICache(depth: Int) extends Module { +/** Cache line request to SMA + * + * Conversion of cache line requests to SMA interface with 32-bit bandwidth. + * @param cacheLineDepth + * Require 32-bit aligned cache line depth + */ +class CacheLineRequest2SMA(cacheLineDepth: Int) { val io = IO(new Bundle { - val request = Flipped(new ICacheLineRequestIO(depth)) - val downReader = new SMAReaderIO() - - val flush = Flipped(new FlushCacheIO()) + val request = Flipped(new CacheLineRequestIO(cacheLineDepth)) + val smaReader = new SMAReaderIO() }) + private object Status extends ChiselEnum { val idle, working, finish = Value } + private val statusReg = RegInit(Status.idle) private val incr = WireInit(false.B) - private val (counterReg, nextValue) = CoreUtils.pointer(depth, incr) - private val dataReg = RegInit(Vec(depth, UInt(16.W)).zero) + private val (counterReg, nextValue) = CoreUtils.pointer(cacheLineDepth / 2, incr) + private val dataReg = RegInit(Vec(cacheLineDepth, UInt(16.W)).zero) when(statusReg === Status.idle) { when(io.request.valid) { @@ -56,18 +61,19 @@ class TrivialICache(depth: Int) extends Module { } } - io.downReader.valid := false.B - io.downReader.readType := MemoryAccessLength.half - io.downReader.address := io.request.address(31, log2Ceil(depth) + 1) ## counterReg ## 0.U + io.smaReader.valid := false.B + io.smaReader.readType := MemoryAccessLength.word + io.smaReader.address := io.request.address(31, log2Ceil(cacheLineDepth) + 1) ## counterReg ## 0.U(2.W) when(statusReg === Status.working) { - io.downReader.valid := true.B + io.smaReader.valid := true.B - when(io.downReader.ready) { + when(io.smaReader.ready) { incr := true.B - dataReg(counterReg) := io.downReader.data + dataReg(counterReg ## 1.U) := io.smaReader.data(31, 16) + dataReg(counterReg ## 0.U) := io.smaReader.data(15, 0) - when(counterReg === (depth - 1).U) { + when(counterReg === (cacheLineDepth / 2 - 1).U) { statusReg := Status.finish } } @@ -84,6 +90,117 @@ class TrivialICache(depth: Int) extends Module { statusReg := Status.idle } } +} - io.flush.empty := true.B +/** Set Cache (Write-Through) + * + * @param cacheLineDepth + */ +class SetCache(tagDepth: Int, wayDepth: Int, cacheLineDepth: Int) { + val io = IO(new Bundle { + // In + val inWriter = Flipped(new SMAWriterIO()) + val inReader = Flipped(new CacheLineRequestIO(cacheLineDepth)) + + // Out + val outWriter = new SMAWriterIO() + val outReader = new CacheLineRequestIO(cacheLineDepth) + }) + + private class TagEntry extends Bundle { + val address = UInt(31.W) + val valid = Bool() + } + + private val tagMem = Seq.fill(wayDepth)(SyncReadMem(log2Ceil(tagDepth), new TagEntry())) + private val dataMem = Seq.fill(wayDepth)(SyncReadMem(log2Ceil(tagDepth), Vec(cacheLineDepth, UInt(16.W)))) + + private val victimIncr = WireInit(false.B) + private val (victimReg, _) = CoreUtils.pointer(wayDepth, victimIncr) + + private object Status extends ChiselEnum { + val idle, lookup, read, write = Value; + } + private val statusReg = RegInit(Status.idle) + private val responseReadReg = RegInit(false.B) + + private def getTag(address: UInt) = address(31, 32 - log2Ceil(tagDepth)) + private def getCacheAddress(address: UInt) = address(31, log2Ceil(cacheLineDepth) + 1) + + when(statusReg === Status.idle) { + val address = Mux(responseReadReg, io.inReader.address, io.inWriter.address) + tagMem.zip(dataMem).foreach { case (tagWay, dataWay) => + tagWay.read(getTag(address)) + dataWay.read(getTag(address)) + } + + when(io.inReader.valid) { + responseReadReg := true.B + statusReg := Status.lookup + }.elsewhen(io.inWriter.valid) { + responseReadReg := false.B + statusReg := Status.lookup + } + } + + when(statusReg === Status.lookup) { + val address = Mux(responseReadReg, io.inReader.address, io.inWriter.address) + val hit = WireInit(false.B) + + tagMem.zip(dataMem).foreach { case (tagWay, dataWay) => + val tagCell = tagWay.read(getTag(address)) + val dataCell = dataWay.read(getTag(address)) + when(tagCell.valid && tagCell.address === getCacheAddress(address)) { // Hit + hit := true.B + when(responseReadReg) { // Hit read + io.inReader.error := false.B + io.inReader.data := dataCell + io.inReader.ready := true.B + + statusReg := Status.idle + }.otherwise { // Hit write + val newData = Wire(Vec(cacheLineDepth, UInt(16.W))) + // TODO: Write + dataWay.write(getTag(address), newData) + } + } + } + + when(responseReadReg && !hit) { + statusReg := Status.read + }.elsewhen(!responseReadReg) { + statusReg := Status.write + } + } + + when(statusReg === Status.read) { + val address = io.inReader.address + io.outReader.address := getCacheAddress(address) + io.outReader.valid := true.B + + when(io.outReader.ready) { + tagMem.zip(dataMem).zipWithIndex.foreach { case ((tagWay, dataWay), way) => + when(way.U === victimReg) { + val entry = Wire(new TagEntry()) + entry.address := getCacheAddress(address) + entry.valid := true.B + tagWay.write(getTag(address), entry) + dataWay.write(getTag(address), io.outReader.data) + } + } + victimIncr := true.B + + io.inReader.error := false.B + io.inReader.data := io.outReader.data + io.inReader.ready := true.B + statusReg := Status.idle + } + } + + when(statusReg === Status.write) { + io.inWriter <> io.outWriter + when(io.outWriter.ready) { + statusReg := Status.idle + } + } } diff --git a/src/main/scala/lltriscv/cache/CacheEntry.scala b/src/main/scala/lltriscv/cache/CacheEntry.scala index 6fda25d..018c41d 100644 --- a/src/main/scala/lltriscv/cache/CacheEntry.scala +++ b/src/main/scala/lltriscv/cache/CacheEntry.scala @@ -27,7 +27,7 @@ class FlushCacheIO extends Bundle { * @param cacheLineDepth * Size(bits) = 16 * Cache line depth */ -class ICacheLineRequestIO(cacheLineDepth: Int) extends Bundle { +class CacheLineRequestIO(cacheLineDepth: Int) extends Bundle { val address = Output(DataType.address) // Aligned cache line address val data = Input(Vec(cacheLineDepth, UInt(16.W))) // Group data val error = Input(Bool()) // Memory error diff --git a/src/main/scala/lltriscv/core/fetch/Fetch.scala b/src/main/scala/lltriscv/core/fetch/Fetch.scala index 037cdbe..50e86cc 100644 --- a/src/main/scala/lltriscv/core/fetch/Fetch.scala +++ b/src/main/scala/lltriscv/core/fetch/Fetch.scala @@ -8,7 +8,7 @@ import lltriscv.core.decode.DecodeStageEntry import lltriscv.core.record.TLBRequestIO import lltriscv.core.execute.MemoryErrorCode -import lltriscv.cache.ICacheLineRequestIO +import lltriscv.cache.CacheLineRequestIO import lltriscv.utils.CoreUtils._ import lltriscv.utils.ChiselUtils._ @@ -38,7 +38,7 @@ import lltriscv.utils.Sv32 class Fetch(cacheLineDepth: Int, queueDepth: Int, predictorDepth: Int, pcInit: Int) extends Module { val io = IO(new Bundle { val itlb = new TLBRequestIO() - val icache = new ICacheLineRequestIO(cacheLineDepth) + val icache = new CacheLineRequestIO(cacheLineDepth) val out = DecoupledIO(Vec2(new DecodeStageEntry())) // Address space ID val asid = Input(DataType.asid) @@ -99,7 +99,7 @@ class InstructionFetcher(cacheLineDepth: Int) extends Module { // Instruction TLB request interface val itlb = new TLBRequestIO() // Instruction cache request interface - val icache = new ICacheLineRequestIO(cacheLineDepth) + val icache = new CacheLineRequestIO(cacheLineDepth) val recover = Input(Bool()) }) @@ -205,7 +205,7 @@ class InstructionFetcher(cacheLineDepth: Int) extends Module { private val iCacheVictim = RegInit(0.U) private val iCacheTaskFlag = RegInit(false.B) - io.icache <> new ICacheLineRequestIO(cacheLineDepth).zero + io.icache <> new CacheLineRequestIO(cacheLineDepth).zero switch(iCacheStatusReg) { is(Status.idle) { diff --git a/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala b/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala index f6eb152..03775c1 100644 --- a/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala +++ b/src/test/scala/lltriscv/test/riscvtests/RV32PTest.scala @@ -45,7 +45,7 @@ class RV32PTest extends AnyFlatSpec with ChiselScalatestTester { private val uapTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32ua-p-.*\.bin")) private val umpTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32um-p-.*\.bin")) - private val uivTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32ui-v-add\.bin")) + private val uivTests = new File("riscv-tests/isa").listFiles().filter(_.getName().matches(raw"rv32ui-v-.*\.bin")) private val needToTest = uivTests