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.
This commit is contained in:
parent
d345fb7654
commit
bbf11b830b
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
3
Module.h
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
|
||||
|
|
|
|||
74
elaborate.cc
74
elaborate.cc
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
||||
// 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());
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
74
lexor.lex
74
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<int> 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); }
|
||||
<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}?.* { }
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
15
parse.y
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
pform.cc
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);
|
||||
|
|
|
|||
3
pform.h
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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue