blob: 5b4a1da194e0fd215442803802f577d018f80213 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 vta.shell
import chisel3._
import chisel3.util._
import vta.util.config._
import vta.util.genericbundle._
import vta.interface.axi._
/** VME parameters.
*
* These parameters are used on VME interfaces and modules.
*/
case class VMEParams
(val nReadClients: Int = 5,
val nWriteClients: Int = 1,
val clientBits : Int = 3,
val RequestQueueDepth : Int = 16,
val vmeParams : Int = 18,
val clientCmdQueueDepth : Int = 1,
val clientTagBitWidth : Int = 21,
val clientDataQueueDepth : Int = 16) {
val RequestQueueMaskBits : Int = RequestQueueDepth.toInt
require(nReadClients > 0,
s"\n\n[VTA] [VMEParams] nReadClients must be larger than 0\n\n")
require(
nWriteClients == 1,
s"\n\n[VTA] [VMEParams] nWriteClients must be 1, only one-write-client support atm\n\n")
}
/** VMEBase. Parametrize base class. */
abstract class VMEBase(implicit p: Parameters) extends GenericParameterizedBundle(p)
/** VMECmd.
*
* This interface is used for creating write and read requests to memory.
*/
class clientTag(implicit p:Parameters) extends Bundle{
val clientBits = p(ShellKey).vmeParams.clientBits
val RequestQueueDepth = p(ShellKey).vmeParams.RequestQueueDepth
val RequestQueueMaskBits = p(ShellKey).vmeParams.RequestQueueMaskBits
val client_id = UInt(clientBits.W)
val client_tag = UInt(p(ShellKey).vmeParams.clientTagBitWidth.W)
val client_mask = UInt(RequestQueueMaskBits.W)
}
class VMECmd(implicit p: Parameters) extends VMEBase {
val addrBits = p(ShellKey).memParams.addrBits
val lenBits = p(ShellKey).memParams.lenBits
val tagBits = p(ShellKey).vmeParams.clientTagBitWidth
val addr = UInt(addrBits.W)
val len = UInt(lenBits.W)
val tag = UInt(tagBits.W)
}
class VMECmdData(implicit p: Parameters) extends VMEBase {
val data = UInt(p(ShellKey).memParams.dataBits.W)
val last = Bool()
}
class VMEData(implicit p: Parameters) extends VMEBase {
val dataBits = p(ShellKey).memParams.dataBits
val data = UInt(dataBits.W)
val tag = UInt(p(ShellKey).vmeParams.clientTagBitWidth.W)
val last = Bool()
}
/** VMEReadMaster.
*
* This interface is used by modules inside the core to generate read requests
* and receive responses from VME.
*/
class VMEReadMaster(implicit p: Parameters) extends Bundle {
val dataBits = p(ShellKey).memParams.dataBits
val cmd = Decoupled(new VMECmd)
val data = Flipped(Decoupled(new VMEData))
}
/** VMEReadClient.
*
* This interface is used by the VME to receive read requests and generate
* responses to modules inside the core.
*/
class VMEReadClient(implicit p: Parameters) extends Bundle {
val dataBits = p(ShellKey).memParams.dataBits
val cmd = Flipped(Decoupled(new VMECmd))
val data = Decoupled(new VMEData)
}
/** VMEWriteData.
*
* This interface is used by the VME to handle write requests from modules inside
* the core.
*/
class VMEWriteData(implicit p: Parameters) extends Bundle {
val dataBits = p(ShellKey).memParams.dataBits
val strbBits = dataBits/8
val data = UInt(dataBits.W)
val strb = UInt(strbBits.W)
}
/** VMEWriteMaster.
*
* This interface is used by modules inside the core to generate write requests
* to the VME.
*/
class VMEWriteMaster(implicit p: Parameters) extends Bundle {
val dataBits = p(ShellKey).memParams.dataBits
val cmd = Decoupled(new VMECmd)
val data = Decoupled(new VMEWriteData)
val ack = Input(Bool())
}
/** VMEWriteClient.
*
* This interface is used by the VME to handle write requests from modules inside
* the core.
*/
class VMEWriteClient(implicit p: Parameters) extends Bundle {
val dataBits = p(ShellKey).memParams.dataBits
val cmd = Flipped(Decoupled(new VMECmd))
val data = Flipped(Decoupled(new VMEWriteData))
val ack = Output(Bool())
}
/** VMEMaster.
*
* Pack nRd number of VMEReadMaster interfaces and nWr number of VMEWriteMaster
* interfaces.
*/
class VMEMaster(implicit p: Parameters) extends Bundle {
val nRd = p(ShellKey).vmeParams.nReadClients
val nWr = p(ShellKey).vmeParams.nWriteClients
val rd = Vec(nRd, new VMEReadMaster)
val wr = Vec(nWr, new VMEWriteMaster)
}
/** VMEClient.
*
* Pack nRd number of VMEReadClient interfaces and nWr number of VMEWriteClient
* interfaces.
*/
class VMEClient(implicit p: Parameters) extends Bundle {
val nRd = p(ShellKey).vmeParams.nReadClients
val nWr = p(ShellKey).vmeParams.nWriteClients
val rd = Vec(nRd, new VMEReadClient)
val wr = Vec(nWr, new VMEWriteClient)
}
/** VTA Memory Engine (VME).
*
* This unit multiplexes the memory controller interface for the Core. Currently,
* it supports single-writer and multiple-reader mode and it is also based on AXI.
*/
class VME(implicit p: Parameters) extends Module {
val io = IO(new Bundle {
val mem = new AXIMaster(p(ShellKey).memParams)
val vme = new VMEClient
})
val clientCmdQueueDepth = p(ShellKey).vmeParams.clientCmdQueueDepth
val clientDataQueueDepth = p(ShellKey).vmeParams.clientDataQueueDepth
val RequestQueueDepth = p(ShellKey).vmeParams.RequestQueueDepth
val RequestQueueAddrWidth = log2Ceil(RequestQueueDepth.toInt)
val dataBits = p(ShellKey).memParams.dataBits
val nReadClients = p(ShellKey).vmeParams.nReadClients
val addrBits = p(ShellKey).memParams.addrBits
val lenBits = p(ShellKey).memParams.lenBits
val idBits = p(ShellKey).memParams.idBits
val vmeTag_array = SyncReadMem(RequestQueueDepth,(new(clientTag)))
val vmeTag_array_wr_data = Wire(new(clientTag))
val vmeTag_array_wr_addr = Wire(UInt(RequestQueueAddrWidth.W))
val vmeTag_array_rd_addr = Wire(UInt(RequestQueueAddrWidth.W))
val vmeTag_array_wr_en = Wire(Bool())
val localTag_out = Wire(new(clientTag))
val availableEntriesEn = Wire(Bool())
val availableEntriesNext = Wire(UInt(RequestQueueDepth.W))
val availableEntries = Reg(availableEntriesNext.cloneType)
val freeTagLocation = Wire(UInt(RequestQueueDepth.W))
val (resetEntry,newEntry,firstPostn) = firstOneOH(availableEntries.asUInt)
val updateEntry = Wire(UInt(RequestQueueDepth.W))
when(io.mem.r.bits.last & io.mem.r.valid){
availableEntriesNext := updateEntry | availableEntries
}.elsewhen(availableEntriesEn && availableEntries =/= 0.U && !(io.mem.r.bits.last & io.mem.r.valid)){
availableEntriesNext:= newEntry
}.otherwise{
availableEntriesNext:= availableEntries
}
when(reset.asBool){
availableEntries := VecInit(Seq.fill(RequestQueueDepth)(true.B)).asUInt
updateEntry := 0.U
}.otherwise{
availableEntries := availableEntriesNext
updateEntry := VecInit(IndexedSeq.tabulate(RequestQueueDepth){ i => i.U === (io.mem.r.bits.id).asUInt }).asUInt
}
// Cmd Queues for eaach VME client
val VMEcmd_Qs = IndexedSeq.fill(5){ Module(new Queue(new VMECmd, clientCmdQueueDepth))}
//---------------------------------------
//--- Find available buffer entries -----
//---------------------------------------
def firstOneOH (in: UInt) = {
val oneHotIdx = for(bitIdx <- 0 until in.getWidth) yield {
if (bitIdx == 0){
in(0)
}
else{
in(bitIdx) && ~in(bitIdx-1,0).orR
}
}
val oHot = VecInit(oneHotIdx).asUInt
val newVec = in&(~oHot) // turn bit to 0
val bitPostn = PriorityEncoder(oneHotIdx)
(oHot, newVec,bitPostn)
}
val default_tag = Wire(new(clientTag))
default_tag.client_tag := 0.U
default_tag.client_id := 0.U
default_tag.client_mask := 0.U
val cmd_valids = for { q <- VMEcmd_Qs } yield q.io.deq.valid
val vme_select = PriorityEncoder(cmd_valids :+ true.B)
val any_cmd_valid = cmd_valids.foldLeft(false.B){ case (x,y) => x || y}
availableEntriesEn := io.mem.ar.ready & any_cmd_valid
for { i <- 0 until 5} {
VMEcmd_Qs(i).io.enq.valid := io.vme.rd(i).cmd.valid & VMEcmd_Qs(i).io.enq.ready
VMEcmd_Qs(i).io.enq.bits := io.vme.rd(i).cmd.bits
VMEcmd_Qs(i).io.deq.ready := io.mem.ar.ready &
(vme_select === i.U) & (availableEntries.asUInt =/= 0.U) &
!(io.mem.r.bits.last & io.mem.r.valid)
io.vme.rd(i).cmd.ready := VMEcmd_Qs(i).io.enq.ready
}
vmeTag_array_wr_addr := firstPostn.asUInt
val cmd_readys = for { q <- VMEcmd_Qs} yield q.io.deq.ready
val any_cmd_ready = cmd_readys.foldLeft(false.B){ case (x,y) => x || y}
vmeTag_array_wr_en := any_cmd_ready
when(vmeTag_array_wr_en){
val rdwrPort = vmeTag_array(vmeTag_array_wr_addr)
rdwrPort := vmeTag_array_wr_data
}
io.mem.ar.bits.addr := 0.U
io.mem.ar.bits.len := 0.U
io.mem.ar.valid := 0.U
io.mem.ar.bits.id := 0.U
vmeTag_array_wr_data := default_tag
// Last assign wins so do this in reverse order
for { i <- 4 to 0 by -1} {
when(VMEcmd_Qs(i).io.deq.ready){
io.mem.ar.bits.addr := VMEcmd_Qs(i).io.deq.bits.addr
io.mem.ar.bits.len := VMEcmd_Qs(i).io.deq.bits.len
io.mem.ar.valid := VMEcmd_Qs(i).io.deq.valid
io.mem.ar.bits.id := vmeTag_array_wr_addr
vmeTag_array_wr_data.client_id := i.U
vmeTag_array_wr_data.client_tag := VMEcmd_Qs(i).io.deq.bits.tag
vmeTag_array_wr_data.client_mask := resetEntry
}
}
// We need one clock cycle to look up the local tag from the
// centralized tag buffer vmeTag_array
// Adding a flop stage for mem.r.data, mem.r.last, mem.r.valid
// till local tag lookup is performed.
io.mem.r.ready := true.B
vmeTag_array_rd_addr := io.mem.r.bits.id
localTag_out := vmeTag_array(vmeTag_array_rd_addr)
freeTagLocation := localTag_out.client_mask
for (i <- 0 until nReadClients) {
io.vme.rd(i).data.valid := ((RegNext(io.mem.r.valid, init = false.B)) && ((localTag_out.client_id) === i.U)
&& io.vme.rd(i).data.ready)
//VME doesnt stop on not ready
assert(io.vme.rd(i).data.ready || ~io.vme.rd(i).data.valid)
io.vme.rd(i).data.bits.data := RegNext(io.mem.r.bits.data, init = false.B)
io.vme.rd(i).data.bits.last := RegNext(io.mem.r.bits.last, init = false.B)
io.vme.rd(i).data.bits.tag := localTag_out.client_tag
}
// VME <-> AXI write interface
val wr_len = RegInit(0.U(lenBits.W))
val wr_addr = RegInit(0.U(addrBits.W))
val sWriteIdle :: sWriteAddr :: sWriteData :: sWriteResp :: Nil = Enum(4)
val wstate = RegInit(sWriteIdle)
val wr_cnt = RegInit(0.U(lenBits.W))
io.vme.wr(0).cmd.ready := wstate === sWriteIdle
io.vme.wr(0).ack := io.mem.b.fire
io.vme.wr(0).data.ready := wstate === sWriteData & io.mem.w.ready
io.mem.aw.valid := wstate === sWriteAddr
io.mem.aw.bits.addr := wr_addr
io.mem.aw.bits.len := wr_len
io.mem.aw.bits.id := p(ShellKey).memParams.idConst.U // no support for multiple writes
io.mem.w.valid := wstate === sWriteData & io.vme.wr(0).data.valid
io.mem.w.bits.data := io.vme.wr(0).data.bits.data
io.mem.w.bits.strb := io.vme.wr(0).data.bits.strb
io.mem.w.bits.last := wr_cnt === wr_len
io.mem.w.bits.id := p(ShellKey).memParams.idConst.U // no support for multiple writes
io.mem.b.ready := wstate === sWriteResp
when(io.vme.wr(0).cmd.fire) {
wr_len := io.vme.wr(0).cmd.bits.len
wr_addr := io.vme.wr(0).cmd.bits.addr
}
when(wstate === sWriteIdle) {
wr_cnt := 0.U
}
.elsewhen(io.mem.w.fire){
wr_cnt := wr_cnt + 1.U
}
switch(wstate){
is(sWriteIdle){
when(io.vme.wr(0).cmd.valid){
wstate := sWriteAddr
}
}
is(sWriteAddr){
when(io.mem.aw.ready){
wstate := sWriteData
}
}
is(sWriteData){
when(io.vme.wr(0).data.valid && io.mem.w.ready && wr_cnt === wr_len) {
wstate := sWriteResp
}
}
is(sWriteResp) {
when(io.mem.b.valid) {
wstate := sWriteIdle
}
}
}
// AXI constants - statically define
io.mem.setConst()
}
/** VTA Memory Engine (VME).
*
* This unit multiplexes the memory controller interface for the Core. Currently,
* it supports single-writer and multiple-reader mode and it is also based on AXI.
*/
class VMETop(implicit p: Parameters) extends Module {
val io = IO(new Bundle {
val mem = new AXIMaster(p(ShellKey).memParams)
val vme = new VMEClient
})
val forceSimpleVME = false // force simple vme for simple tensor load/uop/fetch
if (forceSimpleVME) {
val vme = Module(new VMESimple)
io <> vme.io
} else {
val vme = Module(new VME)
io <> vme.io
}
}