| // 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 common |
| |
| import chisel3._ |
| import chisel3.util._ |
| import _root_.circt.stage.ChiselStage |
| |
| object FifoIxO { |
| def apply[T <: Data](t: T, i: Int, o: Int, n: Int) = { |
| Module(new FifoIxO(t, i, o, n)) |
| } |
| } |
| |
| // Input accepted with a common handshake and per lane select. |
| // Outputs are transacted independently, and ordered {[0], [0,1], [0,1,2], [0,1,2,3]}. |
| // Outputs are not registered, assumes passes directly into shallow combinatorial. |
| class FifoIxO[T <: Data](t: T, i: Int, o: Int, n: Int /* depth */) extends Module { |
| val io = IO(new Bundle { |
| val in = Flipped(Decoupled(Vec(i, Valid(t)))) |
| val out = Vec(o, Decoupled(t)) |
| val count = Output(UInt(log2Ceil(n+1).W)) |
| val nempty = Output(Bool()) |
| }) |
| |
| val m = n |
| |
| val mb = log2Ceil(m) |
| val n1b = log2Ceil(n + 1) |
| |
| def Increment(a: UInt, b: UInt): UInt = { |
| val c = a +& b |
| val d = Mux(c < m.U, c, c - m.U)(a.getWidth - 1, 0) |
| d |
| } |
| |
| val mem = Reg(Vec(n, t)) |
| |
| val inpos = Reg(Vec(i, UInt(mb.W))) // reset below |
| val outpos = Reg(Vec(o, UInt(mb.W))) // reset below |
| |
| val mcount = RegInit(0.U(n1b.W)) |
| val nempty = RegInit(false.B) |
| val inready = RegInit(false.B) |
| val outvalid = RegInit(0.U(o.W)) |
| |
| val ivalid = io.in.valid && io.in.ready |
| |
| val iactive = Cat((0 until i).reverse.map(x => io.in.bits(x).valid)).asUInt |
| |
| val icount = (io.in.bits.map(x => x.valid.asUInt).reduce(_ +& _))(log2Ceil(i),0) |
| |
| val oactiveBits = Cat((0 until o).reverse.map(x => io.out(x).valid && io.out(x).ready)) |
| |
| val ovalid = oactiveBits =/= 0.U |
| |
| val ocount = (0 until o).map(x => oactiveBits(x).asUInt).reduce(_ +& _)(log2Ceil(o),0) |
| |
| for (n <- 1 until o) { |
| assert(!(oactiveBits(n) === 1.U && oactiveBits(n - 1,0) =/= ((1 << n) - 1).U)) |
| } |
| |
| val ovalidBits = Cat((0 until o).reverse.map(x => io.out(x).valid)) |
| |
| for (n <- 1 until o) { |
| assert(!(ovalidBits(n) === 1.U && ovalidBits(n - 1, 0) =/= ((1 << n) - 1).U)) |
| } |
| |
| val oreadyBits = Cat((0 until o).reverse.map(x => io.out(x).ready)) |
| |
| for (n <- 1 until o) { |
| assert(!(oreadyBits(n) === 1.U && oreadyBits(n - 1, 0) =/= ((1 << n) - 1).U)) |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Fifo Control. |
| when (reset.asBool) { |
| for (i <- 0 until i) { |
| inpos(i) := i.U |
| } |
| } .elsewhen (ivalid) { |
| for (i <- 0 until i) { |
| inpos(i) := Increment(inpos(i), icount) |
| } |
| } |
| |
| when (reset.asBool) { |
| for (i <- 0 until o) { |
| outpos(i) := i.U |
| } |
| } .elsewhen (ovalid) { |
| for (i <- 0 until o) { |
| outpos(i) := Increment(outpos(i), ocount) |
| } |
| } |
| |
| val inc = MuxOR(ivalid, icount) |
| val dec = MuxOR(ovalid, ocount) |
| |
| when (ivalid || ovalid) { |
| val nxtmcount = mcount + inc - dec |
| inready := nxtmcount <= (m.U - i.U) |
| mcount := nxtmcount |
| nempty := nxtmcount =/= 0.U |
| outvalid := Cat((0 until o).reverse.map(x => nxtmcount >= (x + 1).U)) |
| } .otherwise { |
| inready := mcount <= (m.U - i.U) |
| outvalid := Cat((0 until o).reverse.map(x => mcount >= (x + 1).U)) |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Fifo Input. |
| val inxvalid = FifoXValid(iactive) |
| |
| for (q <- 0 until m) { |
| val valid = Cat( |
| (0 until i).reverse.map(x => |
| if (x == 0) { inpos(0) === q.U && inxvalid(0)(0) } else { |
| (0 to x).map(y => |
| inpos(y) === q.U && inxvalid(y)(x) |
| ).reduce(_ || _) |
| } |
| ) |
| ) |
| |
| if (true) { |
| val data = (0 until i).map(x => MuxOR(valid(x), io.in.bits(x).bits.asUInt)).reduce(_ | _) |
| |
| when (ivalid && valid =/= 0.U) { |
| mem(q) := data.asTypeOf(t) |
| } |
| } else { |
| when (ivalid) { |
| when(PopCount(valid) >= 1.U) { |
| val idx = PriorityEncoder(valid) |
| mem(q) := io.in.bits(idx).bits |
| } |
| } |
| } |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Interface. |
| io.in.ready := inready |
| |
| for (i <- 0 until o) { |
| io.out(i).valid := outvalid(i) |
| io.out(i).bits := mem(outpos(i)) // TODO: VecAt() |
| } |
| |
| io.count := mcount |
| |
| io.nempty := nempty |
| |
| assert(io.count <= m.U) |
| } |
| |
| object EmitFifoIxO extends App { |
| ChiselStage.emitSystemVerilogFile(new FifoIxO(UInt(32.W), 4, 4, 24), args) |
| } |