coriolis/anabatic/src/NetBuilderM2.cpp

353 lines
13 KiB
C++
Raw Normal View History

// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2008-2018, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | A n a b a t i c - Routing Toolbox |
// | |
// | Author : Jean-Paul CHAPUT |
// | E-mail : Jean-Paul.Chaput@lip6.fr |
// | =============================================================== |
// | C++ Module : "./NetBuilderM2.cpp" |
// +-----------------------------------------------------------------+
#include <cstdlib>
#include <sstream>
#include "hurricane/Bug.h"
#include "hurricane/Breakpoint.h"
#include "hurricane/Error.h"
#include "hurricane/Warning.h"
#include "hurricane/DebugSession.h"
#include "hurricane/Layer.h"
#include "hurricane/BasicLayer.h"
#include "hurricane/RegularLayer.h"
#include "hurricane/Technology.h"
#include "hurricane/DataBase.h"
#include "hurricane/Net.h"
#include "hurricane/NetExternalComponents.h"
#include "hurricane/NetRoutingProperty.h"
#include "hurricane/RoutingPad.h"
#include "hurricane/RoutingPads.h"
#include "hurricane/Pad.h"
#include "hurricane/Plug.h"
#include "hurricane/Cell.h"
#include "hurricane/Instance.h"
#include "hurricane/Vertical.h"
#include "hurricane/Horizontal.h"
#include "crlcore/AllianceFramework.h"
#include "crlcore/RoutingGauge.h"
#include "crlcore/Measures.h"
#include "anabatic/AutoContactTerminal.h"
#include "anabatic/AutoContactTurn.h"
#include "anabatic/AutoContactHTee.h"
#include "anabatic/AutoContactVTee.h"
#include "anabatic/AutoSegment.h"
#include "anabatic/NetBuilderM2.h"
#include "anabatic/AnabaticEngine.h"
namespace Anabatic {
using std::swap;
NetBuilderM2::NetBuilderM2 ()
: NetBuilder()
{ }
NetBuilderM2::~NetBuilderM2 () { }
void NetBuilderM2::doRp_AutoContacts ( GCell* gcell
, Component* rp
, AutoContact*& source
, AutoContact*& target
, uint64_t flags
)
{
cdebug_log(145,1) << "NetBuilderM2::doRp_AutoContacts()" << endl;
cdebug_log(145,0) << rp << endl;
source = target = NULL;
Point sourcePosition;
Point targetPosition;
const Layer* rpLayer = rp->getLayer();
size_t rpDepth = Session::getLayerDepth( rp->getLayer() );
Flags direction = Session::getDirection ( rpDepth );
DbU::Unit viaSide = Session::getViaWidth ( rpDepth );
getPositions( rp, sourcePosition, targetPosition );
if (sourcePosition.getX() > targetPosition.getX()) swap( sourcePosition, targetPosition );
if (sourcePosition.getY() > targetPosition.getY()) swap( sourcePosition, targetPosition );
GCell* sourceGCell = Session::getAnabatic()->getGCellUnder( sourcePosition );
GCell* targetGCell = Session::getAnabatic()->getGCellUnder( targetPosition );
if (rpDepth == 0) {
rpLayer = Session::getContactLayer(0);
direction = Flags::Horizontal;
viaSide = Session::getViaWidth( rpDepth );
}
// Non-M1 terminal or punctual M1 protections.
if ((rpDepth != 0) or (sourcePosition == targetPosition)) {
map<Component*,AutoSegment*>::iterator irp = getRpLookup().find( rp );
if (irp == getRpLookup().end()) {
AutoContact* sourceProtect = AutoContactTerminal::create( sourceGCell
, rp
, rpLayer
, sourcePosition
, viaSide, viaSide
);
AutoContact* targetProtect = AutoContactTerminal::create( targetGCell
, rp
, rpLayer
, targetPosition
, viaSide, viaSide
);
sourceProtect->setFlags( CntFixed );
targetProtect->setFlags( CntFixed );
AutoSegment* segment = AutoSegment::create( sourceProtect, targetProtect, direction );
segment->setFlags( AutoSegment::SegFixed );
getRpLookup().insert( make_pair(rp,segment) );
}
}
if (sourcePosition != targetPosition) {
if (flags & DoSourceContact)
source = AutoContactTerminal::create( sourceGCell
, rp
, rpLayer
, sourcePosition
, viaSide, viaSide
);
if (flags & DoTargetContact)
target = AutoContactTerminal::create( targetGCell
, rp
, rpLayer
, targetPosition
, viaSide, viaSide
);
}
if (not source and not target) {
source = target = AutoContactTerminal::create( gcell
, rp
, rpLayer
, rp->getCenter()
, viaSide, viaSide
);
}
cdebug_tabw(145,-1);
return;
}
AutoContact* NetBuilderM2::doRp_Access ( GCell* gcell, Component* rp, uint64_t flags )
{
cdebug_log(145,1) << getTypeName() << "::doRp_Access()" << endl;
cdebug_log(145,0) << rp << endl;
Point sourcePosition;
Point targetPosition;
const Layer* rpLayer = rp->getLayer();
const Layer* viaLayer = Session::getDContactLayer();
DbU::Unit viaSide = Session::getDContactWidth();
DbU::Unit ypitch = Session::getDVerticalPitch();
getPositions( rp, sourcePosition, targetPosition );
if (sourcePosition.getX() > targetPosition.getX()) swap( sourcePosition, targetPosition );
if (sourcePosition.getY() > targetPosition.getY()) swap( sourcePosition, targetPosition );
Point position = rp->getCenter();
if (not (flags & Middle)) {
if (flags & NorthBound) position = targetPosition;
if (flags & SouthBound) position = sourcePosition;
}
DbU::Unit ycontact = (flags & SouthBound) ? gcell->getYMin() : gcell->getYMax()-ypitch;
AutoContact* rpContact = AutoContactTerminal::create( gcell, rp, rpLayer, position, viaSide, viaSide );
AutoContact* contact1 = AutoContactTurn::create( gcell, getNet(), viaLayer );
AutoContact* contact2 = AutoContactTurn::create( gcell, getNet(), viaLayer );
contact1->setPosition( position.getX(), ycontact );
contact2->setPosition( position.getX(), ycontact );
rpContact->setFlags( CntFixed );
contact1 ->setFlags( CntFixed );
contact2 ->setFlags( CntFixed );
AutoSegment* fixed = AutoSegment::create( rpContact, contact1, Flags::Vertical );
Update the channel routing feature to integrate with the OTC P&R. * Update: In CRL/node600/phenitec/kite.py, update the routing gauge to the new format. So now we can use again SxLib-2M (channel routing SxLib for two metal technologies). * Change: In CRL::BlifParser, if a master cell is not found in the AllianceFramework, then try in the Blif supplied libraries. This is used to load the zero, one and tie cells. Add a Blif::getCell() static function to look into the Blif supplied libraries. * Change: In CRL::LefImport, sometimes there can be discrepencies between the LEF ROUTING layers and the Coriolis routing gauge. Now ignore routing layers that are *not* presents in the Coriolis gauge. * Change: In AnabaticEngine, moved routingMode attribute from KatanaEngine, as some setup operations needs it. * Change: In AutoSegment::canReduce(), allow fixed segments to be reduced if they are "jumpers" (turn+turn and top+top or bot+bot). This case arise on the edge of routing channels for fixed wires to connect terminals. * Change: In AutoSegment::getTopologicalinfos(), compute differently the (leftBound,rightBound) interval when in channel mode. In over-the-cell mode, this interval is the one of the whole GCells under the wire. In channel mode, for fixed wires (that is, verticals connecteds to cells) this interval is reduced to half the GCell height, on the connected side of said channel. This allows Manipulator::_insertToTrack() to issue disantangling requests (push left/push right) for fixed segments that are face to face in the channel. * Change: In Anabatic::Configuration CTOR, allow the cellGauge to have a different name from the routingGauge. Now if the cell gauge that should match the routing gauge is not found, fallback to the name set in "anabatic.cellGauge" parameter. Case occur when we try to match with CORE sites from LEF files. * Change: In Etesian::Configuration CTOR, same change as in the Anabatic configuration. * Change: In Anabatic::GCell::updateDensity(), never set the GoStraight flag in channel mode. This flag makes sense when there is at least 4 routing layers (so we have 2 contiguous free of blockages). * Bug: In Anabatic::Session::_getNearestGridpoint(), sometimes the nearest on grid point is outside the constraint box. Now force the point to remains inside constraints even if offgrid. * Change: In Katana::DataNegociate::update(), perpandiculars that are either reduced or in non-preferred routing direction should not trigger a bug message. * Change: In KatanaEngine::_check(), do not check for fixed, horizontal non-prefs AutoSegments in channel mode (avoid false bug display). * Bug: In Manipulator::_forceToTrack(), slighty shrink (-1) the interval to free. The intersection function of intervals returns true when the two intervals *exactly* touches (1.vMax == 2.vMin). But in this specific case, they are not *overlapping* and no action should be taken... * Bug: In Manipulator::_insertInTrack(), do not reject the track when we are overlapping a fixed vertical segment in channel mode. (Hmm, maybe already corrected by the previous one). * Change: In Katana::NegociateOverlapCost(), in channel mode, do not put two overlaping vertical fixed segments into infinite cost. This happens when two cell connected verticals are face to face in a channel. We want them negociated the track (by shrinking their length) instead of excluding it right away. * Change: In NegociateWindow::createTrackSegment(), in channel mode, do not attempt to create a track segment over a fixed and reduced AutoSegment. Do not attempt to put a non-preferred AutoSegment on a Track either. * Bug: In RoutingEvent::revalidate(), the number of availables tracks was badly computed when in the pure constraint case, when there was only one it was reporting zero. * Change: In TrackElements::TrackElements_Perpandicular::Locator, do not issue a bug when an non-pref or reduced AutoSegment do not have an associated TrackElement. * Change: In TrackSegmentCost::update(), do not issue a bug when a perpandicular is reduded or non-pref and do not have a TrackElement.
2022-10-22 09:39:22 -05:00
AutoSegment* dogleg = AutoSegment::create( contact1 , contact2, Flags::Horizontal|Flags::UseNonPref );
fixed ->setFlags( AutoSegment::SegFixed );
dogleg->setFlags( AutoSegment::SegFixed );
cdebug_tabw(145,-1);
return contact2;
}
bool NetBuilderM2::_do_1G_1M1 ()
{
cdebug_log(145,1) << getTypeName() << "::_do_1G_1M1()" << endl;
uint64_t flags = NoFlags;
if (north()) flags |= NorthBound;
else if (south()) flags |= SouthBound;
AutoContact* contact = NULL;
contact = doRp_Access( getGCell(), getRoutingPads()[0], flags );
setNorthEastContact( contact );
push( north(), contact, SouthWest );
push( south(), contact, SouthWest );
cdebug_tabw(145,-1);
return true;
}
bool NetBuilderM2::_do_2G_1M1 ()
{
cdebug_log(145,1) << getTypeName() << "::_do_2G_1M1()" << endl;
AutoContact* contact = NULL;
contact = doRp_Access( getGCell(), getRoutingPads()[0], SouthBound|NorthBound );
push( north(), contact, SouthWest|Middle );
contact = doRp_Access( getGCell(), getRoutingPads()[0], SouthBound|NorthBound );
push( south(), contact, SouthWest|Middle );
cdebug_tabw(145,-1);
return true;
}
bool NetBuilderM2::_do_xG ()
{
cdebug_log(145,1) << getTypeName() << "::_do_xG()" << endl;
vector<Hook*> hooksNS = getNorths();
hooksNS.insert( hooksNS.end(), getSouths().begin(), getSouths().end() );
In Anabatic/Katana, add support for VH gauges (real technos). * Change: In Anabatic::AutoContactTerminal::getNativeConstraintBox(), when the anchor is a RoutingPad (which must be always the case), perform the true computation of it's position based on the segment occurrence. It is a important change, previously the area was in fact the "center line" of the connector while now it is really an area (mandatory for "half-offgrid" terminals of real technologies). The change is not complete yet, the area should be shrinked by the half size of a VIA, because the area applies to the center coordinate of the VIA (to be done quickly). * Bug: In Anabatic::AutoContactTurn::updateTopology(), when a dogleg is created (restore connexity after a layer change) the layer of the VIA, based on the segments it connects to must be re-computed *after* the dogleg has been made. * Change: In all files of Anabatic, when comparing two layers, no longer use the Layer pointer itself, but the layer mask. This allow a transparent management of both real and symbolic layers (which do share the same mask). Real metal layers (not VIAs) will be BasicLayer and symbolic metal layers will be RegularLayer. * New: Anabatic::Configuration::selectRpComponent(), select the best RoutingPad component for metal1 terminals. Look for the metal1 component with the biggest accessibility on-grid. RoutingPad using other metals are left untoucheds. * New: New function Anabatic::Vertex::getNeighbor(Edge*) to get the neighbor Vertex through an Edge*. This method allows to write clearer code as we no longer need to access the neighbor through the underlying GCell. Also add proxies for GCell methods in Vertex. * Bug: In Anabatic::Dijkstra::_toSources(), in the ripup stage, when a component with multiples vertexes is reached *and* two of it's vertexes are reached *at the same time* (one from which we backtrack and one still in the queue) extraneous edges may be created by _materialize(). Case occurs on snx/c35b4, "abc_5360_n903_1". To solve this, Dijkstra::_toSource() is modificated, the "from" edges of the newly reacheds vertexes are reset to NULL, *except* for the one we will be backtracking from. That is, the one given in the source argument. * Change: In Anabatic::NetBuilder class, put the various Hooks and RoutingPad sorting functions as class ones. * Bug: In AutoSegment::setLayer(), raise the SegInvalidatedFayer flag. This unset flag was causing AutoContactTurn::updateTopology() to not work as expected and making gaps, this was the cause of the last remaining warnings about layer connexity.
2018-01-06 09:55:53 -06:00
sortHookByX( hooksNS, NoFlags );
const Layer* viaLayer = Session::getDContactLayer();
AutoContact* contactW = NULL;
AutoContact* contactE = NULL;
// Simple turn.
if ( (west() and not east() and (hooksNS.size() == 1))
or (east() and not west() and (hooksNS.size() == 1)) ) {
contactW = AutoContactTurn::create( getGCell(), getNet(), viaLayer );
push( west() , contactW, SouthWest );
push( east() , contactW, SouthWest );
push( hooksNS[0], contactW, SouthWest );
cdebug_tabw(145,-1);
return true;
}
// Simple HTee.
if (west() and east() and (hooksNS.size() == 1)) {
contactW = AutoContactHTee::create( getGCell(), getNet(), viaLayer );
push( west() , contactW, SouthWest );
push( east() , contactW, SouthWest );
push( hooksNS[0], contactW, SouthWest );
cdebug_tabw(145,-1);
return true;
}
cdebug_log(145,0) << "West side processing." << endl;
// West side processing.
if (west()) {
contactW = AutoContactHTee::create( getGCell(), getNet(), viaLayer );
push( west() , contactW, SouthWest );
push( hooksNS[0], contactW, SouthWest );
} else {
contactW = AutoContactTurn::create( getGCell(), getNet(), viaLayer );
push( hooksNS[0], contactW, SouthWest );
}
cdebug_log(145,0) << "Middle processing." << endl;
// Middle (North & South) processing.
if (hooksNS.size() > 2) {
for ( size_t i=1 ; i<hooksNS.size()-1 ; ++i ) {
AutoContact* current = AutoContactHTee::create( getGCell(), getNet(), viaLayer );
AutoSegment::create( contactW, current, Flags::Horizontal );
push( hooksNS[i], current, SouthWest );
contactW = current;
}
}
cdebug_log(145,0) << "East side processing." << endl;
// East side processing.
if (east()) {
contactE = AutoContactHTee::create( getGCell(), getNet(), viaLayer );
push( east(), contactE, SouthWest );
if (hooksNS.size() > 1)
push( hooksNS[hooksNS.size()-1], contactE, SouthWest );
} else {
contactE = AutoContactTurn::create( getGCell(), getNet(), viaLayer );
push( hooksNS[hooksNS.size()-1], contactE, SouthWest );
}
AutoSegment::create( contactW, contactE, Flags::Horizontal );
cdebug_tabw(145,-1);
return true;
}
bool NetBuilderM2::_do_globalSegment ()
{
cdebug_log(145,1) << getTypeName() << "::_do_globalSegment()" << endl;
if (getSourceContact()) {
Segment* segment = static_cast <Segment*> ( getFromHook()->getComponent() );
AutoSegment* globalSegment = AutoSegment::create( getSourceContact(), getSouthWestContact(), segment );
globalSegment->setFlags( (getDegree() == 2) ? AutoSegment::SegBipoint : 0 );
cdebug_log(145,0) << "Create global segment: " << globalSegment << endl;
// HARDCODED VALUE.
Rewrite support for minimum area metal wires (stacked VIAs). We use the segments extensions (dxTarget & dyTarget) to enlarge if needed the segments. This new implementation is completely at Anabatic level and should not be seen (i.e. managed) at Katana level. * Change: In AutoHorizontal & AutoVertical, change the semantic of getSourceU() and getTargetU(). formerly they where the end position of the segment (with extension included), now they gives the position of the anchor contacts, that is the axis of the perpandiculars. * New: AutoSegment::getLength() is still a proxy toward the Segment::getLength() which returns the length of segment with dxSource & dxTarget. We introduce a getAnchoredlength() which returns the length between the centers of the S/T anchors. That is axis to axis. This is this length which is now used througout Anabatic. * New: In AutoSegment::_extentionCaps, add a fourth item to hold the segment minimal length (to respect minimal area given the wire width). * New: In AutoSegment::getExtensionCap(), if the segment has a non-zero S/T extension, returns it instead of the S/T contact extension *if it is greated*. The check of the extension can be disabled by the Flag::NoSegExt flag. * Change: In AutoSegment::isMiddleStack(), security check on the presence of source and targets. More accurate detection of perpandicular in "same layer" with a non-zero length, So the area is OK, even with a short segment. * New: AutoSegment::expandToMinLength(), check if a segment is under the minimal length and expand it if need be by playing with the dxTarget & dxSource. Tag minimal segments with the AutoSegment::SegAtMinArea flag. Also try to keep the segment *inside* it's former (supposedly wider) interval. * New: AutoSegment::unexpandToMinLength(), to be called on a formerly minimal sized segment which as grown up. Reset it's S/T extensions to zero and unset the flag SegAtMinArea. * Change: In AutoHorizontal::updateOrient(), when the extension are non-zero, also swap them if needed, to keep the exact footprint of the segment. * New: In AutoSegment::revalidate(), check that the segment respect the minimal length (area), and expand it if needed. Conversely, if the segment has grown up from a minimal length state, reset it's extensions to zero. * Change: In Anabatic::Session::revalidate(), invalidateds segments are now sorted in such a way that the "middle stack" ones are revalidateds last. Not recall 100% why... * New: In Katana::TrackCost, add a computation of the free interval length we are into (if any). Not used yet, keep it for future use. * Change: In KatanaEngine::finalizeLayout(), remove the call to segments minimum area protection. It is now obsoleted by the new implementation in Anabatic. * Change: In Track::check(), call the minimum size/area checker Track::checkMinArea().
2021-04-04 17:01:54 -05:00
if ( (getTopology() & Global_Fixed) and (globalSegment->getAnchoredLength() < 2*Session::getSliceHeight()) )
addToFixSegments( globalSegment );
In Anabatic/Katana, add support for VH gauges (real technos). * Change: In Anabatic::AutoContactTerminal::getNativeConstraintBox(), when the anchor is a RoutingPad (which must be always the case), perform the true computation of it's position based on the segment occurrence. It is a important change, previously the area was in fact the "center line" of the connector while now it is really an area (mandatory for "half-offgrid" terminals of real technologies). The change is not complete yet, the area should be shrinked by the half size of a VIA, because the area applies to the center coordinate of the VIA (to be done quickly). * Bug: In Anabatic::AutoContactTurn::updateTopology(), when a dogleg is created (restore connexity after a layer change) the layer of the VIA, based on the segments it connects to must be re-computed *after* the dogleg has been made. * Change: In all files of Anabatic, when comparing two layers, no longer use the Layer pointer itself, but the layer mask. This allow a transparent management of both real and symbolic layers (which do share the same mask). Real metal layers (not VIAs) will be BasicLayer and symbolic metal layers will be RegularLayer. * New: Anabatic::Configuration::selectRpComponent(), select the best RoutingPad component for metal1 terminals. Look for the metal1 component with the biggest accessibility on-grid. RoutingPad using other metals are left untoucheds. * New: New function Anabatic::Vertex::getNeighbor(Edge*) to get the neighbor Vertex through an Edge*. This method allows to write clearer code as we no longer need to access the neighbor through the underlying GCell. Also add proxies for GCell methods in Vertex. * Bug: In Anabatic::Dijkstra::_toSources(), in the ripup stage, when a component with multiples vertexes is reached *and* two of it's vertexes are reached *at the same time* (one from which we backtrack and one still in the queue) extraneous edges may be created by _materialize(). Case occurs on snx/c35b4, "abc_5360_n903_1". To solve this, Dijkstra::_toSource() is modificated, the "from" edges of the newly reacheds vertexes are reset to NULL, *except* for the one we will be backtracking from. That is, the one given in the source argument. * Change: In Anabatic::NetBuilder class, put the various Hooks and RoutingPad sorting functions as class ones. * Bug: In AutoSegment::setLayer(), raise the SegInvalidatedFayer flag. This unset flag was causing AutoContactTurn::updateTopology() to not work as expected and making gaps, this was the cause of the last remaining warnings about layer connexity.
2018-01-06 09:55:53 -06:00
if (getConnexity().fields.globals < 2) {
cdebug_tabw(145,-1);
return false;
}
} else
setFromHook( NULL );
In Anabatic/Katana, add support for VH gauges (real technos). * Change: In Anabatic::AutoContactTerminal::getNativeConstraintBox(), when the anchor is a RoutingPad (which must be always the case), perform the true computation of it's position based on the segment occurrence. It is a important change, previously the area was in fact the "center line" of the connector while now it is really an area (mandatory for "half-offgrid" terminals of real technologies). The change is not complete yet, the area should be shrinked by the half size of a VIA, because the area applies to the center coordinate of the VIA (to be done quickly). * Bug: In Anabatic::AutoContactTurn::updateTopology(), when a dogleg is created (restore connexity after a layer change) the layer of the VIA, based on the segments it connects to must be re-computed *after* the dogleg has been made. * Change: In all files of Anabatic, when comparing two layers, no longer use the Layer pointer itself, but the layer mask. This allow a transparent management of both real and symbolic layers (which do share the same mask). Real metal layers (not VIAs) will be BasicLayer and symbolic metal layers will be RegularLayer. * New: Anabatic::Configuration::selectRpComponent(), select the best RoutingPad component for metal1 terminals. Look for the metal1 component with the biggest accessibility on-grid. RoutingPad using other metals are left untoucheds. * New: New function Anabatic::Vertex::getNeighbor(Edge*) to get the neighbor Vertex through an Edge*. This method allows to write clearer code as we no longer need to access the neighbor through the underlying GCell. Also add proxies for GCell methods in Vertex. * Bug: In Anabatic::Dijkstra::_toSources(), in the ripup stage, when a component with multiples vertexes is reached *and* two of it's vertexes are reached *at the same time* (one from which we backtrack and one still in the queue) extraneous edges may be created by _materialize(). Case occurs on snx/c35b4, "abc_5360_n903_1". To solve this, Dijkstra::_toSource() is modificated, the "from" edges of the newly reacheds vertexes are reset to NULL, *except* for the one we will be backtracking from. That is, the one given in the source argument. * Change: In Anabatic::NetBuilder class, put the various Hooks and RoutingPad sorting functions as class ones. * Bug: In AutoSegment::setLayer(), raise the SegInvalidatedFayer flag. This unset flag was causing AutoContactTurn::updateTopology() to not work as expected and making gaps, this was the cause of the last remaining warnings about layer connexity.
2018-01-06 09:55:53 -06:00
cdebug_tabw(145,-1);
return true;
}
bool NetBuilderM2::_do_1G_xM1 () { return false; }
bool NetBuilderM2::_do_xG_1Pad () { return false; }
bool NetBuilderM2::_do_1G_1PinM2 () { return false; }
bool NetBuilderM2::_do_xG_xM2 () { return false; }
bool NetBuilderM2::_do_1G_1M3 () { return false; }
bool NetBuilderM2::_do_xG_xM3 () { return false; }
bool NetBuilderM2::_do_xG_1M1_1M2 () { return false; }
bool NetBuilderM2::_do_xG_xM1_xM3 () { return false; }
bool NetBuilderM2::_do_4G_1M2 () { return false; }
bool NetBuilderM2::_do_2G () { return false; }
string NetBuilderM2::getTypeName () const
{ return "NetBuilderM2"; }
} // Anabatic namespace.