Use helper types for drive strengths and delays

Drive strengths and delays are often handled as a pair of drive values
and a rise/fall/decay triple. Add small helper types to carry these
groups and use them in the continuous assignment and gate/UDP elaboration
paths.

Use the same helper types when propagating drive and delay values through
netlist links.

Also add helpers for dumping the values in debug output. This keeps the
behavior consistent and fixes one small bug where some of the debug
dumps printed the pointer value for the delays, rather than the actual
delay values.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-05-17 12:55:30 -07:00
parent cc86f69a89
commit 85c58d0a7a
14 changed files with 187 additions and 201 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2025 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -130,10 +130,11 @@ static NetExpr* make_delay_nets(Design*des, NetScope*scope, NetExpr*expr)
return expr;
}
static NetExpr* calc_decay_time(NetExpr *rise, NetExpr *fall)
static const NetExpr *calc_decay_time(const NetExpr *rise,
const NetExpr *fall)
{
const NetEConst *c_rise = dynamic_cast<NetEConst*>(rise);
const NetEConst *c_fall = dynamic_cast<NetEConst*>(fall);
const NetEConst *c_rise = dynamic_cast<const NetEConst*>(rise);
const NetEConst *c_fall = dynamic_cast<const NetEConst*>(fall);
if (c_rise && c_fall) {
if (c_rise->value() < c_fall->value()) return rise;
else return fall;
@ -142,44 +143,43 @@ static NetExpr* calc_decay_time(NetExpr *rise, NetExpr *fall)
return 0;
}
void PDelays::eval_delays(Design*des, NetScope*scope,
NetExpr*&rise_time,
NetExpr*&fall_time,
NetExpr*&decay_time,
void PDelays::eval_delays(Design*des, NetScope*scope, delay_exprs_t &delays,
bool as_nets_flag) const
{
assert(scope);
if (delay_[0]) {
rise_time = calculate_val(des, scope, delay_[0]);
NetExpr *rise = calculate_val(des, scope, delay_[0]);
if (as_nets_flag)
rise_time = make_delay_nets(des, scope, rise_time);
rise = make_delay_nets(des, scope, rise);
delays.rise = rise;
if (delay_[1]) {
fall_time = calculate_val(des, scope, delay_[1]);
NetExpr *fall = calculate_val(des, scope, delay_[1]);
if (as_nets_flag)
fall_time = make_delay_nets(des, scope, fall_time);
fall = make_delay_nets(des, scope, fall);
delays.fall = fall;
if (delay_[2]) {
decay_time = calculate_val(des, scope, delay_[2]);
NetExpr *decay = calculate_val(des, scope, delay_[2]);
if (as_nets_flag)
decay_time = make_delay_nets(des, scope,
decay_time);
decay = make_delay_nets(des, scope, decay);
delays.decay = decay;
} else {
// If this is zero then we need to do the min()
// at run time.
decay_time = calc_decay_time(rise_time, fall_time);
delays.decay = calc_decay_time(delays.rise,
delays.fall);
}
} else {
assert(delay_[2] == 0);
fall_time = rise_time;
decay_time = rise_time;
delays.fall = delays.rise;
delays.decay = delays.rise;
}
} else {
rise_time = 0;
fall_time = 0;
decay_time = 0;
delays.rise = nullptr;
delays.fall = nullptr;
delays.decay = nullptr;
}
}

View File

@ -1,7 +1,7 @@
#ifndef IVL_PDelays_H
#define IVL_PDelays_H
/*
* Copyright (c) 1999-2021 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -27,6 +27,7 @@ class Design;
class NetScope;
class NetExpr;
class PExpr;
struct delay_exprs_t;
/*
* Various PForm objects can carry delays. These delays include rise,
@ -46,10 +47,7 @@ class PDelays {
unsigned delay_count() const;
void eval_delays(Design*des, NetScope*scope,
NetExpr*&rise_time,
NetExpr*&fall_time,
NetExpr*&decay_time,
void eval_delays(Design*des, NetScope*scope, delay_exprs_t &delays,
bool as_nets_flag =false) const;
void dump_delays(std::ostream&out) const;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2025 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -41,29 +41,23 @@ void PGate::set_pins_(list<PExpr*>*pins)
}
PGate::PGate(perm_string name, list<PExpr*>*pins, const list<PExpr*>*del)
: name_(name), pins_(pins? pins->size() : 0), ranges_(0)
: name_(name), pins_(pins? pins->size() : 0), ranges_(nullptr)
{
if (pins) set_pins_(pins);
if (del) delay_.set_delays(del);
str0_ = IVL_DR_STRONG;
str1_ = IVL_DR_STRONG;
}
PGate::PGate(perm_string name, list<PExpr*>*pins, PExpr*del)
: name_(name), pins_(pins? pins->size() : 0), ranges_(0)
: name_(name), pins_(pins? pins->size() : 0), ranges_(nullptr)
{
if (pins) set_pins_(pins);
if (del) delay_.set_delay(del);
str0_ = IVL_DR_STRONG;
str1_ = IVL_DR_STRONG;
}
PGate::PGate(perm_string name, list<PExpr*>*pins)
: name_(name), pins_(pins? pins->size() : 0), ranges_(0)
: name_(name), pins_(pins? pins->size() : 0), ranges_(nullptr)
{
if (pins) set_pins_(pins);
str0_ = IVL_DR_STRONG;
str1_ = IVL_DR_STRONG;
}
PGate::~PGate()
@ -76,24 +70,14 @@ void PGate::set_ranges(list<pform_range_t>*ranges)
ranges_ = ranges;
}
ivl_drive_t PGate::strength0() const
drive_strength_t PGate::strength() const
{
return str0_;
return strength_;
}
void PGate::strength0(ivl_drive_t s)
void PGate::strength(const drive_strength_t &str)
{
str0_ = s;
}
ivl_drive_t PGate::strength1() const
{
return str1_;
}
void PGate::strength1(ivl_drive_t s)
{
str1_ = s;
strength_ = str;
}
void PGate::elaborate_scope(Design*, NetScope*) const
@ -109,15 +93,10 @@ void PGate::elaborate_scope(Design*, NetScope*) const
* numbers of expressions.
*/
void PGate::eval_delays(Design*des, NetScope*scope,
NetExpr*&rise_expr,
NetExpr*&fall_expr,
NetExpr*&decay_expr,
void PGate::eval_delays(Design*des, NetScope*scope, delay_exprs_t &delays,
bool as_net_flag) const
{
delay_.eval_delays(des, scope,
rise_expr, fall_expr, decay_expr,
as_net_flag);
delay_.eval_delays(des, scope, delays, as_net_flag);
}
unsigned PGate::delay_count() const

14
PGate.h
View File

@ -31,6 +31,7 @@
class PExpr;
class PUdp;
class Module;
struct delay_exprs_t;
/*
* A PGate represents a Verilog gate. The gate has a name and other
@ -66,10 +67,7 @@ class PGate : public PNamedItem {
// This evaluates the delays as far as possible, but returns
// an expression, and do not signal errors.
void eval_delays(Design*des, NetScope*scope,
NetExpr*&rise_time,
NetExpr*&fall_time,
NetExpr*&decay_time,
void eval_delays(Design*des, NetScope*scope, delay_exprs_t &delays,
bool as_net_flag =false) const;
unsigned delay_count() const;
@ -77,11 +75,9 @@ class PGate : public PNamedItem {
unsigned pin_count() const { return pins_.size(); }
PExpr*pin(unsigned idx) const { return pins_[idx]; }
ivl_drive_t strength0() const;
ivl_drive_t strength1() const;
drive_strength_t strength() const;
void strength0(ivl_drive_t);
void strength1(ivl_drive_t);
void strength(const drive_strength_t &str);
std::map<perm_string,PExpr*> attributes;
@ -109,7 +105,7 @@ class PGate : public PNamedItem {
std::list<pform_range_t>*ranges_;
ivl_drive_t str0_, str1_;
drive_strength_t strength_;
void set_pins_(std::list<PExpr*>*pins);

View File

@ -180,9 +180,7 @@ void cprop_functor::lpm_mux(Design*des, NetMux*obj)
<< "Replace binary MUX with constant select=" << sel_val
<< " with a BUFZ to the selected input." << endl;
tmp->rise_time(obj->rise_time());
tmp->fall_time(obj->fall_time());
tmp->decay_time(obj->decay_time());
tmp->delay_times(obj->delay_times());
connect(tmp->pin(0), obj->pin_Result());
if (sel_val == verinum::V1)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2021 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -90,6 +90,30 @@ ostream& operator << (ostream&o, ivl_drive_t str)
return o;
}
ostream &operator << (ostream &o, const drive_strength_t &strength)
{
o << strength.drive0 << "0 " << strength.drive1 << "1";
return o;
}
static void dump_delay_expr(ostream &o, const NetExpr *expr)
{
if (expr)
o << *expr;
else
o << "0";
}
ostream &operator << (ostream &o, const delay_exprs_t &delays)
{
dump_delay_expr(o, delays.rise);
o << ",";
dump_delay_expr(o, delays.fall);
o << ",";
dump_delay_expr(o, delays.decay);
return o;
}
ostream& operator << (ostream&o, ivl_variable_type_t val)
{
switch (val) {
@ -454,8 +478,7 @@ void NetNet::dump_net(ostream&o, unsigned ind) const
o << " (eref=" << peek_eref() << ", lref=" << peek_lref() << ")";
if (scope())
o << " scope=" << scope_path(scope());
o << " #(" << rise_time() << "," << fall_time() << ","
<< decay_time() << ") vector_width=" << vector_width()
o << " #(" << delay_times() << ") vector_width=" << vector_width()
<< " pin_count=" << pin_count();
if (pins_are_virtual()) {
o << " pins_are_virtual" << endl;
@ -486,8 +509,7 @@ void NetNet::dump_net(ostream&o, unsigned ind) const
void NetNode::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "node: ";
o << typeid(*this).name() << " #(" << rise_time()
<< "," << fall_time() << "," << decay_time() << ") " << name()
o << typeid(*this).name() << " #(" << delay_times() << ") " << name()
<< endl;
dump_node_pins(o, ind+4);
@ -518,8 +540,7 @@ void NetPins::dump_node_pins(ostream&o, unsigned ind, const char**pin_names) con
break;
}
o << " (" << pin(idx).drive0() << "0 "
<< pin(idx).drive1() << "1): ";
o << " (" << pin(idx).drive() << "): ";
if (pin(idx).is_linked()) {
const Nexus*nex = pin(idx).nexus();
@ -622,11 +643,7 @@ void NetConcat::dump_node(ostream&o, unsigned ind) const
o << setw(ind) << "" << "NetConcat: ";
o << name();
if (rise_time())
o << " #(" << *rise_time()
<< "," << *fall_time() << "," << *decay_time() << ")";
else
o << " #(0,0,0)";
o << " #(" << delay_times() << ")";
o << " scope=" << scope_path(scope())
<< " width=" << width_ << endl;
dump_node_pins(o, ind+4);
@ -651,14 +668,7 @@ void NetPow::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "LPM_POW (NetPow): " << name()
<< " scope=" << scope_path(scope())
<< " delay=(";
if (rise_time())
o << *rise_time() << "," << *fall_time() << ","
<< *decay_time();
else
o << "0,0,0";
o << ")" << endl;
<< " delay=(" << delay_times() << ")" << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);
}
@ -676,8 +686,7 @@ void NetBUFZ::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "NetBUFZ: " << name()
<< " scope=" << scope_path(scope())
<< " delay=(" << rise_time() << "," << fall_time() << "," <<
decay_time() << ") width=" << width()
<< " delay=(" << delay_times() << ") width=" << width()
<< (transparent()? " " : " non-") << "transparent" << endl;
dump_node_pins(o, ind+4);
}
@ -693,10 +702,8 @@ void NetConst::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "constant " << value_;
o << ": " << name();
if (rise_time())
o << " #(" << *rise_time()
<< "," << *fall_time()
<< "," << *decay_time() << ")";
if (delay_times().has_delay())
o << " #(" << delay_times() << ")";
else
o << " #(.,.,.)";
o << endl;
@ -729,10 +736,8 @@ void NetLiteral::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "constant real " << real_
<< ": " << name();
if (rise_time())
o << " #(" << *rise_time()
<< "," << *fall_time()
<< "," << *decay_time() << ")";
if (delay_times().has_delay())
o << " #(" << delay_times() << ")";
else
o << " #(.,.,.)";
o << endl;
@ -810,8 +815,7 @@ void NetLogic::dump_node(ostream&o, unsigned ind) const
o << "xor";
break;
}
o << " #(" << rise_time()
<< "," << fall_time() << "," << decay_time() << ") " << name()
o << " #(" << delay_times() << ") " << name()
<< " scope=" << scope_path(scope())
<< endl;
@ -839,10 +843,8 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const
}
o << setw(ind) << "" << "NetPartSelect(" << pt << "): "
<< name();
if (rise_time())
o << " #(" << *rise_time()
<< "," << *fall_time()
<< "," << *decay_time() << ")";
if (delay_times().has_delay())
o << " #(" << delay_times() << ")";
else
o << " #(.,.,.)";
o << " off=" << off_ << " wid=" << wid_ <<endl;
@ -854,10 +856,8 @@ void NetSubstitute::dump_node(ostream&fd, unsigned ind) const
{
fd << setw(ind) << "" << "NetSubstitute: "
<< name();
if (rise_time())
fd << " #(" << *rise_time()
<< "," << *fall_time()
<< "," << *decay_time() << ")";
if (delay_times().has_delay())
fd << " #(" << delay_times() << ")";
else
fd << " #(.,.,.)";
fd << " width=" << wid_ << " base=" << off_ <<endl;
@ -877,10 +877,8 @@ void NetReplicate::dump_node(ostream&o, unsigned ind) const
void NetSignExtend::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "NetSignExtend: " << name();
if (rise_time())
o << " #(" << *rise_time()
<< "," << *fall_time()
<< "," << *decay_time() << ")";
if (delay_times().has_delay())
o << " #(" << delay_times() << ")";
else
o << " #(.,.,.)";
o << " output width=" << width_ << endl;
@ -914,8 +912,7 @@ void NetUReduce::dump_node(ostream&o, unsigned ind) const
o << "xnor";
break;
}
o << " #(" << rise_time()
<< "," << fall_time() << "," << decay_time() << ") " << name()
o << " #(" << delay_times() << ") " << name()
<< " scope=" << scope_path(scope())
<< endl;
@ -935,10 +932,8 @@ void NetUserFunc::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "USER FUNC: "
<< scope_path(def_);
if (rise_time())
o << " #(" <<*rise_time()
<<","<<*fall_time()
<< "," <<*decay_time() << ")";
if (delay_times().has_delay())
o << " #(" << delay_times() << ")";
o << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);
@ -986,14 +981,7 @@ void NetTran::dump_node(ostream&o, unsigned ind) const
<< " part=" << part_width()
<< " offset=" << part_offset();
}
o << " delay=(";
if (rise_time())
o << *rise_time() << "," << *fall_time() << ","
<< *decay_time();
else
o << "0,0,0";
o << ")" << endl;
o << " delay=(" << delay_times() << ")" << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);
}
@ -1001,8 +989,7 @@ void NetTran::dump_node(ostream&o, unsigned ind) const
void NetUDP::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "UDP (" << udp_name() << "): ";
o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() <<
") " << name() << endl;
o << " #(" << delay_times() << ") " << name() << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);

View File

@ -1197,8 +1197,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope)
pull = new NetLogic(scope, scope->local_symbol(),
1, pull_type, wid);
pull->set_line(*this);
pull->pin(0).drive0(IVL_DR_SUPPLY);
pull->pin(0).drive1(IVL_DR_SUPPLY);
pull->pin(0).drive(drive_strength_t(IVL_DR_SUPPLY,
IVL_DR_SUPPLY));
des->add_node(pull);
wtype = NetNet::WIRE;
}

View File

@ -115,19 +115,16 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
{
ivl_assert(*this, scope);
NetExpr* rise_time, *fall_time, *decay_time;
eval_delays(des, scope, rise_time, fall_time, decay_time, true);
ivl_drive_t drive0 = strength0();
ivl_drive_t drive1 = strength1();
drive_strength_t drive = strength();
delay_exprs_t delays;
eval_delays(des, scope, delays, true);
ivl_assert(*this, pin(0));
ivl_assert(*this, pin(1));
/* Elaborate the l-value. */
// A continuous assignment can drive a variable if the default strength is used.
bool var_allowed_in_sv = (drive0 == IVL_DR_STRONG &&
drive1 == IVL_DR_STRONG) ? true : false;
bool var_allowed_in_sv = !drive.has_drive();
NetNet*lval = pin(0)->elaborate_lnet(des, scope, var_allowed_in_sv);
if (lval == 0) {
return;
@ -214,7 +211,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
/* When we are given a non-default strength value and if the drive
* source is a bit, part, indexed select or a concatenation we need
* to add a driver (BUFZ) to convey the strength information. */
if ((drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG) &&
if (drive.has_drive() &&
((dynamic_cast<NetESelect*>(rval_expr)) ||
(dynamic_cast<NetEConcat*>(rval_expr)))) {
need_driver_flag = true;
@ -242,11 +239,11 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
/* Set the drive and delays for the r-val. */
if (drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG)
rval->pin(0).drivers_drive(drive0, drive1);
if (drive.has_drive())
rval->pin(0).drivers_drive(drive);
if (rise_time || fall_time || decay_time)
rval->pin(0).drivers_delays(rise_time, fall_time, decay_time);
if (delays.has_delay())
rval->pin(0).drivers_delays(delays);
connect(lval->pin(0), rval->pin(0));
@ -826,8 +823,9 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
values are given, they are taken as specified. */
if (check_delay_count(des)) return;
NetExpr* rise_time, *fall_time, *decay_time;
eval_delays(des, scope, rise_time, fall_time, decay_time, true);
delay_exprs_t delays;
eval_delays(des, scope, delays, true);
drive_strength_t drive = strength();
struct attrib_list_t*attrib_list;
unsigned attrib_list_n = 0;
@ -859,12 +857,8 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
attrib_list[adx].val);
/* Set the delays and drive strength for all built in gates. */
cur[idx]->rise_time(rise_time);
cur[idx]->fall_time(fall_time);
cur[idx]->decay_time(decay_time);
cur[idx]->pin(0).drive0(strength0());
cur[idx]->pin(0).drive1(strength1());
cur[idx]->delay_times(delays);
cur[idx]->pin(0).drive(drive);
cur[idx]->set_line(*this);
des->add_node(cur[idx]);
@ -2457,7 +2451,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
{
NetExpr*rise_expr =0, *fall_expr =0, *decay_expr =0;
delay_exprs_t delays;
perm_string my_name = get_name();
if (my_name == 0)
@ -2474,8 +2468,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
} else {
PDelays tmp_del;
tmp_del.set_delays(overrides_, false);
tmp_del.eval_delays(des, scope, rise_expr, fall_expr,
decay_expr, true);
tmp_del.eval_delays(des, scope, delays, true);
}
}
@ -2494,9 +2487,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
ivl_assert(*this, udp);
NetUDP*net = new NetUDP(scope, my_name, udp->ports.size(), udp);
net->set_line(*this);
net->rise_time(rise_expr);
net->fall_time(fall_expr);
net->decay_time(decay_expr);
net->delay_times(delays);
struct attrib_list_t*attrib_list;
unsigned attrib_list_n = 0;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2025 Stephen Williams (steve@icarus.com)
* Copyright (c) 2002-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -41,8 +41,7 @@ NetUserFunc::NetUserFunc(NetScope*s, perm_string n, NetScope*d,
for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) {
pin(idx).set_dir(Link::INPUT);
pin(idx).drive0(IVL_DR_HiZ);
pin(idx).drive1(IVL_DR_HiZ);
pin(idx).drive(drive_strength_t::hiz);
}
}
@ -103,8 +102,7 @@ NetSysFunc::NetSysFunc(NetScope*s, perm_string n,
for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) {
pin(idx).set_dir(Link::INPUT);
pin(idx).drive0(IVL_DR_HiZ);
pin(idx).drive1(IVL_DR_HiZ);
pin(idx).drive(drive_strength_t::hiz);
}
}

View File

@ -164,25 +164,26 @@ Link::DIR Link::get_dir() const
return dir_;
}
void Link::drivers_delays(const NetExpr*rise, const NetExpr*fall, const NetExpr*decay)
void Link::drivers_delays(const delay_exprs_t &delays)
{
find_nexus_()->drivers_delays(rise, fall, decay);
find_nexus_()->drivers_delays(delays);
}
void Link::drivers_drive(ivl_drive_t drive0__, ivl_drive_t drive1__)
void Link::drivers_drive(const drive_strength_t &drive)
{
find_nexus_()->drivers_drive(drive0__, drive1__);
find_nexus_()->drivers_drive(drive);
}
void Link::drive0(ivl_drive_t str)
void Link::drive(const drive_strength_t &drive)
{
drive0_ = str;
drive0_ = drive.drive0;
drive1_ = drive.drive1;
}
void Link::drive1(ivl_drive_t str)
drive_strength_t Link::drive() const
{
drive1_ = str;
return drive_strength_t(drive0_, drive1_);
}
ivl_drive_t Link::drive0() const
@ -358,7 +359,7 @@ bool Nexus::drivers_present() const
return false;
}
void Nexus::drivers_delays(const NetExpr*rise, const NetExpr*fall, const NetExpr*decay)
void Nexus::drivers_delays(const delay_exprs_t &delays)
{
for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
if (cur->get_dir() != Link::OUTPUT)
@ -368,20 +369,17 @@ void Nexus::drivers_delays(const NetExpr*rise, const NetExpr*fall, const NetExpr
if (obj == 0)
continue;
obj->rise_time(rise);
obj->fall_time(fall);
obj->decay_time(decay);
obj->delay_times(delays);
}
}
void Nexus::drivers_drive(ivl_drive_t drive0, ivl_drive_t drive1)
void Nexus::drivers_drive(const drive_strength_t &drive)
{
for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
if (cur->get_dir() != Link::OUTPUT)
continue;
cur->drive0(drive0);
cur->drive1(drive1);
cur->drive(drive);
}
}

View File

@ -40,6 +40,8 @@
using namespace std;
const drive_strength_t drive_strength_t::hiz(IVL_DR_HiZ, IVL_DR_HiZ);
ostream& operator<< (ostream&o, NetNet::Type t)
{
switch (t) {
@ -237,7 +239,7 @@ bool NetPins::is_linked(void) const
}
NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
: NetPins(np), scope_(s), name_(n), delay1_(0), delay2_(0), delay3_(0)
: NetPins(np), scope_(s), name_(n), delays_()
{
/* Don't
ivl_assert(*this, np > 0);
@ -260,6 +262,11 @@ const NetScope* NetObj::scope() const
return scope_;
}
void NetObj::delay_times(const delay_exprs_t &delays)
{
delays_ = delays;
}
NetNode::NetNode(NetScope*s, perm_string n, unsigned npins)
: NetObj(s, n, npins), node_next_(0), node_prev_(0), design_(0)
{

View File

@ -103,6 +103,42 @@ struct functor_t;
# define ENUM_UNSIGNED_INT
#endif
struct drive_strength_t {
static const drive_strength_t hiz;
explicit drive_strength_t(ivl_drive_t d0 = IVL_DR_STRONG,
ivl_drive_t d1 = IVL_DR_STRONG)
: drive0(d0), drive1(d1)
{ }
bool has_drive() const
{
return drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG;
}
ivl_drive_t drive0;
ivl_drive_t drive1;
};
struct delay_exprs_t {
explicit delay_exprs_t(const NetExpr *r = nullptr,
const NetExpr *f = nullptr,
const NetExpr *d = nullptr)
: rise(r), fall(f), decay(d)
{ }
bool has_delay() const
{
return rise || fall || decay;
}
const NetExpr *rise;
const NetExpr *fall;
const NetExpr *decay;
};
std::ostream &operator << (std::ostream &o, const drive_strength_t &strength);
std::ostream &operator << (std::ostream &o, const delay_exprs_t &delays);
std::ostream& operator << (std::ostream&o, ivl_variable_type_t val);
extern void join_island(NetPins*obj);
@ -126,17 +162,17 @@ class Link {
DIR get_dir() const;
// Set the delay for all the drivers to this nexus.
void drivers_delays(const NetExpr*rise, const NetExpr*fall, const NetExpr*decay);
void drivers_delays(const delay_exprs_t &delays);
// A link has a drive strength for 0 and 1 values. The drive0
// strength is for when the link has the value 0, and drive1
// strength is for when the link has a value 1.
void drive0(ivl_drive_t);
void drive1(ivl_drive_t);
void drive(const drive_strength_t &drive);
drive_strength_t drive() const;
// This sets the drives for all drivers of this link, and not
// just the current link.
void drivers_drive(ivl_drive_t d0, ivl_drive_t d1);
void drivers_drive(const drive_strength_t &drive);
ivl_drive_t drive0() const;
ivl_drive_t drive1() const;
@ -269,13 +305,15 @@ class NetObj : public NetPins, public Attrib {
perm_string name() const { return name_; }
void rename(perm_string n) { name_ = n; }
const NetExpr* rise_time() const { return delay1_; }
const NetExpr* fall_time() const { return delay2_; }
const NetExpr* decay_time() const { return delay3_; }
const NetExpr *rise_time() const { return delays_.rise; }
const NetExpr *fall_time() const { return delays_.fall; }
const NetExpr *decay_time() const { return delays_.decay; }
delay_exprs_t delay_times() const
{
return delays_;
}
void rise_time(const NetExpr* d) { delay1_ = d; }
void fall_time(const NetExpr* d) { delay2_ = d; }
void decay_time(const NetExpr* d) { delay3_ = d; }
void delay_times(const delay_exprs_t &delays);
void dump_obj_attr(std::ostream&, unsigned) const;
@ -284,9 +322,7 @@ class NetObj : public NetPins, public Attrib {
private:
NetScope*scope_;
perm_string name_;
const NetExpr* delay1_;
const NetExpr* delay2_;
const NetExpr* delay3_;
delay_exprs_t delays_;
};
/*
@ -374,8 +410,8 @@ class Nexus {
const char* name() const;
void drivers_delays(const NetExpr*rise, const NetExpr*fall, const NetExpr*decay);
void drivers_drive(ivl_drive_t d0, ivl_drive_t d1);
void drivers_delays(const delay_exprs_t &delays);
void drivers_drive(const drive_strength_t &drive);
Link*first_nlink();
const Link* first_nlink()const;

View File

@ -2260,8 +2260,7 @@ static void pform_makegate(PGBuiltin::Type type,
// pform_bind_attributes function to keep the attr object.
pform_bind_attributes(cur->attributes, attr, true);
cur->strength0(str.str0);
cur->strength1(str.str1);
cur->strength(drive_strength_t(str.str0, str.str1));
cur->set_line(info);
if (pform_cur_generate) {
@ -2483,8 +2482,7 @@ static PGAssign* pform_make_pgassign(PExpr*lval, PExpr*rval,
else
cur = new PGAssign(wires, del);
cur->strength0(str.str0);
cur->strength1(str.str1);
cur->strength(drive_strength_t(str.str0, str.str1));
if (pform_cur_generate)
pform_cur_generate->add_gate(cur);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2025 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2026 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -741,7 +741,7 @@ void PGate::dump(ostream&out, unsigned ind) const
void PGAssign::dump(ostream&out, unsigned ind) const
{
out << setw(ind) << "";
out << "assign (" << strength0() << "0 " << strength1() << "1) ";
out << "assign (" << strength() << ") ";
dump_delays(out);
out << " " << *pin(0) << " = " << *pin(1) << ";" << endl;
}
@ -787,7 +787,7 @@ void PGBuiltin::dump(ostream&out, unsigned ind) const
out << "builtin gate ";
}
out << "(" << strength0() << "0 " << strength1() << "1) ";
out << "(" << strength() << ") ";
dump_delays(out);
out << " " << get_name();
dump_ranges(out);