Skip to content

Commit

Permalink
Add support for unconnected_drive.
Browse files Browse the repository at this point in the history
Icarus has recognized this directive, but it did not do anything
with the information. This patch adds the functionality for most
common cases. It adds this by changing the input net type from
wire/tri to tri1 or tri0 depending on the pull. The issue is that
if the input net is not a wire or tri this is not safe and should
really be done as an external pull gate connected to the input.
We will need to handle this is it ever comes up. For now a sorry
message is printed.
  • Loading branch information
caryr authored and steveicarus committed Jun 7, 2009
1 parent 364cf99 commit d06f6df
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 24 deletions.
1 change: 1 addition & 0 deletions Module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Module::Module(perm_string n)
{
library_flag = false;
is_cell = false;
uc_drive = UCD_NONE;
default_nettype = NetNet::NONE;
timescale_warn_done = false;
}
Expand Down
3 changes: 3 additions & 0 deletions Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class Module : public PScope, public LineInfo {

bool is_cell;

enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 };
UCDriveType uc_drive;

NetNet::Type default_nettype;

/* specparams are simpler then other params, in that they have
Expand Down
80 changes: 62 additions & 18 deletions elaborate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,28 @@ static bool need_bufz_for_input_port(const vector<NetNet*>&prts)
return false;
}

/*
* Convert a wire or tri to a tri0 or tri1 as needed to make
* an unconnected drive pull for floating inputs.
*/
static void convert_net(Design*des, const LineInfo *line,
NetNet *net, NetNet::Type type)
{
// If the types already match just return.
if (net->type() == type) return;

// We can only covert a wire or tri to have a default pull.
if (net->type() == NetNet::WIRE || net->type() == NetNet::TRI) {
net->type(type);
return;
}

// We may have to support this at some point in time!
cerr << line->get_fileline() << ": sorry: Can not pull floating "
"input type '" << net->type() << "'." << endl;
des->errors += 1;
}

/*
* Instantiate a module by recursively elaborating it. Set the path of
* the recursive elaboration so that signal names get properly
Expand Down Expand Up @@ -1105,28 +1127,50 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
// null parameter is passed in.

if (pins[idx] == 0) {
// We need this information to support the
// unconnected_drive directive and for a
// unconnected input warning when asked for.
vector<PEIdent*> mport = rmod->get_port(idx);
if (mport.size() == 0) continue;

perm_string pname = peek_tail_name(mport[0]->path());

NetNet*tmp = instance[0]->find_signal(pname);
assert(tmp);

if (tmp->port_type() == NetNet::PINPUT) {
// If we have an unconnected input convert it
// as needed if an unconnected_drive directive
// was given. This only works for tri or wire!
switch (rmod->uc_drive) {
case Module::UCD_PULL0:
convert_net(des, this, tmp, NetNet::TRI0);
break;
case Module::UCD_PULL1:
convert_net(des, this, tmp, NetNet::TRI1);
break;
case Module::UCD_NONE:
break;
}

// While we're here, look to see if this
// unconnected (from the outside) port is an
// input. If so, consider printing a port binding
// warning.
if (warn_portbinding) {
vector<PEIdent*> mport = rmod->get_port(idx);
if (mport.size() == 0)
continue;

perm_string pname = peek_tail_name(mport[0]->path());

NetNet*tmp = instance[0]->find_signal(pname);
assert(tmp);

if (tmp->port_type() == NetNet::PINPUT) {
// Print a waring for an unconnected input.
if (warn_portbinding) {
cerr << get_fileline() << ": warning: "
<< "Instantiating module "
<< rmod->mod_name()
<< " with dangling input port "
<< rmod->ports[idx]->name
<< "." << endl;
<< " with dangling input port '"
<< rmod->ports[idx]->name;
switch (rmod->uc_drive) {
case Module::UCD_PULL0:
cerr << "' (pulled low)." << endl;
break;
case Module::UCD_PULL1:
cerr << "' (pulled high)." << endl;
break;
case Module::UCD_NONE:
cerr << "' (floating)." << endl;
break;
}
}
}

Expand Down
74 changes: 71 additions & 3 deletions lexor.lex
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ static verinum*make_unsized_hex(const char*txt);
static int dec_buf_div2(char *buf);

static void process_timescale(const char*txt);
static void process_ucdrive(const char*txt);

static list<int> keyword_mask_stack;

static int comment_enter;
static bool in_module = false;
static bool in_UDP = false;
bool in_celldefine = false;
UCDriveType uc_drive = UCD_NONE;
%}

%x CCOMMENT
Expand All @@ -95,6 +97,7 @@ bool in_celldefine = false;
%x CSTRING
%s UDPTABLE
%x PPTIMESCALE
%x PPUCDRIVE
%x PPDEFAULT_NETTYPE
%x PPBEGIN_KEYWORDS
%s EDGES
Expand Down Expand Up @@ -392,16 +395,36 @@ S [afpnumkKMGT]
pform_set_default_nettype(NetNet::WIRE, yylloc.text,
yylloc.first_line);
in_celldefine = false;
uc_drive = UCD_NONE;
pform_set_timescale(def_ts_units, def_ts_prec, 0, 0);
/* Add `nounconnected_drive when implemented. */
} }

/* Notice and handle the `unconnected_drive directive. */
^{W}?`unconnected_drive { BEGIN(PPUCDRIVE); }
<PPUCDRIVE>.* { process_ucdrive(yytext); }
<PPUCDRIVE>\n {
if (in_module) {
cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
"`unconnected_drive directive can not be inside a "
"module definition." << endl;
error_count += 1;
}
yylloc.first_line += 1;
BEGIN(0); }

^{W}?`nounconnected_drive{W}? {
if (in_module) {
cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
"`nounconnected_drive directive can not be inside a "
"module definition." << endl;
error_count += 1;
}
uc_drive = UCD_NONE; }

/* These are directives that I do not yet support. I think that IVL
should handle these, not an external preprocessor. */
/* From 1364-2005 Chapter 19. */
^{W}?`nounconnected_drive{W}?.* { }
^{W}?`pragme{W}?.* { }
^{W}?`unconnected_drive{W}?.* { }

/* From 1364-2005 Annex D. */
^{W}?`default_decay_time{W}?.* { }
Expand Down Expand Up @@ -1082,6 +1105,51 @@ static bool get_timescale_const(const char *&cp, int &res, bool is_unit)
}


/*
* process either a pull0 or a pull1.
*/
static void process_ucdrive(const char*txt)
{
UCDriveType ucd = UCD_NONE;
const char*cp = txt + strspn(txt, " \t");

/* Skip the space after the `unconnected_drive directive. */
if (cp == txt) {
VLerror(yylloc, "Space required after `unconnected_drive "
"directive.");
return;
}

/* Check for the pull keyword. */
if (strncmp("pull", cp, 4) != 0) {
VLerror(yylloc, "pull required for `unconnected_drive "
"directive.");
return;
}
cp += 4;
if (*cp == '0') ucd = UCD_PULL0;
else if (*cp == '1') ucd = UCD_PULL1;
else {
cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
"`unconnected_drive does not support 'pull" << *cp
<< "'." << endl;
error_count += 1;
return;
}
cp += 1;

/* Verify that only space and/or a single line comment is left. */
cp += strspn(cp, " \t");
if (strncmp(cp, "//", 2) != 0 &&
(size_t)(cp-yytext) != strlen(yytext)) {
VLerror(yylloc, "Invalid `unconnected_dirve directive (extra "
"garbage after precision).");
return;
}

uc_drive = ucd;
}

/*
* The timescale parameter has the form:
* " <num> xs / <num> xs"
Expand Down
15 changes: 14 additions & 1 deletion parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -1923,7 +1923,20 @@ module : attribute_list_opt module_start IDENTIFIER
{ pform_module_set_ports($6); }
module_item_list_opt
K_endmodule
{ pform_endmodule($3, in_celldefine);
{ Module::UCDriveType ucd;
switch (uc_drive) {
case UCD_NONE:
default:
ucd = Module::UCD_NONE;
break;
case UCD_PULL0:
ucd = Module::UCD_PULL0;
break;
case UCD_PULL1:
ucd = Module::UCD_PULL1;
break;
}
pform_endmodule($3, in_celldefine, ucd);
delete[]$3;
}

Expand Down
2 changes: 2 additions & 0 deletions parse_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,7 @@ extern unsigned error_count, warn_count;
extern unsigned long based_size;

extern bool in_celldefine;
enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 };
extern UCDriveType uc_drive;

#endif
4 changes: 3 additions & 1 deletion pform.cc
Original file line number Diff line number Diff line change
Expand Up @@ -467,12 +467,14 @@ void pform_module_set_ports(vector<Module::port_t*>*ports)
}
}

void pform_endmodule(const char*name, bool in_celldefine)
void pform_endmodule(const char*name, bool in_celldefine,
Module::UCDriveType uc_drive)
{
assert(pform_cur_module);
perm_string mod_name = pform_cur_module->mod_name();
assert(strcmp(name, mod_name) == 0);
pform_cur_module->is_cell = in_celldefine;
pform_cur_module->uc_drive = uc_drive;

map<perm_string,Module*>::const_iterator test =
pform_modules.find(mod_name);
Expand Down
3 changes: 2 additions & 1 deletion pform.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ extern void pform_module_define_port(const struct vlltype&li,
extern Module::port_t* pform_module_port_reference(perm_string name,
const char*file,
unsigned lineno);
extern void pform_endmodule(const char*, bool in_celldefine);
extern void pform_endmodule(const char*, bool in_celldefine,
Module::UCDriveType uc_drive);

extern void pform_make_udp(perm_string name, list<perm_string>*parms,
svector<PWire*>*decl, list<string>*table,
Expand Down

0 comments on commit d06f6df

Please sign in to comment.