package SpecialFIFOs; // ================================================================ // // This package contains several FIFO variants. The two main variants // are 1-element FIFOs that both allow simultaneous enq and deq, but // with different scheduling semantics. These are useful for achieving // latency and throughput goals in certain designs // // Scheduling semantics // - a PipelineFIFO allows a simultaneous enq and deq when full, // behaving like a deq followed by enq, so that the value already // in the FIFO is returned by the deq, and the new enq'd value // then occupies the FIFO, and the FIFO remains full. // Note: every value spends at least 1 cycle in the FIFO // // Thus, this FIFO is useful for "registered" data, // where the expectation is a latency of 1 tick due to registering. // // - a BypassFIFO allows a simultaneous enq and deq when empty, // behaving like an enq followed by deq so that the new value // being enq'd "flies through" (or is "bypassed through") the FIFO // and is returned by the deq operation, and the FIFO remains empty. // Note: in this situation, the value spends 0 cycles in the FIFO // Note: there is a combinational path from enq'd data to deq'd data // // Thus, this FIFO is useful for 0-tick latency situations // // The BSV library provides 'mkLFIFOF' also known as a "loopy FIFO" // and this is just a 1-element PipelineFIFO (with guarded ends) // // ================================================================ import FIFO::*; import FIFOF::*; import FIFOLevel::*; import RevertingVirtualReg::*; export mkPipelineFIFO; export mkPipelineFIFOF; export mkBypassFIFO; export mkBypassFIFOF; export mkDFIFOF; export mkSizedDFIFOF; export mkSizedBypassFIFOF; export mkBypassFIFOLevel; // ================================================================ // 1-element "pipeline FIFO" // It's a 1-element FIFO (register with Valid/Invalid tag bit), where // - if empty, can only enq, cannot deq, leaving it full // - if full, can // - either just deq, leaving it empty // - or deq and enq simultaneously (logically: deq before enq), leaving it full module mkPipelineFIFO (FIFO#(a)) provisos (Bits#(a,sa)); (* hide *) FIFOF#(a) _ifc <- mkPipelineFIFOF; // return fifofToFifo(_ifc) method enq = _ifc.enq; method deq = _ifc.deq; method first = _ifc.first; method clear = _ifc.clear; endmodule: mkPipelineFIFO module mkPipelineFIFOF (FIFOF#(a)) provisos (Bits#(a,sa)); // STATE ---------------- Reg#(Maybe#(a)) rv[3] <- mkCReg(3, tagged Invalid); // INTERFACE ---------------- Bool enq_ok = ! isValid(rv[1]); Bool deq_ok = isValid(rv[0]); method notFull = enq_ok; method Action enq(v) if (enq_ok); rv[1] <= tagged Valid v; endmethod method notEmpty = deq_ok; method Action deq() if (deq_ok); rv[0] <= tagged Invalid; endmethod method first() if (rv[0] matches tagged Valid .v); // deq_ok return v; endmethod method Action clear(); rv[2] <= tagged Invalid; endmethod endmodule // ================================================================ // 1-element "bypass FIFO". // It's a 1-element FIFO (register with Valid/Invalid tag bit), where // - if full, can only deq, cannot enq, leaving it empty // - if empty, can // - either just enq, leaving it full // - or enq and deq simultaneously (logically: enq before deq), leaving it empty module mkBypassFIFO (FIFO#(a)) provisos (Bits#(a,sa)); (* hide *) FIFOF#(a) _ifc <- mkBypassFIFOF; // return fifofToFifo(_ifc) method enq = _ifc.enq; method deq = _ifc.deq; method first = _ifc.first; method clear = _ifc.clear; endmodule module mkBypassFIFOF (FIFOF#(a)) provisos (Bits#(a,sa)); // STATE ---------------- Reg#(Maybe#(a)) rv[3] <- mkCReg(3, tagged Invalid); // INTERFACE ---------------- Bool enq_ok = ! isValid(rv[0]); Bool deq_ok = isValid(rv[1]); method notFull = enq_ok; method Action enq(v) if (enq_ok); rv[0] <= tagged Valid v; endmethod method notEmpty = deq_ok; method Action deq() if (deq_ok); rv[1] <= tagged Invalid; endmethod method first() if (rv[1] matches tagged Valid .v); // deq_ok return v; endmethod method Action clear(); rv[2] <= tagged Invalid; endmethod endmodule //////////////////////////////////////////////////////////////////////////////// /// A FIFOF with unguarded deq and first methods (thus they have no /// implicit conditions). /// The 'first' method returns a specified "default" value when the /// FIFO is empty. //////////////////////////////////////////////////////////////////////////////// module mkDFIFOF#(a dflt) (FIFOF#(a)) provisos (Bits#(a,sa)); (*hide*) let _ifc <- mkSizedDFIFOF(2, dflt); return _ifc; endmodule module mkSizedDFIFOF#(Integer n, a dflt) (FIFOF#(a)) provisos (Bits#(a,sa)); // If the queue contains n elements, they are in q[0]..q[n-1]. The head of // the queue (the "first" element) is in q[0], the tail in q[n-1]. Reg#(a) q[n]; for (Integer i=0; i , 0, ififoDepth -1 , "isGreaterThan" ) ; endmethod endmodule // Common function to test the validity arguments to methods function Bool rangeTest( UInt#(cntSz) value, Integer comp, function Bool foperation(UInt#(cntSz) a, UInt#(cntSz) b ), Integer minValue, Integer maxValue, String methodName ); return ((comp >= minValue) && (comp <= maxValue )) ? (foperation (value, fromInteger( comp ))) : error( "Argument of " + methodName + " must be in the range of " + integerToString( minValue) + " to " + integerToString( maxValue ) + "; " + integerToString( comp ) + " is out of range.") ; endfunction endpackage