blob: f44b0bff48f517bafeb2c17e4416ec7fb357fb16 [file] [log] [blame]
// SPDX-FileCopyrightText: 2020 Muhammad Hadir Khan
//
// 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.
// SPDX-License-Identifier: Apache-2.0
package primitives
import chisel3._
import chisel3.util.{Cat, log2Ceil, log2Up}
/** @param
* width -> fifo word width for each row
* pass -> allows request to pass through empty fifo
* depth -> total rows in the fifo
* depthWidth -> the bits required to hold depth
*/
class Fifo(width: Int = 16, pass: Boolean = true, depth: Int = 4, outputZeroIfEmpty: Boolean = true) extends Module {
val io = IO(new Bundle {
val clr_i = Input(Bool()) // clear the fifo if true
// write port
val wvalid_i = Input(Bool()) // data from producer is valid
val wready_o = Output(Bool()) // is fifo ready to accept a write
val wdata_i = Input(UInt(width.W)) // producer data to write into the fifo
// read port
val rvalid_o = Output(Bool()) // the data being sent out of fifo is valid
val rready_i = Input(Bool()) // the consumer is ready to receive the data
val rdata_o = Output(UInt(width.W)) // the fifo data out to the consumer
// current status of the depth of FIFO
val depth_o = Output(UInt())
})
if(pass && depth == 0) {
require(depth == 0)
io.depth_o := 0.U
io.rvalid_o := io.wvalid_i
io.rdata_o := io.wdata_i
io.wready_o := io.rready_i
} else {
require(depth > 0)
// Normal FIFO construction
val PTRV_W = log2Up(depth) // if depth = 1 -> PTRV_W = 1 (log2Ceil(1) would make it 0)
val PTR_WIDTH = PTRV_W + 1
val fifo_wptr, fifo_rptr = RegInit(0.U(PTR_WIDTH.W))
val fifo_incr_wptr, fifo_incr_rptr, fifo_empty = Wire(Bool())
val full, empty = Wire(Bool())
val wptr_msb, rptr_msb = Wire(UInt(1.W))
val wptr_value, rptr_value = Wire(UInt(PTRV_W.W))
wptr_msb := fifo_wptr(PTR_WIDTH-1)
rptr_msb := fifo_rptr(PTR_WIDTH-1)
wptr_value := fifo_wptr(PTRV_W-1,0)
rptr_value := fifo_rptr(PTRV_W-1,0)
io.depth_o := Mux(full, depth.asUInt, Mux(wptr_msb === rptr_msb, wptr_value - rptr_value, depth.asUInt - rptr_value + wptr_value))
fifo_incr_wptr := io.wvalid_i & io.wready_o // producer giving valid data & FIFO ready to accept it then increase write pointer
fifo_incr_rptr := io.rvalid_o & io.rready_i // FIFO providing valid data to consumer & consumer ready to accept it then increase read pointer
io.wready_o := ~full // FIFO is ready to accept data if it is not empty
io.rvalid_o := ~empty // FIFO producing valid data for consumer if it is not empty.
when(io.clr_i) {
fifo_wptr := 0.U(PTR_WIDTH.W)
} .elsewhen(fifo_incr_wptr) {
when(fifo_wptr(PTR_WIDTH-2,0) === (depth - 1).asUInt) {
fifo_wptr := Cat(~fifo_wptr(PTR_WIDTH-1), 0.U((PTR_WIDTH-1).W))
} .otherwise {
fifo_wptr := fifo_wptr + Cat(0.U((PTR_WIDTH-1).W), 1.U(1.W))
}
// 16 depth
// 5 PTR_WIDTH bits
// wptr rptr
// 00000 00000
// 00000 + 00001 = 00001 00000
// 00001 + 00001 = 00010 00000
// ....
// 01111 00001 ^ 10000 = 10001
// 10000
// 10000 + 00001
// 10001 + 00001
// 10010
// ....
// 11111
// 00000
// [3:0] == 15
}
when(io.clr_i) {
fifo_rptr := 0.U(PTR_WIDTH.W)
} .elsewhen(fifo_incr_rptr) {
when(fifo_rptr(PTR_WIDTH-2) === (depth-1).asUInt) {
fifo_rptr := Cat(~fifo_rptr(PTR_WIDTH-1), 0.U((PTR_WIDTH-1).W))
} .otherwise {
fifo_rptr := fifo_rptr + Cat(0.U((PTR_WIDTH-1).W), 1.U(1.W))
}
}
full := fifo_wptr === (fifo_rptr ^ Cat(1.U(1.W), 0.U((PTR_WIDTH-1).W)))
// 00000 === 10000 ^ 10000 = 00000
fifo_empty := fifo_wptr === fifo_rptr
val storage = RegInit(VecInit(Seq.fill(depth)(0.U(width.W))))
val storage_rdata = Wire(UInt(width.W))
if(depth == 1) {
storage_rdata := storage(0)
when(fifo_incr_wptr) {
storage(0) := io.wdata_i
}
} else {
// fifo with more than one storage element
storage_rdata := storage(fifo_rptr(PTR_WIDTH-2,0))
when(fifo_incr_wptr) {
storage(fifo_wptr(PTR_WIDTH-2,0)) := io.wdata_i
}
}
val rdata_int = Wire(UInt(width.W))
if (pass == true) {
rdata_int := Mux(fifo_empty && io.wvalid_i, io.wdata_i, storage_rdata)
empty := fifo_empty & ~io.wvalid_i
} else {
rdata_int := storage_rdata
empty := fifo_empty
}
if (outputZeroIfEmpty) {
io.rdata_o := Mux(empty, 0.U, rdata_int)
} else {
io.rdata_o := rdata_int
}
}
}