3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-27 02:45:52 +00:00

Synthesis support for SystemVerilog interfaces

This time doing the changes mostly in AST before RTLIL generation
This commit is contained in:
Ruben Undheim 2018-10-11 23:33:31 +02:00
parent 9850de405a
commit 75009ada3c
10 changed files with 501 additions and 21 deletions

View file

@ -905,7 +905,7 @@ RTLIL::Const AstNode::realAsConst(int width)
// create a new AstModule from an AST_MODULE AST node
static AstModule* process_module(AstNode *ast, bool defer)
{
log_assert(ast->type == AST_MODULE);
log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE);
if (defer)
log("Storing AST representation for module `%s'.\n", ast->str.c_str());
@ -916,6 +916,7 @@ static AstModule* process_module(AstNode *ast, bool defer)
current_module->ast = NULL;
current_module->name = ast->str;
current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
current_module->set_bool_attribute("\\cells_not_processed");
current_ast_mod = ast;
AstNode *ast_before_simplify = ast->clone();
@ -989,6 +990,8 @@ static AstModule* process_module(AstNode *ast, bool defer)
ignoreThisSignalsInInitial = RTLIL::SigSpec();
}
if (ast->type == AST_INTERFACE)
current_module->set_bool_attribute("\\is_interface");
current_module->ast = ast_before_simplify;
current_module->nolatches = flag_nolatches;
current_module->nomeminit = flag_nomeminit;
@ -1031,7 +1034,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
log_assert(current_ast->type == AST_DESIGN);
for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
{
if ((*it)->type == AST_MODULE)
if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE)
{
for (auto n : design->verilog_globals)
(*it)->children.push_back(n->clone());
@ -1083,8 +1086,123 @@ AstModule::~AstModule()
delete ast;
}
void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces)
{
bool is_top = false;
AstNode *new_ast = ast->clone();
for (auto &intf : local_interfaces) {
std::string intfname = intf.first.str();
RTLIL::Module *intfmodule = intf.second;
for (auto &wire_it : intfmodule->wires_){
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
std::string newname = log_id(wire_it.first);
newname = intfname + "." + newname;
wire->str = newname;
new_ast->children.push_back(wire);
}
}
std::string original_name = this->name.str();
std::string changed_name = original_name + "_before_replacing_local_interfaces";
design->rename(this, changed_name);
this->set_bool_attribute("\\to_delete");
if (this->get_bool_attribute("\\initial_top")) {
this->attributes.erase("\\initial_top");
is_top = true;
}
AstModule *newmod = process_module(new_ast, false);
design->add(newmod);
RTLIL::Module* mod = design->module(original_name);
if (is_top)
mod->set_bool_attribute("\\top");
mod->set_bool_attribute("\\interfaces_replaced_in_module");
}
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, bool mayfail)
{
AstNode *new_ast = NULL;
std::string modname = derive_common(design, parameters, &new_ast, mayfail);
// Since interfaces themselves may be instantiated with different parameters,
// "modname" must also take those into account, so that unique modules
// are derived for any variant of interface connections:
std::string interf_info = "";
bool has_interfaces = false;
for(auto &intf : interfaces) {
interf_info += log_id(intf.second->name);
has_interfaces = true;
}
if (has_interfaces)
modname += "$interfaces$" + interf_info;
if (!design->has(modname)) {
new_ast->str = modname;
for(auto &intf : interfaces) {
RTLIL::Module * intfmodule = intf.second;
std::string intfname = intf.first.str();
for (auto &wire_it : intfmodule->wires_){
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
std::string origname = log_id(wire_it.first);
std::string newname = intfname + "." + origname;
wire->str = newname;
wire->is_input = true;
wire->is_output = true;
new_ast->children.push_back(wire);
}
}
design->add(process_module(new_ast, false));
design->module(modname)->check();
RTLIL::Module* mod = design->module(modname);
for(auto &intf : interfaces) {
if(mod->wires_.count(intf.first)) {
mod->wires_.erase(intf.first);
mod->fixup_ports();
RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name);
new_subcell->set_bool_attribute("\\is_interface");
}
else {
log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname);
}
}
if (interfaces.size() > 0) {
mod->set_bool_attribute("\\interfaces_replaced_in_module");
}
} else {
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
}
delete new_ast;
return modname;
}
// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail)
{
AstNode *new_ast = NULL;
std::string modname = derive_common(design, parameters, &new_ast, mayfail);
if (!design->has(modname)) {
new_ast->str = modname;
design->add(process_module(new_ast, false));
design->module(modname)->check();
} else {
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
}
delete new_ast;
return modname;
}
// create a new parametric module (when needed) and return the name of the generated module
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool)
std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool)
{
std::string stripped_name = name.str();
@ -1156,15 +1274,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
else
modname = "$paramod" + stripped_name + para_info;
if (!design->has(modname)) {
new_ast->str = modname;
design->add(process_module(new_ast, false));
design->module(modname)->check();
} else {
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
}
delete new_ast;
(*new_ast_out) = new_ast;
return modname;
}

View file

@ -142,6 +142,9 @@ namespace AST
AST_NEGEDGE,
AST_EDGE,
AST_INTERFACE,
AST_INTERFACEPORT,
AST_INTERFACEPORTTYPE,
AST_PACKAGE
};
@ -284,6 +287,9 @@ namespace AST
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
~AstModule() YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, bool mayfail) YS_OVERRIDE;
std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
RTLIL::Module *clone() const YS_OVERRIDE;
};

View file

@ -854,6 +854,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_GENCASE:
case AST_PACKAGE:
break;
case AST_INTERFACEPORT: {
// If a port in a module with unknown type is found, mark it as "is_interface=true"
// This is used by the hierarchy pass to know when it can replace interface connection with the individual
// signals.
RTLIL::Wire *wire = current_module->addWire(str, 1);
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
wire->start_offset = 0;
wire->port_id = port_id;
wire->port_input = true;
wire->port_output = true;
wire->set_bool_attribute("\\is_interface");
wire->upto = 0;
}
break;
case AST_INTERFACEPORTTYPE:
break;
// remember the parameter, needed for example in techmap
case AST_PARAMETER:
@ -949,6 +965,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
{
RTLIL::Wire *wire = NULL;
RTLIL::SigChunk chunk;
bool is_interface = false;
int add_undef_bits_msb = 0;
int add_undef_bits_lsb = 0;
@ -969,15 +986,41 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk = RTLIL::Const(id2ast->children[0]->bits);
goto use_const_chunk;
}
else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)
else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
RTLIL::Wire *current_wire = current_module->wire(str);
if (current_wire->get_bool_attribute("\\is_interface"))
is_interface = true;
// Ignore
}
// If an identifier is found that is not already known, assume that it is an interface:
else if (1) { // FIXME: Check if sv_mode first?
is_interface = true;
}
else {
log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n",
str.c_str());
}
if (id2ast->type == AST_MEMORY)
log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n",
str.c_str());
// If identifier is an interface, create a RTLIL::SigSpec object and set is_interface to true.
// This makes it possible for the hierarchy pass to see what are interface connections and then replace them
// with the individual signals:
if (is_interface) {
RTLIL::Wire *dummy_wire;
std::string dummy_wire_name = "$dummywireforinterface" + str;
if (current_module->wires_.count(dummy_wire_name))
dummy_wire = current_module->wires_[dummy_wire_name];
else {
dummy_wire = current_module->addWire(dummy_wire_name);
dummy_wire->set_bool_attribute("\\is_interface");
}
RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire);
return tmp;
}
wire = current_module->wires_[str];
chunk.wire = wire;
chunk.width = wire->width;
@ -1423,6 +1466,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
RTLIL::Cell *cell = current_module->addCell(str, "");
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
cell->set_bool_attribute("\\module_not_derived");
for (auto it = children.begin(); it != children.end(); it++) {
AstNode *child = *it;

View file

@ -71,7 +71,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (stage == 0)
{
log_assert(type == AST_MODULE);
log_assert(type == AST_MODULE || type == AST_INTERFACE);
last_blocking_assignment_warn = pair<string, int>();
deep_recursion_warning = true;

View file

@ -150,6 +150,9 @@ YOSYS_NAMESPACE_END
"specparam" { return TOK_SPECPARAM; }
"package" { SV_KEYWORD(TOK_PACKAGE); }
"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); }
"interface" { SV_KEYWORD(TOK_INTERFACE); }
"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); }
"modport" { SV_KEYWORD(TOK_MODPORT); }
"parameter" { return TOK_PARAMETER; }
"localparam" { return TOK_LOCALPARAM; }
"defparam" { return TOK_DEFPARAM; }
@ -295,6 +298,11 @@ supply1 { return TOK_SUPPLY1; }
return TOK_ID;
}
[a-zA-Z_$][a-zA-Z0-9_$\.]* {
frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
return TOK_ID;
}
"/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" {
static bool printed_warning = false;
if (!printed_warning) {

View file

@ -106,6 +106,7 @@ static void free_attr(std::map<std::string, AstNode*> *al)
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
@ -168,6 +169,7 @@ design:
param_decl design |
localparam_decl design |
package design |
interface design |
/* empty */;
attr:
@ -320,6 +322,21 @@ module_arg:
}
delete $1;
} module_arg_opt_assignment |
TOK_ID {
astbuf1 = new AstNode(AST_INTERFACEPORT);
astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE));
astbuf1->children[0]->str = *$1;
delete $1;
} TOK_ID { /* SV interfaces */
if (!sv_mode)
frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str());
astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type.
astbuf2->str = *$3;
delete $3;
astbuf2->port_id = ++port_counter;
ast_stack.back()->children.push_back(astbuf2);
delete astbuf1; // really only needed if multiple instances of same type.
} module_arg_opt_assignment |
attr wire_type range TOK_ID {
AstNode *node = $2;
node->str = *$4;
@ -357,6 +374,33 @@ package_body:
package_body_stmt:
localparam_decl;
interface:
TOK_INTERFACE TOK_ID {
do_not_require_port_stubs = false;
AstNode *intf = new AstNode(AST_INTERFACE);
ast_stack.back()->children.push_back(intf);
ast_stack.push_back(intf);
current_ast_mod = intf;
port_stubs.clear();
port_counter = 0;
intf->str = *$2;
delete $2;
} module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE {
if (port_stubs.size() != 0)
frontend_verilog_yyerror("Missing details for module port `%s'.",
port_stubs.begin()->first.c_str());
ast_stack.pop_back();
log_assert(ast_stack.size() == 1);
current_ast_mod = NULL;
};
interface_body:
interface_body interface_body_stmt |;
interface_body_stmt:
param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
modport_stmt;
non_opt_delay:
'#' TOK_ID { delete $2; } |
'#' TOK_CONSTVAL { delete $2; } |
@ -1280,6 +1324,22 @@ opt_property:
opt_stmt_label:
TOK_ID ':' | /* empty */;
modport_stmt:
TOK_MODPORT TOK_ID modport_args_opt ';'
modport_args_opt:
'(' ')' | '(' modport_args optional_comma ')';
modport_args:
modport_arg | modport_args ',' modport_arg;
modport_arg:
modport_type_token TOK_ID |
TOK_ID
modport_type_token:
TOK_INPUT | TOK_OUTPUT
assert:
opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' {
if (noassert_mode)