<commit_before>/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2019, The Regents of the University of California
// All rights reserved.
//
// BSD 3-Clause License
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////

#include <cmath>
#include <fstream>
#include <iostream>

#include "db_sta/dbNetwork.hh"
#include "ifp/InitFloorplan.hh"
#include "odb/db.h"
#include "odb/dbTransform.h"
#include "odb/util.h"
#include "sta/FuncExpr.hh"
#include "sta/Liberty.hh"
#include "sta/PortDirection.hh"
#include "sta/StringUtil.hh"
#include "sta/Vector.hh"
#include "utl/Logger.h"
#include "utl/validation.h"

namespace ifp {

using std::abs;
using std::ceil;
using std::map;
using std::round;
using std::set;
using std::string;

using sta::dbNetwork;
using sta::stdstrPrint;
using sta::stringEqual;
using sta::StringVector;
using sta::Vector;

using utl::IFP;
using utl::Logger;

using odb::dbBlock;
using odb::dbBox;
using odb::dbBPin;
using odb::dbBTerm;
using odb::dbChip;
using odb::dbDatabase;
using odb::dbGroup;
using odb::dbGroupType;
using odb::dbInst;
using odb::dbLib;
using odb::dbMaster;
using odb::dbOrientType;
using odb::dbPlacementStatus;
using odb::dbRegion;
using odb::dbRow;
using odb::dbRowDir;
using odb::dbSet;
using odb::dbSite;
using odb::dbTech;
using odb::dbTechLayer;
using odb::dbTechLayerDir;
using odb::dbTechLayerType;
using odb::dbTrackGrid;
using odb::dbTransform;
using odb::Rect;

InitFloorplan::InitFloorplan(dbBlock* block,
                             Logger* logger,
                             sta::dbNetwork* network)
    : block_(block), logger_(logger), network_(network)
{
}

void InitFloorplan::initFloorplan(double utilization,
                                  double aspect_ratio,
                                  int core_space_bottom,
                                  int core_space_top,
                                  int core_space_left,
                                  int core_space_right,
                                  const char* site_name)
{
  utl::Validator v(logger_, IFP);
  v.check_percentage("utilization", utilization, 12);
  v.check_non_negative("core_space_bottom", core_space_bottom, 32);
  v.check_non_negative("core_space_top", core_space_top, 33);
  v.check_non_negative("core_space_left", core_space_left, 34);
  v.check_non_negative("core_space_right", core_space_right, 35);
  v.check_non_negative("aspect_ratio", aspect_ratio, 36);
  v.check_non_null("site_name", site_name, 37);

  utilization /= 100;
  const double design_area = designArea();
  const double core_area = design_area / utilization;
  const int core_width = std::sqrt(core_area / aspect_ratio);
  const int core_height = round(core_width * aspect_ratio);

  const int core_lx = core_space_left;
  const int core_ly = core_space_bottom;
  const int core_ux = core_lx + core_width;
  const int core_uy = core_ly + core_height;
  const int die_lx = 0;
  const int die_ly = 0;
  const int die_ux = core_ux + core_space_right;
  const int die_uy = core_uy + core_space_top;
  initFloorplan({die_lx, die_ly, die_ux, die_uy},
                {core_lx, core_ly, core_ux, core_uy},
                site_name);
}

double InitFloorplan::designArea()
{
  double design_area = 0.0;
  for (dbInst* inst : block_->getInsts()) {
    dbMaster* master = inst->getMaster();
    const double area
        = master->getHeight() * static_cast<double>(master->getWidth());
    design_area += area;
  }
  return design_area;
}

static int divCeil(int dividend, int divisor)
{
  return ceil(static_cast<double>(dividend) / divisor);
}

void InitFloorplan::initFloorplan(const odb::Rect& die,
                                  const odb::Rect& core,
                                  const char* site_name)
{
  Rect die_area(snapToMfgGrid(die.xMin()),
                snapToMfgGrid(die.yMin()),
                snapToMfgGrid(die.xMax()),
                snapToMfgGrid(die.yMax()));
  block_->setDieArea(die_area);

  if (site_name && site_name[0] && core.xMin() >= 0 && core.yMin() >= 0) {
    dbSite* site = findSite(site_name);
    if (site) {
      // Destroy any existing rows.
      auto rows = block_->getRows();
      for (dbSet<dbRow>::iterator row_itr = rows.begin(); row_itr != rows.end();
           row_itr = dbRow::destroy(row_itr))
        ;

      uint site_dx = site->getWidth();
      uint site_dy = site->getHeight();
      // core lower left corner to multiple of site dx/dy.
      int clx = divCeil(core.xMin(), site_dx) * site_dx;
      int cly = divCeil(core.yMin(), site_dy) * site_dy;
      if (clx != core.xMin() || cly != core.yMin()) {
        dbTech* tech = block_->getDataBase()->getTech();
        const double dbu = tech->getDbUnitsPerMicron();
        logger_->warn(IFP,
                      28,
                      "Core area lower left ({:.3f}, {:.3f}) snapped to "
                      "({:.3f}, {:.3f}).",
                      core.xMin() / dbu,
                      core.yMin() / dbu,
                      clx / dbu,
                      cly / dbu);
      }
      int cux = core.xMax();
      int cuy = core.yMax();
      makeRows(site, clx, cly, cux, cuy);
      updateVoltageDomain(site, clx, cly, cux, cuy);
    } else
      logger_->warn(IFP, 9, "SITE {} not found.", site_name);
  }

  std::vector<dbBox*> blockage_bboxes;
  for (auto blockage : block_->getBlockages()) {
    blockage_bboxes.push_back(blockage->getBBox());
  }

  odb::cutRows(block_,
               /* min_row_width */ 0,
               blockage_bboxes,
               /* halo_x */ 0,
               /* halo_y */ 0,
               logger_);
}

// this function is used to create regions ( split overlapped rows and create
// new ones )
void InitFloorplan::updateVoltageDomain(dbSite* site,
                                        int core_lx,
                                        int core_ly,
                                        int core_ux,
                                        int core_uy)
{
  // The unit for power_domain_y_space is the site height. The real space is
  // power_domain_y_space * site_dy
  static constexpr int power_domain_y_space = 6;
  uint site_dy = site->getHeight();
  uint site_dx = site->getWidth();

  // checks if a group is defined as a voltage domain, if so it creates a region
  for (dbGroup* group : block_->getGroups()) {
    if (group->getType() == dbGroupType::VOLTAGE_DOMAIN) {
      dbRegion* domain_region = dbRegion::create(block_, group->getName());

      string domain_name = group->getName();
      Rect domain_rect = group->getBox();
      int domain_xMin = domain_rect.xMin();
      int domain_yMin = domain_rect.yMin();
      int domain_xMax = domain_rect.xMax();
      int domain_yMax = domain_rect.yMax();
      dbBox::create(
          domain_region, domain_xMin, domain_yMin, domain_xMax, domain_yMax);

      dbSet<dbRow> rows = block_->getRows();
      int total_row_count = rows.size();

      dbSet<dbRow>::iterator row_itr = rows.begin();
      for (int row_processed = 0; row_processed < total_row_count;
           row_processed++) {
        dbRow* row = *row_itr;
        Rect row_bbox;
        row->getBBox(row_bbox);
        int row_yMin = row_bbox.yMin();
        int row_yMax = row_bbox.yMax();

        // check if the rows overlapped with the area of a defined voltage
        // domains + margin
        if (row_yMax + power_domain_y_space * site_dy <= domain_yMin
            || row_yMin >= domain_yMax + power_domain_y_space * site_dy) {
          row_itr++;
        } else {
          string row_name = row->getName();
          dbOrientType orient = row->getOrient();
          row_itr = dbRow::destroy(row_itr);

          // lcr stands for left core row
          int lcr_xMax = domain_xMin - power_domain_y_space * site_dy;
          // in case there is at least one valid site width on the left, create
          // left core rows
          if (lcr_xMax > core_lx + site_dx) {
            string lcr_name = row_name + "_1";
            // warning message since tap cells might not be inserted
            if (lcr_xMax < core_lx + 10 * site_dx) {
              logger_->warn(IFP,
                            26,
                            "left core row: {} has less than 10 sites",
                            lcr_name);
            }
            int lcr_sites = (lcr_xMax - core_lx) / site_dx;
            dbRow::create(block_,
                          lcr_name.c_str(),
                          site,
                          core_lx,
                          row_yMin,
                          orient,
                          dbRowDir::HORIZONTAL,
                          lcr_sites,
                          site_dx);
          }

          // rcr stands for right core row
          // rcr_dx_site_number is the max number of site_dx that is less than
          // power_domain_y_space * site_dy. This helps align the rcr_xMin on
          // the multiple of site_dx.
          int rcr_dx_site_number = (power_domain_y_space * site_dy) / site_dx;
          int rcr_xMin = domain_xMax + rcr_dx_site_number * site_dx;

          // in case there is at least one valid site width on the right, create
          // right core rows
          if (rcr_xMin + site_dx < core_ux) {
            string rcr_name = row_name + "_2";
            if (rcr_xMin + 10 * site_dx > core_ux) {
              logger_->warn(IFP,
                            27,
                            "right core row: {} has less than 10 sites",
                            rcr_name);
            }
            int rcr_sites = (core_ux - rcr_xMin) / site_dx;
            dbRow::create(block_,
                          rcr_name.c_str(),
                          site,
                          rcr_xMin,
                          row_yMin,
                          orient,
                          dbRowDir::HORIZONTAL,
                          rcr_sites,
                          site_dx);
          }

          int domain_row_sites = (domain_xMax - domain_xMin) / site_dx;
          // create domain rows if current iterations are not in margin area
          if (row_yMin >= domain_yMin && row_yMax <= domain_yMax) {
            string domain_row_name = row_name + "_" + domain_name;
            dbRow::create(block_,
                          domain_row_name.c_str(),
                          site,
                          domain_xMin,
                          row_yMin,
                          orient,
                          dbRowDir::HORIZONTAL,
                          domain_row_sites,
                          site_dx);
          }
        }
      }
    }
  }
}

void InitFloorplan::makeRows(dbSite* site,
                             int core_lx,
                             int core_ly,
                             int core_ux,
                             int core_uy)
{
  int core_dx = abs(core_ux - core_lx);
  int core_dy = abs(core_uy - core_ly);
  uint site_dx = site->getWidth();
  uint site_dy = site->getHeight();
  int rows_x = core_dx / site_dx;
  int rows_y = core_dy / site_dy;

  int y = core_ly;
  for (int row = 0; row < rows_y; row++) {
    dbOrientType orient = (row % 2 == 0) ? dbOrientType::R0   // N
                                         : dbOrientType::MX;  // FS
    string row_name = stdstrPrint("ROW_%d", row);
    dbRow::create(block_,
                  row_name.c_str(),
                  site,
                  core_lx,
                  y,
                  orient,
                  dbRowDir::HORIZONTAL,
                  rows_x,
                  site_dx);
    y += site_dy;
  }
  logger_->info(IFP, 1, "Added {} rows of {} sites.", rows_y, rows_x);
}

dbSite* InitFloorplan::findSite(const char* site_name)
{
  for (dbLib* lib : block_->getDataBase()->getLibs()) {
    dbSite* site = lib->findSite(site_name);
    if (site)
      return site;
  }
  return nullptr;
}

////////////////////////////////////////////////////////////////

void InitFloorplan::autoPlacePins(const char* pin_layer_name)
{
  dbTech* tech = block_->getDataBase()->getTech();
  dbTechLayer* pin_layer = tech->findLayer(pin_layer_name);
  if (pin_layer) {
    odb::Rect core;
    block_->getCoreArea(core);
    autoPlacePins(pin_layer, core);
  } else
    logger_->warn(IFP, 2, "pin layer {} not found.", pin_layer_name);
}

void InitFloorplan::autoPlacePins(dbTechLayer* pin_layer, Rect& core)
{
  dbSet<dbBTerm> bterms = block_->getBTerms();
  int pin_count = bterms.size();

  if (pin_count > 0) {
    int dx = core.dx();
    int dy = core.dy();
    int perimeter = dx * 2 + dy * 2;
    double location = 0.0;
    int pin_dist = perimeter / pin_count;

    for (dbBTerm* bterm : bterms) {
      int x, y;
      dbOrientType orient;
      if (location < dx) {
        // bottom
        x = core.xMin() + location;
        y = core.yMin();
        orient = dbOrientType::R180;  // S
      } else if (location < (dx + dy)) {
        // right
        x = core.xMax();
        y = core.yMin() + (location - dx);
        orient = dbOrientType::R270;  // E
      } else if (location < (dx * 2 + dy)) {
        // top
        x = core.xMax() - (location - (dx + dy));
        y = core.yMax();
        orient = dbOrientType::R0;  // N
      } else {
        // left
        x = core.xMin();
        y = core.yMax() - (location - (dx * 2 + dy));
        orient = dbOrientType::R90;  // W
      }

      // Delete existing BPins.
      dbSet<dbBPin> bpins = bterm->getBPins();
      for (auto bpin_itr = bpins.begin(); bpin_itr != bpins.end();)
        bpin_itr = dbBPin::destroy(bpin_itr);

      dbBPin* bpin = dbBPin::create(bterm);
      bpin->setPlacementStatus(dbPlacementStatus::FIRM);
      dbBox::create(bpin, pin_layer, x, y, x, y);

      location += pin_dist;
    }
  }
}

int InitFloorplan::snapToMfgGrid(int coord) const
{
  dbTech* tech = block_->getDataBase()->getTech();
  if (tech->hasManufacturingGrid()) {
    int grid = tech->getManufacturingGrid();
    return round(coord / (double) grid) * grid;
  }

  return coord;
}

void InitFloorplan::insertTiecells(odb::dbMTerm* tie_term, const char* prefix)
{
  auto* master = tie_term->getMaster();

  auto* port = network_->dbToSta(tie_term);
  auto* lib_port = network_->libertyPort(port);
  auto func_operation = lib_port->function()->op();
  const bool is_zero = func_operation == sta::FuncExpr::op_zero;
  const bool is_one = func_operation == sta::FuncExpr::op_one;

  odb::dbSigType look_for;
  if (is_zero) {
    look_for = odb::dbSigType::GROUND;
  } else if (is_one) {
    look_for = odb::dbSigType::POWER;
  } else {
    logger_->error(utl::IFP,
                   29,
                   "Unable to determine tiecell ({}) function.",
                   master->getName());
  }

  int count = 0;

  for (auto* net : block_->getNets()) {
    if (net->isSpecial()) {
      continue;
    }
    if (look_for != net->getSigType()) {
      continue;
    }

    std::string inst_name = prefix;
    inst_name += net->getName();

    auto* inst = odb::dbInst::create(block_, master, inst_name.c_str());
    auto* iterm = inst->findITerm(tie_term->getConstName());
    iterm->connect(net);
    net->setSigType(odb::dbSigType::SIGNAL);
    count++;
  }

  logger_->info(utl::IFP,
                30,
                "Inserted {} tiecells using {}/{}.",
                count,
                master->getName(),
                tie_term->getName());
}

void InitFloorplan::makeTracks()
{
  for (auto layer : block_->getDataBase()->getTech()->getLayers()) {
    if (layer->getType() == dbTechLayerType::ROUTING
        && layer->getRoutingLevel() != 0) {
      makeTracks(layer,
                 layer->getOffsetX(),
                 layer->getPitchX(),
                 layer->getOffsetY(),
                 layer->getPitchY());
    }
  }
}

void InitFloorplan::makeTracks(odb::dbTechLayer* layer,
                               int x_offset,
                               int x_pitch,
                               int y_offset,
                               int y_pitch)
{
  Rect die_area;
  block_->getDieArea(die_area);
  auto grid = block_->findTrackGrid(layer);
  if (!grid) {
    grid = dbTrackGrid::create(block_, layer);
  }

  if (y_offset == 0) {
    y_offset = y_pitch;
  }
  if (x_offset > die_area.dx()) {
    logger_->warn(
        IFP,
        21,
        "Track pattern for {} will be skipped due to x_offset > die width.",
        layer->getName());
    return;
  }
  if (y_offset > die_area.dy()) {
    logger_->warn(
        IFP,
        22,
        "Track pattern for {} will be skipped due to y_offset > die height.",
        layer->getName());
    return;
  }
  auto x_track_count = int((die_area.dx() - x_offset) / x_pitch) + 1;
  grid->addGridPatternX(die_area.xMin() + x_offset, x_track_count, x_pitch);
<commit_msg>ifp: add validation to makeTracks<commit_after>/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2019, The Regents of the University of California
// All rights reserved.
//
// BSD 3-Clause License
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////

#include <cmath>
#include <fstream>
#include <iostream>

#include "db_sta/dbNetwork.hh"
#include "ifp/InitFloorplan.hh"
#include "odb/db.h"
#include "odb/dbTransform.h"
#include "odb/util.h"
#include "sta/FuncExpr.hh"
#include "sta/Liberty.hh"
#include "sta/PortDirection.hh"
#include "sta/StringUtil.hh"
#include "sta/Vector.hh"
#include "utl/Logger.h"
#include "utl/validation.h"

namespace ifp {

using std::abs;
using std::ceil;
using std::map;
using std::round;
using std::set;
using std::string;

using sta::dbNetwork;
using sta::stdstrPrint;
using sta::stringEqual;
using sta::StringVector;
using sta::Vector;

using utl::IFP;
using utl::Logger;

using odb::dbBlock;
using odb::dbBox;
using odb::dbBPin;
using odb::dbBTerm;
using odb::dbChip;
using odb::dbDatabase;
using odb::dbGroup;
using odb::dbGroupType;
using odb::dbInst;
using odb::dbLib;
using odb::dbMaster;
using odb::dbOrientType;
using odb::dbPlacementStatus;
using odb::dbRegion;
using odb::dbRow;
using odb::dbRowDir;
using odb::dbSet;
using odb::dbSite;
using odb::dbTech;
using odb::dbTechLayer;
using odb::dbTechLayerDir;
using odb::dbTechLayerType;
using odb::dbTrackGrid;
using odb::dbTransform;
using odb::Rect;

InitFloorplan::InitFloorplan(dbBlock* block,
                             Logger* logger,
                             sta::dbNetwork* network)
    : block_(block), logger_(logger), network_(network)
{
}

void InitFloorplan::initFloorplan(double utilization,
                                  double aspect_ratio,
                                  int core_space_bottom,
                                  int core_space_top,
                                  int core_space_left,
                                  int core_space_right,
                                  const char* site_name)
{
  utl::Validator v(logger_, IFP);
  v.check_percentage("utilization", utilization, 12);
  v.check_non_negative("core_space_bottom", core_space_bottom, 32);
  v.check_non_negative("core_space_top", core_space_top, 33);
  v.check_non_negative("core_space_left", core_space_left, 34);
  v.check_non_negative("core_space_right", core_space_right, 35);
  v.check_non_negative("aspect_ratio", aspect_ratio, 36);
  v.check_non_null("site_name", site_name, 37);

  utilization /= 100;
  const double design_area = designArea();
  const double core_area = design_area / utilization;
  const int core_width = std::sqrt(core_area / aspect_ratio);
  const int core_height = round(core_width * aspect_ratio);

  const int core_lx = core_space_left;
  const int core_ly = core_space_bottom;
  const int core_ux = core_lx + core_width;
  const int core_uy = core_ly + core_height;
  const int die_lx = 0;
  const int die_ly = 0;
  const int die_ux = core_ux + core_space_right;
  const int die_uy = core_uy + core_space_top;
  initFloorplan({die_lx, die_ly, die_ux, die_uy},
                {core_lx, core_ly, core_ux, core_uy},
                site_name);
}

double InitFloorplan::designArea()
{
  double design_area = 0.0;
  for (dbInst* inst : block_->getInsts()) {
    dbMaster* master = inst->getMaster();
    const double area
        = master->getHeight() * static_cast<double>(master->getWidth());
    design_area += area;
  }
  return design_area;
}

static int divCeil(int dividend, int divisor)
{
  return ceil(static_cast<double>(dividend) / divisor);
}

void InitFloorplan::initFloorplan(const odb::Rect& die,
                                  const odb::Rect& core,
                                  const char* site_name)
{
  Rect die_area(snapToMfgGrid(die.xMin()),
                snapToMfgGrid(die.yMin()),
                snapToMfgGrid(die.xMax()),
                snapToMfgGrid(die.yMax()));
  block_->setDieArea(die_area);

  if (site_name && site_name[0] && core.xMin() >= 0 && core.yMin() >= 0) {
    dbSite* site = findSite(site_name);
    if (site) {
      // Destroy any existing rows.
      auto rows = block_->getRows();
      for (dbSet<dbRow>::iterator row_itr = rows.begin(); row_itr != rows.end();
           row_itr = dbRow::destroy(row_itr))
        ;

      uint site_dx = site->getWidth();
      uint site_dy = site->getHeight();
      // core lower left corner to multiple of site dx/dy.
      int clx = divCeil(core.xMin(), site_dx) * site_dx;
      int cly = divCeil(core.yMin(), site_dy) * site_dy;
      if (clx != core.xMin() || cly != core.yMin()) {
        dbTech* tech = block_->getDataBase()->getTech();
        const double dbu = tech->getDbUnitsPerMicron();
        logger_->warn(IFP,
                      28,
                      "Core area lower left ({:.3f}, {:.3f}) snapped to "
                      "({:.3f}, {:.3f}).",
                      core.xMin() / dbu,
                      core.yMin() / dbu,
                      clx / dbu,
                      cly / dbu);
      }
      int cux = core.xMax();
      int cuy = core.yMax();
      makeRows(site, clx, cly, cux, cuy);
      updateVoltageDomain(site, clx, cly, cux, cuy);
    } else
      logger_->warn(IFP, 9, "SITE {} not found.", site_name);
  }

  std::vector<dbBox*> blockage_bboxes;
  for (auto blockage : block_->getBlockages()) {
    blockage_bboxes.push_back(blockage->getBBox());
  }

  odb::cutRows(block_,
               /* min_row_width */ 0,
               blockage_bboxes,
               /* halo_x */ 0,
               /* halo_y */ 0,
               logger_);
}

// this function is used to create regions ( split overlapped rows and create
// new ones )
void InitFloorplan::updateVoltageDomain(dbSite* site,
                                        int core_lx,
                                        int core_ly,
                                        int core_ux,
                                        int core_uy)
{
  // The unit for power_domain_y_space is the site height. The real space is
  // power_domain_y_space * site_dy
  static constexpr int power_domain_y_space = 6;
  uint site_dy = site->getHeight();
  uint site_dx = site->getWidth();

  // checks if a group is defined as a voltage domain, if so it creates a region
  for (dbGroup* group : block_->getGroups()) {
    if (group->getType() == dbGroupType::VOLTAGE_DOMAIN) {
      dbRegion* domain_region = dbRegion::create(block_, group->getName());

      string domain_name = group->getName();
      Rect domain_rect = group->getBox();
      int domain_xMin = domain_rect.xMin();
      int domain_yMin = domain_rect.yMin();
      int domain_xMax = domain_rect.xMax();
      int domain_yMax = domain_rect.yMax();
      dbBox::create(
          domain_region, domain_xMin, domain_yMin, domain_xMax, domain_yMax);

      dbSet<dbRow> rows = block_->getRows();
      int total_row_count = rows.size();

      dbSet<dbRow>::iterator row_itr = rows.begin();
      for (int row_processed = 0; row_processed < total_row_count;
           row_processed++) {
        dbRow* row = *row_itr;
        Rect row_bbox;
        row->getBBox(row_bbox);
        int row_yMin = row_bbox.yMin();
        int row_yMax = row_bbox.yMax();

        // check if the rows overlapped with the area of a defined voltage
        // domains + margin
        if (row_yMax + power_domain_y_space * site_dy <= domain_yMin
            || row_yMin >= domain_yMax + power_domain_y_space * site_dy) {
          row_itr++;
        } else {
          string row_name = row->getName();
          dbOrientType orient = row->getOrient();
          row_itr = dbRow::destroy(row_itr);

          // lcr stands for left core row
          int lcr_xMax = domain_xMin - power_domain_y_space * site_dy;
          // in case there is at least one valid site width on the left, create
          // left core rows
          if (lcr_xMax > core_lx + site_dx) {
            string lcr_name = row_name + "_1";
            // warning message since tap cells might not be inserted
            if (lcr_xMax < core_lx + 10 * site_dx) {
              logger_->warn(IFP,
                            26,
                            "left core row: {} has less than 10 sites",
                            lcr_name);
            }
            int lcr_sites = (lcr_xMax - core_lx) / site_dx;
            dbRow::create(block_,
                          lcr_name.c_str(),
                          site,
                          core_lx,
                          row_yMin,
                          orient,
                          dbRowDir::HORIZONTAL,
                          lcr_sites,
                          site_dx);
          }

          // rcr stands for right core row
          // rcr_dx_site_number is the max number of site_dx that is less than
          // power_domain_y_space * site_dy. This helps align the rcr_xMin on
          // the multiple of site_dx.
          int rcr_dx_site_number = (power_domain_y_space * site_dy) / site_dx;
          int rcr_xMin = domain_xMax + rcr_dx_site_number * site_dx;

          // in case there is at least one valid site width on the right, create
          // right core rows
          if (rcr_xMin + site_dx < core_ux) {
            string rcr_name = row_name + "_2";
            if (rcr_xMin + 10 * site_dx > core_ux) {
              logger_->warn(IFP,
                            27,
                            "right core row: {} has less than 10 sites",
                            rcr_name);
            }
            int rcr_sites = (core_ux - rcr_xMin) / site_dx;
            dbRow::create(block_,
                          rcr_name.c_str(),
                          site,
                          rcr_xMin,
                          row_yMin,
                          orient,
                          dbRowDir::HORIZONTAL,
                          rcr_sites,
                          site_dx);
          }

          int domain_row_sites = (domain_xMax - domain_xMin) / site_dx;
          // create domain rows if current iterations are not in margin area
          if (row_yMin >= domain_yMin && row_yMax <= domain_yMax) {
            string domain_row_name = row_name + "_" + domain_name;
            dbRow::create(block_,
                          domain_row_name.c_str(),
                          site,
                          domain_xMin,
                          row_yMin,
                          orient,
                          dbRowDir::HORIZONTAL,
                          domain_row_sites,
                          site_dx);
          }
        }
      }
    }
  }
}

void InitFloorplan::makeRows(dbSite* site,
                             int core_lx,
                             int core_ly,
                             int core_ux,
                             int core_uy)
{
  int core_dx = abs(core_ux - core_lx);
  int core_dy = abs(core_uy - core_ly);
  uint site_dx = site->getWidth();
  uint site_dy = site->getHeight();
  int rows_x = core_dx / site_dx;
  int rows_y = core_dy / site_dy;

  int y = core_ly;
  for (int row = 0; row < rows_y; row++) {
    dbOrientType orient = (row % 2 == 0) ? dbOrientType::R0   // N
                                         : dbOrientType::MX;  // FS
    string row_name = stdstrPrint("ROW_%d", row);
    dbRow::create(block_,
                  row_name.c_str(),
                  site,
                  core_lx,
                  y,
                  orient,
                  dbRowDir::HORIZONTAL,
                  rows_x,
                  site_dx);
    y += site_dy;
  }
  logger_->info(IFP, 1, "Added {} rows of {} sites.", rows_y, rows_x);
}

dbSite* InitFloorplan::findSite(const char* site_name)
{
  for (dbLib* lib : block_->getDataBase()->getLibs()) {
    dbSite* site = lib->findSite(site_name);
    if (site)
      return site;
  }
  return nullptr;
}

////////////////////////////////////////////////////////////////

void InitFloorplan::autoPlacePins(const char* pin_layer_name)
{
  dbTech* tech = block_->getDataBase()->getTech();
  dbTechLayer* pin_layer = tech->findLayer(pin_layer_name);
  if (pin_layer) {
    odb::Rect core;
    block_->getCoreArea(core);
    autoPlacePins(pin_layer, core);
  } else
    logger_->warn(IFP, 2, "pin layer {} not found.", pin_layer_name);
}

void InitFloorplan::autoPlacePins(dbTechLayer* pin_layer, Rect& core)
{
  dbSet<dbBTerm> bterms = block_->getBTerms();
  int pin_count = bterms.size();

  if (pin_count > 0) {
    int dx = core.dx();
    int dy = core.dy();
    int perimeter = dx * 2 + dy * 2;
    double location = 0.0;
    int pin_dist = perimeter / pin_count;

    for (dbBTerm* bterm : bterms) {
      int x, y;
      dbOrientType orient;
      if (location < dx) {
        // bottom
        x = core.xMin() + location;
        y = core.yMin();
        orient = dbOrientType::R180;  // S
      } else if (location < (dx + dy)) {
        // right
        x = core.xMax();
        y = core.yMin() + (location - dx);
        orient = dbOrientType::R270;  // E
      } else if (location < (dx * 2 + dy)) {
        // top
        x = core.xMax() - (location - (dx + dy));
        y = core.yMax();
        orient = dbOrientType::R0;  // N
      } else {
        // left
        x = core.xMin();
        y = core.yMax() - (location - (dx * 2 + dy));
        orient = dbOrientType::R90;  // W
      }

      // Delete existing BPins.
      dbSet<dbBPin> bpins = bterm->getBPins();
      for (auto bpin_itr = bpins.begin(); bpin_itr != bpins.end();)
        bpin_itr = dbBPin::destroy(bpin_itr);

      dbBPin* bpin = dbBPin::create(bterm);
      bpin->setPlacementStatus(dbPlacementStatus::FIRM);
      dbBox::create(bpin, pin_layer, x, y, x, y);

      location += pin_dist;
    }
  }
}

int InitFloorplan::snapToMfgGrid(int coord) const
{
  dbTech* tech = block_->getDataBase()->getTech();
  if (tech->hasManufacturingGrid()) {
    int grid = tech->getManufacturingGrid();
    return round(coord / (double) grid) * grid;
  }

  return coord;
}

void InitFloorplan::insertTiecells(odb::dbMTerm* tie_term, const char* prefix)
{
  auto* master = tie_term->getMaster();

  auto* port = network_->dbToSta(tie_term);
  auto* lib_port = network_->libertyPort(port);
  auto func_operation = lib_port->function()->op();
  const bool is_zero = func_operation == sta::FuncExpr::op_zero;
  const bool is_one = func_operation == sta::FuncExpr::op_one;

  odb::dbSigType look_for;
  if (is_zero) {
    look_for = odb::dbSigType::GROUND;
  } else if (is_one) {
    look_for = odb::dbSigType::POWER;
  } else {
    logger_->error(utl::IFP,
                   29,
                   "Unable to determine tiecell ({}) function.",
                   master->getName());
  }

  int count = 0;

  for (auto* net : block_->getNets()) {
    if (net->isSpecial()) {
      continue;
    }
    if (look_for != net->getSigType()) {
      continue;
    }

    std::string inst_name = prefix;
    inst_name += net->getName();

    auto* inst = odb::dbInst::create(block_, master, inst_name.c_str());
    auto* iterm = inst->findITerm(tie_term->getConstName());
    iterm->connect(net);
    net->setSigType(odb::dbSigType::SIGNAL);
    count++;
  }

  logger_->info(utl::IFP,
                30,
                "Inserted {} tiecells using {}/{}.",
                count,
                master->getName(),
                tie_term->getName());
}

void InitFloorplan::makeTracks()
{
  for (auto layer : block_->getDataBase()->getTech()->getLayers()) {
    if (layer->getType() == dbTechLayerType::ROUTING
        && layer->getRoutingLevel() != 0) {
      makeTracks(layer,
                 layer->getOffsetX(),
                 layer->getPitchX(),
                 layer->getOffsetY(),
                 layer->getPitchY());
    }
  }
}

void InitFloorplan::makeTracks(odb::dbTechLayer* layer,
                               int x_offset,
                               int x_pitch,
                               int y_offset,
                               int y_pitch)
{
  utl::Validator v(logger_, IFP);
  v.check_non_null("layer", layer, 38);
  v.check_non_negative("x_offset", x_offset, 39);
  v.check_positive("x_pitch", x_offset, 40);
  v.check_non_negative("y_offset", x_offset, 41);
  v.check_positive("y_pitch", x_offset, 42);

  Rect die_area;
  block_->getDieArea(die_area);
  auto grid = block_->findTrackGrid(layer);
  if (!grid) {
    grid = dbTrackGrid::create(block_, layer);
  }

  if (y_offset == 0) {
    y_offset = y_pitch;
  }
  if (x_offset > die_area.dx()) {
    logger_->warn(
        IFP,
        21,
        "Track pattern for {} will be skipped due to x_offset > die width.",
        layer->getName());
    return;
  }
  if (y_offset > die_area.dy()) {
    logger_->warn(
        IFP,
        22,
        "Track pattern for {} will be skipped due to y_offset > die height.",
        layer->getName());
    return;
  }
  auto x_track_count = int((die_area.dx() - x_offset) / x_pitch) + 1;
  grid->addGridPatternX(die_area.xMin() + x_offset, x_track_count, x_pitch);

{'fraction_non_alphanumeric': 0.12616050886452834, 'fraction_numerical': 0.005088645283529571, 'mean_word_length': 1.977355145458941, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 207, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7433683', 'n_tokens_mistral': 11779, 'n_tokens_neox': 11287, 'n_words': 2872}