diff --git a/.gitignore b/.gitignore index 92e4f53c..16d9c302 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,8 @@ Makefile # /test/ /test/gmon.out /test/results +# ngspice turd +/test/b3v3_1check.log /test_native diff --git a/doc/BugLog b/doc/BugLog index 8f05d735..8aed0965 100644 --- a/doc/BugLog +++ b/doc/BugLog @@ -16,3 +16,4 @@ Release 2.0 Patches 2019/01/31 generated clk -divide_by 16384 cycle accting fallout 2019/02/17 report_power internal power accuracy 2019/02/18 write_path_spice first line is comment +2019/02/21 write_path_spice include side load pins diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index c8a6cde7..f3924f37 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 6cc7bbd8..2f25d252 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -29,6 +29,7 @@ #include "TableModel.hh" #include "Liberty.hh" #include "TimingArc.hh" +#include "PortDirection.hh" #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" @@ -95,8 +96,13 @@ private: void writeMeasureStmts(); void writeMeasureStmt(const Pin *pin); void writeGateStage(Stage stage); - void writeStageVoltageSources(Stage stage, - StringVector *spice_port_names); + void writeSubcktInst(const Pin *input_pin); + void writeSubcktInstVoltSrcs(Stage stage, + const Pin *input_pin, + int &volt_index, + LibertyPortLogicValues &port_values, + const Clock *clk, + DcalcAPIndex dcalc_ap_index); void writeStageParasitics(Stage stage); void writeSubckts(); void findPathCellnames(// Return values. @@ -115,15 +121,25 @@ private: Path *to_path); void writeMeasureSlewStmt(Stage stage, Path *path); - void sensitizationValues(const Instance *inst, - FuncExpr *expr, - LibertyPort *input_port, - // Return values. - LibertyPortLogicValues &port_values); - void seqSensitizationValues(Sequential *seq, - const TransRiseFall *tr, - // Return values. - LibertyPortLogicValues &port_values); + void gatePortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index); + void regPortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index); + void gatePortValues(const Instance *inst, + FuncExpr *expr, + LibertyPort *input_port, + // Return values. + LibertyPortLogicValues &port_values); + void seqPortValues(Sequential *seq, + const TransRiseFall *tr, + // Return values. + LibertyPortLogicValues &port_values); void writeInputWaveform(); void writeClkWaveform(); void writeWaveformEdge(const TransRiseFall *tr, @@ -160,7 +176,7 @@ private: // gate |/ drvr load|/ // input // - // A path from an input port has no GateInputPath. + // A path from an input port has no GateInputPath (the input port is the drvr). // Internally a stage index from stageFirst() to stageLast() // is turned into an index into path_expanded_. // @@ -179,10 +195,14 @@ private: Edge *stageWireEdge(Stage stage); Pin *stageGateInputPin(Stage stage); Pin *stageDrvrPin(Stage stage); + LibertyPort *stageGateInputPort(Stage stage); + LibertyPort *stageDrvrPort(Stage stage); Pin *stageLoadPin(Stage stage); const char *stageGateInputPinName(Stage stage); const char *stageDrvrPinName(Stage stage); const char *stageLoadPinName(Stage stage); + LibertyCell *stageLibertyCell(Stage stage); + Instance *stageInstance(Stage stage); Path *path_; const char *spice_filename_; @@ -721,20 +741,62 @@ WritePathSpice::writeGateStage(Stage stage) { auto input_pin = stageGateInputPin(stage); auto input_pin_name = stageGateInputPinName(stage); + auto drvr_pin = stageDrvrPin(stage); auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin = stageLoadPin(stage); auto load_pin_name = stageLoadPinName(stage); streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", stage, input_pin_name, drvr_pin_name, load_pin_name); + // Driver subckt call. + auto inst = stageInstance(stage); + auto input_port = stageGateInputPort(stage); + auto drvr_port = stageDrvrPort(stage); + streamPrint(spice_stream_, "* Gate %s %s -> %s\n", + network_->pathName(inst), + input_port->name(), + drvr_port->name()); + writeSubcktInst(input_pin); + LibertyPortLogicValues port_values; + DcalcAPIndex dcalc_ap_index; + const Clock *clk; + int volt_index = 1; + gatePortValues(stage, port_values, clk, dcalc_ap_index); + writeSubcktInstVoltSrcs(stage, input_pin, volt_index, + port_values, clk, dcalc_ap_index); + streamPrint(spice_stream_, "\n"); + + port_values.clear(); + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + if (pin != drvr_pin + && pin != load_pin + && network_->direction(pin)->isAnyInput() + && !network_->isHierarchical(pin) + && !network_->isTopLevelPort(pin)) { + streamPrint(spice_stream_, "* Side load %s\n", network_->pathName(pin)); + writeSubcktInst(pin); + writeSubcktInstVoltSrcs(stage, pin, volt_index, port_values, NULL, 0); + streamPrint(spice_stream_, "\n"); + } + } + delete pin_iter; + + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +void +WritePathSpice::writeSubcktInst(const Pin *input_pin) +{ auto inst = network_->instance(input_pin); auto inst_name = network_->pathName(inst); auto cell = network_->libertyCell(inst); auto cell_name = cell->name(); auto spice_port_names = cell_spice_port_names_[cell_name]; - - // Instance subckt call. streamPrint(spice_stream_, "x%s", inst_name); StringVector::Iterator port_iter(spice_port_names); while (port_iter.hasNext()) { @@ -750,55 +812,30 @@ WritePathSpice::writeGateStage(Stage stage) streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_name); } streamPrint(spice_stream_, " %s\n", cell_name); - - writeStageVoltageSources(stage, spice_port_names); - writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); } // Power/ground and input voltage sources. void -WritePathSpice::writeStageVoltageSources(Stage stage, - StringVector *spice_port_names) +WritePathSpice::writeSubcktInstVoltSrcs(Stage stage, + const Pin *input_pin, + int &volt_index, + LibertyPortLogicValues &port_values, + const Clock *clk, + DcalcAPIndex dcalc_ap_index) + { - auto input_pin = stageGateInputPin(stage); + auto inst = network_->instance(input_pin); + auto cell = network_->libertyCell(inst); + auto cell_name = cell->name(); + auto spice_port_names = cell_spice_port_names_[cell_name]; + auto drvr_pin = stageDrvrPin(stage); auto input_port = network_->libertyPort(input_pin); auto drvr_port = network_->libertyPort(drvr_pin); auto input_port_name = input_port->name(); auto drvr_port_name = drvr_port->name(); - auto inst = network_->instance(input_pin); auto inst_name = network_->pathName(inst); - auto cell = network_->libertyCell(inst); - auto gate_edge = stageGateEdge(stage); - LibertyPortLogicValues port_values; - const Clock *clk = NULL; - DcalcAPIndex dcalc_ap_index = 0; - if (gate_edge->role()->genericRole() == TimingRole::regClkToQ()) { - auto drvr_expr = drvr_port->function(); - if (drvr_expr) { - auto q_port = drvr_expr->port(); - if (q_port) { - // Drvr (register/latch output) function should be a reference - // to an internal port like IQ or IQN. - auto seq = cell->outputPortSequential(q_port); - if (seq) { - auto drvr_path = stageDrvrPath(stage); - auto drvr_tr = drvr_path->transition(this); - seqSensitizationValues(seq, drvr_tr, port_values); - clk = drvr_path->clock(this); - dcalc_ap_index = drvr_path->dcalcAnalysisPt(this)->index(); - } - else - report_->error("no register/latch found for path from %s to %s,\n", - input_port_name, drvr_port_name); - } - } - } - else if (drvr_port->function()) - sensitizationValues(inst, drvr_port->function(), input_port, port_values); - int volt_index = 1; debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name()); StringVector::Iterator port_iter(spice_port_names); while (port_iter.hasNext()) { @@ -818,7 +855,8 @@ WritePathSpice::writeStageVoltageSources(Stage stage, || stringEq(subckt_port_name, drvr_port_name))) { // Input voltage to sensitize path from gate input to output. auto port = cell->findLibertyPort(subckt_port_name); - if (port) { + if (port + && port->direction()->isAnyInput()) { const Pin *pin = network_->findPin(inst, port); // Look for tie high/low or propagated constant values. LogicValue port_value = sim_->logicValue(pin); @@ -831,6 +869,7 @@ WritePathSpice::writeStageVoltageSources(Stage stage, } switch (port_value) { case logic_zero: + case logic_unknown: writeVoltageSource(cell, inst_name, subckt_port_name, port->relatedGroundPin(), volt_index); @@ -848,8 +887,6 @@ WritePathSpice::writeStageVoltageSources(Stage stage, writeClkedStepSource(pin, TransRiseFall::fall(), clk, dcalc_ap_index, volt_index); break; - case logic_unknown: - break; } } } @@ -900,13 +937,66 @@ WritePathSpice::writeVoltageSource(LibertyCell *cell, pg_port_name); } +void +WritePathSpice::gatePortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index) +{ + clk = NULL; + dcalc_ap_index = 0; + + auto gate_edge = stageGateEdge(stage); + auto drvr_port = stageDrvrPort(stage); + if (gate_edge->role()->genericRole() == TimingRole::regClkToQ()) + regPortValues(stage, port_values, clk, dcalc_ap_index); + else if (drvr_port->function()) { + auto input_pin = stageGateInputPin(stage); + auto input_port = network_->libertyPort(input_pin); + auto inst = network_->instance(input_pin); + gatePortValues(inst, drvr_port->function(), input_port, port_values); + } +} + +void +WritePathSpice::regPortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index) +{ + auto drvr_port = stageDrvrPort(stage); + auto drvr_expr = drvr_port->function(); + if (drvr_expr) { + auto q_port = drvr_expr->port(); + if (q_port) { + // Drvr (register/latch output) function should be a reference + // to an internal port like IQ or IQN. + auto cell = stageLibertyCell(stage); + auto seq = cell->outputPortSequential(q_port); + if (seq) { + auto drvr_path = stageDrvrPath(stage); + auto drvr_tr = drvr_path->transition(this); + seqPortValues(seq, drvr_tr, port_values); + clk = drvr_path->clock(this); + dcalc_ap_index = drvr_path->dcalcAnalysisPt(this)->index(); + } + else + report_->error("no register/latch found for path from %s to %s,\n", + stageGateInputPort(stage)->name(), + stageDrvrPort(stage)->name()); + } + } +} + // Find the logic values for expression inputs to enable paths input_port. void -WritePathSpice::sensitizationValues(const Instance *inst, - FuncExpr *expr, - LibertyPort *input_port, - // Return values. - LibertyPortLogicValues &port_values) +WritePathSpice::gatePortValues(const Instance *inst, + FuncExpr *expr, + LibertyPort *input_port, + // Return values. + LibertyPortLogicValues &port_values) { auto left = expr->left(); auto right = expr->right(); @@ -914,7 +1004,7 @@ WritePathSpice::sensitizationValues(const Instance *inst, case FuncExpr::op_port: break; case FuncExpr::op_not: - sensitizationValues(inst, left, input_port, port_values); + gatePortValues(inst, left, input_port, port_values); break; case FuncExpr::op_or: if (left->hasPort(input_port) @@ -934,8 +1024,8 @@ WritePathSpice::sensitizationValues(const Instance *inst, // input_port + !left_port port_values[left->left()->port()] = logic_one; else { - sensitizationValues(inst, left, input_port, port_values); - sensitizationValues(inst, right, input_port, port_values); + gatePortValues(inst, left, input_port, port_values); + gatePortValues(inst, right, input_port, port_values); } break; case FuncExpr::op_and: @@ -956,8 +1046,8 @@ WritePathSpice::sensitizationValues(const Instance *inst, // input_port * !left_port port_values[left->left()->port()] = logic_zero; else { - sensitizationValues(inst, left, input_port, port_values); - sensitizationValues(inst, right, input_port, port_values); + gatePortValues(inst, left, input_port, port_values); + gatePortValues(inst, right, input_port, port_values); } break; case FuncExpr::op_xor: @@ -969,8 +1059,8 @@ WritePathSpice::sensitizationValues(const Instance *inst, && left->op() == FuncExpr::op_port) port_values[left->port()] = logic_zero; else { - sensitizationValues(inst, left, input_port, port_values); - sensitizationValues(inst, right, input_port, port_values); + gatePortValues(inst, left, input_port, port_values); + gatePortValues(inst, right, input_port, port_values); } break; case FuncExpr::op_one: @@ -980,10 +1070,10 @@ WritePathSpice::sensitizationValues(const Instance *inst, } void -WritePathSpice::seqSensitizationValues(Sequential *seq, - const TransRiseFall *tr, - // Return values. - LibertyPortLogicValues &port_values) +WritePathSpice::seqPortValues(Sequential *seq, + const TransRiseFall *tr, + // Return values. + LibertyPortLogicValues &port_values) { auto data = seq->data(); auto port = onePort(data); @@ -1041,16 +1131,15 @@ WritePathSpice::writeStageParasitics(Stage stage) { auto drvr_path = stageDrvrPath(stage); auto drvr_pin = stageDrvrPin(stage); - auto load_pin = stageLoadPin(stage); auto dcalc_ap = drvr_path->dcalcAnalysisPt(this); auto parasitic_ap = dcalc_ap->parasiticAnalysisPt(); auto parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - int resistor_index = 1; + Set reachable_pins; + int res_index = 1; int cap_index = 1; if (parasitic) { auto net = network_->net(drvr_pin); - auto net_name = - net ? network_->pathName(net) : network_->pathName(drvr_pin); + auto net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); initNodeMap(net_name); streamPrint(spice_stream_, "* Net %s\n", net_name); ParasiticDeviceIterator *device_iter = parasitics_->deviceIterator(parasitic); @@ -1058,14 +1147,19 @@ WritePathSpice::writeStageParasitics(Stage stage) auto device = device_iter->next(); auto resistance = parasitics_->value(device, parasitic_ap); if (parasitics_->isResistor(device)) { - ParasiticNode *node1 = parasitics_->node1(device); - ParasiticNode *node2 = parasitics_->node2(device); + auto node1 = parasitics_->node1(device); + auto node2 = parasitics_->node2(device); streamPrint(spice_stream_, "R%d %s %s %.3e\n", - resistor_index, + res_index, nodeName(node1), nodeName(node2), resistance); - resistor_index++; + res_index++; + + auto pin1 = parasitics_->connectionPin(node1); + reachable_pins.insert(pin1); + auto pin2 = parasitics_->connectionPin(node2); + reachable_pins.insert(pin2); } else if (parasitics_->isCouplingCap(device)) { // Ground coupling caps for now. @@ -1079,6 +1173,29 @@ WritePathSpice::writeStageParasitics(Stage stage) } } delete device_iter; + } + else + streamPrint(spice_stream_, "* No parasitics found for this net.\n"); + + // Add resistors from drvr to load for missing parasitic connections. + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + if (pin != drvr_pin + && network_->isLoad(pin) + && !network_->isHierarchical(pin) + && !reachable_pins.hasKey(pin)) { + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + network_->pathName(drvr_pin), + network_->pathName(pin), + short_ckt_resistance_); + res_index++; + } + } + delete pin_iter; + + if (parasitic) { ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic); while (node_iter->hasNext()) { auto node = node_iter->next(); @@ -1094,13 +1211,6 @@ WritePathSpice::writeStageParasitics(Stage stage) } delete node_iter; } - else { - streamPrint(spice_stream_, "* No parasitics found for this net.\n"); - streamPrint(spice_stream_, "R1 %s %s %.3e\n", - network_->pathName(drvr_pin), - network_->pathName(load_pin), - short_ckt_resistance_); - } } void @@ -1204,6 +1314,18 @@ WritePathSpice::findPathCellnames(// Return values. debugPrint1(debug_, "write_spice", 2, "cell %s\n", cell->name()); path_cell_names.insert(cell->name()); } + // Include side receivers. + auto drvr_pin = stageDrvrPin(stage); + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + auto port = network_->libertyPort(pin); + if (port) { + auto cell = port->libertyCell(); + path_cell_names.insert(cell->name()); + } + } + delete pin_iter; } } } @@ -1329,6 +1451,13 @@ WritePathSpice::stageGateInputPin(Stage stage) return path->pin(this); } +LibertyPort * +WritePathSpice::stageGateInputPort(Stage stage) +{ + auto pin = stageGateInputPin(stage); + return network_->libertyPort(pin); +} + Pin * WritePathSpice::stageDrvrPin(Stage stage) { @@ -1336,6 +1465,13 @@ WritePathSpice::stageDrvrPin(Stage stage) return path->pin(this); } +LibertyPort * +WritePathSpice::stageDrvrPort(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->libertyPort(pin); +} + Pin * WritePathSpice::stageLoadPin(Stage stage) { @@ -1364,6 +1500,20 @@ WritePathSpice::stageLoadPinName(Stage stage) return network_->pathName(pin); } +Instance * +WritePathSpice::stageInstance(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->instance(pin); +} + +LibertyCell * +WritePathSpice::stageLibertyCell(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->libertyPort(pin)->libertyCell(); +} + //////////////////////////////////////////////////////////////// // fprintf for c++ streams.