From d06f6dfc516b5958da5d1501486fa219bebea2b5 Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 27 May 2009 13:41:54 -0700 Subject: [PATCH] Add support for unconnected_drive. 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. --- Module.cc | 1 + Module.h | 3 +++ elaborate.cc | 74 +++++++++++++++++++++++++++++++++++++++++----------- lexor.lex | 74 +++++++++++++++++++++++++++++++++++++++++++++++++--- parse.y | 15 ++++++++++- parse_misc.h | 2 ++ pform.cc | 4 ++- pform.h | 3 ++- 8 files changed, 155 insertions(+), 21 deletions(-) diff --git a/Module.cc b/Module.cc index e608a6401..774dfdbf7 100644 --- a/Module.cc +++ b/Module.cc @@ -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; } diff --git a/Module.h b/Module.h index a2960bc74..80d43031a 100644 --- a/Module.h +++ b/Module.h @@ -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 diff --git a/elaborate.cc b/elaborate.cc index e5674c474..3dec83463 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -978,6 +978,28 @@ static bool need_bufz_for_input_port(const vector&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 @@ -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 mport = rmod->get_port(idx); + if (mport.size() == 0) continue; - // 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 mport = rmod->get_port(idx); - if (mport.size() == 0) - continue; + perm_string pname = peek_tail_name(mport[0]->path()); - perm_string pname = peek_tail_name(mport[0]->path()); + NetNet*tmp = instance[0]->find_signal(pname); + assert(tmp); - 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; + } - 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; + } } } diff --git a/lexor.lex b/lexor.lex index 1d991563e..6f3aa2176 100644 --- a/lexor.lex +++ b/lexor.lex @@ -80,6 +80,7 @@ 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 keyword_mask_stack; @@ -87,6 +88,7 @@ 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 @@ -95,6 +97,7 @@ bool in_celldefine = false; %x CSTRING %s UDPTABLE %x PPTIMESCALE +%x PPUCDRIVE %x PPDEFAULT_NETTYPE %x PPBEGIN_KEYWORDS %s EDGES @@ -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); } +.* { process_ucdrive(yytext); } +\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}?.* { } @@ -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: * " xs / xs" diff --git a/parse.y b/parse.y index e24d67bbc..ce7f66a70 100644 --- a/parse.y +++ b/parse.y @@ -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; } diff --git a/parse_misc.h b/parse_misc.h index 6aa623a13..0a33c6999 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -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 diff --git a/pform.cc b/pform.cc index 52dfed03c..69ca9de36 100644 --- a/pform.cc +++ b/pform.cc @@ -467,12 +467,14 @@ void pform_module_set_ports(vector*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::const_iterator test = pform_modules.find(mod_name); diff --git a/pform.h b/pform.h index 92166d41b..53e56f38e 100644 --- a/pform.h +++ b/pform.h @@ -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*parms, svector*decl, list*table,