Skip to content

Commit

Permalink
Merge pull request #659 from rubund/sv_interfaces
Browse files Browse the repository at this point in the history
Support for SystemVerilog interfaces and modports
  • Loading branch information
cliffordwolf committed Oct 18, 2018
2 parents 24a5c65 + 736105b commit f24bc1e
Show file tree
Hide file tree
Showing 11 changed files with 649 additions and 21 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -452,6 +452,9 @@ from SystemVerilog:
into a design with ``read_verilog``, all its packages are available to
SystemVerilog files being read into the same design afterwards.

- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
ports are inputs or outputs are supported.


Building the documentation
==========================
Expand Down
190 changes: 179 additions & 11 deletions frontends/ast/ast.cc
Expand Up @@ -2,6 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
Expand Down Expand Up @@ -905,7 +906,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());
Expand All @@ -916,6 +917,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();
Expand Down Expand Up @@ -989,6 +991,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;
Expand Down Expand Up @@ -1031,7 +1035,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());
Expand Down Expand Up @@ -1083,8 +1087,179 @@ AstModule::~AstModule()
delete ast;
}

// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
// from AST. The interface members are copied into the AST module with the prefix of the interface.
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);
}
}

// The old module will be deleted. Rename and mark for deletion:
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");

// Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
// new module.
if (this->get_bool_attribute("\\initial_top")) {
this->attributes.erase("\\initial_top");
is_top = true;
}

// Generate RTLIL from AST for the new module and add to the design:
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");

// Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
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
// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, 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;

// Iterate over all interfaces which are ports in this module:
for(auto &intf : interfaces) {
RTLIL::Module * intfmodule = intf.second;
std::string intfname = intf.first.str();
// Check if a modport applies for the interface port:
AstNode *modport = NULL;
if (modports.count(intfname) > 0) {
std::string interface_modport = modports.at(intfname).str();
AstModule *ast_module_of_interface = (AstModule*)intfmodule;
AstNode *ast_node_of_interface = ast_module_of_interface->ast;
for (auto &ch : ast_node_of_interface->children) {
if (ch->type == AST_MODPORT) {
if (ch->str == interface_modport) { // Modport found
modport = ch;
}
}
}
}
// Iterate over all wires in the interface and add them to the module:
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;
if (modport != NULL) {
bool found_in_modport = false;
// Search for the current wire in the wire list for the current modport
for (auto &ch : modport->children) {
if (ch->type == AST_MODPORTMEMBER) {
std::string compare_name = "\\" + origname;
if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output
found_in_modport = true;
wire->is_input = ch->is_input;
wire->is_output = ch->is_output;
break;
}
}
}
if (found_in_modport) {
new_ast->children.push_back(wire);
}
else { // If not found in modport, do not create port
delete wire;
}
}
else { // If no modport, set inout
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);

// Now that the interfaces have been exploded, we can delete the dummy port related to every interface.
for(auto &intf : interfaces) {
if(mod->wires_.count(intf.first)) {
mod->wires_.erase(intf.first);
mod->fixup_ports();
// We copy the cell of the interface to the sub-module such that it can further be found if it is propagated
// down to sub-sub-modules etc.
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.c_str());
}
}

// If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module':
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();

Expand Down Expand Up @@ -1156,15 +1331,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;
}

Expand Down
8 changes: 8 additions & 0 deletions frontends/ast/ast.h
Expand Up @@ -142,6 +142,11 @@ namespace AST
AST_NEGEDGE,
AST_EDGE,

AST_INTERFACE,
AST_INTERFACEPORT,
AST_INTERFACEPORTTYPE,
AST_MODPORT,
AST_MODPORTMEMBER,
AST_PACKAGE
};

Expand Down Expand Up @@ -284,6 +289,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, dict<RTLIL::IdString, RTLIL::IdString> modports, 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;
};

Expand Down
79 changes: 77 additions & 2 deletions frontends/ast/genrtlil.cc
Expand Up @@ -853,6 +853,52 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_GENIF:
case AST_GENCASE:
case AST_PACKAGE:
case AST_MODPORT:
case AST_MODPORTMEMBER:
break;
case AST_INTERFACEPORT: {
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
// 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");
if (children.size() > 0) {
for(size_t i=0; i<children.size();i++) {
if(children[i]->type == AST_INTERFACEPORTTYPE) {
std::string name_type = children[i]->str;
size_t ndots = std::count(name_type.begin(), name_type.end(), '.');
// Separate the interface instance name from any modports:
if (ndots == 0) { // Does not have modport
wire->attributes["\\interface_type"] = name_type;
}
else {
std::stringstream name_type_stream(name_type);
std::string segment;
std::vector<std::string> seglist;
while(std::getline(name_type_stream, segment, '.')) {
seglist.push_back(segment);
}
if (ndots == 1) { // Has modport
wire->attributes["\\interface_type"] = seglist[0];
wire->attributes["\\interface_modport"] = seglist[1];
}
else { // Erroneous port type
log_error("More than two '.' in signal port type (%s)\n", name_type.c_str());
}
}
break;
}
}
}
wire->upto = 0;
}
break;
case AST_INTERFACEPORTTYPE:
break;

// remember the parameter, needed for example in techmap
Expand Down Expand Up @@ -949,6 +995,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;
Expand All @@ -969,15 +1016,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 with a dummy wire with a attribute called 'is_interface'
// 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;
Expand Down Expand Up @@ -1423,6 +1496,8 @@ 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);
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
cell->set_bool_attribute("\\module_not_derived");

for (auto it = children.begin(); it != children.end(); it++) {
AstNode *child = *it;
Expand Down
2 changes: 1 addition & 1 deletion frontends/ast/simplify.cc
Expand Up @@ -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;
Expand Down

0 comments on commit f24bc1e

Please sign in to comment.