support equiv cells across libraries
This commit is contained in:
parent
e26da7c37e
commit
5f23536b17
|
|
@ -15,7 +15,6 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Machine.hh"
|
||||
#include "UnorderedMap.hh"
|
||||
#include "PortDirection.hh"
|
||||
#include "Transition.hh"
|
||||
#include "MinMax.hh"
|
||||
|
|
@ -31,17 +30,6 @@ namespace sta {
|
|||
|
||||
using std::max;
|
||||
|
||||
typedef UnorderedMap<unsigned, LibertyCellSeq*> LibertyCellHashMap;
|
||||
typedef Set<LibertyCell*> LibertyCellSet;
|
||||
|
||||
static void
|
||||
findEquivCells1(const LibertyLibrary *library,
|
||||
LibertyCellSet &cell_equivs);
|
||||
static void
|
||||
sortCellEquivs(LibertyCellSet &cell_equivs);
|
||||
static float
|
||||
cellDriveResistance(const LibertyCell *cell);
|
||||
|
||||
static unsigned
|
||||
hashCell(const LibertyCell *cell);
|
||||
static unsigned
|
||||
|
|
@ -55,91 +43,110 @@ hashPort(const LibertyPort *port);
|
|||
static unsigned
|
||||
hashString(const char *str);
|
||||
|
||||
static bool
|
||||
equivCellSequentials(const LibertyCell *cell1,
|
||||
const LibertyCell *cell2);
|
||||
|
||||
void
|
||||
findEquivCells(const LibertyLibrary *library)
|
||||
class CellDriveResistanceLess
|
||||
{
|
||||
LibertyCellSet cell_equivs;
|
||||
findEquivCells1(library, cell_equivs);
|
||||
// Sort by drive strength.
|
||||
sortCellEquivs(cell_equivs);
|
||||
public:
|
||||
bool operator()(const LibertyCell *cell1,
|
||||
const LibertyCell *cell2) const
|
||||
{
|
||||
return cell1->driveResistance() > cell2->driveResistance();
|
||||
}
|
||||
};
|
||||
|
||||
EquivCells::EquivCells(LibertyLibrarySeq *equiv_libs,
|
||||
LibertyLibrarySeq *map_libs)
|
||||
{
|
||||
LibertyCellHashMap hash_matches;
|
||||
for (auto lib : *equiv_libs)
|
||||
findEquivCells(lib, hash_matches);
|
||||
// Sort the equiv sets by drive resistance.
|
||||
for (auto cell : unique_equiv_cells_) {
|
||||
auto equivs = equiv_cells_.findKey(cell);
|
||||
sort(equivs, CellDriveResistanceLess());
|
||||
}
|
||||
if (map_libs) {
|
||||
for (auto lib : *map_libs)
|
||||
mapEquivCells(lib, hash_matches);
|
||||
}
|
||||
hash_matches.deleteContents();
|
||||
}
|
||||
|
||||
static void
|
||||
findEquivCells1(const LibertyLibrary *library,
|
||||
LibertyCellSet &cell_equivs)
|
||||
EquivCells::~EquivCells()
|
||||
{
|
||||
for (auto cell : unique_equiv_cells_)
|
||||
delete equiv_cells_.findKey(cell);
|
||||
}
|
||||
|
||||
LibertyCellSeq *
|
||||
EquivCells::equivs(LibertyCell *cell)
|
||||
{
|
||||
return equiv_cells_.findKey(cell);
|
||||
}
|
||||
|
||||
// Use a comprehensive hash on cell properties to segregate
|
||||
// cells into groups of potential matches.
|
||||
void
|
||||
EquivCells::findEquivCells(const LibertyLibrary *library,
|
||||
LibertyCellHashMap &hash_matches)
|
||||
{
|
||||
LibertyCellHashMap cell_hash;
|
||||
LibertyCellIterator cell_iter(library);
|
||||
while (cell_iter.hasNext()) {
|
||||
LibertyCell *cell = cell_iter.next();
|
||||
if (!cell->dontUse()) {
|
||||
if (!cell->dontUse()
|
||||
&& !stringBeginEqual(cell->name(), "DLY")) {
|
||||
unsigned hash = hashCell(cell);
|
||||
// Use a comprehensive hash on cell properties to segregate
|
||||
// cells into groups of potential matches.
|
||||
LibertyCellSeq *matches = cell_hash[hash];
|
||||
LibertyCellSeq *matches = hash_matches.findKey(hash);
|
||||
if (matches) {
|
||||
LibertyCellSeq::Iterator match_iter(matches);
|
||||
while (match_iter.hasNext()) {
|
||||
LibertyCell *match = match_iter.next();
|
||||
if (equivCells(match, cell)) {
|
||||
LibertyCellSeq *equivs = match->equivCellsRaw();
|
||||
LibertyCellSeq *equivs = equiv_cells_.findKey(match);
|
||||
if (equivs == nullptr) {
|
||||
equivs = new LibertyCellSeq;
|
||||
equivs->push_back(match);
|
||||
match->setEquivCells(equivs);
|
||||
cell_equivs.insert(match);
|
||||
unique_equiv_cells_.push_back(match);
|
||||
equiv_cells_[match] = equivs;
|
||||
}
|
||||
equivs->push_back(cell);
|
||||
cell->setEquivCells(equivs);
|
||||
equiv_cells_[cell] = equivs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
matches->push_back(cell);
|
||||
}
|
||||
else {
|
||||
matches = new LibertyCellSeq;
|
||||
hash_matches[hash] = matches;
|
||||
matches->push_back(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map library cells to equiv cells.
|
||||
void
|
||||
EquivCells::mapEquivCells(const LibertyLibrary *library,
|
||||
LibertyCellHashMap &hash_matches)
|
||||
{
|
||||
|
||||
LibertyCellIterator cell_iter(library);
|
||||
while (cell_iter.hasNext()) {
|
||||
LibertyCell *cell = cell_iter.next();
|
||||
if (!cell->dontUse()) {
|
||||
unsigned hash = hashCell(cell);
|
||||
LibertyCellSeq *matches = hash_matches.findKey(hash);
|
||||
if (matches) {
|
||||
LibertyCellSeq::Iterator match_iter(matches);
|
||||
while (match_iter.hasNext()) {
|
||||
LibertyCell *match = match_iter.next();
|
||||
if (equivCells(match, cell)) {
|
||||
LibertyCellSeq *equivs = equiv_cells_.findKey(match);
|
||||
equiv_cells_[cell] = equivs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
matches = new LibertyCellSeq;
|
||||
cell_hash[hash] = matches;
|
||||
}
|
||||
matches->push_back(cell);
|
||||
}
|
||||
}
|
||||
cell_hash.deleteContents();
|
||||
}
|
||||
|
||||
struct CellDriveResistanceLess
|
||||
{
|
||||
bool operator()(const LibertyCell *cell1,
|
||||
const LibertyCell *cell2) const
|
||||
{
|
||||
return cellDriveResistance(cell1) > cellDriveResistance(cell2);
|
||||
}
|
||||
};
|
||||
|
||||
static float
|
||||
cellDriveResistance(const LibertyCell *cell)
|
||||
{
|
||||
return max(cell->driveResistance(TransRiseFall::rise()),
|
||||
cell->driveResistance(TransRiseFall::fall()));
|
||||
}
|
||||
|
||||
static void
|
||||
sortCellEquivs(LibertyCellSet &cell_equivs)
|
||||
{
|
||||
for (auto equiv : cell_equivs) {
|
||||
LibertyCellSeq *equivs = equiv->equivCells();
|
||||
sort(equivs, CellDriveResistanceLess());
|
||||
LibertyCell *lower = nullptr;
|
||||
LibertyCellSeq::Iterator cell_iter(equivs);
|
||||
while (cell_iter.hasNext()) {
|
||||
LibertyCell *cell = cell_iter.next();
|
||||
if (lower) {
|
||||
lower->setHigherDrive(cell);
|
||||
cell->setLowerDrive(lower);
|
||||
}
|
||||
lower = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -273,7 +280,7 @@ equivCellPorts(const LibertyCell *cell1,
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
bool
|
||||
equivCellSequentials(const LibertyCell *cell1,
|
||||
const LibertyCell *cell2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,15 +17,37 @@
|
|||
#ifndef STA_EQUIV_CELLS_H
|
||||
#define STA_EQUIV_CELLS_H
|
||||
|
||||
#include "Vector.hh"
|
||||
#include "Map.hh"
|
||||
#include "UnorderedMap.hh"
|
||||
#include "LibertyClass.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
// Find equivalent cells, sort by drive strength and
|
||||
// and set cell->equivCells/higherDrive/lowerDrive.
|
||||
void
|
||||
findEquivCells(const LibertyLibrary *library);
|
||||
typedef Map<LibertyCell*, LibertyCellSeq*> EquivCellMap;
|
||||
typedef UnorderedMap<unsigned, LibertyCellSeq*> LibertyCellHashMap;
|
||||
|
||||
class EquivCells
|
||||
{
|
||||
public:
|
||||
// Find equivalent cells in equiv_libs.
|
||||
// Optionally add mappings for cells in map_libs.
|
||||
EquivCells(LibertyLibrarySeq *equiv_libs,
|
||||
LibertyLibrarySeq *map_libs);
|
||||
~EquivCells();
|
||||
// Find equivalents for cell (member of from_libs) in to_libs.
|
||||
LibertyCellSeq *equivs(LibertyCell *cell);
|
||||
|
||||
protected:
|
||||
void findEquivCells(const LibertyLibrary *library,
|
||||
LibertyCellHashMap &hash_matches);
|
||||
void mapEquivCells(const LibertyLibrary *library,
|
||||
LibertyCellHashMap &hash_matches);
|
||||
|
||||
EquivCellMap equiv_cells_;
|
||||
// Unique cell for each equiv cell group.
|
||||
LibertyCellSeq unique_equiv_cells_;
|
||||
};
|
||||
|
||||
// Predicate that is true when the ports, functions, sequentials and
|
||||
// timing arcs match.
|
||||
|
|
@ -48,5 +70,9 @@ bool
|
|||
equivCellTimingArcSets(const LibertyCell *cell1,
|
||||
const LibertyCell *cell2);
|
||||
|
||||
bool
|
||||
equivCellSequentials(const LibertyCell *cell1,
|
||||
const LibertyCell *cell2);
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ LibertyLibrary::LibertyLibrary(const char *name,
|
|||
default_operating_conditions_(nullptr),
|
||||
ocv_arc_depth_(0.0),
|
||||
default_ocv_derate_(nullptr),
|
||||
found_equiv_cells_(false),
|
||||
buffers_(nullptr)
|
||||
{
|
||||
// Scalar templates are builtin.
|
||||
|
|
@ -153,15 +152,6 @@ LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LibertyLibrary::ensureEquivCells()
|
||||
{
|
||||
if (!found_equiv_cells_) {
|
||||
findEquivCells(this);
|
||||
found_equiv_cells_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
LibertyCellSeq *
|
||||
LibertyLibrary::buffers()
|
||||
{
|
||||
|
|
@ -866,10 +856,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library,
|
|||
ocv_derate_(nullptr),
|
||||
is_disabled_constraint_(false),
|
||||
leakage_power_(0.0),
|
||||
leakage_power_exists_(false),
|
||||
equiv_cells_(nullptr),
|
||||
higher_drive_(nullptr),
|
||||
lower_drive_(nullptr)
|
||||
leakage_power_exists_(false)
|
||||
{
|
||||
liberty_cell_ = this;
|
||||
}
|
||||
|
|
@ -885,16 +872,6 @@ LibertyCell::~LibertyCell()
|
|||
timing_arc_set_from_map_.deleteContents();
|
||||
timing_arc_set_to_map_.deleteContents();
|
||||
|
||||
if (equiv_cells_) {
|
||||
// Carefull because loop below nulls equiv_cells_.
|
||||
auto equiv_cells = equiv_cells_;
|
||||
// equiv_cells_ is shared by all of the equivalent cells, so
|
||||
// delete it once for all of them and null the others.
|
||||
for (auto equiv : *equiv_cells_)
|
||||
equiv->setEquivCells(nullptr);
|
||||
delete equiv_cells;
|
||||
}
|
||||
|
||||
deleteInternalPowerAttrs();
|
||||
internal_powers_.deleteContents();
|
||||
leakage_powers_.deleteContents();
|
||||
|
|
@ -1463,20 +1440,6 @@ LibertyCell::setCornerCell(LibertyCell *corner_cell,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
LibertyCellSeq *
|
||||
LibertyCell::equivCells()
|
||||
{
|
||||
if (equiv_cells_ == nullptr)
|
||||
liberty_library_->ensureEquivCells();
|
||||
return equiv_cells_;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::setEquivCells(LibertyCellSeq *equiv_cells)
|
||||
{
|
||||
equiv_cells_ = equiv_cells;
|
||||
}
|
||||
|
||||
// Use the worst "drive" for all the timing arcs in the cell.
|
||||
float
|
||||
LibertyCell::driveResistance(const TransRiseFall *tr) const
|
||||
|
|
@ -1489,7 +1452,8 @@ LibertyCell::driveResistance(const TransRiseFall *tr) const
|
|||
TimingArcSetArcIterator arc_iter(set);
|
||||
while (arc_iter.hasNext()) {
|
||||
TimingArc *arc = arc_iter.next();
|
||||
if (arc->toTrans()->asRiseFall() == tr) {
|
||||
if (tr == nullptr
|
||||
|| arc->toTrans()->asRiseFall() == tr) {
|
||||
GateTimingModel *model = dynamic_cast<GateTimingModel*>(arc->model());
|
||||
if (model) {
|
||||
float drive = model->driveResistance(this, nullptr);
|
||||
|
|
@ -1503,30 +1467,10 @@ LibertyCell::driveResistance(const TransRiseFall *tr) const
|
|||
return max_drive;
|
||||
}
|
||||
|
||||
LibertyCell *
|
||||
LibertyCell::higherDrive() const
|
||||
float
|
||||
LibertyCell::driveResistance() const
|
||||
{
|
||||
liberty_library_->ensureEquivCells();
|
||||
return higher_drive_;
|
||||
}
|
||||
|
||||
LibertyCell *
|
||||
LibertyCell::lowerDrive() const
|
||||
{
|
||||
liberty_library_->ensureEquivCells();
|
||||
return lower_drive_;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::setHigherDrive(LibertyCell *cell)
|
||||
{
|
||||
higher_drive_ = cell;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::setLowerDrive(LibertyCell *cell)
|
||||
{
|
||||
lower_drive_ = cell;
|
||||
return driveResistance(nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -289,7 +289,6 @@ protected:
|
|||
const TableModel *model,
|
||||
float in_slew,
|
||||
float wire_delay) const;
|
||||
void ensureEquivCells();
|
||||
|
||||
Units *units_;
|
||||
DelayModelType delay_model_type_;
|
||||
|
|
@ -330,7 +329,6 @@ protected:
|
|||
OcvDerate *default_ocv_derate_;
|
||||
OcvDerateMap ocv_derate_map_;
|
||||
SupplyVoltageMap supply_voltage_map_;
|
||||
bool found_equiv_cells_;
|
||||
LibertyCellSeq *buffers_;
|
||||
|
||||
// Set if any library has rise/fall capacitances.
|
||||
|
|
@ -492,13 +490,9 @@ public:
|
|||
virtual void finish(bool infer_latches,
|
||||
Report *report,
|
||||
Debug *debug);
|
||||
LibertyCellSeq *equivCells();
|
||||
// Internal.
|
||||
LibertyCellSeq *equivCellsRaw() { return equiv_cells_; }
|
||||
void setEquivCells(LibertyCellSeq *equiv_cells);
|
||||
float driveResistance(const TransRiseFall *tr) const;
|
||||
void setHigherDrive(LibertyCell *cell);
|
||||
void setLowerDrive(LibertyCell *cell);
|
||||
// Max of rise/fall.
|
||||
float driveResistance() const;
|
||||
bool isBuffer() const;
|
||||
// Only valid when isBuffer() returns true.
|
||||
void bufferPorts(// Return values.
|
||||
|
|
@ -569,10 +563,6 @@ protected:
|
|||
float leakage_power_;
|
||||
bool leakage_power_exists_;
|
||||
LibertyPgPortMap pg_port_map_;
|
||||
LibertyCellSeq *equiv_cells_;
|
||||
// Next higher/lower drive equivalent cells.
|
||||
LibertyCell *higher_drive_;
|
||||
LibertyCell *lower_drive_;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(LibertyCell);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class TransRiseFall;
|
|||
class TransRiseFallBoth;
|
||||
class LibertyCellSequentialIterator;
|
||||
|
||||
typedef Vector<LibertyLibrary*> LibertyLibrarySeq;
|
||||
typedef Vector<LibertyCell*> LibertyCellSeq;
|
||||
typedef Vector<Sequential*> SequentialSeq;
|
||||
typedef Map<LibertyCell*, LibertyCellSeq*> LibertyCellEquivMap;
|
||||
|
|
|
|||
|
|
@ -531,10 +531,6 @@ getProperty(const LibertyCell *cell,
|
|||
return PropertyValue(cell->driveResistance(TransRiseFall::rise()));
|
||||
else if (stringEqual(property, "drive_resistance_fall"))
|
||||
return PropertyValue(cell->driveResistance(TransRiseFall::fall()));
|
||||
else if (stringEqual(property, "higher_drive"))
|
||||
return PropertyValue(cell->higherDrive());
|
||||
else if (stringEqual(property, "lower_drive"))
|
||||
return PropertyValue(cell->lowerDrive());
|
||||
else if (stringEqual(property, "is_buffer"))
|
||||
return PropertyValue(cell->isBuffer());
|
||||
else if (stringEqual(property, "dont_use"))
|
||||
|
|
|
|||
|
|
@ -276,7 +276,8 @@ Sta::Sta() :
|
|||
report_path_(nullptr),
|
||||
power_(nullptr),
|
||||
link_make_black_boxes_(true),
|
||||
update_genclks_(false)
|
||||
update_genclks_(false),
|
||||
equiv_cells_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -517,6 +518,7 @@ Sta::~Sta()
|
|||
delete units_;
|
||||
delete report_;
|
||||
delete power_;
|
||||
delete equiv_cells_;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -3644,7 +3646,7 @@ Sta::replaceCell(Instance *inst,
|
|||
{
|
||||
NetworkEdit *network = networkCmdEdit();
|
||||
LibertyCell *from_lib_cell = network->libertyCell(inst);
|
||||
if (equivCells(from_lib_cell, to_lib_cell)) {
|
||||
if (sta::equivCells(from_lib_cell, to_lib_cell)) {
|
||||
replaceEquivCellBefore(inst, to_lib_cell);
|
||||
network->replaceCell(inst, to_cell);
|
||||
replaceEquivCellAfter(inst);
|
||||
|
|
@ -4966,6 +4968,24 @@ Sta::reportCheck(MaxSkewCheck *check,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs,
|
||||
LibertyLibrarySeq *map_libs)
|
||||
{
|
||||
delete equiv_cells_;
|
||||
equiv_cells_ = new EquivCells(equiv_libs, map_libs);
|
||||
}
|
||||
|
||||
LibertyCellSeq *
|
||||
Sta::equivCells(LibertyCell *cell)
|
||||
{
|
||||
if (equiv_cells_)
|
||||
return equiv_cells_->equivs(cell);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void
|
||||
Sta::powerPreamble()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class ReportField;
|
|||
class Power;
|
||||
class PowerResult;
|
||||
class ClockIterator;
|
||||
class EquivCells;
|
||||
|
||||
typedef InstanceSeq::Iterator SlowDrvrIterator;
|
||||
typedef Vector<const char*> CheckError;
|
||||
|
|
@ -1182,6 +1183,12 @@ public:
|
|||
// Return values.
|
||||
PowerResult &result);
|
||||
|
||||
// Find equivalent cells in equiv_libs.
|
||||
// Optionally add mappings for cells in map_libs.
|
||||
void makeEquivCells(LibertyLibrarySeq *equiv_libs,
|
||||
LibertyLibrarySeq *map_libs);
|
||||
LibertyCellSeq *equivCells(LibertyCell *cell);
|
||||
|
||||
protected:
|
||||
// Default constructors that are called by makeComponents in the Sta
|
||||
// constructor. These can be redefined by a derived class to
|
||||
|
|
@ -1311,6 +1318,7 @@ protected:
|
|||
Tcl_Interp *tcl_interp_;
|
||||
bool link_make_black_boxes_;
|
||||
bool update_genclks_;
|
||||
EquivCells *equiv_cells_;
|
||||
|
||||
// Singleton sta used by tcl command interpreter.
|
||||
static Sta *sta_;
|
||||
|
|
|
|||
32
tcl/StaTcl.i
32
tcl/StaTcl.i
|
|
@ -50,8 +50,8 @@
|
|||
#include "Transition.hh"
|
||||
#include "TimingRole.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "EquivCells.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "EquivCells.hh"
|
||||
#include "Network.hh"
|
||||
#include "Clock.hh"
|
||||
#include "PortDelay.hh"
|
||||
|
|
@ -2279,19 +2279,41 @@ find_library_buffers(LibertyLibrary *library)
|
|||
return library->buffers();
|
||||
}
|
||||
|
||||
LibertyCellSeq *
|
||||
equiv_cells(LibertyCell *cell)
|
||||
void
|
||||
make_equiv_cells(LibertyLibrary *lib)
|
||||
{
|
||||
return cell->equivCells();
|
||||
LibertyLibrarySeq libs;
|
||||
libs.push_back(lib);
|
||||
Sta::sta()->makeEquivCells(&libs, nullptr);
|
||||
}
|
||||
|
||||
LibertyCellSeq *
|
||||
find_equiv_cells(LibertyCell *cell)
|
||||
{
|
||||
return Sta::sta()->equivCells(cell);
|
||||
}
|
||||
|
||||
bool
|
||||
equiv_cells(LibertyCell *cell1,
|
||||
LibertyCell *cell2)
|
||||
{
|
||||
return sta::equivCells(cell1, cell2);
|
||||
}
|
||||
|
||||
bool
|
||||
equiv_cell_ports(LibertyCell *cell1,
|
||||
LibertyCell *cell2)
|
||||
LibertyCell *cell2)
|
||||
{
|
||||
return equivCellPorts(cell1, cell2);
|
||||
}
|
||||
|
||||
bool
|
||||
equiv_cell_timing_arcs(LibertyCell *cell1,
|
||||
LibertyCell *cell2)
|
||||
{
|
||||
return equivCellTimingArcSets(cell1, cell2);
|
||||
}
|
||||
|
||||
void
|
||||
set_cmd_namespace_cmd(const char *namespc)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue