prima mcmm bugs

Signed-off-by: James Cherry <cherry@CerezoBook.local>
This commit is contained in:
James Cherry 2026-07-28 19:44:13 -07:00
parent 7fdc304e12
commit c00b847b20
8 changed files with 95 additions and 52 deletions

View File

@ -45,8 +45,9 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg,
Graph *graph = sta->graph();
Report *report = sta->report();
const Pin *in_pin = dcalc_arg.inPin();
LibertyPort *port = network->libertyPort(in_pin);
if (port) {
LibertyPort *link_port = network->libertyPort(in_pin);
if (link_port) {
LibertyPort *port = link_port->scenePort(scene, min_max);
const RiseFall *in_rf = dcalc_arg.inEdge();
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
if (driver_waveform) {

View File

@ -186,7 +186,7 @@ ArcDcalcArg::drvrCell() const
return arc_->to()->libertyCell();
}
const LibertyLibrary *
LibertyLibrary *
ArcDcalcArg::drvrLibrary() const
{
return arc_->to()->libertyLibrary();
@ -198,6 +198,13 @@ ArcDcalcArg::drvrEdge() const
return arc_->toEdge()->asRiseFall();
}
void
ArcDcalcArg::setSceneArc(const Scene *scene,
const MinMax *min_max)
{
arc_ = arc_->sceneArc(scene->libertyIndex(min_max));
}
const Net *
ArcDcalcArg::drvrNet(const Network *network) const
{

View File

@ -227,6 +227,7 @@ DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate,
gate.edge(),
scene, min_max);
gate.setInSlew(in_slew);
gate.setSceneArc(scene, min_max);
}
}

View File

@ -183,6 +183,7 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap,
parasitic);
dcalc_args[0].setSceneArc(scene, min_max);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map,
scene, min_max);
return dcalc_results[0];
@ -256,9 +257,10 @@ PrimaDelayCalc::checkArgs(ArcDcalcArgSeq &dcalc_args,
if (output_waveforms->slewAxis()->inBounds(in_slew)) {
if (output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
output_waveforms_[drvr_idx] = output_waveforms;
debugPrint(debug_, "prima", 1, "{} {}",
debugPrint(debug_, "prima", 1, "{} {} {}",
dcalc_arg.drvrCell()->name(),
dcalc_arg.drvrEdge()->to_string().c_str());
dcalc_arg.drvrEdge()->to_string().c_str(),
scene->name());
LibertyCell *drvr_cell = dcalc_arg.drvrCell();
drvr_cell->ensureVoltageWaveforms(scenes_);
}
@ -362,7 +364,7 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = v_prev_ = x_to_v * x_init;
time_step_ = time_step_prev_ = timeStep();
debugPrint(debug_, "ccs_dcalc", 1, "time step {}",
debugPrint(debug_, "prima", 1, "time step {}",
delayAsString(time_step_, this));
MatrixSd A(order, order);
@ -402,7 +404,7 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = x_to_v * x;
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0];
debugPrint(debug_, "ccs_dcalc", 3, "{} ceff {} VDrvr {:.4f} Idrvr {}",
debugPrint(debug_, "prima", 3, "{} ceff {} VDrvr {:.4f} Idrvr {}",
delayAsString(time, this),
units_->capacitanceUnit()->asString(ceff_[0]),
voltage(dcalc_arg.drvrPin()),
@ -427,7 +429,7 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
double
PrimaDelayCalc::timeStep()
{
// Needs to use LTE for time step dynamic control.
// Should use LTE for dynamic time step control.
return driverResistance() * load_cap_ * .02;
}
@ -443,7 +445,8 @@ PrimaDelayCalc::driverResistance()
{
const Pin *drvr_pin = (*dcalc_args_)[0].drvrPin();
LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
return drvr_port->driveResistance(drvr_rf_, min_max_);
LibertyPort *scene_port = drvr_port->scenePort(scene_, min_max_);
return scene_port->driveResistance(drvr_rf_, min_max_);
}
void
@ -526,7 +529,7 @@ PrimaDelayCalc::placeNode(ParasiticNode *node,
const Pin *pin = parasitics_->pin(node);
if (pin) {
pin_node_map_[pin] = index;
debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
debugPrint(debug_, "prima", 1, "pin {} node {}",
network_->pathName(pin), index);
}
}
@ -606,7 +609,7 @@ PrimaDelayCalc::stampEqns()
}
}
if (debug_->check("ccs_dcalc", 3)) {
if (debug_->check("prima", 3)) {
reportMatrix("G", G_);
reportMatrix("C", C_);
reportMatrix("B", B_);
@ -834,7 +837,8 @@ PrimaDelayCalc::measureThresholds(double time)
if ((v_prev < th && th <= v) || (v_prev > th && th >= v)) {
double t_cross =
time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
debugPrint(debug_, "ccs_measure", 1, "node {} cross {:.2f} {}", node_idx, th,
debugPrint(debug_, "prima_measure", 1, "node {} cross {:.2f} {}",
node_idx, th,
delayAsString(t_cross, this));
threshold_times_[node_idx][m] = t_cross;
}
@ -882,7 +886,7 @@ PrimaDelayCalc::dcalcResults()
dcalc_result.setGateDelay(gate_delay2);
dcalc_result.setDrvrSlew(drvr_slew2);
debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}",
debugPrint(debug_, "prima", 2, "{} gate delay {} slew {}",
network_->pathName(drvr_pin), delayAsString(gate_delay, this),
delayAsString(drvr_slew, this));
@ -895,7 +899,7 @@ PrimaDelayCalc::dcalcResults()
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
debugPrint(debug_, "ccs_dcalc", 2, "load {} {} delay {} slew {}",
debugPrint(debug_, "prima", 2, "load {} {} delay {} slew {}",
network_->pathName(load_pin),
drvr_rf_->shortName(),
delayAsString(wire_delay, this),
@ -987,7 +991,7 @@ PrimaDelayCalc::primaReduce()
// solve x_init = Vq * x~_init for x~_init
xq_init_ = Vq_.colPivHouseholderQr().solve(x_init_);
if (debug_->check("ccs_dcalc", 3)) {
if (debug_->check("prima", 3)) {
reportMatrix("Vq", Vq_);
reportMatrix("G~", Gq_);
reportMatrix("C~", Cq_);
@ -1050,7 +1054,7 @@ PrimaDelayCalc::primaReduce2()
// solve x_init = Vq * x~_init for x~_init
xq_init_ = Vq_.colPivHouseholderQr().solve(x_init_);
if (debug_->check("ccs_dcalc", 3)) {
if (debug_->check("prima", 3)) {
reportMatrix("Vq", Vq_);
reportMatrix("G~", Gq_);
reportMatrix("C~", Cq_);

View File

@ -76,11 +76,13 @@ public:
const Pin *drvrPin() const { return drvr_pin_; }
Vertex *drvrVertex(const Graph *graph) const;
LibertyCell *drvrCell() const;
const LibertyLibrary *drvrLibrary() const;
LibertyLibrary *drvrLibrary() const;
const RiseFall *drvrEdge() const;
const Net *drvrNet(const Network *network) const;
Edge *edge() const { return edge_; }
const TimingArc *arc() const { return arc_; }
void setSceneArc(const Scene *scene,
const MinMax *min_max);
const Slew &inSlew() const { return in_slew_; }
float inSlewFlt() const;
void setInSlew(Slew in_slew);

View File

@ -72,6 +72,7 @@ public:
void writeSpice();
private:
void initPowerGnd();
void writeHeader();
void writePrintStmt();
void writeStageInstances();
@ -150,6 +151,7 @@ private:
using WriteSpice::writeMeasureDelayStmt;
using WriteSpice::writeMeasureSlewStmt;
using WriteSpice::findSlew;
using WriteSpice::initPowerGnd;
};
////////////////////////////////////////////////////////////////
@ -187,7 +189,6 @@ WritePathSpice::WritePathSpice(const Path *path,
path_expanded_(sta),
written_insts_(network_)
{
initPowerGnd();
}
void
@ -196,6 +197,8 @@ WritePathSpice::writeSpice()
spice_stream_.open(spice_filename_);
if (spice_stream_.is_open()) {
path_expanded_.expand(path_, true);
initPowerGnd();
// Find subckt port names as a side-effect of writeSubckts.
writeSubckts();
writeHeader();
@ -212,6 +215,27 @@ WritePathSpice::writeSpice()
throw FileNotWritable(spice_filename_);
}
void
WritePathSpice::initPowerGnd()
{
Scene *scene = path_->scene(this);
const MinMax *min_max = path_->minMax(this);
LibertyLibrary *threshold_lib = nullptr;
for (size_t i = 0; i < path_expanded_.size(); i++) {
const Path *path = path_expanded_.path(i);
const Pin *pin = path->pin(this);
const LibertyPort *port = network_->libertyPort(pin);
if (port) {
threshold_lib = port->scenePort(scene, min_max)->libertyLibrary();
break;
}
}
if (threshold_lib)
initPowerGnd(threshold_lib);
else
report_->error(1606, "No instance with Liberty cell found in path.");
}
void
WritePathSpice::writeHeader()
{
@ -330,7 +354,7 @@ WritePathSpice::writeInputWaveform()
const TimingArc *next_arc = stageGateArc(input_stage + 1);
float slew0 = findSlew(input_path, rf, next_arc);
float threshold = default_library_->inputThreshold(rf);
float threshold = threshold_library_->inputThreshold(rf);
float dt = railToRailSlew(slew0, rf);
float time0 = dt * threshold;
@ -518,7 +542,7 @@ WritePathSpice::writeGateStage(Stage stage)
const RiseFall *input_rf = gate_input_path->transition(this);
const Edge *gate_edge = stageGateEdge(stage);
LibertyPortLogicValues port_values;
PortLogicValues port_values;
bool is_clked;
gatePortValues(input_pin, drvr_pin, input_rf, drvr_rf, gate_edge,
port_values, is_clked);

View File

@ -76,17 +76,18 @@ WriteSpice::WriteSpice(std::string_view spice_filename,
ckt_sim_(ckt_sim),
scene_(scene),
min_max_(min_max),
default_library_(network_->defaultLibertyLibrary()),
threshold_library_(nullptr),
bdd_(sta),
parasitics_(scene->parasitics(min_max))
{
}
void
WriteSpice::initPowerGnd()
WriteSpice::initPowerGnd(LibertyLibrary *threshold_library)
{
threshold_library_ = threshold_library;
bool exists = false;
default_library_->supplyVoltage(power_name_, power_voltage_, exists);
threshold_library_->supplyVoltage(power_name_, power_voltage_, exists);
if (!exists) {
const OperatingConditions *op_cond =
scene_->sdc()->operatingConditions(min_max_);
@ -94,7 +95,7 @@ WriteSpice::initPowerGnd()
op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions();
power_voltage_ = op_cond->voltage();
}
default_library_->supplyVoltage(gnd_name_, gnd_voltage_, exists);
threshold_library_->supplyVoltage(gnd_name_, gnd_voltage_, exists);
if (!exists)
gnd_voltage_ = 0.0;
}
@ -309,10 +310,11 @@ WriteSpice::writeSubcktInst(const Instance *inst)
// Power/ground and input voltage sources.
void
WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst,
LibertyPortLogicValues &port_values,
PortLogicValues &port_values,
const PinSet &excluded_input_pins)
{
LibertyCell *cell = network_->libertyCell(inst);
LibertyCell *link_cell = network_->libertyCell(inst);
LibertyCell *cell = link_cell->sceneCell(scene_, min_max_);
const std::string &cell_name = cell->name();
StringSeq &spice_port_names = cell_spice_port_names_[cell_name];
std::string inst_name = network_->pathName(inst);
@ -339,7 +341,7 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst,
if (port_value == LogicValue::unknown) {
bool has_value;
LogicValue value;
findKeyValue(port_values, port, value, has_value);
findKeyValue(port_values, port->name(), value, has_value);
if (has_value)
port_value = value;
}
@ -732,7 +734,7 @@ WriteSpice::writeWaveformEdge(const RiseFall *rf,
volt0 = power_voltage_;
volt1 = gnd_voltage_;
}
float threshold = default_library_->inputThreshold(rf);
float threshold = threshold_library_->inputThreshold(rf);
float dt = railToRailSlew(slew, rf);
float time0 = time - dt * threshold;
float time1 = time0 + dt;
@ -745,8 +747,8 @@ float
WriteSpice::railToRailSlew(float slew,
const RiseFall *rf)
{
float lower = default_library_->slewLowerThreshold(rf);
float upper = default_library_->slewUpperThreshold(rf);
float lower = threshold_library_->slewLowerThreshold(rf);
float upper = threshold_library_->slewUpperThreshold(rf);
return slew / (upper - lower);
}
@ -760,7 +762,7 @@ WriteSpice::gatePortValues(const Pin *input_pin,
const RiseFall *drvr_rf,
const Edge *gate_edge,
// Return values.
LibertyPortLogicValues &port_values,
PortLogicValues &port_values,
bool &is_clked)
{
is_clked = false;
@ -783,7 +785,7 @@ WriteSpice::gatePortValues(const Instance *,
const RiseFall *input_rf,
const RiseFall *drvr_rf,
// Return values.
LibertyPortLogicValues &port_values)
PortLogicValues &port_values)
{
DdManager *cudd_mgr = bdd_.cuddMgr();
DdNode *bdd = bdd_.funcBdd(expr);
@ -827,7 +829,7 @@ WriteSpice::gatePortValues(const Instance *,
port_value = LogicValue::unknown;
break;
}
port_values[port] = port_value;
port_values[port->name()] = port_value;
}
}
}
@ -844,7 +846,7 @@ WriteSpice::regPortValues(const Pin *input_pin,
const LibertyPort *drvr_port,
const FuncExpr *drvr_func,
// Return values.
LibertyPortLogicValues &port_values,
PortLogicValues &port_values,
bool &is_clked)
{
is_clked = false;
@ -870,7 +872,7 @@ void
WriteSpice::seqPortValues(Sequential *seq,
const RiseFall *rf,
// Return values.
LibertyPortLogicValues &port_values)
PortLogicValues &port_values)
{
FuncExpr *data = seq->data();
// SHOULD choose values for all ports of data to make output rise/fall
@ -878,18 +880,19 @@ WriteSpice::seqPortValues(Sequential *seq,
LibertyPort *port = onePort(data);
if (port) {
TimingSense sense = data->portTimingSense(port);
const std::string &port_name = port->name();
switch (sense) {
case TimingSense::positive_unate:
if (rf == RiseFall::rise())
port_values[port] = LogicValue::one;
port_values[port_name] = LogicValue::one;
else
port_values[port] = LogicValue::zero;
port_values[port_name] = LogicValue::zero;
break;
case TimingSense::negative_unate:
if (rf == RiseFall::rise())
port_values[port] = LogicValue::zero;
port_values[port_name] = LogicValue::zero;
else
port_values[port] = LogicValue::one;
port_values[port_name] = LogicValue::one;
break;
case TimingSense::non_unate:
case TimingSense::none:
@ -954,7 +957,7 @@ WriteSpice::writeSubcktInstLoads(const Pin *drvr_pin,
sta::print(spice_stream_, "* Load pins\n");
PinSeq drvr_loads = drvrLoads(drvr_pin);
// Do not sensitize side load gates.
LibertyPortLogicValues port_values;
PortLogicValues port_values;
for (const Pin *load_pin : drvr_loads) {
const Instance *load_inst = network_->instance(load_pin);
if (load_pin != path_load && network_->direction(load_pin)->isAnyInput()
@ -978,9 +981,9 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin,
std::string_view prefix)
{
std::string from_pin_name = network_->pathName(from_pin);
float from_threshold = power_voltage_ * default_library_->inputThreshold(from_rf);
float from_threshold = power_voltage_ * threshold_library_->inputThreshold(from_rf);
std::string to_pin_name = network_->pathName(to_pin);
float to_threshold = power_voltage_ * default_library_->inputThreshold(to_rf);
float to_threshold = power_voltage_ * threshold_library_->inputThreshold(to_rf);
sta::print(spice_stream_, ".measure tran {}_{}_delay_{}\n", prefix,
from_pin_name, to_pin_name);
sta::print(spice_stream_, "+trig v({}) val={:.3f} {}=last\n", from_pin_name,
@ -996,8 +999,8 @@ WriteSpice::writeMeasureSlewStmt(const Pin *pin,
{
std::string pin_name = network_->pathName(pin);
std::string_view spice_rf = spiceTrans(rf);
float lower = power_voltage_ * default_library_->slewLowerThreshold(rf);
float upper = power_voltage_ * default_library_->slewUpperThreshold(rf);
float lower = power_voltage_ * threshold_library_->slewLowerThreshold(rf);
float upper = power_voltage_ * threshold_library_->slewUpperThreshold(rf);
float threshold1, threshold2;
if (rf == RiseFall::rise()) {
threshold1 = lower;

View File

@ -43,7 +43,8 @@ namespace sta {
using ParasiticNodeMap = std::map<const ParasiticNode*, int>;
using CellSpicePortNames = std::map<std::string, StringSeq, std::less<>>;
using LibertyPortLogicValues = std::map<const LibertyPort*, LogicValue>;
// Use port name so lookup works across scenes.
using PortLogicValues = std::map<std::string, LogicValue>;
// Utilities for writing a spice deck.
class WriteSpice : public StaState
@ -61,7 +62,7 @@ public:
const StaState *sta);
protected:
void initPowerGnd();
void initPowerGnd(LibertyLibrary *threshold_library);
void writeHeader(std::string &title,
float max_time,
float time_step);
@ -73,7 +74,7 @@ protected:
StringSeq &tokens);
void writeSubcktInst(const Instance *inst);
void writeSubcktInstVoltSrcs(const Instance *inst,
LibertyPortLogicValues &port_values,
PortLogicValues &port_values,
const PinSet &excluded_input_pins);
float pgPortVoltage(const LibertyPort *pg_port);
void writeVoltageSource(std::string_view inst_name,
@ -120,7 +121,7 @@ protected:
void seqPortValues(Sequential *seq,
const RiseFall *rf,
// Return values.
LibertyPortLogicValues &port_values);
PortLogicValues &port_values);
LibertyPort *onePort(FuncExpr *expr);
void writeMeasureDelayStmt(const Pin *from_pin,
const RiseFall *from_rf,
@ -143,14 +144,14 @@ protected:
const RiseFall *drvr_rf,
const Edge *gate_edge,
// Return values.
LibertyPortLogicValues &port_values,
PortLogicValues &port_values,
bool &is_clked);
void regPortValues(const Pin *input_pin,
const RiseFall *drvr_rf,
const LibertyPort *drvr_port,
const FuncExpr *drvr_func,
// Return values.
LibertyPortLogicValues &port_values,
PortLogicValues &port_values,
bool &is_clked);
void gatePortValues(const Instance *inst,
const FuncExpr *expr,
@ -158,7 +159,7 @@ protected:
const RiseFall *input_rf,
const RiseFall *drvr_rf,
// Return values.
LibertyPortLogicValues &port_values);
PortLogicValues &port_values);
void writeSubcktInstLoads(const Pin *drvr_pin,
const Pin *path_load,
const PinSet &excluded_input_pins,
@ -179,7 +180,7 @@ protected:
const MinMax *min_max_;
std::ofstream spice_stream_;
LibertyLibrary *default_library_;
LibertyLibrary *threshold_library_;
float power_voltage_;
float gnd_voltage_;
float max_time_;