// Copyright (c) 2007 Bluespec, Inc.  All Rights Reserved
// Do not distribute without permission from Bluespec, Inc.
// ----------------------------------------------------------------

#ifndef _EDGE_FIFOS_HPP_
#define _EDGE_FIFOS_HPP_

#include <stdio.h>
#include <systemc.h>
#include <esl.h>

// ================================================================
// This package contains a collection "Edge FIFOs" that are useful
// at the edges of an ESE design to communicate with external
// circuitry that is typically written in Verilog/VHDL

// All these FIFOs are 1-element FIFOs.  There are four versions:
//   mkPipelineFIFO, unguarded enq (1-tick latency, for incoming data)
//   mkPipelineFIFO, unguarded deq (1-tick latency, for outgoing data)
//   mkBypassFIFO,   unguarded enq (0-tick latency, for incoming data)
//   mkBypassFIFO,   unguarded deq (0-tick latency, for outgoing data)

// These FIFOs differ from the ESE library FIFOs in two dimensions,
//     Scheduling, and Unguarded ends
// which are described in more detail below.

// Scheduling
//   - 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 incoming "registered" data,
//     where the expectation is a latency of 1 tick due to regstering.
//
//   - 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" 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 incoming data where one needs
//     0-tick latency access to the incoming data if possible

// Unguarded ends
//   When dealing with external circuits, the ESE methods in the
//   interface often have to be "always ready, always enabled".
//
//   Thus, if the method tries to enq into or deq out of a FIFO
//   inside the ESE design, it can't have any implicit conditions.
//   So, these methods have to be "unguarded", i.e., they need
//   explicit 'ok_to_enq' and 'ok_to_deq' methods, and the
//   'enq' and 'deq' methods should only be used after explicitly
//   testing these conditions, because they don't have any
//   implicit conditions.
//
//   On an edge FIFO, only the end of the FIFO pointing towards
//   the external circuit needs to be unguarded.  The other end
//   of the FIFO, pointing into the BSV design, should have the
//   usual guarded FIFO semantics

// ================================================================

// ================================================================
// 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

// ----------------
// Version with "unguarded enq"

ESL_MODULE_TEMPLATE ( mkPipelineFIFO1_ug_enq, FIFO, T ) {

  // STATE ----------------

  // The fifo
  esl_reg<T>            regh;
  esl_reg<bool>         fifoFull;

  // Method signals
  esl_rwire<T>       *rw_enq;
  esl_rwire<void>    *rw_deq;

  // RULES ----------------
   
  ESL_RULE (update_final, (rw_enq->isValid() || rw_deq->isValid())) {
    fifoFull = rw_enq->isValid();
    regh     = rw_enq->wget();
  }

  // INTERFACE ----------------
   
  ESL_METHOD_VALUE (ok_to_enq, true, bool) {
    return ((!fifoFull) || (rw_deq->isValid()));
  }

  ESL_METHOD_ACTION (enq, true, const T& v) {
    if ((!fifoFull) || (rw_deq->isValid()))
      rw_enq->wset(v);
    else
      cout << "mkPipelineFIFO1_ug_enq: error: enq() called when not ok_to_enq()" << endl;
  }

  ESL_METHOD_VALUE (ok_to_deq, true, bool) {
    return fifoFull;
  }

  ESL_METHOD_ACTION (deq, fifoFull) {
     rw_deq->send ();
  }

  ESL_METHOD_VALUE (first, fifoFull, const T&) {
     return  regh;
  }

  ESL_METHOD_ACTION (clear, true) {
    fifoFull = false;
  }

  ESL_CTOR(mkPipelineFIFO1_ug_enq) {
    ESL_NEW_REG (fifoFull, bool, false);
    ESL_NEW_REGU (regh, T);
    rw_enq = new esl_mkRwire <T>    ("rw_enq");
    rw_deq = new esl_mkRwire <void> ("rw_deq");
    ESL_END_CTOR;
  }
}; // mkPipelineFIFO1_ug_enq

// ----------------
// Version with "unguarded deq"

ESL_MODULE_TEMPLATE (mkPipelineFIFO1_ug_deq,  FIFO, T) {

  // STATE ----------------
   
  // The fifo
  esl_reg<T>            regh;
  esl_reg<bool>         fifoFull;

  // Method signals
  esl_rwire<T>       *rw_enq;
  esl_rwire<void>    *rw_deq;

  // RULES ----------------
   
  ESL_RULE (update_final,  (rw_enq->isValid() || rw_deq->isValid())) {
    fifoFull = rw_enq->isValid();
    regh     = rw_enq->wget();
  }

  // INTERFACE ----------------
   
  ESL_METHOD_VALUE (ok_to_enq, true, bool) {
    return ((!fifoFull) || (rw_deq->isValid()));
  }

  ESL_METHOD_ACTION (enq, ((!fifoFull) || (rw_deq->isValid())), const T& v) {
    rw_enq->wset(v);
  }

  ESL_METHOD_VALUE (ok_to_deq, true, bool) {
    return  fifoFull;
  }

  ESL_METHOD_ACTION (deq, true) {
    if (fifoFull)
      rw_deq->send ();
    else
      cout << "mkPipelineFIFO1_ug_deq: error: deq() called when not ok_to_deq()" << endl;
  }

  ESL_METHOD_VALUE (first, true, const T&) {
    return regh; 
  }

  ESL_METHOD_ACTION (clear, true) {
    fifoFull = false;
  }

  ESL_CTOR (mkPipelineFIFO1_ug_deq) {
    ESL_NEW_REG (fifoFull, bool, false);
    ESL_NEW_REGU (regh, T);
    rw_enq = new esl_mkRwire <T>    ("rw_enq");
    rw_deq = new esl_mkRwire <void> ("rw_deq");
    ESL_END_CTOR;
  }
}; // mkPipelineFIFO1_ug_deq

// ================================================================
// 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

// ----------------
// Version with "unguarded deq"

ESL_MODULE_TEMPLATE (mkBypassFIFO1_ug_deq, FIFO, T) {

  // STATE ----------------

  // The fifo
  esl_reg<T>         regh;
  esl_reg<bool>      fifoFull;

  // Method signals
  esl_rwire<T>       *rw_enq;
  esl_rwire<void>    *rw_deq;

  // RULES ----------------
   
  // Note: no rule needed for enq_and_deq_on_Empty, since
  // fifoState remains Empty

  // Note: enq_and_deq_on_Full is disallowed

  ESL_RULE (enq_only_on_Empty,
	    (rw_enq->isValid() && !rw_deq->isValid() && !fifoFull)) {
    regh = rw_enq->wget();
    fifoFull = true;
  }

  ESL_RULE (deq_only_on_Full,
	    (!rw_enq->isValid() && rw_deq->isValid() && fifoFull)) {
    fifoFull = false;
  }

  // INTERFACE ----------------

  ESL_METHOD_VALUE (ok_to_enq, true, bool) {
    return !fifoFull;
  }

  ESL_METHOD_ACTION (enq, !fifoFull, const T& v) {
    rw_enq->wset(v);
  }

  ESL_METHOD_VALUE (ok_to_deq, true, bool) {
    return  fifoFull || rw_enq->isValid();
  }

  ESL_METHOD_ACTION (deq, true) {
    if (fifoFull || rw_enq->isValid())
      rw_deq->send ();
    else
      cout << "mkBypassFIFO1_ug_deq: error: deq() called when not ok_to_deq()" << endl;
  }

  ESL_METHOD_VALUE (first, true, const T&) {
    bool b = (rw_enq->isValid() && !fifoFull);
    return (b ? rw_enq->wget() : regh);
  }

  ESL_METHOD_ACTION (clear, true) {
    fifoFull = false;
  }

  ESL_CTOR(mkBypassFIFO1_ug_deq) {
    ESL_NEW_REG (fifoFull, bool, false);
    ESL_NEW_REGU (regh, T);
    rw_enq = new esl_mkRwire <T>    ("rw_enq");
    rw_deq = new esl_mkRwire <void> ("rw_deq");
    ESL_END_CTOR;
  }
}; // mkBypassFIFO1_ug_deq

// ----------------
// Version with "unguarded enq"

ESL_MODULE_TEMPLATE (mkBypassFIFO1_ug_enq, FIFO, T) {

  // STATE ----------------

  // The fifo
  esl_reg<T>         regh;
  esl_reg<bool>      fifoFull;

  // Method signals
  esl_rwire<T>       *rw_enq;
  esl_rwire<void>    *rw_deq;

  // RULES ----------------
   
  // Note: no rule needed for enq_and_deq_on_Empty, since
  // fifoState remains Empty

  // Note: enq_and_deq_on_Full is disallowed

  ESL_RULE (enq_only_on_Empty,
	    (rw_enq->isValid() && !rw_deq->isValid() && !fifoFull)) {
    regh = rw_enq->wget();
    fifoFull = true;
  }

  ESL_RULE (deq_only_on_Full,
	    (!rw_enq->isValid() && rw_deq->isValid() && fifoFull)) {
    fifoFull = false;
  }

  // INTERFACE ----------------

  ESL_METHOD_VALUE (ok_to_enq, true, bool) {
    return !fifoFull;
  }

  ESL_METHOD_ACTION (enq, true, const T& v) {
    if (!fifoFull)
      rw_enq->wset(v);
    else
      cout << "mkBypassFIFO1_ug_enq: error: enq() called when not ok_to_enq()" << endl;
  }

  ESL_METHOD_VALUE (ok_to_deq, true, bool) {
    return  fifoFull || rw_enq->isValid();
  }

  ESL_METHOD_ACTION (deq, fifoFull || rw_enq->isValid()) {
    rw_deq->send();
  }

  ESL_METHOD_VALUE (first, fifoFull || rw_enq->isValid(), const T&) {
    bool b = (rw_enq->isValid() && !fifoFull);

    return (b ? rw_enq->wget() : regh);
  }

  ESL_METHOD_ACTION (clear, true) {
    fifoFull = false;
  }

  ESL_CTOR (mkBypassFIFO1_ug_enq) {
    ESL_NEW_REG (fifoFull, bool, false);
    ESL_NEW_REGU (regh, T);
    rw_enq = new esl_mkRwire <T>    ("rw_enq");
    rw_deq = new esl_mkRwire <void> ("rw_deq");
    ESL_END_CTOR;
  }
}; // mkBypassFIFO1_ug_enq

// ================================================================

#ifndef ESE_COMP
#include "edge_fifos.epp"
#endif

#endif
