// Generated by pmgen.py from techlibs/microchip/microchip_dsp_CREG.pmg

struct microchip_dsp_CREG_pm {
  Module *module;
  SigMap sigmap;
  std::function<void()> on_accept;
  bool setup_done;
  bool generate_mode;
  int accept_cnt;

  uint32_t rngseed;
  int rng(unsigned int n) {
    rngseed ^= rngseed << 13;
    rngseed ^= rngseed >> 17;
    rngseed ^= rngseed << 5;
    return rngseed % n;
  }

  typedef std::tuple<> index_0_key_type;
  typedef std::tuple<Cell*> index_0_value_type;
  dict<index_0_key_type, vector<index_0_value_type>> index_0;
  typedef std::tuple<SigBit> index_6_key_type;
  typedef std::tuple<Cell*, int> index_6_value_type;
  dict<index_6_key_type, vector<index_6_value_type>> index_6;
  dict<SigBit, pool<Cell*>> sigusers;
  pool<Cell*> blacklist_cells;
  pool<Cell*> autoremove_cells;
  dict<Cell*,int> rollback_cache;
  int rollback;

  struct state_microchip_dsp_packC_t {
    SigSpec argD;
    SigSpec argQ;
    SigBit clock;
    Cell* dsp;
    Cell* ff;
    Cell* ffC;
    int ffoffset;
    SigSpec sigC;
    SigSpec sigP;
  } st_microchip_dsp_packC;

  struct udata_microchip_dsp_packC_t {
    Cell* dff;
    SigSpec dffD;
    SigSpec dffQ;
    SigBit dffclock;
    std::function<SigSpec(const SigSpec&)> unextend;
  } ud_microchip_dsp_packC;

  IdString id_b_ARST_VALUE{"\\ARST_VALUE"};
  IdString id_b_C{"\\C"};
  IdString id_b_CLK{"\\CLK"};
  IdString id_b_CLK_POLARITY{"\\CLK_POLARITY"};
  IdString id_b_C_BYPASS{"\\C_BYPASS"};
  IdString id_b_D{"\\D"};
  IdString id_b_MACC_PA{"\\MACC_PA"};
  IdString id_b_P{"\\P"};
  IdString id_b_Q{"\\Q"};
  IdString id_b_SRST_VALUE{"\\SRST_VALUE"};
  IdString id_b_init{"\\init"};
  IdString id_b_keep{"\\keep"};
  IdString id_d_adff{"$adff"};
  IdString id_d_adffe{"$adffe"};
  IdString id_d_dff{"$dff"};
  IdString id_d_dffe{"$dffe"};
  IdString id_d_sdff{"$sdff"};
  IdString id_d_sdffe{"$sdffe"};

  void add_siguser(const SigSpec &sig, Cell *cell) {
    for (auto bit : sigmap(sig)) {
      if (bit.wire == nullptr) continue;
      sigusers[bit].insert(cell);
    }
  }

  void blacklist(Cell *cell) {
    if (cell != nullptr && blacklist_cells.insert(cell).second) {
      auto ptr = rollback_cache.find(cell);
      if (ptr == rollback_cache.end()) return;
      int rb = ptr->second;
      if (rollback == 0 || rollback > rb)
        rollback = rb;
    }
  }

  void autoremove(Cell *cell) {
    if (cell != nullptr) {
      autoremove_cells.insert(cell);
      blacklist(cell);
    }
  }

  SigSpec port(Cell *cell, IdString portname) {
    try {
      return sigmap(cell->getPort(portname));
    } catch(std::out_of_range&) { log_error("Accessing non existing port %s\n",portname); }
  }

  SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {
    return sigmap(cell->connections_.at(portname, defval));
  }

  Const param(Cell *cell, IdString paramname) {
    try {
      return cell->getParam(paramname);
    } catch(std::out_of_range&) { log_error("Accessing non existing parameter %s\n",paramname); }
  }

  Const param(Cell *cell, IdString paramname, const Const& defval) {
    return cell->parameters.at(paramname, defval);
  }

  int nusers(const SigSpec &sig) {
    pool<Cell*> users;
    for (auto bit : sigmap(sig))
      for (auto user : sigusers[bit])
        users.insert(user);
    return GetSize(users);
  }

  microchip_dsp_CREG_pm(Module *module, const vector<Cell*> &cells) :
      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {
    setup(cells);
  }

  microchip_dsp_CREG_pm(Module *module) :
      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {
  }

  void setup(const vector<Cell*> &cells) {
    ud_microchip_dsp_packC.dff = nullptr;
    ud_microchip_dsp_packC.dffD = SigSpec();
    ud_microchip_dsp_packC.dffQ = SigSpec();
    ud_microchip_dsp_packC.dffclock = SigBit();
    ud_microchip_dsp_packC.unextend = std::function<SigSpec(const SigSpec&)>();
    log_assert(!setup_done);
    setup_done = true;
    for (auto port : module->ports)
      add_siguser(module->wire(port), nullptr);
    for (auto cell : module->cells())
      for (auto &conn : cell->connections())
        add_siguser(conn.second, cell);
    for (auto cell : cells) {
      do {
        Cell *dsp = cell;
        index_0_value_type value;
        std::get<0>(value) = cell;
        if (!(dsp->type.in(id_b_MACC_PA))) continue;
        if (!(port(dsp, id_b_C_BYPASS, SigSpec()).is_fully_ones())) continue;
        if (!(nusers(port(dsp, id_b_C, SigSpec())) > 1)) continue;
        index_0_key_type key;
        index_0[key].push_back(value);
      } while (0);
      do {
        Cell *ff = cell;
        index_6_value_type value;
        std::get<0>(value) = cell;
        if (!(ff->type.in(id_d_dff, id_d_dffe, id_d_sdff, id_d_sdffe, id_d_adff, id_d_adffe))) continue;
        if (!(param(ff, id_b_CLK_POLARITY).as_bool())) continue;
        int &offset = std::get<1>(value);
        for (offset = 0; offset < GetSize(port(ff, id_b_D)); offset++) {
        index_6_key_type key;
        std::get<0>(key) = port(ff, id_b_Q)[offset];
        index_6[key].push_back(value);
        }
      } while (0);
    }
  }

  ~microchip_dsp_CREG_pm() {
    for (auto cell : autoremove_cells)
      module->remove(cell);
  }

  int run_microchip_dsp_packC(std::function<void()> on_accept_f) {
    log_assert(setup_done);
    accept_cnt = 0;
    on_accept = on_accept_f;
    rollback = 0;
    st_microchip_dsp_packC.argD = SigSpec();
    st_microchip_dsp_packC.argQ = SigSpec();
    st_microchip_dsp_packC.clock = SigBit();
    st_microchip_dsp_packC.dsp = nullptr;
    st_microchip_dsp_packC.ff = nullptr;
    st_microchip_dsp_packC.ffC = nullptr;
    st_microchip_dsp_packC.ffoffset = int();
    st_microchip_dsp_packC.sigC = SigSpec();
    st_microchip_dsp_packC.sigP = SigSpec();
    block_0(1);
    log_assert(rollback_cache.empty());
    return accept_cnt;
  }

  int run_microchip_dsp_packC(std::function<void(microchip_dsp_CREG_pm&)> on_accept_f) {
    return run_microchip_dsp_packC([&](){on_accept_f(*this);});
  }

  int run_microchip_dsp_packC() {
    return run_microchip_dsp_packC([](){});
  }

  void block_subpattern_microchip_dsp_packC_(int recursion) { block_0(recursion); }
  void block_subpattern_microchip_dsp_packC_in_dffe(int recursion) { block_5(recursion); }

  // techlibs/microchip/microchip_dsp_CREG.pmg:57
  void block_0(int recursion YS_MAYBE_UNUSED) {
    Cell* &dsp YS_MAYBE_UNUSED = st_microchip_dsp_packC.dsp;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;
    Cell* _pmg_backup_dsp = dsp;

    index_0_key_type key;
    auto cells_ptr = index_0.find(key);

    if (cells_ptr != index_0.end()) {
      const vector<index_0_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        dsp = std::get<0>(cells[_pmg_idx]);
        if (blacklist_cells.count(dsp)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_1(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            dsp = _pmg_backup_dsp;
            return;
          }
          rollback = 0;
        }
      }
    }

    dsp = nullptr;
    dsp = _pmg_backup_dsp;
  }

  // techlibs/microchip/microchip_dsp_CREG.pmg:63
  void block_1(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_microchip_dsp_packC.dsp;
    SigBit &clock YS_MAYBE_UNUSED = st_microchip_dsp_packC.clock;
    SigSpec &sigC YS_MAYBE_UNUSED = st_microchip_dsp_packC.sigC;
    SigSpec &sigP YS_MAYBE_UNUSED = st_microchip_dsp_packC.sigP;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_2(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_packC_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    //helper function to remove unused bits
    unextend = [](const SigSpec &sig) {
      int i;
      for (i = GetSize(sig)-1; i > 0; i--)
        if (sig[i] != sig[i-1])
          break;
      // Do not remove non-const sign bit
      if (sig[i].wire)
        ++i;
      return sig.extract(0, i);
    };
    sigC = unextend(port(dsp, id_b_C, SigSpec()));
    SigSpec P = port(dsp, id_b_P);
    // Only care about those bits that are used
    int i;
    for (i = GetSize(P)-1; i >= 0; i--)
      if (nusers(P[i]) > 1)
        break;
    i++;
    log_assert(nusers(P.extract_end(i)) <= 1);
    sigP = P.extract(0, i);
    clock = port(dsp, id_b_CLK, SigBit());

    block_2(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;

    clock = SigBit();
    sigC = SigSpec();
    sigP = SigSpec();
  }

  // techlibs/microchip/microchip_dsp_CREG.pmg:94
  void block_2(int recursion YS_MAYBE_UNUSED) {
    Cell* const &dsp YS_MAYBE_UNUSED = st_microchip_dsp_packC.dsp;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_microchip_dsp_packC.sigP;
    SigSpec &argQ YS_MAYBE_UNUSED = st_microchip_dsp_packC.argQ;
    SigBit &clock YS_MAYBE_UNUSED = st_microchip_dsp_packC.clock;
    Cell* &ffC YS_MAYBE_UNUSED = st_microchip_dsp_packC.ffC;
    SigSpec &sigC YS_MAYBE_UNUSED = st_microchip_dsp_packC.sigC;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;

    SigBit _pmg_backup_clock = clock;
    SigSpec _pmg_backup_sigC = sigC;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_3(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_packC_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    argQ = sigC;
    subpattern(in_dffe);
    if (dff) {
      ffC = dff;
      clock = dffclock;
      sigC = dffD;
    }

    block_3(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;

    clock = _pmg_backup_clock;
    sigC = _pmg_backup_sigC;
    argQ = SigSpec();
    ffC = nullptr;
  }

  // techlibs/microchip/microchip_dsp_CREG.pmg:104
  void block_3(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_microchip_dsp_packC.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_microchip_dsp_packC.clock;
    Cell* const &dsp YS_MAYBE_UNUSED = st_microchip_dsp_packC.dsp;
    Cell* const &ffC YS_MAYBE_UNUSED = st_microchip_dsp_packC.ffC;
    const SigSpec &sigC YS_MAYBE_UNUSED = st_microchip_dsp_packC.sigC;
    const SigSpec &sigP YS_MAYBE_UNUSED = st_microchip_dsp_packC.sigP;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_4(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_packC_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    if (ffC)
      accept;

    block_4(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
  }

  void block_4(int recursion YS_MAYBE_UNUSED) {
  }

  // techlibs/microchip/microchip_dsp_CREG.pmg:116
  void block_5(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_microchip_dsp_packC.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_microchip_dsp_packC.clock;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_6(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_packC_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    dff = nullptr;
    if (argQ.empty())
      reject;
    for (const auto &c : argQ.chunks()) {
      // Abandon matches when 'Q' is a constant
      if (!c.wire)
        reject;
      // Abandon matches when 'Q' has the keep attribute set
      if (c.wire->get_bool_attribute(id_b_keep))
        reject;
      // Abandon matches when 'Q' has a non-zero init attribute set
      Const init = c.wire->attributes.at(id_b_init, Const());
      if (!init.empty())
        for (auto b : init.extract(c.offset, c.width))
          if (b != State::Sx && b != State::S0)
            reject;
    }

    block_6(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;
  }

  // techlibs/microchip/microchip_dsp_CREG.pmg:136
  void block_6(int recursion YS_MAYBE_UNUSED) {
    const SigSpec &argQ YS_MAYBE_UNUSED = st_microchip_dsp_packC.argQ;
    const SigBit &clock YS_MAYBE_UNUSED = st_microchip_dsp_packC.clock;
    Cell* &ff YS_MAYBE_UNUSED = st_microchip_dsp_packC.ff;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;
    Cell* _pmg_backup_ff = ff;

    index_6_key_type key;
    std::get<0>(key) = argQ[0];
    auto cells_ptr = index_6.find(key);

    if (cells_ptr != index_6.end()) {
      const vector<index_6_value_type> &cells = cells_ptr->second;
      for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {
        ff = std::get<0>(cells[_pmg_idx]);
        const int &offset YS_MAYBE_UNUSED = std::get<1>(cells[_pmg_idx]);
        if (blacklist_cells.count(ff)) continue;
        if (!(GetSize(port(ff, id_b_Q)) >= offset + GetSize(argQ))) continue;
        if (!(port(ff, id_b_Q).extract(offset, GetSize(argQ)) == argQ)) continue;
        if (!(clock == SigBit() || port(ff, id_b_CLK)[0] == clock)) continue;
        auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));
        block_7(recursion+1);
        if (rollback_ptr.second)
          rollback_cache.erase(rollback_ptr.first);
        if (rollback) {
          if (rollback != recursion) {
            ff = _pmg_backup_ff;
            return;
          }
          rollback = 0;
        }
      }
    }

    ff = nullptr;
    ff = _pmg_backup_ff;
  }

  // techlibs/microchip/microchip_dsp_CREG.pmg:151
  void block_7(int recursion YS_MAYBE_UNUSED) {
    const SigBit &clock YS_MAYBE_UNUSED = st_microchip_dsp_packC.clock;
    Cell* const &ff YS_MAYBE_UNUSED = st_microchip_dsp_packC.ff;
    SigSpec &argQ YS_MAYBE_UNUSED = st_microchip_dsp_packC.argQ;
    Cell* &dff YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dff;
    SigSpec &dffD YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffD;
    SigSpec &dffQ YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffQ;
    SigBit &dffclock YS_MAYBE_UNUSED = ud_microchip_dsp_packC.dffclock;
    std::function<SigSpec(const SigSpec&)> &unextend YS_MAYBE_UNUSED = ud_microchip_dsp_packC.unextend;

    SigSpec _pmg_backup_argQ = argQ;

#define reject do { goto rollback_label; } while(0)
#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)
#define finish do { rollback = -1; goto rollback_label; } while(0)
#define branch do { block_8(recursion+1); if (rollback) goto rollback_label; } while(0)
#define subpattern(pattern_name) do { block_subpattern_microchip_dsp_packC_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; } while(0)
    // Check that reset value, if present, is fully 0.
    bool noResetFlop = ff->type.in(id_d_dff, id_d_dffe);
    bool srstZero = ff->type.in(id_d_sdff, id_d_sdffe) && param(ff, id_b_SRST_VALUE).is_fully_zero();
    bool arstZero = ff->type.in(id_d_adff, id_d_adffe) && param(ff, id_b_ARST_VALUE).is_fully_zero();
    bool resetLegal = noResetFlop || srstZero || arstZero;
    if (resetLegal)
    {
      SigSpec Q = port(ff, id_b_Q);
      dff = ff;
      dffclock = port(ff, id_b_CLK);
      dffD = argQ;
      SigSpec D = port(ff, id_b_D);
      argQ = Q;
      dffD.replace(argQ, D);
    }

    block_8(recursion+1);
#undef reject
#undef accept
#undef finish
#undef branch
#undef subpattern

rollback_label:
    YS_MAYBE_UNUSED;

    argQ = _pmg_backup_argQ;
  }

  void block_8(int recursion YS_MAYBE_UNUSED) {
  }
};
