Ensure the delay between assertReset and resetTimer are the same in both iterative deepening and shrinking modes

// BlueCheck 0.2, Matt N.

// Change log
// ==========
//
// 5  Nov 2012: Version 0.1
// 21 Nov 2014: Support for iterative deepening
// 28 Nov 2014: Version 0.2, with support for shrinking

package BlueCheck;

import Randomizable  :: *;
import ModuleCollect :: *;
import StmtFSM       :: *;
import List          :: *;
import Clocks        :: *;
import FIFOF         :: *;
import ConfigReg     :: *;
import Vector        :: *;
import Assert        :: *;

// The max size of the log used for shrinking.  This is tricky to
// implement as a module parameter, hence it is a global parameter.
typedef 32 LogSize;
Integer logSize = valueOf(LogSize);

// BlueCheck module parameters
typedef struct {
  // Display message when a chosen state does not fire
  Bool showNonFire;

  // Display message when a chosen state is a no-op
  Bool showNoOp;

  // Generate a checker based on an iterative deepening strategy
  // (If 'Invalid', a single random state walk is performed)
  Bool useIterativeDeepening;
  // This must contain valid data if 'useIterativeDeepening' is 'True'
  ID_Params id;

  // Attempt to shrink a counter example, if one is found
  // (This is only valid for iterative deepening)
  Bool useShrinking;

  // Number of testing iterations to perform. For iterative deepening,
  // this is the number of times to increase the depth before stopping
  // Otherwise, the it's the length of the random state walk
  Bit#(32) numIterations;
} BlueCheck_Params;

// Sub-parameters for iterative deepening
typedef struct {
  // Iterative deepening requires ability to reset the circuit under test
  MakeResetIfc rst;

  // The number of states to be explored in a single 'test'
  Bit#(32) initialDepth;

  // The number of tests to perform at each depth
  Bit#(32) testsPerDepth;

  // A function to increase the depth
  (function Bit#(32) f(Bit#(32) currentDepth)) incDepth;
} ID_Params;

// The maximum number of states in the equivalance checker.
// You will get a compile-time error message if this parameter
// is not big enough, but it's not likely, unless you use very
// large method frequencies.
typedef 16 LogMaxStates;
typedef UInt#(LogMaxStates) State;

// The frequency that the checker will move to particular state.
typedef Integer Frequency; // Range: 1 to 100.

// A BlueCheck module implicitly collects actions, statements,
// invariants, random generators and loggers, an allowing automatic
// creation of an equivalance checker.
typedef ModuleCollect#(Item) BlueCheck;

typedef union tagged {
  Tuple3#(Frequency, Fmt, Action) ActionItem;
  Tuple3#(Frequency, Fmt, Stmt) StmtItem;
  Tuple2#(Bool, function Action gen(Bool replay)) RandomGenItem;
  Tuple2#(Fmt, Bool) InvariantItem;
  Bool EnsureItem;
  Tuple3#(Action, Action, Action) LogItem;
  Stmt PreStmtItem;
  Stmt PostStmtItem;
} Item;

// Turn an item into a singleton action if it is an ActionItem.
function List#(Tuple3#(Frequency, Fmt, Action)) getActionItem(Item item) =
  case (item) matches
    tagged ActionItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton action if it is an RandomGenItem.
function List#(Tuple2#(Bool, function Action gen(Bool replay)))
  getRandomGenItem(Item item) =
  case (item) matches
    tagged RandomGenItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton pair if it is an InvariantItem.
function List#(Tuple2#(Fmt, Bool)) getInvariantItem(Item item) =
  case (item) matches
    tagged InvariantItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is an StmtItem.
function List#(Tuple3#(Frequency, Fmt, Stmt)) getStmtItem(Item item) =
  case (item) matches
    tagged StmtItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is an EnsureItem.
function List#(Bool) getEnsureItem(Item item) =
  case (item) matches
    tagged EnsureItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is an LogItem.
function List#(Tuple3#(Action, Action, Action))
  getLogItem(Item item) =
  case (item) matches
    tagged LogItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is a PreStmtItem.
function List#(Stmt) getPreStmtItem(Item item) =
  case (item) matches
    tagged PreStmtItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is a PostStmtItem.
function List#(Stmt) getPostStmtItem(Item item) =
  case (item) matches
    tagged PostStmtItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Display a function application, where function and
// arguments are available as Fmt items of a list.
function Fmt formatApp(List#(Fmt) app);
  if (List::tail(app) matches tagged Nil)
    return List::head(app);
  else
    return (List::head(app) + fshow("(") +
             formatArgs(List::tail(app)) + fshow(")"));
endfunction

function Fmt formatArgs(List#(Fmt) args);
  if (List::tail(args) matches tagged Nil)
    return List::head(args);
  else
    return (List::head(args) + fshow(",") + formatArgs(List::tail(args)));
endfunction

// The following type class allows two functions of the same type to
// be applied to random inputs.  If the return values differ, the
// behaviour is to terminate with an error message (a counter-example
// has been found).  The probability that the equivalence will be
// checked on any given step can be specified.
typeclass Equiv#(type a);
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, a f, a g) ();
endtypeclass

module [BlueCheck] equiv#(String name, a f, a g) ()
    provisos(Equiv#(a));
  eq(Cons(fshow(name), Nil), 1, f, g);
endmodule

module [BlueCheck] equivf#(Frequency fr, String name, a f, a g) ()
    provisos(Equiv#(a));
  eq(Cons(fshow(name), Nil), fr, f, g);
endmodule

// Base case 1: execute two actions.
instance Equiv#(Action);
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, Action a, Action b) ();
    Action executeTwo =
      action
        a; b;
      endaction;
    addToCollection(tagged ActionItem
      (tuple3(fr, formatApp(app), executeTwo)));
  endmodule
endinstance

// Base case 2: execute two action-values,
// and check equivalance of results.
instance Equiv#(ActionValue#(t))
  provisos(Eq#(t), Bits#(t, n), FShow#(t));
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr
                                       , ActionValue#(t) a
                                       , ActionValue#(t) b) ();
    Wire#(Bool) success <- mkDWire(True);
    Wire#(t) aWire      <- mkDWire(?);
    Wire#(t) bWire      <- mkDWire(?);
    Fmt msg             =  fshow("Not equal: ") + fshow(aWire)
                        +  fshow(" versus ")    + fshow(bWire);

    Action executeTwoAndCheck =
      action
        t aVal <- a; aWire <= aVal;
        t bVal <- b; bWire <= bVal;
        if (aVal != bVal) success <= False;
      endaction;
      addToCollection(tagged ActionItem
        (tuple3(fr, formatApp(app), executeTwoAndCheck)));
      addToCollection(tagged InvariantItem (tuple2(msg, success)));
  endmodule
endinstance

// Base case 3: execute two statements.
instance Equiv#(Stmt);
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, Stmt a, Stmt b) ();
    Stmt s = par a; b; endpar;
    addToCollection(tagged StmtItem (tuple3(fr, formatApp(app), s)));
  endmodule
endinstance

// Recursive case: generate a random input,
// apply it to each function, and
// recurse on the resulting applications.
instance Equiv#(function b f(a x))
  provisos(Equiv#(b), Bits#(a, n), Bounded#(a), FShow#(a));
    module [BlueCheck] eq#(List#(Fmt) app, Frequency fr
                                         , function b f(a x)
                                         , function b g(a y))();
      Reg#(Bool) init <- mkReg(True);
      Reg#(a) aReg <- mkRegU;
      Randomize#(a) aRandom <- mkGenericRandomizer;
      FIFOF#(a) aLog <- mkUGSizedFIFOF(logSize);

      function Action genRandom(Bool replay) =
        action
          let a <- aRandom.next;
          aReg <= replay ? aLog.first : a;
        endaction;

      rule initialise (init);
        aRandom.cntrl.init;
        init <= False;
      endrule

      Action logEnq   = aLog.enq(aReg);
      Action logRot   = action aLog.deq; aLog.enq(aLog.first); endaction;
      Action logClear = aLog.clear;

      addToCollection(tagged RandomGenItem (tuple2(!init, genRandom)));
      addToCollection(tagged LogItem (tuple3(logEnq, logRot, logClear)));

      eq(List::append(app, Cons(fshow(aReg), Nil)),
           fr, f(aReg), g(aReg));
    endmodule
endinstance

// Base case 4 (fall through): check that two values are equal.
instance Equiv#(a) provisos(Eq#(a), FShow#(a));
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, a x, a y) ();
    Wire#(Bool) success <- mkDWire(True);
    Fmt fmt = formatApp(app) + fshow(" failed: ")
            + fshow(x) + fshow(" v ") + fshow(y);

    rule check;
      if (x != y) success <= False;
    endrule

    addToCollection(tagged InvariantItem (tuple2(fmt, success)));
  endmodule
endinstance

// Like the Equiv type-class, except for a single method.
typeclass Prop#(type a);
  module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, a f) ();
endtypeclass

// Base case 1: execute statement.
instance Prop#(Stmt);
  module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, Stmt a) ();
    addToCollection(tagged StmtItem (tuple3(fr, formatApp(app), a)));
  endmodule
endinstance

// Base case 2: execute action.
instance Prop#(Action);
  module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, Action a) ();
    addToCollection(tagged ActionItem (tuple3(fr, formatApp(app), a)));
  endmodule
endinstance

// Base case 3: execute action-value
instance Prop#(ActionValue#(Bool));
  module [BlueCheck] pr#(List#(Fmt) app,Frequency fr,ActionValue#(Bool) a) ();
    Wire#(Bool) success <- mkDWire(True);
    Fmt msg = fshow("Property failed");

    Action act =
      action
        Bool s <- a;
        if (!s) success <= False;
      endaction;
      addToCollection(tagged ActionItem (tuple3(fr, formatApp(app), act)));
      addToCollection(tagged InvariantItem (tuple2(msg, success)));
  endmodule
endinstance

// Recursive case.
instance Prop#(function b f(a x))
  provisos(Prop#(b), Bits#(a, n), Bounded#(a), FShow#(a));
    module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, function b f(a x))();
      Reg#(Bool) init <- mkReg(True);
      Reg#(a) aReg <- mkRegU;
      Randomize#(a) aRandom <- mkGenericRandomizer;
      FIFOF#(a) aLog <- mkUGSizedFIFOF(logSize);

      function Action genRandom(Bool replay) =
        action
          let a <- aRandom.next;
          aReg <= replay ? aLog.first : a;
        endaction;

      rule initialise (init);
        aRandom.cntrl.init;
        init <= False;
      endrule

      Action logEnq   = aLog.enq(aReg);
      Action logRot   = action aLog.deq; aLog.enq(aLog.first); endaction;
      Action logClear = aLog.clear;

      addToCollection(tagged RandomGenItem (tuple2(!init, genRandom)));
      addToCollection(tagged LogItem (tuple3(logEnq, logRot, logClear)));

      pr(List::append(app, Cons(fshow(aReg), Nil)), fr, f(aReg));
    endmodule
endinstance

module [BlueCheck] prop#(String name, a f) ()
    provisos(Prop#(a));
  pr(Cons(fshow(name), Nil), 1, f);
endmodule

module [BlueCheck] propf#(Frequency fr, String name, a f) ()
    provisos(Prop#(a));
  pr(Cons(fshow(name), Nil), fr, f);
endmodule

// Ensure function -- for making assertions inside properties
typedef (function Action f(Bool cond)) Ensure;

module [BlueCheck] getEnsure (Ensure);
  // Create ensure function
  Wire#(Bool) ok <- mkDWire(True);
  function Action ensureFunc(Bool cond) = action ok <= cond; endaction;
  addToCollection(tagged EnsureItem ok);
  return ensureFunc;
endmodule

// Allow user to add custom pre/post statements for each test
module [BlueCheck] addPreStmt#(Stmt pre) (Empty);
  addToCollection(tagged PreStmtItem pre);
endmodule

module [BlueCheck] addPostStmt#(Stmt post) (Empty);
  addToCollection(tagged PostStmtItem post);
endmodule

// Compute the condition for being in each state of the equivalance
// checker. Some states are visited more frequently than others.
function List#(Bool) stateConds(Reg#(State) s, Integer start,
                                         List#(Frequency) freqs);
  if (freqs matches tagged Nil) return Nil;
  else
    begin
      Frequency f = List::head(freqs);
      return (Cons(s >= fromInteger(start) && s < fromInteger(start+f),
        stateConds(s, start+f, List::tail(freqs))));
    end
endfunction

// Sum a list
function Integer sum(List#(Integer) xs);
  if (xs matches tagged Nil) return 0;
  else return (List::head(xs) + sum(List::tail(xs)));
endfunction

// Sequence a list of statements
function Stmt seqList(List#(Stmt) xs);
  if (xs matches tagged Nil) return (seq delay(1); endseq);
  else return (seq List::head(xs); seqList(List::tail(xs)); endseq);
endfunction

// Turn the list of items gathered in a BlueCheck module into an
// actual equivalence checker.
module [Module] blueCheckCore#( BlueCheck#(Empty) bc
                              , BlueCheck_Params params ) (Stmt);
  // Extract items.
  let concat = List::concat;
  let map    = List::map;
  let append = List::append;
  let {_, items} <- getCollection(bc);
  let actionItems    = concat(map(getActionItem, items));
  let stmtItems      = concat(map(getStmtItem, items));
  let randomGens     = concat(map(getRandomGenItem, items));
  let logItems       = concat(map(getLogItem, items));
  let ensureItems    = concat(map(getEnsureItem, items));
  let invariantBools = concat(map(getInvariantItem, items));
  let preStmt        = seqList(concat(map(getPreStmtItem, items)));
  let postStmt       = seqList(concat(map(getPostStmtItem, items)));
  let actionMsgs     = map(tpl_2, actionItems);
  let stmtMsgs       = map(tpl_2, stmtItems);
  let actions        = map(tpl_3, actionItems);
  let stmts          = map(tpl_3, stmtItems);

  // Setup state machine for equivalence checking.
  // Note state 0 is a no-op state.
  List#(Integer) freqs = append(map(tpl_1, actionItems),
                                map(tpl_1, stmtItems));
  Integer numStates = 1+sum(freqs);
  Randomize#(State) randomState <-
    mkConstrainedRandomizer(0, fromInteger(numStates-1));
  ConfigReg#(State) state <- mkConfigReg(0);
  Wire#(Bool) waitWire <- mkDWire(False);
  Wire#(Bool) didntFire <- mkDWire(False);
  Reg#(Bool) testDone <- mkReg(False);
  List#(Bool) inState = stateConds(state, 1, freqs);
  Reg#(Bool) shrinkingMode <- mkReg(False);

  // When all random generators have initialised
  Reg#(Bool) randGensInitialised <- mkReg(False);

  // When count is 0, actions/statements are disabled
  ConfigReg#(Bit#(32)) count <- mkConfigReg(0);
  Bool actionsEnabled = count != 0;

  // When delayed count is 0, invariant checking is disabled
  ConfigReg#(Bit#(32)) delayedCount <- mkConfigReg(0);
  Bool checkingEnabled = delayedCount != 0;

  rule updateDelayedCount;
    delayedCount <= count;
  endrule

  // Log/replay signals
  FIFOF#(State) stateLog  <- mkSizedFIFOF(logSize);
  FIFOF#(Bit#(64)) timeLog <- mkSizedFIFOF(logSize);
  Wire#(Bool) replayWire <- mkDWire(False);
  Wire#(Bool) logEnqWire <- mkDWire(False);
  Wire#(Bool) logRotWire <- mkDWire(False);
  Wire#(Bool) logClearWire <- mkDWire(False);
  Reg#(Bit#(32)) counterExampleLength <- mkReg(0);
  Reg#(Bit#(32)) omitNum <- mkReg(0);
  Vector#(LogSize, Reg#(Bool)) omitMask <- replicateM(mkReg(False));

  // Track failures
  Reg#(Bool) failureReg    <- mkConfigReg(False);
  Wire#(Bool) resetFailure <- mkDWire(False);
  Bool ensureFailure    = List::any( \== (False), ensureItems);
  Bool invariantFailure = (waitWire || !checkingEnabled) ? False
                        : List::any( \== (False), map (tpl_2, invariantBools));
  Bool failureFound     = ensureFailure || invariantFailure || failureReg;

  rule trackFailure;
    if (resetFailure)
      failureReg <= False;
    else if (ensureFailure || invariantFailure)
      failureReg <= True;
  endrule

  // Local timer
  Reg#(Bit#(64)) timer <- mkReg(0);
  Wire#(Bool) resetTimer <- mkDWire(False);

  rule incTimer;
    if (resetTimer)
      timer <= 0;
    else
      timer <= timer+1;
  endrule

  // Generate rules to generate random data.
  for (Integer i = 0; i < length(randomGens); i=i+1)
    begin
      let go  = tpl_1(randomGens[i]);
      let gen = tpl_2(randomGens[i]);
      rule genRandomData (go && !waitWire);
        gen(replayWire);
      endrule
    end

  // Signal when all random generators have initialised
  rule initRandomGens;
    randGensInitialised <= List::all(tpl_1, randomGens);
  endrule

  // Rules to check 'ensure' assertions.
  rule checkEnsure (!failureReg && List::any( \== (False) , ensureItems));
    $display("%0t: 'ensure' statement failed", timer);
  endrule

  // Generate rules to check invariant booleans.
  for (Integer i = 0; i < length(invariantBools); i=i+1)
    begin
      let msg = tpl_1(invariantBools[i]);
      let b   = tpl_2(invariantBools[i]);
      rule checkInvariantBool (checkingEnabled && !failureReg && !waitWire);
        if (!b) $display("%0t: ", timer, msg);
      endrule
    end

  // Generate rules to run actions, guarded by the current state.
  for (Integer i = 0; i < length(actions); i=i+1)
    begin
      (* preempts = "runAction, runActionNotPossible" *)
      rule runAction (actionsEnabled && inState[i] && !waitWire);
        $display("%0t: ", timer, actionMsgs[i]);
        actions[i];
        if (!shrinkingMode)
          logEnqWire <= True;
      endrule
      rule runActionNotPossible (actionsEnabled && inState[i] && !waitWire);
        didntFire <= True;
        if (params.showNonFire)
          $display("%0t: [did not fire] ", timer, actionMsgs[i]);
      endrule
    end

  // Generate rules to run statements, guarded by the current state.
  // Statements may take many cycles, hence waitWire.
  for (Integer i = 0; i < length(stmts); i=i+1)
    begin
      Integer s = length(actions)+i;
      Reg#(Bool) fsmRunning <- mkReg(False);
      FSM fsm <- mkFSMWithPred(stmts[i], actionsEnabled && inState[s]);

      rule runStmt (actionsEnabled && inState[s] && !fsmRunning);
        $display("%0t: ", timer, stmtMsgs[i]);
        fsm.start;
        fsmRunning <= True;
        waitWire <= True;
        if (!shrinkingMode)
          logEnqWire <= True;
      endrule

      rule assertWait (actionsEnabled && inState[s] && fsmRunning && !fsm.done);
        waitWire <= True;
      endrule

      rule finishStmt (actionsEnabled && inState[s] && fsmRunning && fsm.done);
        fsmRunning <= False;
      endrule
    end

  // Generate rules to modify logs
  if (params.useIterativeDeepening && params.useShrinking)
  begin

    for (Integer i = 0; i < length(logItems); i=i+1)
      begin
        let logEnq   = tpl_1(logItems[i]);
        let logRot   = tpl_2(logItems[i]);
        let logClear = tpl_3(logItems[i]);

        // Enqueue the current state into the log
        (* mutually_exclusive = "triggerEnq,triggerRot" *)
        rule triggerEnq (logEnqWire);
          logEnq;
        endrule

        // Remove the head of the log and append it to the end
        rule triggerRot (logRotWire);
          logRot;
        endrule

        // Clear the log
        rule triggerClear (logClearWire);
          logClear;
        endrule
      end

    // Also update the state and timer logs
    (* mutually_exclusive = "enqLogs,rotLogs" *)
    rule enqLogs (logEnqWire);
      stateLog.enq(state);
      timeLog.enq(timer);
    endrule

    rule rotLogs (logRotWire);
      stateLog.enq(stateLog.first); stateLog.deq;
      timeLog.enq(timeLog.first); timeLog.deq;
    endrule

    rule clearLogs (logClearWire);
      stateLog.clear;
      timeLog.clear;
    endrule
  end

  // No-op.
  rule noOp (actionsEnabled && state == 0);
    if (params.showNoOp)
      $display("%0t: No-op", timer);
  endrule

  // Single walk of state space ===============================================

  // One long walk of the state space.
  Stmt singleWalk =
    seq
      randomState.cntrl.init;
      await(randGensInitialised);
      testDone <= False;
      resetTimer <= True;
      preStmt;
      while (!testDone)
        action
          await(!waitWire);
          let nextState <- randomState.next;
          if (failureFound)
            begin
              count <= 0;
              testDone <= True;
            end
          else
            begin
              state <= nextState;
              if (state != 0 && !didntFire)
                begin
                  if (count < params.numIterations)
                    count <= count+1;
                  else
                    begin
                      count <= 0;
                      testDone <= True;
                    end
                end
            end
        endaction
      postStmt;
      if (failureFound)
        $display("FAILED.");
      else
        $display("OK: passed %0d iterations", params.numIterations);
    endseq;

  // Replaying and shrinking counter examples =================================

  Stmt shrink =
    seq
      // Initialise shrinker
      action
        omitNum <= 0;
        shrinkingMode <= True;
        for (Integer i=0; i < logSize; i=i+1) omitMask[i] <= False;
      endaction

      // Try to omit each element of the failing sequence, and if
      // it succeeds, undo the omission.
      while (omitNum <= counterExampleLength)
        seq
          $display("=== Shrink attempt %0d ===", omitNum);
          omitMask[omitNum] <= True;
          // Reset circuit under test
          params.id.rst.assertReset();
          action
            // Initialise replay
            resetFailure <= True;
            resetTimer <= True;
          endaction

          // Test sequence starts here
          delay(1);
          preStmt;
          while (count < counterExampleLength)
            action
              await(!waitWire);
              if (timer+1 >= timeLog.first)
                begin
                  if (omitMask[count] == False)
                    begin
                      state <= stateLog.first;
                      replayWire <= True;
                    end
                  else
                    state <= 0;
                  logRotWire <= True;
                  count <= count+1;
                end
              else
                state <= 0;
            endaction
          count <= 0;
          postStmt;
          if (!failureFound)
            omitMask[omitNum] <= False;
          omitNum <= omitNum+1;
      endseq
    endseq;

  // Iterative deepening ======================================================

  // State for iterative-deepening
  Reg#(Bit#(32)) currentDepth <- mkReg(0);
  Reg#(Bit#(32)) testNum <- mkReg(0);
  Reg#(Bit#(32)) iterCount <- mkReg(0);

  Stmt iterativeDeepening =
    seq
      // Initialise the random generators
      randomState.cntrl.init;
      await(randGensInitialised);

      // Each iteration will produce N test sequences of size 'depth'.
      // After each iteration, the depth is increased.
      currentDepth <= params.id.initialDepth;
      while (!failureFound && iterCount < params.numIterations)
        seq
          // Check that the depth is OK
          if (params.useShrinking && currentDepth >= fromInteger(logSize))
            seq
              $display("Maximum depth of %0d", logSize-1, " exceeded.");
              $display("Increase the 'logSize' parameter in BlueCheck.bsv.");
              $finish(0);
            endseq

          // Produce a test sequence of size 'currentDepth'
          testNum <= 0;
          while (!failureFound && testNum < params.id.testsPerDepth)
            seq
              // Reset the circuit under test
              params.id.rst.assertReset();

              // Initialise test
              action
                $display("=== Depth %0d, Test %0d ===", currentDepth, testNum);
                testDone <= False;
                counterExampleLength <= currentDepth;
                logClearWire <= True;
                resetTimer <= True;
              endaction

              // Test sequence starts here
              delay(1);
              preStmt;   // Execute user-defined pre-statement
              while (!testDone)
                action
                  // This action only fires when not waiting for a
                  // user-defined statement to finish.
                  await(!waitWire);
                  let nextState <- randomState.next;
                  if (failureFound)
                    begin
                      // We found a counter example smaller than the depth
                      counterExampleLength <= count;
                      count <= 0;
                      testDone <= True;
                    end
                  else
                    begin
                      // Change the state for the next clock cycle
                      state <= nextState;
                      if (state != 0 && !didntFire)
                        begin
                          // Is this the final element of the sequence?
                          if (count < currentDepth)
                            count <= count+1;
                          else
                            begin
                              // A count of '0' disables the checker
                              count <= 0;
                              testDone <= True;
                            end
                        end
                    end
                endaction
              postStmt; // Execute user-defined post-statement
              testNum <= testNum+1;
            endseq

          currentDepth <= params.id.incDepth(currentDepth);
          iterCount <= iterCount+1;
        endseq

      // We've reached the end of iterative deepening.  Either we
      // found a failure or performed the desired number of tests.
      if (!failureFound)
        $display("OK: passed %0d test sequences",
                   params.numIterations*params.id.testsPerDepth);
      else if (params.useShrinking)
        shrink;
    endseq;

  return params.useIterativeDeepening ?
           iterativeDeepening : singleWalk;
endmodule

// Default parameters for single state walk
BlueCheck_Params bcParamsSimple =
  BlueCheck_Params {
    showNonFire           : False
  , showNoOp              : False
  , useIterativeDeepening : False
  , useShrinking          : False
  , id                    : ?
  , numIterations         : 1000
  };

// Default parameters for iterative deepening
function BlueCheck_Params bcParamsID(MakeResetIfc rst);
  function incDepth(x) = x+1;

  ID_Params idParams =
    ID_Params {
      rst           : rst
    , initialDepth  : 10
    , testsPerDepth : 3000
    , incDepth      : incDepth
    };

  BlueCheck_Params params =
    BlueCheck_Params {
      showNonFire           : False
    , showNoOp              : False
    , useIterativeDeepening : True
    , useShrinking          : True
    , id                    : idParams
    , numIterations         : 5
    };

  return params;
endfunction

// Simple version returning a statement
module [Module] blueCheckStmt#(BlueCheck#(Empty) bc)(Stmt);
  Stmt s <- blueCheckCore(bc, bcParamsSimple);
  return s;
endmodule

// Simple version that constructs a checker
module [Module] blueCheck#(BlueCheck#(Empty) bc)();
  Stmt s <- blueCheckStmt(bc);
  mkAutoFSM(s);
endmodule

// Iterative deepening version returning a statement
module [Module] blueCheckStmtID# (BlueCheck#(Empty) bc
                                , MakeResetIfc rst ) (Stmt);
  Stmt s <- blueCheckCore(bc, bcParamsID(rst));
  return s;
endmodule

// Iterative deepening version that constructs a checker
module [Module] blueCheckID#( BlueCheck#(Empty) bc
                            , MakeResetIfc rst ) ();
  Stmt s <- blueCheckStmtID(bc, rst);
  mkAutoFSM(s);
endmodule

endpackage

// BlueCheck 0.2, Matt N.

// Change log
// ==========
//
// 5  Nov 2012: Version 0.1
// 21 Nov 2014: Support for iterative deepening
// 28 Nov 2014: Version 0.2, with support for shrinking

package BlueCheck;

import Randomizable  :: *;
import ModuleCollect :: *;
import StmtFSM       :: *;
import List          :: *;
import Clocks        :: *;
import FIFOF         :: *;
import ConfigReg     :: *;
import Vector        :: *;
import Assert        :: *;

// The max size of the log used for shrinking.  This is tricky to
// implement as a module parameter, hence it is a global parameter.
typedef 32 LogSize;
Integer logSize = valueOf(LogSize);

// BlueCheck module parameters
typedef struct {
  // Display message when a chosen state does not fire
  Bool showNonFire;

  // Display message when a chosen state is a no-op
  Bool showNoOp;

  // Generate a checker based on an iterative deepening strategy
  // (If 'Invalid', a single random state walk is performed)
  Bool useIterativeDeepening;
  // This must contain valid data if 'useIterativeDeepening' is 'True'
  ID_Params id;

  // Attempt to shrink a counter example, if one is found
  // (This is only valid for iterative deepening)
  Bool useShrinking;

  // Number of testing iterations to perform. For iterative deepening,
  // this is the number of times to increase the depth before stopping
  // Otherwise, the it's the length of the random state walk
  Bit#(32) numIterations;
} BlueCheck_Params;

// Sub-parameters for iterative deepening
typedef struct {
  // Iterative deepening requires ability to reset the circuit under test
  MakeResetIfc rst;

  // The number of states to be explored in a single 'test'
  Bit#(32) initialDepth;

  // The number of tests to perform at each depth
  Bit#(32) testsPerDepth;

  // A function to increase the depth
  (function Bit#(32) f(Bit#(32) currentDepth)) incDepth;
} ID_Params;

// The maximum number of states in the equivalance checker.
// You will get a compile-time error message if this parameter
// is not big enough, but it's not likely, unless you use very
// large method frequencies.
typedef 16 LogMaxStates;
typedef UInt#(LogMaxStates) State;

// The frequency that the checker will move to particular state.
typedef Integer Frequency; // Range: 1 to 100.

// A BlueCheck module implicitly collects actions, statements,
// invariants, random generators and loggers, an allowing automatic
// creation of an equivalance checker.
typedef ModuleCollect#(Item) BlueCheck;

typedef union tagged {
  Tuple3#(Frequency, Fmt, Action) ActionItem;
  Tuple3#(Frequency, Fmt, Stmt) StmtItem;
  Tuple2#(Bool, function Action gen(Bool replay)) RandomGenItem;
  Tuple2#(Fmt, Bool) InvariantItem;
  Bool EnsureItem;
  Tuple3#(Action, Action, Action) LogItem;
  Stmt PreStmtItem;
  Stmt PostStmtItem;
} Item;

// Turn an item into a singleton action if it is an ActionItem.
function List#(Tuple3#(Frequency, Fmt, Action)) getActionItem(Item item) =
  case (item) matches
    tagged ActionItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton action if it is an RandomGenItem.
function List#(Tuple2#(Bool, function Action gen(Bool replay)))
  getRandomGenItem(Item item) =
  case (item) matches
    tagged RandomGenItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton pair if it is an InvariantItem.
function List#(Tuple2#(Fmt, Bool)) getInvariantItem(Item item) =
  case (item) matches
    tagged InvariantItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is an StmtItem.
function List#(Tuple3#(Frequency, Fmt, Stmt)) getStmtItem(Item item) =
  case (item) matches
    tagged StmtItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is an EnsureItem.
function List#(Bool) getEnsureItem(Item item) =
  case (item) matches
    tagged EnsureItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is an LogItem.
function List#(Tuple3#(Action, Action, Action))
  getLogItem(Item item) =
  case (item) matches
    tagged LogItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is a PreStmtItem.
function List#(Stmt) getPreStmtItem(Item item) =
  case (item) matches
    tagged PreStmtItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Turn an item into a singleton statement if it is a PostStmtItem.
function List#(Stmt) getPostStmtItem(Item item) =
  case (item) matches
    tagged PostStmtItem .a: return Cons(a, Nil);
    default: return Nil;
  endcase;

// Display a function application, where function and
// arguments are available as Fmt items of a list.
function Fmt formatApp(List#(Fmt) app);
  if (List::tail(app) matches tagged Nil)
    return List::head(app);
  else
    return (List::head(app) + fshow("(") +
             formatArgs(List::tail(app)) + fshow(")"));
endfunction

function Fmt formatArgs(List#(Fmt) args);
  if (List::tail(args) matches tagged Nil)
    return List::head(args);
  else
    return (List::head(args) + fshow(",") + formatArgs(List::tail(args)));
endfunction

// The following type class allows two functions of the same type to
// be applied to random inputs.  If the return values differ, the
// behaviour is to terminate with an error message (a counter-example
// has been found).  The probability that the equivalence will be
// checked on any given step can be specified.
typeclass Equiv#(type a);
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, a f, a g) ();
endtypeclass

module [BlueCheck] equiv#(String name, a f, a g) ()
    provisos(Equiv#(a));
  eq(Cons(fshow(name), Nil), 1, f, g);
endmodule

module [BlueCheck] equivf#(Frequency fr, String name, a f, a g) ()
    provisos(Equiv#(a));
  eq(Cons(fshow(name), Nil), fr, f, g);
endmodule

// Base case 1: execute two actions.
instance Equiv#(Action);
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, Action a, Action b) ();
    Action executeTwo =
      action
        a; b;
      endaction;
    addToCollection(tagged ActionItem
      (tuple3(fr, formatApp(app), executeTwo)));
  endmodule
endinstance

// Base case 2: execute two action-values,
// and check equivalance of results.
instance Equiv#(ActionValue#(t))
  provisos(Eq#(t), Bits#(t, n), FShow#(t));
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr
                                       , ActionValue#(t) a
                                       , ActionValue#(t) b) ();
    Wire#(Bool) success <- mkDWire(True);
    Wire#(t) aWire      <- mkDWire(?);
    Wire#(t) bWire      <- mkDWire(?);
    Fmt msg             =  fshow("Not equal: ") + fshow(aWire)
                        +  fshow(" versus ")    + fshow(bWire);

    Action executeTwoAndCheck =
      action
        t aVal <- a; aWire <= aVal;
        t bVal <- b; bWire <= bVal;
        if (aVal != bVal) success <= False;
      endaction;
      addToCollection(tagged ActionItem
        (tuple3(fr, formatApp(app), executeTwoAndCheck)));
      addToCollection(tagged InvariantItem (tuple2(msg, success)));
  endmodule
endinstance

// Base case 3: execute two statements.
instance Equiv#(Stmt);
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, Stmt a, Stmt b) ();
    Stmt s = par a; b; endpar;
    addToCollection(tagged StmtItem (tuple3(fr, formatApp(app), s)));
  endmodule
endinstance

// Recursive case: generate a random input,
// apply it to each function, and
// recurse on the resulting applications.
instance Equiv#(function b f(a x))
  provisos(Equiv#(b), Bits#(a, n), Bounded#(a), FShow#(a));
    module [BlueCheck] eq#(List#(Fmt) app, Frequency fr
                                         , function b f(a x)
                                         , function b g(a y))();
      Reg#(Bool) init <- mkReg(True);
      Reg#(a) aReg <- mkRegU;
      Randomize#(a) aRandom <- mkGenericRandomizer;
      FIFOF#(a) aLog <- mkUGSizedFIFOF(logSize);

      function Action genRandom(Bool replay) =
        action
          let a <- aRandom.next;
          aReg <= replay ? aLog.first : a;
        endaction;

      rule initialise (init);
        aRandom.cntrl.init;
        init <= False;
      endrule

      Action logEnq   = aLog.enq(aReg);
      Action logRot   = action aLog.deq; aLog.enq(aLog.first); endaction;
      Action logClear = aLog.clear;

      addToCollection(tagged RandomGenItem (tuple2(!init, genRandom)));
      addToCollection(tagged LogItem (tuple3(logEnq, logRot, logClear)));

      eq(List::append(app, Cons(fshow(aReg), Nil)),
           fr, f(aReg), g(aReg));
    endmodule
endinstance

// Base case 4 (fall through): check that two values are equal.
instance Equiv#(a) provisos(Eq#(a), FShow#(a));
  module [BlueCheck] eq#(List#(Fmt) app, Frequency fr, a x, a y) ();
    Wire#(Bool) success <- mkDWire(True);
    Fmt fmt = formatApp(app) + fshow(" failed: ")
            + fshow(x) + fshow(" v ") + fshow(y);

    rule check;
      if (x != y) success <= False;
    endrule

    addToCollection(tagged InvariantItem (tuple2(fmt, success)));
  endmodule
endinstance

// Like the Equiv type-class, except for a single method.
typeclass Prop#(type a);
  module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, a f) ();
endtypeclass

// Base case 1: execute statement.
instance Prop#(Stmt);
  module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, Stmt a) ();
    addToCollection(tagged StmtItem (tuple3(fr, formatApp(app), a)));
  endmodule
endinstance

// Base case 2: execute action.
instance Prop#(Action);
  module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, Action a) ();
    addToCollection(tagged ActionItem (tuple3(fr, formatApp(app), a)));
  endmodule
endinstance

// Base case 3: execute action-value
instance Prop#(ActionValue#(Bool));
  module [BlueCheck] pr#(List#(Fmt) app,Frequency fr,ActionValue#(Bool) a) ();
    Wire#(Bool) success <- mkDWire(True);
    Fmt msg = fshow("Property failed");

    Action act =
      action
        Bool s <- a;
        if (!s) success <= False;
      endaction;
      addToCollection(tagged ActionItem (tuple3(fr, formatApp(app), act)));
      addToCollection(tagged InvariantItem (tuple2(msg, success)));
  endmodule
endinstance

// Recursive case.
instance Prop#(function b f(a x))
  provisos(Prop#(b), Bits#(a, n), Bounded#(a), FShow#(a));
    module [BlueCheck] pr#(List#(Fmt) app, Frequency fr, function b f(a x))();
      Reg#(Bool) init <- mkReg(True);
      Reg#(a) aReg <- mkRegU;
      Randomize#(a) aRandom <- mkGenericRandomizer;
      FIFOF#(a) aLog <- mkUGSizedFIFOF(logSize);

      function Action genRandom(Bool replay) =
        action
          let a <- aRandom.next;
          aReg <= replay ? aLog.first : a;
        endaction;

      rule initialise (init);
        aRandom.cntrl.init;
        init <= False;
      endrule

      Action logEnq   = aLog.enq(aReg);
      Action logRot   = action aLog.deq; aLog.enq(aLog.first); endaction;
      Action logClear = aLog.clear;

      addToCollection(tagged RandomGenItem (tuple2(!init, genRandom)));
      addToCollection(tagged LogItem (tuple3(logEnq, logRot, logClear)));

      pr(List::append(app, Cons(fshow(aReg), Nil)), fr, f(aReg));
    endmodule
endinstance

module [BlueCheck] prop#(String name, a f) ()
    provisos(Prop#(a));
  pr(Cons(fshow(name), Nil), 1, f);
endmodule

module [BlueCheck] propf#(Frequency fr, String name, a f) ()
    provisos(Prop#(a));
  pr(Cons(fshow(name), Nil), fr, f);
endmodule

// Ensure function -- for making assertions inside properties
typedef (function Action f(Bool cond)) Ensure;

module [BlueCheck] getEnsure (Ensure);
  // Create ensure function
  Wire#(Bool) ok <- mkDWire(True);
  function Action ensureFunc(Bool cond) = action ok <= cond; endaction;
  addToCollection(tagged EnsureItem ok);
  return ensureFunc;
endmodule

// Allow user to add custom pre/post statements for each test
module [BlueCheck] addPreStmt#(Stmt pre) (Empty);
  addToCollection(tagged PreStmtItem pre);
endmodule

module [BlueCheck] addPostStmt#(Stmt post) (Empty);
  addToCollection(tagged PostStmtItem post);
endmodule

// Compute the condition for being in each state of the equivalance
// checker. Some states are visited more frequently than others.
function List#(Bool) stateConds(Reg#(State) s, Integer start,
                                         List#(Frequency) freqs);
  if (freqs matches tagged Nil) return Nil;
  else
    begin
      Frequency f = List::head(freqs);
      return (Cons(s >= fromInteger(start) && s < fromInteger(start+f),
        stateConds(s, start+f, List::tail(freqs))));
    end
endfunction

// Sum a list
function Integer sum(List#(Integer) xs);
  if (xs matches tagged Nil) return 0;
  else return (List::head(xs) + sum(List::tail(xs)));
endfunction

// Sequence a list of statements
function Stmt seqList(List#(Stmt) xs);
  if (xs matches tagged Nil) return (seq delay(1); endseq);
  else return (seq List::head(xs); seqList(List::tail(xs)); endseq);
endfunction

// Turn the list of items gathered in a BlueCheck module into an
// actual equivalence checker.
module [Module] blueCheckCore#( BlueCheck#(Empty) bc
                              , BlueCheck_Params params ) (Stmt);

  // Extract items.
  let concat = List::concat;
  let map    = List::map;
  let append = List::append;
  let {_, items} <- getCollection(bc);
  let actionItems    = concat(map(getActionItem, items));
  let stmtItems      = concat(map(getStmtItem, items));
  let randomGens     = concat(map(getRandomGenItem, items));
  let logItems       = concat(map(getLogItem, items));
  let ensureItems    = concat(map(getEnsureItem, items));
  let invariantBools = concat(map(getInvariantItem, items));
  let preStmt        = seqList(concat(map(getPreStmtItem, items)));
  let postStmt       = seqList(concat(map(getPostStmtItem, items)));
  let actionMsgs     = map(tpl_2, actionItems);
  let stmtMsgs       = map(tpl_2, stmtItems);
  let actions        = map(tpl_3, actionItems);
  let stmts          = map(tpl_3, stmtItems);

  // Setup state machine for equivalence checking.
  // Note state 0 is a no-op state.
  List#(Integer) freqs = append(map(tpl_1, actionItems),
                                map(tpl_1, stmtItems));
  Integer numStates = 1+sum(freqs);
  Randomize#(State) randomState <-
    mkConstrainedRandomizer(0, fromInteger(numStates-1));
  ConfigReg#(State) state <- mkConfigReg(0);
  Wire#(Bool) waitWire <- mkDWire(False);
  Wire#(Bool) didntFire <- mkDWire(False);
  Reg#(Bool) testDone <- mkReg(False);
  List#(Bool) inState = stateConds(state, 1, freqs);
  Reg#(Bool) shrinkingMode <- mkReg(False);

  // When all random generators have initialised
  Reg#(Bool) randGensInitialised <- mkReg(False);

  // When count is 0, actions/statements are disabled
  ConfigReg#(Bit#(32)) count <- mkConfigReg(0);
  Bool actionsEnabled = count != 0;

  // When delayed count is 0, invariant checking is disabled
  ConfigReg#(Bit#(32)) delayedCount <- mkConfigReg(0);
  Bool checkingEnabled = delayedCount != 0;

  rule updateDelayedCount;
    delayedCount <= count;
  endrule

  // Log/replay signals
  FIFOF#(State) stateLog  <- mkSizedFIFOF(logSize);
  FIFOF#(Bit#(64)) timeLog <- mkSizedFIFOF(logSize);
  Wire#(Bool) replayWire <- mkDWire(False);
  Wire#(Bool) logEnqWire <- mkDWire(False);
  Wire#(Bool) logRotWire <- mkDWire(False);
  Wire#(Bool) logClearWire <- mkDWire(False);
  Reg#(Bit#(32)) counterExampleLength <- mkReg(0);
  Reg#(Bit#(32)) omitNum <- mkReg(0);
  Vector#(LogSize, Reg#(Bool)) omitMask <- replicateM(mkReg(False));

  // Track failures
  Reg#(Bool) failureReg    <- mkConfigReg(False);
  Wire#(Bool) resetFailure <- mkDWire(False);
  Bool ensureFailure    = List::any( \== (False), ensureItems);
  Bool invariantFailure = (waitWire || !checkingEnabled) ? False
                        : List::any( \== (False), map (tpl_2, invariantBools));
  Bool failureFound     = ensureFailure || invariantFailure || failureReg;

  rule trackFailure;
    if (resetFailure)
      failureReg <= False;
    else if (ensureFailure || invariantFailure)
      failureReg <= True;
  endrule

  // Local timer
  Reg#(Bit#(64)) timer <- mkReg(0);
  Wire#(Bool) resetTimer <- mkDWire(False);

  rule incTimer;
    if (resetTimer)
      timer <= 0;
    else
      timer <= timer+1;
  endrule

  // Generate rules to generate random data.
  for (Integer i = 0; i < length(randomGens); i=i+1)
    begin
      let go  = tpl_1(randomGens[i]);
      let gen = tpl_2(randomGens[i]);
      rule genRandomData (go && !waitWire);
        gen(replayWire);
      endrule
    end

  // Signal when all random generators have initialised
  rule initRandomGens;
    randGensInitialised <= List::all(tpl_1, randomGens);
  endrule

  // Rules to check 'ensure' assertions.
  rule checkEnsure (!failureReg && List::any( \== (False) , ensureItems));
    $display("%0t: 'ensure' statement failed", timer);
  endrule

  // Generate rules to check invariant booleans.
  for (Integer i = 0; i < length(invariantBools); i=i+1)
    begin
      let msg = tpl_1(invariantBools[i]);
      let b   = tpl_2(invariantBools[i]);
      rule checkInvariantBool (checkingEnabled && !failureReg && !waitWire);
        if (!b) $display("%0t: ", timer, msg);
      endrule
    end

  // Generate rules to run actions, guarded by the current state.
  for (Integer i = 0; i < length(actions); i=i+1)
    begin
      (* preempts = "runAction, runActionNotPossible" *)
      rule runAction (actionsEnabled && inState[i] && !waitWire);
        $display("%0t: ", timer, actionMsgs[i]);
        actions[i];
        if (!shrinkingMode)
          logEnqWire <= True;
      endrule
      rule runActionNotPossible (actionsEnabled && inState[i] && !waitWire);
        didntFire <= True;
        if (params.showNonFire)
          $display("%0t: [did not fire] ", timer, actionMsgs[i]);
      endrule
    end

  // Generate rules to run statements, guarded by the current state.
  // Statements may take many cycles, hence waitWire.
  for (Integer i = 0; i < length(stmts); i=i+1)
    begin
      Integer s = length(actions)+i;
      Reg#(Bool) fsmRunning <- mkReg(False);
      FSM fsm <- mkFSMWithPred(stmts[i], actionsEnabled && inState[s]);

      rule runStmt (actionsEnabled && inState[s] && !fsmRunning);
        $display("%0t: ", timer, stmtMsgs[i]);
        fsm.start;
        fsmRunning <= True;
        waitWire <= True;
        if (!shrinkingMode)
          logEnqWire <= True;
      endrule

      rule assertWait (actionsEnabled && inState[s] && fsmRunning && !fsm.done);
        waitWire <= True;
      endrule

      rule finishStmt (actionsEnabled && inState[s] && fsmRunning && fsm.done);
        fsmRunning <= False;
      endrule
    end

  // Generate rules to modify logs
  if (params.useIterativeDeepening && params.useShrinking)
  begin

    for (Integer i = 0; i < length(logItems); i=i+1)
      begin
        let logEnq   = tpl_1(logItems[i]);
        let logRot   = tpl_2(logItems[i]);
        let logClear = tpl_3(logItems[i]);

        // Enqueue the current state into the log
        (* mutually_exclusive = "triggerEnq,triggerRot" *)
        rule triggerEnq (logEnqWire);
          logEnq;
        endrule

        // Remove the head of the log and append it to the end
        rule triggerRot (logRotWire);
          logRot;
        endrule

        // Clear the log
        rule triggerClear (logClearWire);
          logClear;
        endrule
      end

    // Also update the state and timer logs
    (* mutually_exclusive = "enqLogs,rotLogs" *)
    rule enqLogs (logEnqWire);
      stateLog.enq(state);
      timeLog.enq(timer);
    endrule

    rule rotLogs (logRotWire);
      stateLog.enq(stateLog.first); stateLog.deq;
      timeLog.enq(timeLog.first); timeLog.deq;
    endrule

    rule clearLogs (logClearWire);
      stateLog.clear;
      timeLog.clear;
    endrule
  end

  // No-op.
  rule noOp (actionsEnabled && state == 0);
    if (params.showNoOp)
      $display("%0t: No-op", timer);
  endrule

  // Single walk of state space ===============================================

  // One long walk of the state space.
  Stmt singleWalk =
    seq
      randomState.cntrl.init;
      await(randGensInitialised);
      testDone <= False;
      resetTimer <= True;
      preStmt;
      while (!testDone)
        action
          await(!waitWire);
          let nextState <- randomState.next;
          if (failureFound)
            begin
              count <= 0;
              testDone <= True;
            end
          else
            begin
              state <= nextState;
              if (state != 0 && !didntFire)
                begin
                  if (count < params.numIterations)
                    count <= count+1;
                  else
                    begin
                      count <= 0;
                      testDone <= True;
                    end
                end
            end
        endaction
      postStmt;
      if (failureFound)
        $display("FAILED.");
      else
        $display("OK: passed %0d iterations", params.numIterations);
    endseq;

  // Replaying and shrinking counter examples =================================

  Stmt shrink =
    seq
      // Initialise shrinker
      action
        omitNum <= 0;
        shrinkingMode <= True;
        for (Integer i=0; i < logSize; i=i+1) omitMask[i] <= False;
      endaction

      // Try to omit each element of the failing sequence, and if
      // it succeeds, undo the omission.
      while (omitNum <= counterExampleLength)
        seq
          $display("=== Shrink attempt %0d ===", omitNum);
          omitMask[omitNum] <= True;
          // Reset circuit under test
          params.id.rst.assertReset();
          // Initialise replay
          resetFailure <= True;
          resetTimer <= True;
          preStmt;
          while (count < counterExampleLength)
            action
              await(!waitWire);
              if (timer+1 >= timeLog.first)
                begin
                  if (omitMask[count] == False)
                    begin
                      state <= stateLog.first;
                      replayWire <= True;
                    end
                  else
                    state <= 0;
                  logRotWire <= True;
                  count <= count+1;
                end
              else
                state <= 0;
            endaction
          count <= 0;
          postStmt;
          if (!failureFound)
            omitMask[omitNum] <= False;
          omitNum <= omitNum+1;
      endseq
    endseq;

  // Iterative deepening ======================================================

  // State for iterative-deepening
  Reg#(Bit#(32)) currentDepth <- mkReg(0);
  Reg#(Bit#(32)) testNum <- mkReg(0);
  Reg#(Bit#(32)) iterCount <- mkReg(0);

  Stmt iterativeDeepening =
    seq
      // Initialise the random generators
      randomState.cntrl.init;
      await(randGensInitialised);

      // Each iteration will produce N test sequences of size 'depth'.
      // After each iteration, the depth is increased.
      currentDepth <= params.id.initialDepth;
      while (!failureFound && iterCount < params.numIterations)
        seq
          // Produce a test sequence of size 'currentDepth'
          testNum <= 0;
          while (!failureFound && testNum < params.id.testsPerDepth)
            seq
              // Initialise test
              action
                $display("=== Depth %0d, Test %0d ===", currentDepth, testNum);
                testDone <= False;
                counterExampleLength <= currentDepth;
                logClearWire <= True;
                resetTimer <= True;
              endaction

              // Test sequence starts here
              delay(1);  // To support replay/shrinking
              preStmt;   // Execute user-defined pre-statement
              while (!testDone)
                action
                  // This action only fires when not waiting for a
                  // user-defined statement to finish.
                  await(!waitWire);
                  let nextState <- randomState.next;
                  if (failureFound)
                    begin
                      // We found a counter example smaller than the depth
                      counterExampleLength <= count;
                      count <= 0;
                      testDone <= True;
                    end
                  else
                    begin
                      // Change the state for the next clock cycle
                      state <= nextState;
                      if (state != 0 && !didntFire)
                        begin
                          // Is this the final element of the sequence?
                          if (count < currentDepth)
                            count <= count+1;
                          else
                            begin
                              // A count of '0' disables the checker
                              count <= 0;
                              testDone <= True;
                            end
                        end
                    end
                endaction
              postStmt; // Execute user-defined post-statement
              // If a failure has still not been observed then reset
              // circuit in preparation for a new test sequence.
              action
                if (!failureFound)
                  begin
                    testNum <= testNum+1;
                    params.id.rst.assertReset();
                  end
              endaction
            endseq

          currentDepth <= params.id.incDepth(currentDepth);
          iterCount <= iterCount+1;
        endseq

      // We've reached the end of iterative deepening.  Either we
      // found a failure or performed the desired number of tests.
      if (!failureFound)
        $display("OK: passed %0d test sequences",
                   params.numIterations*params.id.testsPerDepth);
      else if (params.useShrinking)
        shrink;
    endseq;

  return params.useIterativeDeepening ?
           iterativeDeepening : singleWalk;
endmodule

// Default parameters for single state walk
BlueCheck_Params bcParamsSimple =
  BlueCheck_Params {
    showNonFire           : False
  , showNoOp              : False
  , useIterativeDeepening : False
  , useShrinking          : False
  , id                    : ?
  , numIterations         : 1000
  };

// Default parameters for iterative deepening
function BlueCheck_Params bcParamsID(MakeResetIfc rst);
  function incDepth(x) = x+1;

  ID_Params idParams =
    ID_Params {
      rst           : rst
    , initialDepth  : 10
    , testsPerDepth : 3000
    , incDepth      : incDepth
    };

  BlueCheck_Params params =
    BlueCheck_Params {
      showNonFire           : False
    , showNoOp              : False
    , useIterativeDeepening : True
    , useShrinking          : True
    , id                    : idParams
    , numIterations         : 5
    };

  return params;
endfunction

// Simple version returning a statement
module [Module] blueCheckStmt#(BlueCheck#(Empty) bc)(Stmt);
  Stmt s <- blueCheckCore(bc, bcParamsSimple);
  return s;
endmodule

// Simple version that constructs a checker
module [Module] blueCheck#(BlueCheck#(Empty) bc)();
  Stmt s <- blueCheckStmt(bc);
  mkAutoFSM(s);
endmodule

// Iterative deepening version returning a statement
module [Module] blueCheckStmtID# (BlueCheck#(Empty) bc
                                , MakeResetIfc rst ) (Stmt);
  Stmt s <- blueCheckCore(bc, bcParamsID(rst));
  return s;
endmodule

// Iterative deepening version that constructs a checker
module [Module] blueCheckID#( BlueCheck#(Empty) bc
                            , MakeResetIfc rst ) ();
  Stmt s <- blueCheckStmtID(bc, rst);
  mkAutoFSM(s);
endmodule

endpackage


limit number of read requests outstanding

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import BuildVector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import ClientServer::*;
import GetPut::*;
import HostInterface::*;
import MemTypes::*;
import Pipe::*;
import ConfigCounter::*;

typedef TDiv#(DataBusWidth,32) WordsPerBeat;

interface MemcopyRequest;
   method Action startCopy(Bit#(32) readPointer, Bit#(32) writePointer, Bit#(32) numWords, Bit#(32) numReqs, Bit#(32) burstLen);
endinterface

interface MemcopyIndication;
   method Action copyDone(Bit#(32) v);
   method Action copyProgress(Bit#(32) v);
endinterface

interface Memcopy;
   interface MemcopyRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClients;
endinterface

module  mkMemcopy#(MemcopyIndication indication) (Memcopy);
   Reg#(SGLId)   readPointer <- mkReg(0);
   Reg#(SGLId)   writePointer <- mkReg(0);
   Reg#(Bit#(32))        numReqs <- mkReg(0);
   Reg#(Bit#(32))        numDone <- mkReg(0);
   Reg#(Bit#(MemOffsetSize)) reqOffset <- mkReg(0);
   Reg#(Bit#(3))                   tag <- mkReg(0);
   Reg#(Bit#(32))             numWords <- mkReg(0);
   Reg#(Bit#(BurstLenSize)) burstLenBytes <- mkReg(0);
   Reg#(Bit#(32))              srcGens <- mkReg(0);

   ConfigCounter#(16) counter          <- mkConfigCounter(0);
   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFO#(MemRequest) writeReqFifo <- mkFIFO();
   FIFO#(MemData#(DataBusWidth))   dataFifo <- mkSizedBRAMFIFO(1024);
   FIFO#(Bit#(MemTagSize)) doneFifo <- mkFIFO();

   let verboseProgress = False;

   rule startReqRule if (numReqs != 0 && counter.read() <= unpack(extend(burstLenBytes) << 3));
      counter.increment(unpack(extend(burstLenBytes)));
      readReqFifo.enq(MemRequest { sglId: readPointer, offset: reqOffset, burstLen: burstLenBytes, tag: extend(tag) });
      writeReqFifo.enq(MemRequest { sglId: writePointer, offset: reqOffset, burstLen: burstLenBytes, tag: extend(tag) });

      numReqs <= numReqs - 1;
      reqOffset <= reqOffset + extend(burstLenBytes);
      tag <= tag + 1;
      $display("start numReqs %d offset %d", numReqs, reqOffset);
   endrule

   rule finish;
      let donetag <- toGet(doneFifo).get();
      $display("finished num todo=%d", numDone);
      if (numDone == 1) begin
         indication.copyDone(0);
      end
      numDone <= numDone - 1;
      if (verboseProgress)
	 indication.copyProgress(extend(donetag));
   endrule

   MemReadClient#(DataBusWidth) readClient = (interface MemReadClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData;
	 method Action put(MemData#(DataBusWidth) md);
	    dataFifo.enq(md);
	    counter.decrement(fromInteger(valueOf(TDiv#(DataBusWidth,8))));
	 endmethod
      endinterface
   endinterface );
   MemWriteClient#(DataBusWidth) writeClient = (interface MemWriteClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(dataFifo);
      interface Put writeDone = toPut(doneFifo);
   endinterface );

   interface MemcopyRequest request;
       method Action startCopy(Bit#(32) rp, Bit#(32) wp, Bit#(32) nw, Bit#(32) nreq, Bit#(32) bl);
	  $dumpvars();
          $display("startCopy readPointer=%d writePointer=%d numWords=%d (%d) numReqs=%d burstLen=%d", rp, wp, nw, nreq*bl, nreq, bl);
          readPointer <= rp;
          writePointer <= wp;
          numWords  <= nw;
          burstLenBytes <= truncate(bl);
	  numReqs <= nreq;
	  numDone <= nreq;
       endmethod
   endinterface
   interface readClients = vec(readClient);
   interface writeClients = vec(writeClient);

endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import BuildVector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import ClientServer::*;
import GetPut::*;
import HostInterface::*;
import MemTypes::*;
import Pipe::*;

typedef TDiv#(DataBusWidth,32) WordsPerBeat;

interface MemcopyRequest;
   method Action startCopy(Bit#(32) readPointer, Bit#(32) writePointer, Bit#(32) numWords, Bit#(32) numReqs, Bit#(32) burstLen);
endinterface

interface MemcopyIndication;
   method Action copyDone(Bit#(32) v);
   method Action copyProgress(Bit#(32) v);
endinterface

interface Memcopy;
   interface MemcopyRequest request;
   interface Vector#(1, MemReadClient#(64)) readClients;
   interface Vector#(1, MemWriteClient#(64)) writeClients;
endinterface

module  mkMemcopy#(MemcopyIndication indication) (Memcopy);
   Reg#(SGLId)   readPointer <- mkReg(0);
   Reg#(SGLId)   writePointer <- mkReg(0);
   Reg#(Bit#(32))        numReqs <- mkReg(0);
   Reg#(Bit#(32))        numDone <- mkReg(0);
   Reg#(Bit#(MemOffsetSize)) reqOffset <- mkReg(0);
   Reg#(Bit#(3))                   tag <- mkReg(0);
   Reg#(Bit#(32))             numWords <- mkReg(0);
   Reg#(Bit#(BurstLenSize)) burstLenBytes <- mkReg(0);
   Reg#(Bit#(32))              srcGens <- mkReg(0);

   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFO#(MemRequest) writeReqFifo <- mkFIFO();
   FIFO#(MemData#(DataBusWidth))   dataFifo <- mkSizedBRAMFIFO(1024);
   FIFO#(Bit#(MemTagSize)) doneFifo <- mkFIFO();

   let verboseProgress = False;

   rule startReqRule if (numReqs != 0);
      readReqFifo.enq(MemRequest { sglId: readPointer, offset: reqOffset, burstLen: burstLenBytes, tag: extend(tag) });
      writeReqFifo.enq(MemRequest { sglId: writePointer, offset: reqOffset, burstLen: burstLenBytes, tag: extend(tag) });

      numReqs <= numReqs - 1;
      reqOffset <= reqOffset + extend(burstLenBytes);
      tag <= tag + 1;
      //$display("start numReqs", numReqs);
   endrule

   rule finish;
      let donetag <- toGet(doneFifo).get();
      //$display("finished num todo=%d", numDone);
      if (numDone == 1) begin
         indication.copyDone(0);
      end
      numDone <= numDone - 1;
      if (verboseProgress)
	 indication.copyProgress(extend(donetag));
   endrule

   MemReadClient#(64) readClient = (interface MemReadClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData = toPut(dataFifo);
   endinterface );
   MemWriteClient#(64) writeClient = (interface MemWriteClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(dataFifo);
      interface Put writeDone = toPut(doneFifo);
   endinterface );

   interface MemcopyRequest request;
       method Action startCopy(Bit#(32) rp, Bit#(32) wp, Bit#(32) nw, Bit#(32) nreq, Bit#(32) bl);
	  $dumpvars();
          $display("startCopy readPointer=%d writePointer=%d numWords=%d (%d) numReqs=%d burstLen=%d", rp, wp, nw, nreq*bl, nreq, bl);
          readPointer <= rp;
          writePointer <= wp;
          numWords  <= nw;
          burstLenBytes <= truncate(bl);
	  numReqs <= nreq;
	  numDone <= nreq;
       endmethod
   endinterface
   interface readClients = vec(readClient);
   interface writeClients = vec(writeClient);

endmodule


updated hdmidisplay example

updated hdmidisplay example

// Copyright (c) 2014 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Clocks :: *;
import Vector            :: *;
import Connectable       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import AxiMasterSlave    :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import FIFOF::*;
import HDMI::*;
import ConnectableWithTrace::*;
import CtrlMux::*;
import AxiDma            :: *;

//`define TRACE_AXI
//`define AXI_READ_TIMING

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
   (* prefix="GPIO" *)
   interface LEDS             leds;
   (* prefix="XADC" *)
   interface XADC             xadc;
   (* prefix="hdmi" *)
   interface pins             pins;
   interface Vector#(4, Clock) unused_clock;
   interface Vector#(4, Reset) unused_reset;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins)) mkpt(Clock clk1)) MkPortalTop#(type ipins);

module [Module] mkZynqTopFromPortal#(MkPortalTop#(ipins) constructor)(ZynqTop#(ipins));
   PS7 ps7 <- mkPS7();
   Clock mainclock = ps7.fclkclk[0];
   Reset mainreset = ps7.fclkreset[0];

   let top <- constructor(ps7.fclkclk[1], clocked_by mainclock, reset_by mainreset);

   mkConnectionWithTrace(ps7.m_axi_gp[0].client, top.ctrl, clocked_by mainclock, reset_by mainreset);
   Axi3Master#(32,64,6) m_axi <- mkAxiDmaServer(top.read_client, top.write_client, clocked_by mainclock, reset_by mainreset);
   mkConnection(m_axi, ps7.s_axi_hp[0].axi.server, clocked_by mainclock, reset_by mainreset);

   let intr_mux <- mkInterruptMux(top.interrupt);
   rule send_int_rule;
      ps7.interrupt(pack(intr_mux));
   endrule

   interface zynq = ps7.pins;
   interface leds = top.leds;
   interface XADC xadc;
       method Bit#(4) gpio;
           return 0;
       endmethod
   endinterface
   interface pins = top.pins;

   // these are exported to make bsc happy, and then the ports are disconnected after synthesis
   interface unused_clock = ps7.fclkclk;
   interface unused_reset = ps7.fclkreset;
endmodule

module mkHdmiZynqTop(ZynqTop#(HDMI));
   let top <- mkZynqTopFromPortal(mkPortalTop);
   return top;
endmodule

// Copyright (c) 2014 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Clocks :: *;
import Vector            :: *;
import Connectable       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import AxiMasterSlave    :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import FIFOF::*;
import HDMI::*;
import ConnectableWithTrace::*;
import CtrlMux::*;

//`define TRACE_AXI
//`define AXI_READ_TIMING

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
   (* prefix="GPIO" *)
   interface LEDS             leds;
   (* prefix="XADC" *)
   interface XADC             xadc;
   (* prefix="hdmi" *)
   interface pins             pins;
   interface Vector#(4, Clock) unused_clock;
   interface Vector#(4, Reset) unused_reset;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins)) mkpt(Clock clk1)) MkPortalTop#(type ipins);

module [Module] mkZynqTopFromPortal#(MkPortalTop#(ipins) constructor)(ZynqTop#(ipins));
   PS7 ps7 <- mkPS7();
   Clock mainclock = ps7.fclkclk[0];
   Reset mainreset = ps7.fclkreset[0];

   let top <- constructor(ps7.fclkclk[1], clocked_by mainclock, reset_by mainreset);

   mkConnectionWithTrace(ps7.m_axi_gp[0].client, top.ctrl);
   mkConnection(top.m_axi, ps7.s_axi_hp[0].axi.server);

   let intr_mux <- mkInterruptMux(top.interrupt);
   rule send_int_rule;
      ps7.interrupt(pack(intr_mux));
   endrule

   interface zynq = ps7.pins;
   interface leds = top.leds;
   interface XADC xadc;
       method Bit#(4) gpio;
           return 0;
       endmethod
   endinterface
   interface pins = top.pins;

   // these are exported to make bsc happy, and then the ports are disconnected after synthesis
   interface unused_clock = ps7.fclkclk;
   interface unused_reset = ps7.fclkreset;
endmodule

module mkHdmiZynqTop(ZynqTop#(HDMI));
   let top <- mkZynqTopFromPortal(mkPortalTop);
   return top;
endmodule


add a register to the inverted reset to shorten critical path in DSP48E1

import Clocks::*;

interface Dsp48E1;
   method Bit#(48) p();
// Control: 4-bit (each) input: Control Inputs/Status Bits
   method Action alumode(Bit#(4) v);		// 4-bit input: ALU control input
   method Action carryinsel(Bit#(3) v);		// 3-bit input: Carry select input
   method Action inmode(Bit#(5) v);		// 5-bit input: INMODE control input
   method Action opmode(Bit#(7) v);		// 7-bit input: Operation mode input
   method Action a(Bit#(30) v);
   method Action b(Bit#(18) v);
   method Action c(Bit#(48) v);
   method Action d(Bit#(25) v);
   method Action last(Bit#(1) v);
endinterface

(* always_ready, always_enabled *)
interface PRIM_DSP48E1;
   method Bit#(48) p();
// Control: 4-bit (each) input: Control Inputs/Status Bits
   method Action alumode(Bit#(4) v);		// 4-bit input: ALU control input
   method Action carryinsel(Bit#(3) v);		// 3-bit input: Carry select input
   method Action inmode(Bit#(5) v);		// 5-bit input: INMODE control input
   method Action opmode(Bit#(7) v);		// 7-bit input: Operation mode input
   method Action a(Bit#(30) v);
   method Action b(Bit#(18) v);
   method Action c(Bit#(48) v);
   method Action d(Bit#(25) v);
   method Action acin(Bit#(30) v);
   method Action bcin(Bit#(18) v);
   method Action carrycascin(Bit#(1) v);
   method Action carryin(Bit#(1) v);
   method Action multsignin(Bit#(1) v);
   method Action pcin(Bit#(48) v);

   method Action cea1(Bit#(1) en);		// 1-bit input: Clock enable input for 1st stage AREG
   method Action cea2(Bit#(1) en);		// 1-bit input: Clock enable input for 2nd stage AREG
   method Action cead(Bit#(1) en);		// 1-bit input: Clock enable input for ADREG
   method Action ceb1(Bit#(1) en);		// 1-bit input: Clock enable input for 1st stage BREG
   method Action ceb2(Bit#(1) en);		// 1-bit input: Clock enable input for 2nd stage BREG
   method Action cealumode(Bit#(1) en);		// 1-bit input: Clock enable input for ALUMODE
   method Action cec(Bit#(1) en);		// 1-bit input: Clock enable input for CREG
   method Action cecarryin(Bit#(1) en);		// 1-bit input: Clock enable input for CARRYINREG
   method Action cectrl(Bit#(1) en);		// 1-bit input: Clock enable input for OPMODEREG and CARRYINSELREG
   method Action ced(Bit#(1) en);		// 1-bit input: Clock enable input for DREG
   method Action ceinmode(Bit#(1) en);		// 1-bit input: Clock enable input for INMODEREG
   method Action cem(Bit#(1) en);		// 1-bit input: Clock enable input for MREG
   method Action cep(Bit#(1) en);		// 1-bit input: Clock enable input for PREG
endinterface

import "BVI" DSP48E1 =
module vmkDSP48E1(PRIM_DSP48E1);
   let currentClock <- exposeCurrentClock;
   let currentReset <- exposeCurrentReset;
   let invertedReset0 <- mkResetInverter(currentReset);
   let invertedReset <- mkAsyncReset(1, invertedReset0, currentClock);
   default_clock clk(CLK);

   default_reset rsta(RSTA) = invertedReset;
   input_reset rstb(RSTB) = invertedReset;
   input_reset rstc(RSTC) = invertedReset;
   input_reset rstd(RSTD) = invertedReset;
   input_reset rstallcarryin(RSTALLCARRYIN) = invertedReset;
   input_reset rstalumode(RSTALUMODE) = invertedReset;
   input_reset rstctrl(RSTCTRL) = invertedReset;
   input_reset rstinmode(RSTINMODE) = invertedReset;
   input_reset rstm(RSTM) = invertedReset;
   input_reset rstp(RSTP) = invertedReset;

   method P p();
   method alumode(ALUMODE) enable ((*inhigh*)EN_alumode);
   method carryinsel(CARRYINSEL) enable ((*inhigh*)EN_carryinsel);
   method inmode(INMODE) enable ((*inhigh*)EN_inmode);
   method opmode(OPMODE) enable ((*inhigh*)EN_opmode);
   method a(A) enable ((*inhigh*)EN_a);
   method b(B) enable ((*inhigh*)EN_b);
   method c(C) enable ((*inhigh*)EN_c);
   method d(D) enable ((*inhigh*)EN_d);
   method acin(ACIN) enable ((*inhigh*)EN_acin);
   method bcin(BCIN) enable ((*inhigh*)EN_bcin);
   method carrycascin(CARRYCASCIN) enable ((*inhigh*)EN_carrycascin);
   method carryin(CARRYIN) enable ((*inhigh*)EN_carryin);
   method multsignin(MULTSIGNIN) enable ((*inhigh*)EN_multsignin);
   method pcin(PCIN) enable ((*inhigh*)EN_pcin);

   method cea1(CEA1) enable ((*inhigh*)EN_cea1);
   method cea2(CEA2) enable ((*inhigh*)EN_cea2);
   method cead(CEAD) enable ((*inhigh*)EN_cead);
   method ceb1(CEB1) enable ((*inhigh*)EN_ceb1);
   method ceb2(CEB2) enable ((*inhigh*)EN_ceb2);
   method cealumode(CEALUMODE) enable ((*inhigh*)EN_cealumode);
   method cec(CEC) enable ((*inhigh*)EN_cec);
   method cecarryin(CECARRYIN) enable ((*inhigh*)EN_cecarryin);
   method cectrl(CECTRL) enable ((*inhigh*)EN_cectrl);
   method ced(CED) enable ((*inhigh*)EN_ced);
   method ceinmode(CEINMODE) enable ((*inhigh*)EN_ceinmode);
   method cem(CEM) enable ((*inhigh*)EN_cem);
   method cep(CEP) enable ((*inhigh*)EN_cep);
   schedule (alumode,carryinsel,inmode,opmode,a,b,c,d,acin,bcin,carrycascin,carryin,multsignin,pcin,cea1,cea2,cead,ceb1,ceb2,cealumode,cec,cecarryin,cectrl,ced,ceinmode,cem,cep,p)
      CF (alumode,carryinsel,inmode,opmode,a,b,c,d,acin,bcin,carrycascin,carryin,multsignin,pcin,cea1,cea2,cead,ceb1,ceb2,cealumode,cec,cecarryin,cectrl,ced,ceinmode,cem,cep,p);
endmodule

(* synthesize *)
module mkDsp48E1(Dsp48E1);
   let dsp <- vmkDSP48E1();
   let defaultReset <- exposeCurrentReset;
   let optionalReset = defaultReset; // noReset
   Wire#(Bit#(4)) alumodeWire <- mkDWire(0);
   Reg#(Bit#(4)) alumodeReg <- mkReg(0);
   Wire#(Bit#(3)) carryinselWire <- mkDWire(0);
   Wire#(Bit#(5)) inmodeWire <- mkDWire(0);
   Wire#(Bit#(7)) opmodeWire <- mkDWire(7'h20);
   Reg#(Bit#(7)) opmode1Reg <- mkReg(0);
   Reg#(Bit#(7)) opmode2Reg <- mkReg(0);
   Reg#(Bit#(7)) opmode3Reg <- mkReg(0);
   Reg#(Bit#(7)) opmode4Reg <- mkReg(0);

   Wire#(Bit#(30)) aWire <- mkDWire(0);
   Wire#(Bit#(18)) bWire <- mkDWire(0);
   Wire#(Bit#(48)) cWire <- mkDWire(0);
   Wire#(Bit#(25)) dWire <- mkDWire(0);
   Reg#(Bit#(48))  c1Reg <- mkReg(0);
   Reg#(Bit#(48))  c2Reg <- mkReg(0);
   Reg#(Bit#(48))  c3Reg <- mkReg(0);

   Wire#(Bit#(1)) ce1Wire <- mkDWire(0);
   Reg#(Bit#(1)) ce2Reg <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1)) cepReg <- mkReg(0, reset_by optionalReset);
   Wire#(Bit#(1)) lastWire <- mkDWire(0);
   Reg#(Bit#(1))  last1Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last2Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last3Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last4Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last5Reg  <- mkReg(0, reset_by optionalReset);

   Reg#(Bit#(32)) cycles <- mkReg(0);
   rule cyclesRule;
      cycles <= cycles+1;
   endrule

   rule clock_enable_and_reset;
      ce2Reg <= ce1Wire;
      cepReg <= ce2Reg;

      last1Reg <= lastWire;
      last2Reg <= last1Reg;
      last3Reg <= last2Reg;
      last4Reg <= last3Reg;
      last5Reg <= last4Reg;

      dsp.cea1(1); // (ce1Wire);
      dsp.cea2(1); // (ce2Reg);
      dsp.cead(1);
      dsp.ceb1(1); // (ce1Wire);
      dsp.ceb2(1); // (ce2Reg);
      dsp.cealumode(1); // (ce1Wire);
      dsp.cec(1);
      dsp.cecarryin(1);
      dsp.cectrl(1);
      dsp.ced(1);
      dsp.ceinmode(1);
      dsp.cem(1);
      dsp.cep(1); // (cepReg);

   endrule

   rule driveInputs;
      opmode1Reg <= opmodeWire;
      opmode2Reg <= opmode1Reg;
      opmode3Reg <= opmode2Reg;
      opmode4Reg <= opmode3Reg;

      alumodeReg <= alumodeWire;

      c1Reg <= cWire;
      c2Reg <= c1Reg;
      c3Reg <= c2Reg;

      //if (lastWire == 1 || last1Reg == 1 || last2Reg == 1 || last3Reg == 1 || last4Reg == 1 || last5Reg == 1)
	 $display("%d: a=%h b=%h c=%h p=%h", cycles, aWire, bWire, c3Reg, dsp.p());

      dsp.alumode(alumodeReg);
      dsp.carryinsel(carryinselWire);
      dsp.inmode(inmodeWire);
      dsp.opmode(opmode1Reg);
      dsp.a(aWire);
      dsp.b(bWire);
      dsp.c(c1Reg);
      //dsp.d(dWire);

      dsp.acin(0);
      dsp.bcin(0);
      dsp.carrycascin(0);
      dsp.carryin(0);
      dsp.pcin(0);
   endrule

   method Action alumode(Bit#(4) v);
      alumodeWire <= v;
   endmethod
   method Action carryinsel(Bit#(3) v);
      carryinselWire <= v;
   endmethod
   method Action inmode(Bit#(5) v);
      inmodeWire <= v;
   endmethod
   method Action opmode(Bit#(7) v);
      opmodeWire <= v;
   endmethod
   method Action a(Bit#(30) v);
      ce1Wire <= 1;
      aWire <= v;
   endmethod
   method Action b(Bit#(18) v);
      bWire <= v;
   endmethod
   method Action c(Bit#(48) v);
      cWire <= v;
   endmethod
   method Action d(Bit#(25) v);
      dsp.d(v);
   endmethod
   method Bit#(48) p() if (last5Reg == 1);
      return dsp.p();
   endmethod
   method Action last(Bit#(1) v);
      lastWire <= v;
   endmethod
endmodule

import Clocks::*;

interface Dsp48E1;
   method Bit#(48) p();
// Control: 4-bit (each) input: Control Inputs/Status Bits
   method Action alumode(Bit#(4) v);		// 4-bit input: ALU control input
   method Action carryinsel(Bit#(3) v);		// 3-bit input: Carry select input
   method Action inmode(Bit#(5) v);		// 5-bit input: INMODE control input
   method Action opmode(Bit#(7) v);		// 7-bit input: Operation mode input
   method Action a(Bit#(30) v);
   method Action b(Bit#(18) v);
   method Action c(Bit#(48) v);
   method Action d(Bit#(25) v);
   method Action last(Bit#(1) v);
endinterface

(* always_ready, always_enabled *)
interface PRIM_DSP48E1;
   method Bit#(48) p();
// Control: 4-bit (each) input: Control Inputs/Status Bits
   method Action alumode(Bit#(4) v);		// 4-bit input: ALU control input
   method Action carryinsel(Bit#(3) v);		// 3-bit input: Carry select input
   method Action inmode(Bit#(5) v);		// 5-bit input: INMODE control input
   method Action opmode(Bit#(7) v);		// 7-bit input: Operation mode input
   method Action a(Bit#(30) v);
   method Action b(Bit#(18) v);
   method Action c(Bit#(48) v);
   method Action d(Bit#(25) v);
   method Action acin(Bit#(30) v);
   method Action bcin(Bit#(18) v);
   method Action carrycascin(Bit#(1) v);
   method Action carryin(Bit#(1) v);
   method Action multsignin(Bit#(1) v);
   method Action pcin(Bit#(48) v);

   method Action cea1(Bit#(1) en);		// 1-bit input: Clock enable input for 1st stage AREG
   method Action cea2(Bit#(1) en);		// 1-bit input: Clock enable input for 2nd stage AREG
   method Action cead(Bit#(1) en);		// 1-bit input: Clock enable input for ADREG
   method Action ceb1(Bit#(1) en);		// 1-bit input: Clock enable input for 1st stage BREG
   method Action ceb2(Bit#(1) en);		// 1-bit input: Clock enable input for 2nd stage BREG
   method Action cealumode(Bit#(1) en);		// 1-bit input: Clock enable input for ALUMODE
   method Action cec(Bit#(1) en);		// 1-bit input: Clock enable input for CREG
   method Action cecarryin(Bit#(1) en);		// 1-bit input: Clock enable input for CARRYINREG
   method Action cectrl(Bit#(1) en);		// 1-bit input: Clock enable input for OPMODEREG and CARRYINSELREG
   method Action ced(Bit#(1) en);		// 1-bit input: Clock enable input for DREG
   method Action ceinmode(Bit#(1) en);		// 1-bit input: Clock enable input for INMODEREG
   method Action cem(Bit#(1) en);		// 1-bit input: Clock enable input for MREG
   method Action cep(Bit#(1) en);		// 1-bit input: Clock enable input for PREG
endinterface

import "BVI" DSP48E1 =
module vmkDSP48E1(PRIM_DSP48E1);
   let currentReset <- exposeCurrentReset;
   let invertedReset <- mkResetInverter(currentReset);
   default_clock clk(CLK);

   default_reset rsta(RSTA) = invertedReset;
   input_reset rstb(RSTB) = invertedReset;
   input_reset rstc(RSTC) = invertedReset;
   input_reset rstd(RSTD) = invertedReset;
   input_reset rstallcarryin(RSTALLCARRYIN) = invertedReset;
   input_reset rstalumode(RSTALUMODE) = invertedReset;
   input_reset rstctrl(RSTCTRL) = invertedReset;
   input_reset rstinmode(RSTINMODE) = invertedReset;
   input_reset rstm(RSTM) = invertedReset;
   input_reset rstp(RSTP) = invertedReset;

   method P p();
   method alumode(ALUMODE) enable ((*inhigh*)EN_alumode);
   method carryinsel(CARRYINSEL) enable ((*inhigh*)EN_carryinsel);
   method inmode(INMODE) enable ((*inhigh*)EN_inmode);
   method opmode(OPMODE) enable ((*inhigh*)EN_opmode);
   method a(A) enable ((*inhigh*)EN_a);
   method b(B) enable ((*inhigh*)EN_b);
   method c(C) enable ((*inhigh*)EN_c);
   method d(D) enable ((*inhigh*)EN_d);
   method acin(ACIN) enable ((*inhigh*)EN_acin);
   method bcin(BCIN) enable ((*inhigh*)EN_bcin);
   method carrycascin(CARRYCASCIN) enable ((*inhigh*)EN_carrycascin);
   method carryin(CARRYIN) enable ((*inhigh*)EN_carryin);
   method multsignin(MULTSIGNIN) enable ((*inhigh*)EN_multsignin);
   method pcin(PCIN) enable ((*inhigh*)EN_pcin);

   method cea1(CEA1) enable ((*inhigh*)EN_cea1);
   method cea2(CEA2) enable ((*inhigh*)EN_cea2);
   method cead(CEAD) enable ((*inhigh*)EN_cead);
   method ceb1(CEB1) enable ((*inhigh*)EN_ceb1);
   method ceb2(CEB2) enable ((*inhigh*)EN_ceb2);
   method cealumode(CEALUMODE) enable ((*inhigh*)EN_cealumode);
   method cec(CEC) enable ((*inhigh*)EN_cec);
   method cecarryin(CECARRYIN) enable ((*inhigh*)EN_cecarryin);
   method cectrl(CECTRL) enable ((*inhigh*)EN_cectrl);
   method ced(CED) enable ((*inhigh*)EN_ced);
   method ceinmode(CEINMODE) enable ((*inhigh*)EN_ceinmode);
   method cem(CEM) enable ((*inhigh*)EN_cem);
   method cep(CEP) enable ((*inhigh*)EN_cep);
   schedule (alumode,carryinsel,inmode,opmode,a,b,c,d,acin,bcin,carrycascin,carryin,multsignin,pcin,cea1,cea2,cead,ceb1,ceb2,cealumode,cec,cecarryin,cectrl,ced,ceinmode,cem,cep,p)
      CF (alumode,carryinsel,inmode,opmode,a,b,c,d,acin,bcin,carrycascin,carryin,multsignin,pcin,cea1,cea2,cead,ceb1,ceb2,cealumode,cec,cecarryin,cectrl,ced,ceinmode,cem,cep,p);
endmodule

(* synthesize *)
module mkDsp48E1(Dsp48E1);
   let dsp <- vmkDSP48E1();
   let defaultReset <- exposeCurrentReset;
   let optionalReset = defaultReset; // noReset
   Wire#(Bit#(4)) alumodeWire <- mkDWire(0);
   Reg#(Bit#(4)) alumodeReg <- mkReg(0);
   Wire#(Bit#(3)) carryinselWire <- mkDWire(0);
   Wire#(Bit#(5)) inmodeWire <- mkDWire(0);
   Wire#(Bit#(7)) opmodeWire <- mkDWire(7'h20);
   Reg#(Bit#(7)) opmode1Reg <- mkReg(0);
   Reg#(Bit#(7)) opmode2Reg <- mkReg(0);
   Reg#(Bit#(7)) opmode3Reg <- mkReg(0);
   Reg#(Bit#(7)) opmode4Reg <- mkReg(0);

   Wire#(Bit#(30)) aWire <- mkDWire(0);
   Wire#(Bit#(18)) bWire <- mkDWire(0);
   Wire#(Bit#(48)) cWire <- mkDWire(0);
   Wire#(Bit#(25)) dWire <- mkDWire(0);
   Reg#(Bit#(48))  c1Reg <- mkReg(0);
   Reg#(Bit#(48))  c2Reg <- mkReg(0);
   Reg#(Bit#(48))  c3Reg <- mkReg(0);

   Wire#(Bit#(1)) ce1Wire <- mkDWire(0);
   Reg#(Bit#(1)) ce2Reg <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1)) cepReg <- mkReg(0, reset_by optionalReset);
   Wire#(Bit#(1)) lastWire <- mkDWire(0);
   Reg#(Bit#(1))  last1Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last2Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last3Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last4Reg  <- mkReg(0, reset_by optionalReset);
   Reg#(Bit#(1))  last5Reg  <- mkReg(0, reset_by optionalReset);

   Reg#(Bit#(32)) cycles <- mkReg(0);
   rule cyclesRule;
      cycles <= cycles+1;
   endrule

   rule clock_enable_and_reset;
      ce2Reg <= ce1Wire;
      cepReg <= ce2Reg;

      last1Reg <= lastWire;
      last2Reg <= last1Reg;
      last3Reg <= last2Reg;
      last4Reg <= last3Reg;
      last5Reg <= last4Reg;

      dsp.cea1(1); // (ce1Wire);
      dsp.cea2(1); // (ce2Reg);
      dsp.cead(1);
      dsp.ceb1(1); // (ce1Wire);
      dsp.ceb2(1); // (ce2Reg);
      dsp.cealumode(1); // (ce1Wire);
      dsp.cec(1);
      dsp.cecarryin(1);
      dsp.cectrl(1);
      dsp.ced(1);
      dsp.ceinmode(1);
      dsp.cem(1);
      dsp.cep(1); // (cepReg);

   endrule

   rule driveInputs;
      opmode1Reg <= opmodeWire;
      opmode2Reg <= opmode1Reg;
      opmode3Reg <= opmode2Reg;
      opmode4Reg <= opmode3Reg;

      alumodeReg <= alumodeWire;

      c1Reg <= cWire;
      c2Reg <= c1Reg;
      c3Reg <= c2Reg;

      //if (lastWire == 1 || last1Reg == 1 || last2Reg == 1 || last3Reg == 1 || last4Reg == 1 || last5Reg == 1)
	 $display("%d: a=%h b=%h c=%h p=%h", cycles, aWire, bWire, c3Reg, dsp.p());

      dsp.alumode(alumodeReg);
      dsp.carryinsel(carryinselWire);
      dsp.inmode(inmodeWire);
      dsp.opmode(opmode1Reg);
      dsp.a(aWire);
      dsp.b(bWire);
      dsp.c(c1Reg);
      //dsp.d(dWire);

      dsp.acin(0);
      dsp.bcin(0);
      dsp.carrycascin(0);
      dsp.carryin(0);
      dsp.pcin(0);
   endrule

   method Action alumode(Bit#(4) v);
      alumodeWire <= v;
   endmethod
   method Action carryinsel(Bit#(3) v);
      carryinselWire <= v;
   endmethod
   method Action inmode(Bit#(5) v);
      inmodeWire <= v;
   endmethod
   method Action opmode(Bit#(7) v);
      opmodeWire <= v;
   endmethod
   method Action a(Bit#(30) v);
      ce1Wire <= 1;
      aWire <= v;
   endmethod
   method Action b(Bit#(18) v);
      bWire <= v;
   endmethod
   method Action c(Bit#(48) v);
      cWire <= v;
   endmethod
   method Action d(Bit#(25) v);
      dsp.d(v);
   endmethod
   method Bit#(48) p() if (last5Reg == 1);
      return dsp.p();
   endmethod
   method Action last(Bit#(1) v);
      lastWire <= v;
   endmethod
endmodule


Change to DmaOffsetSize

Change to DmaOffsetSize

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFOF::*;
import GetPutF::*;
import Dma::*;
import RingTypes::*;
import StmtFSM::*;

// The interface to the copyengine is a pair of fifos which supply and accept
// blocks of 8 64 bit words
// A Command:
//  word 0: COPY[63:56] TAG[31:0]
//  word 1: readMemHandle[63:32]
//  word 1: readAddress[23:0]
//  word 2  writeMemHandle[63:32]
//  word 2: writeAddress[23:0]
//  word 3: bytecount[15:0]
//  word 4-7 unused
// Status
//  word0-6 all 0
//  word7  TAG[31:0]

module mkCopyEngine#(DmaReadServer#(64) copy_read_chan, DmaWriteServer#(64) copy_write_chan) ( ServerF#(Bit#(64), Bit#(64)));
   FIFOF#(Bit#(64)) f_in  <- mkSizedFIFOF(16);    // to buffer incoming requests
   FIFOF#(Bit#(64)) f_out <- mkSizedFIFOF(16);    // to buffer outgoing responses
   Reg#(Bit#(16)) copyReadCount <- mkReg(0);
   Reg#(Bit#(16)) copyWriteCount <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) copyReadAddr <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) copyWriteAddr <- mkReg(0);
   Reg#(DmaPointer) copyReadHandle <- mkReg(0);
   Reg#(DmaPointer) copyWriteHandle <- mkReg(0);
   Reg#(Bit#(32)) copyTag <- mkReg(0);
   Reg#(Bool) copyBusy <- mkReg(False);
   Reg#(Bit#(4)) cmdCtr <- mkReg(0);
   Stmt copyStart =
   seq
      while(True)
	 seq
	    while (copyBusy) noAction;
	    /*
	    for (cmdCtr <= 0; cmdCtr < 8; cmdCtr <= cmdCtr + 1)
	       action
		  $display("COPY %h %h", cmdCtr, f_in.first());
		  f_in.deq();
	       endaction

	    */
	    f_in.deq();  // word 0
	    action
	       copyReadHandle <= f_in.first()[63:32];
	       copyReadAddr <= f_in.first()[23:0];
	       f_in.deq();  // word 1
	    endaction
	    action
	       copyWriteHandle <= f_in.first()[63:32];
	       copyWriteAddr <= f_in.first()[23:0];
	       f_in.deq();  // word 2
	    endaction
	    action
	       copyReadCount <= f_in.first()[15:0];
	       copyWriteCount <= f_in.first()[15:0];
	       f_in.deq();  // word 3
	    endaction
	       f_in.deq;  // discard words 4-7
	       f_in.deq;  // discard words 4-7
	       f_in.deq;  // discard words 4-7
	    action
	       copyTag <= f_in.first()[31:0];
	       f_in.deq;  // discard words 4-7
	    endaction

	    //$display("copyStart from %h to %h count %h",
	    //   copyReadAddr, copyWriteAddr, copyReadCount);
	    copyBusy <= True;
	 endseq
   endseq;

    rule copyReadRule (copyBusy && (copyReadCount != 0));
       //$display("copyRead %h, count %h", copyReadAddr, copyReadCount);
       copy_read_chan.readReq.put(DmaRequest{handle: copyReadHandle, address: copyReadAddr, burstLen: 1, tag: copyReadAddr[8:3]});
       copyReadAddr <= copyReadAddr + 8;
       copyReadCount <= copyReadCount - 8;
    endrule

    rule copyReadWriteRule (copyBusy);
       let data <- copy_read_chan.readData.get;
       //$display("copyReadWrite addr %h", copyWriteAddr);
       copy_write_chan.writeReq.put(DmaRequest{handle: copyWriteHandle, address: copyWriteAddr, burstLen: 1, tag: copyWriteAddr[8:3]});
       copy_write_chan.writeData.put(DmaData{data: data.data, tag: copyWriteAddr[8:3]});
       copyWriteAddr <= copyWriteAddr + 8;
    endrule

   Stmt copyFinish =
   seq
      while(True)
	 seq
	    while (!copyBusy) noAction;
	    while (copyWriteCount > 0)
	       action
		  let v <- copy_write_chan.writeDone.get;
		  //$display("copyWriteAck");
		  copyWriteCount <= copyWriteCount - 8;
	       endaction
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(extend(copyTag));
	    copyBusy <= False;
	 endseq
   endseq;

   mkAutoFSM(copyStart);
   mkAutoFSM(copyFinish);

   interface PutF request = toPutF(f_in);
   interface GetF response = toGetF (f_out);

endmodule: mkCopyEngine

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFOF::*;
import GetPutF::*;
import Dma::*;
import RingTypes::*;
import StmtFSM::*;

// The interface to the copyengine is a pair of fifos which supply and accept
// blocks of 8 64 bit words
// A Command:
//  word 0: COPY[63:56] TAG[31:0]
//  word 1: readMemHandle[63:32]
//  word 1: readAddress[23:0]
//  word 2  writeMemHandle[63:32]
//  word 2: writeAddress[23:0]
//  word 3: bytecount[15:0]
//  word 4-7 unused
// Status
//  word0-6 all 0
//  word7  TAG[31:0]

module mkCopyEngine#(DmaReadServer#(64) copy_read_chan, DmaWriteServer#(64) copy_write_chan) ( ServerF#(Bit#(64), Bit#(64)));
   FIFOF#(Bit#(64)) f_in  <- mkSizedFIFOF(16);    // to buffer incoming requests
   FIFOF#(Bit#(64)) f_out <- mkSizedFIFOF(16);    // to buffer outgoing responses
   Reg#(Bit#(16)) copyReadCount <- mkReg(0);
   Reg#(Bit#(16)) copyWriteCount <- mkReg(0);
   Reg#(Bit#(DmaAddrSize)) copyReadAddr <- mkReg(0);
   Reg#(Bit#(DmaAddrSize)) copyWriteAddr <- mkReg(0);
   Reg#(DmaPointer) copyReadHandle <- mkReg(0);
   Reg#(DmaPointer) copyWriteHandle <- mkReg(0);
   Reg#(Bit#(32)) copyTag <- mkReg(0);
   Reg#(Bool) copyBusy <- mkReg(False);
   Reg#(Bit#(4)) cmdCtr <- mkReg(0);
   Stmt copyStart =
   seq
      while(True)
	 seq
	    while (copyBusy) noAction;
	    /*
	    for (cmdCtr <= 0; cmdCtr < 8; cmdCtr <= cmdCtr + 1)
	       action
		  $display("COPY %h %h", cmdCtr, f_in.first());
		  f_in.deq();
	       endaction

	    */
	    f_in.deq();  // word 0
	    action
	       copyReadHandle <= f_in.first()[63:32];
	       copyReadAddr <= f_in.first()[23:0];
	       f_in.deq();  // word 1
	    endaction
	    action
	       copyWriteHandle <= f_in.first()[63:32];
	       copyWriteAddr <= f_in.first()[23:0];
	       f_in.deq();  // word 2
	    endaction
	    action
	       copyReadCount <= f_in.first()[15:0];
	       copyWriteCount <= f_in.first()[15:0];
	       f_in.deq();  // word 3
	    endaction
	       f_in.deq;  // discard words 4-7
	       f_in.deq;  // discard words 4-7
	       f_in.deq;  // discard words 4-7
	    action
	       copyTag <= f_in.first()[31:0];
	       f_in.deq;  // discard words 4-7
	    endaction

	    //$display("copyStart from %h to %h count %h",
	    //   copyReadAddr, copyWriteAddr, copyReadCount);
	    copyBusy <= True;
	 endseq
   endseq;

    rule copyReadRule (copyBusy && (copyReadCount != 0));
       //$display("copyRead %h, count %h", copyReadAddr, copyReadCount);
       copy_read_chan.readReq.put(DmaRequest{handle: copyReadHandle, address: copyReadAddr, burstLen: 1, tag: copyReadAddr[8:3]});
       copyReadAddr <= copyReadAddr + 8;
       copyReadCount <= copyReadCount - 8;
    endrule

    rule copyReadWriteRule (copyBusy);
       let data <- copy_read_chan.readData.get;
       //$display("copyReadWrite addr %h", copyWriteAddr);
       copy_write_chan.writeReq.put(DmaRequest{handle: copyWriteHandle, address: copyWriteAddr, burstLen: 1, tag: copyWriteAddr[8:3]});
       copy_write_chan.writeData.put(DmaData{data: data.data, tag: copyWriteAddr[8:3]});
       copyWriteAddr <= copyWriteAddr + 8;
    endrule

   Stmt copyFinish =
   seq
      while(True)
	 seq
	    while (!copyBusy) noAction;
	    while (copyWriteCount > 0)
	       action
		  let v <- copy_write_chan.writeDone.get;
		  //$display("copyWriteAck");
		  copyWriteCount <= copyWriteCount - 8;
	       endaction
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(0);
	    f_out.enq(extend(copyTag));
	    copyBusy <= False;
	 endseq
   endseq;

   mkAutoFSM(copyStart);
   mkAutoFSM(copyFinish);

   interface PutF request = toPutF(f_in);
   interface GetF response = toGetF (f_out);

endmodule: mkCopyEngine