
#include "spRippleAdderSCDpathBuilder.h"
#include "spLeafCellArrayBuilder.h"
#include "strtools.h"
#include "spPen.h"

namespace spp {

  spRippleAdderSCDpathBuilder::spRippleAdderSCDpathBuilder( 
                             std::string instName, spCell* leafCellPtr )
   : m_array("fa",leafCellPtr), m_instName(instName), 
     m_leafCellPtr(leafCellPtr), m_bitWidth(1) { }

  void 
  spRippleAdderSCDpathBuilder::setBitWidth( int bitWidth ) 
  {
    m_bitWidth = bitWidth;

    m_array.setDim(1,m_bitWidth);

    std::string abutLayer = m_leafCellPtr->getTechnology().getValue("abutmentLayer");
    assert(abutLayer != string(""));
    m_array.useAbutmentBox(abutLayer);
    m_array.flipEvenRows();

    m_array.promoteLabelAndPort("A");
    m_array.promoteLabelAndPort("B");
    m_array.promoteLabelAndPort("S");
    m_array.promotePowerNets( "VDD", "VSS" );

  }

  std::string 
  spRippleAdderSCDpathBuilder::getName() const 
  { 
    return m_instName; 
  }

  sics::box 
  spRippleAdderSCDpathBuilder::getGridDim() const 
  {
    return sics::box( point(0,0), point(0,m_bitWidth-1) );
  }

  sics::box 
  spRippleAdderSCDpathBuilder::getPlacementBox( sics::point relativeLoc ) const 
  {
    return m_array.getPlacementBox(relativeLoc);
  }

  void 
  spRippleAdderSCDpathBuilder::fixPlacementBox( sics::point relativeLoc, sics::box fixBox ) 
  {
    sics::box bb = getPlacementBox(relativeLoc);
    assert((bb.getWidth() == fixBox.getWidth()) && (bb.getHeight() == fixBox.getHeight()));
  }

  list<string> 
  spRippleAdderSCDpathBuilder::getPortList( sics::point relativeLoc ) const 
  {
    return m_array.getPortList(relativeLoc);
  }

  sics::box 
  spRippleAdderSCDpathBuilder::getLabelBox( sics::point relPoint, string labelName ) const 
  {
    return m_array.getLabelBox(relPoint,labelName);
  }

  void 
  spRippleAdderSCDpathBuilder::setPortTrack( sics::point relPoint, 
                                             string pname, int trackAllocation ) 
  {
    m_array.setPortTrack(relPoint,pname,trackAllocation);
  }

  boost::shared_ptr<spBuilder> 
  spRippleAdderSCDpathBuilder::clone() const 
  {
    return boost::shared_ptr<spBuilder>(new spRippleAdderSCDpathBuilder(*this));
  }

  void 
  spRippleAdderSCDpathBuilder::genLayout( spCell& targetCell, sics::point loc ) 
  {
    using namespace strtools;
    using namespace sics;

    // Get some technology parameters

    spTechnology tech = targetCell.getTechnology();
    int M2_MIN_WIDTH = strToInt(tech.getValue("spLayer","metal2","minWidth"));
    int M2_MIN_SPACE = strToInt(tech.getValue("spLayer","metal2","minSpace"));

    m_array.genLayout(targetCell,loc);

    // Wire up the carry chain between adder cells

    spPen carryPen( targetCell, "metal2", "via12", "metal1" );
    carryPen.setViaExtensions( spPen::VIA_EXT_LR, spPen::VIA_EXT_LR );

    for ( int i = 0; i < m_bitWidth-1; i++ ) {

      spCellRef& FACell     = dynamic_cast<spCellRef&>(targetCell["fa_"+intToStr(i)+"_"]);
      spCellRef& nextFACell = dynamic_cast<spCellRef&>(targetCell["fa_"+intToStr(i+1)+"_"]);

      point coutPt    = FACell.getTransformedLabel("CO").getBoundingBox().getCenter();
      point cinPt     = FACell.getTransformedLabel("CI").getBoundingBox().getCenter();
      point nextCinPt = nextFACell.getTransformedLabel("CI").getBoundingBox().getCenter();

      carryPen.moveTo(coutPt);
      carryPen.drawVia();

      int drawDownAbsAmt = coutPt.getY() - M2_MIN_SPACE - M2_MIN_WIDTH;
      if ( cinPt.getY() < coutPt.getY() )
        drawDownAbsAmt = cinPt.getY() - M2_MIN_SPACE - M2_MIN_WIDTH;

      carryPen.drawDownAbs( drawDownAbsAmt );
      carryPen.drawLeft( coutPt.getX() - cinPt.getX() );
      carryPen.drawTo( nextCinPt );
      carryPen.drawVia();

      if ( targetCell.getModulePtr() != 0 )
        targetCell.getModulePtr()->wire(FACell.getName()+".CO",nextFACell.getName()+".CI");

    }

    // Wire up first carry in 

    spCellRef& firstFACell = dynamic_cast<spCellRef&>(targetCell["fa_0_"]);    
    point cinPt = firstFACell.getTransformedLabel("CI").getBoundingBox().getCenter();
    point topPt( cinPt.getX(), firstFACell.getBoundingBox().getTopR().getY() );

    carryPen.moveTo( cinPt );
    carryPen.drawVia();
    carryPen.drawTo( topPt );
    targetCell.add( spLabel("CI","metal2_pin",topPt) );

    if ( targetCell.getModulePtr() != 0 ) {
      targetCell.getModulePtr()->addPort( "CI", nlst::PORT_IN, 1 );
      targetCell.getModulePtr()->wire("CI","fa_0_.CI");
    }

    // Wire up last carry out

    spCellRef& lastFACell = dynamic_cast<spCellRef&>(targetCell["fa_"+intToStr(m_bitWidth-1)+"_"]);
    point coutPt = lastFACell.getTransformedLabel("CO").getBoundingBox().getCenter();
    point botPt( coutPt.getX(), lastFACell.getBoundingBox().getBotL().getY() );

    carryPen.moveTo( coutPt );
    carryPen.drawVia();
    carryPen.drawTo( botPt );
    targetCell.add( spLabel("CO","metal2_pin",botPt) );

    if ( targetCell.getModulePtr() != 0 ) {
      targetCell.getModulePtr()->addPort( "CO", nlst::PORT_OUT, 1 );
      targetCell.getModulePtr()->wire("CO",lastFACell.getName()+".CO");
    }

  }

  void 
  spRippleAdderSCDpathBuilder::genAbstractModule( nlst::Module& mod ) 
  {
    mod.addPort( "A", nlst::PORT_IN,  m_bitWidth );
    mod.addPort( "B", nlst::PORT_IN,  m_bitWidth );
    mod.addPort( "S", nlst::PORT_OUT, m_bitWidth );
    mod.addPort( "CI", nlst::PORT_IN, 1 );
    mod.addPort( "CO", nlst::PORT_OUT, 1 );
  }

}
