text: /*
   *  nextpnr -- Next Generation Place and Route
   *
   *  Copyright (C) 2018  Clifford Wolf <clifford@symbioticeda.com>
   *
   *  Permission to use, copy, modify, and/or distribute this software for any
   *  purpose with or without fee is hereby granted, provided that the above
   *  copyright notice and this permission notice appear in all copies.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   *
   */
  
  #include "nextpnr.h"
  #include "log.h"
  
  NEXTPNR_NAMESPACE_BEGIN
  
  assertion_failure::assertion_failure(std::string msg, std::string expr_str, std::string filename, int line)
          : runtime_error("Assertion failure: " + msg + " (" + filename + ":" + std::to_string(line) + ")"), msg(msg),
            expr_str(expr_str), filename(filename), line(line)
  {
      log_flush();
  }
  
  void IdString::set(const BaseCtx *ctx, const std::string &s)
  {
      auto it = ctx->idstring_str_to_idx->find(s);
      if (it == ctx->idstring_str_to_idx->end()) {
          index = ctx->idstring_idx_to_str->size();
          auto insert_rc = ctx->idstring_str_to_idx->insert({s, index});
          ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
      } else {
          index = it->second;
      }
  }
  
  const std::string &IdString::str(const BaseCtx *ctx) const { return *ctx->idstring_idx_to_str->at(index); }
  
  const char *IdString::c_str(const BaseCtx *ctx) const { return str(ctx).c_str(); }
  
  void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)
  {
      NPNR_ASSERT(ctx->idstring_str_to_idx->count(s) == 0);
      NPNR_ASSERT(int(ctx->idstring_idx_to_str->size()) == idx);
      auto insert_rc = ctx->idstring_str_to_idx->insert({s, idx});
      ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
  }
  
  TimingConstrObjectId BaseCtx::timingWildcardObject()
  {
      TimingConstrObjectId id;
      id.index = 0;
      return id;
  }
  
  TimingConstrObjectId BaseCtx::timingClockDomainObject(NetInfo *clockDomain)
  {
      NPNR_ASSERT(clockDomain->clkconstr != nullptr);
      if (clockDomain->clkconstr->domain_tmg_id != TimingConstrObjectId()) {
          return clockDomain->clkconstr->domain_tmg_id;
      } else {
          TimingConstraintObject obj;
          TimingConstrObjectId id;
          id.index = int(constraintObjects.size());
          obj.id = id;
          obj.type = TimingConstraintObject::CLOCK_DOMAIN;
          obj.entity = clockDomain->name;
          clockDomain->clkconstr->domain_tmg_id = id;
          constraintObjects.push_back(obj);
          return id;
      }
  }
  
  TimingConstrObjectId BaseCtx::timingNetObject(NetInfo *net)
  {
      if (net->tmg_id != TimingConstrObjectId()) {
          return net->tmg_id;
      } else {
          TimingConstraintObject obj;
          TimingConstrObjectId id;
          id.index = int(constraintObjects.size());
          obj.id = id;
          obj.type = TimingConstraintObject::NET;
          obj.entity = net->name;
          constraintObjects.push_back(obj);
          net->tmg_id = id;
          return id;
      }
  }
  
  TimingConstrObjectId BaseCtx::timingCellObject(CellInfo *cell)
  {
      if (cell->tmg_id != TimingConstrObjectId()) {
          return cell->tmg_id;
      } else {
          TimingConstraintObject obj;
          TimingConstrObjectId id;
          id.index = int(constraintObjects.size());
          obj.id = id;
          obj.type = TimingConstraintObject::CELL;
          obj.entity = cell->name;
          constraintObjects.push_back(obj);
          cell->tmg_id = id;
          return id;
      }
  }
  
  TimingConstrObjectId BaseCtx::timingPortObject(CellInfo *cell, IdString port)
  {
      if (cell->ports.at(port).tmg_id != TimingConstrObjectId()) {
          return cell->ports.at(port).tmg_id;
      } else {
          TimingConstraintObject obj;
          TimingConstrObjectId id;
          id.index = int(constraintObjects.size());
          obj.id = id;
          obj.type = TimingConstraintObject::CELL_PORT;
          obj.entity = cell->name;
          obj.port = port;
          constraintObjects.push_back(obj);
          cell->ports.at(port).tmg_id = id;
          return id;
      }
  }
  
  void BaseCtx::addConstraint(std::unique_ptr<TimingConstraint> constr)
  {
      for (auto fromObj : constr->from)
          constrsFrom.emplace(fromObj, constr.get());
      for (auto toObj : constr->to)
          constrsTo.emplace(toObj, constr.get());
      IdString name = constr->name;
      constraints[name] = std::move(constr);
  }
  
  void BaseCtx::removeConstraint(IdString constrName)
  {
      TimingConstraint *constr = constraints[constrName].get();
      for (auto fromObj : constr->from) {
          auto fromConstrs = constrsFrom.equal_range(fromObj);
          constrsFrom.erase(std::find(fromConstrs.first, fromConstrs.second, std::make_pair(fromObj, constr)));
      }
      for (auto toObj : constr->to) {
          auto toConstrs = constrsFrom.equal_range(toObj);
          constrsFrom.erase(std::find(toConstrs.first, toConstrs.second, std::make_pair(toObj, constr)));
      }
      constraints.erase(constrName);
  }
  
  const char *BaseCtx::nameOfBel(BelId bel) const
  {
      const Context *ctx = getCtx();
      return ctx->getBelName(bel).c_str(ctx);
  }
  
  const char *BaseCtx::nameOfWire(WireId wire) const
  {
      const Context *ctx = getCtx();
      return ctx->getWireName(wire).c_str(ctx);
  }
  
  const char *BaseCtx::nameOfPip(PipId pip) const
  {
      const Context *ctx = getCtx();
      return ctx->getPipName(pip).c_str(ctx);
  }
  
  const char *BaseCtx::nameOfGroup(GroupId group) const
  {
      const Context *ctx = getCtx();
      return ctx->getGroupName(group).c_str(ctx);
  }
  
  WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
  {
      if (net_info->driver.cell == nullptr)
          return WireId();
  
      auto src_bel = net_info->driver.cell->bel;
  
      if (src_bel == BelId())
          return WireId();
  
      IdString driver_port = net_info->driver.port;
  
      auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
      if (driver_port_it != net_info->driver.cell->pins.end())
          driver_port = driver_port_it->second;
  
      return getBelPinWire(src_bel, driver_port);
  }
  
  WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &user_info) const
  {
      auto dst_bel = user_info.cell->bel;
  
      if (dst_bel == BelId())
          return WireId();
  
      IdString user_port = user_info.port;
  
      auto user_port_it = user_info.cell->pins.find(user_port);
  
      if (user_port_it != user_info.cell->pins.end())
          user_port = user_port_it->second;
  
      return getBelPinWire(dst_bel, user_port);
  }
  
  delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const
  {
  #ifdef ARCH_ECP5
      if (net_info->is_global)
          return 0;
  #endif
  
      WireId src_wire = getNetinfoSourceWire(net_info);
      if (src_wire == WireId())
          return 0;
  
      WireId dst_wire = getNetinfoSinkWire(net_info, user_info);
      WireId cursor = dst_wire;
      delay_t delay = 0;
  
      while (cursor != WireId() && cursor != src_wire) {
          auto it = net_info->wires.find(cursor);
  
          if (it == net_info->wires.end())
              break;
  
          PipId pip = it->second.pip;
          delay += getPipDelay(pip).maxDelay();
          delay += getWireDelay(cursor).maxDelay();
          cursor = getPipSrcWire(pip);
      }
  
      if (cursor == src_wire)
          return delay + getWireDelay(src_wire).maxDelay();
  
      return predictDelay(net_info, user_info);
  }
  
  static uint32_t xorshift32(uint32_t x)
  {
      x ^= x << 13;
      x ^= x >> 17;
      x ^= x << 5;
      return x;
  }
  
  uint32_t Context::checksum() const
  {
      uint32_t cksum = xorshift32(123456789);
  
      uint32_t cksum_nets_sum = 0;
      for (auto &it : nets) {
          auto &ni = *it.second;
          uint32_t x = 123456789;
          x = xorshift32(x + xorshift32(it.first.index));
          x = xorshift32(x + xorshift32(ni.name.index));
          if (ni.driver.cell)
              x = xorshift32(x + xorshift32(ni.driver.cell->name.index));
          x = xorshift32(x + xorshift32(ni.driver.port.index));
          x = xorshift32(x + xorshift32(getDelayChecksum(ni.driver.budget)));
  
          for (auto &u : ni.users) {
              if (u.cell)
                  x = xorshift32(x + xorshift32(u.cell->name.index));
              x = xorshift32(x + xorshift32(u.port.index));
              x = xorshift32(x + xorshift32(getDelayChecksum(u.budget)));
          }
  
          uint32_t attr_x_sum = 0;
          for (auto &a : ni.attrs) {
              uint32_t attr_x = 123456789;
              attr_x = xorshift32(attr_x + xorshift32(a.first.index));
              for (uint8_t ch : a.second)
                  attr_x = xorshift32(attr_x + xorshift32(ch));
              attr_x_sum += attr_x;
          }
          x = xorshift32(x + xorshift32(attr_x_sum));
  
          uint32_t wire_x_sum = 0;
          for (auto &w : ni.wires) {
              uint32_t wire_x = 123456789;
              wire_x = xorshift32(wire_x + xorshift32(getWireChecksum(w.first)));
              wire_x = xorshift32(wire_x + xorshift32(getPipChecksum(w.second.pip)));
              wire_x = xorshift32(wire_x + xorshift32(int(w.second.strength)));
              wire_x_sum += wire_x;
          }
          x = xorshift32(x + xorshift32(wire_x_sum));
  
          cksum_nets_sum += x;
      }
      cksum = xorshift32(cksum + xorshift32(cksum_nets_sum));
  
      uint32_t cksum_cells_sum = 0;
      for (auto &it : cells) {
          auto &ci = *it.second;
          uint32_t x = 123456789;
          x = xorshift32(x + xorshift32(it.first.index));
          x = xorshift32(x + xorshift32(ci.name.index));
          x = xorshift32(x + xorshift32(ci.type.index));
  
          uint32_t port_x_sum = 0;
          for (auto &p : ci.ports) {
              uint32_t port_x = 123456789;
              port_x = xorshift32(port_x + xorshift32(p.first.index));
              port_x = xorshift32(port_x + xorshift32(p.second.name.index));
              if (p.second.net)
                  port_x = xorshift32(port_x + xorshift32(p.second.net->name.index));
              port_x = xorshift32(port_x + xorshift32(p.second.type));
              port_x_sum += port_x;
          }
          x = xorshift32(x + xorshift32(port_x_sum));
  
          uint32_t attr_x_sum = 0;
          for (auto &a : ci.attrs) {
              uint32_t attr_x = 123456789;
              attr_x = xorshift32(attr_x + xorshift32(a.first.index));
              for (uint8_t ch : a.second)
                  attr_x = xorshift32(attr_x + xorshift32(ch));
              attr_x_sum += attr_x;
          }
          x = xorshift32(x + xorshift32(attr_x_sum));
  
          uint32_t param_x_sum = 0;
          for (auto &p : ci.params) {
              uint32_t param_x = 123456789;
              param_x = xorshift32(param_x + xorshift32(p.first.index));
              for (uint8_t ch : p.second)
                  param_x = xorshift32(param_x + xorshift32(ch));
              param_x_sum += param_x;
          }
          x = xorshift32(x + xorshift32(param_x_sum));
  
          x = xorshift32(x + xorshift32(getBelChecksum(ci.bel)));
          x = xorshift32(x + xorshift32(ci.belStrength));
  
          uint32_t pin_x_sum = 0;
          for (auto &a : ci.pins) {
              uint32_t pin_x = 123456789;
              pin_x = xorshift32(pin_x + xorshift32(a.first.index));
              pin_x = xorshift32(pin_x + xorshift32(a.second.index));
              pin_x_sum += pin_x;
          }
          x = xorshift32(x + xorshift32(pin_x_sum));
  
          cksum_cells_sum += x;
      }
      cksum = xorshift32(cksum + xorshift32(cksum_cells_sum));
  
      return cksum;
  }
  
  void Context::check() const
  {
      for (auto &n : nets) {
          auto ni = n.second.get();
          NPNR_ASSERT(n.first == ni->name);
          for (auto &w : ni->wires) {
              NPNR_ASSERT(ni == getBoundWireNet(w.first));
              if (w.second.pip != PipId()) {
                  NPNR_ASSERT(w.first == getPipDstWire(w.second.pip));
                  NPNR_ASSERT(ni == getBoundPipNet(w.second.pip));
              }
          }
          if (ni->driver.cell != nullptr)
              NPNR_ASSERT(ni->driver.cell->ports.at(ni->driver.port).net == ni);
          for (auto user : ni->users) {
              NPNR_ASSERT(user.cell->ports.at(user.port).net == ni);
          }
      }
  
      for (auto w : getWires()) {
          auto ni = getBoundWireNet(w);
          if (ni != nullptr) {
              NPNR_ASSERT(ni->wires.count(w));
          }
      }
  
      for (auto &c : cells) {
          auto ci = c.second.get();
          NPNR_ASSERT(c.first == ci->name);
          if (ci->bel != BelId())
              NPNR_ASSERT(getBoundBelCell(c.second->bel) == ci);
          for (auto &port : c.second->ports) {
              NetInfo *net = port.second.net;
              if (net != nullptr) {
                  NPNR_ASSERT(nets.find(net->name) != nets.end());
                  if (port.second.type == PORT_OUT) {
                      NPNR_ASSERT(net->driver.cell == c.second.get() && net->driver.port == port.first);
                  } else if (port.second.type == PORT_IN) {
                      NPNR_ASSERT(std::count_if(net->users.begin(), net->users.end(), [&](const PortRef &pr) {
                                      return pr.cell == c.second.get() && pr.port == port.first;
                                  }) == 1);
                  }
              }
          }
      }
  }
  
  void BaseCtx::addClock(IdString net, float freq)
  {
      log_info("constraining clock net '%s' to %.02f MHz\n", net.c_str(this), freq);
      std::unique_ptr<ClockConstraint> cc(new ClockConstraint());
      cc->period = getCtx()->getDelayFromNS(1000 / freq);
      cc->high = getCtx()->getDelayFromNS(500 / freq);
      cc->low = getCtx()->getDelayFromNS(500 / freq);
      nets.at(net)->clkconstr = std::move(cc);
  }
  
  NEXTPNR_NAMESPACE_END
  