Permalink
Browse files

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.
  • Loading branch information...
1 parent 364cf99 commit d06f6dfc516b5958da5d1501486fa219bebea2b5 @caryr caryr committed with May 27, 2009
Showing with 158 additions and 24 deletions.
  1. +1 −0 Module.cc
  2. +3 −0 Module.h
  3. +62 −18 elaborate.cc
  4. +71 −3 lexor.lex
  5. +14 −1 parse.y
  6. +2 −0 parse_misc.h
  7. +3 −1 pform.cc
  8. +2 −1 pform.h
View
1 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;
}
View
3 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
View
80 elaborate.cc
@@ -979,6 +979,28 @@ static bool need_bufz_for_input_port(const vector<NetNet*>&prts)
}
/*
+ * 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
* set. Connect the ports of the instantiated module to the signals of
@@ -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;
+ }
}
}
View
74 lexor.lex
@@ -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
@@ -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); }
+<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}?.* { }
@@ -1083,6 +1106,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"
*/
View
15 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;
}
View
2 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
View
4 pform.cc
@@ -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);
View
3 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<perm_string>*parms,
svector<PWire*>*decl, list<string>*table,

0 comments on commit d06f6df

Please sign in to comment.