3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-21 21:33:40 +00:00

verilog: use derived module info to elaborate cell connections

- Attempt to lookup a derived module if it potentially contains a port
  connection with elaboration ambiguities
- Mark the cell if module has not yet been derived
- This can be extended to implement automatic hierarchical port
  connections in a future change
This commit is contained in:
Zachary Snow 2021-10-19 18:46:26 -06:00 committed by Zachary Snow
parent bd16d01c0e
commit e833c6a418
15 changed files with 397 additions and 42 deletions

View file

@ -564,6 +564,115 @@ static std::string prefix_id(const std::string &prefix, const std::string &str)
return prefix + str;
}
// direct access to this global should be limited to the following two functions
static const RTLIL::Design *simplify_design_context = nullptr;
void AST::set_simplify_design_context(const RTLIL::Design *design)
{
log_assert(!simplify_design_context || !design);
simplify_design_context = design;
}
// lookup the module with the given name in the current design context
static const RTLIL::Module* lookup_module(const std::string &name)
{
return simplify_design_context->module(name);
}
const RTLIL::Module* AstNode::lookup_cell_module()
{
log_assert(type == AST_CELL);
auto reprocess_after = [this] (const std::string &modname) {
if (!attributes.count(ID::reprocess_after))
attributes[ID::reprocess_after] = AstNode::mkconst_str(modname);
};
const AstNode *celltype = nullptr;
for (const AstNode *child : children)
if (child->type == AST_CELLTYPE) {
celltype = child;
break;
}
log_assert(celltype != nullptr);
const RTLIL::Module *module = lookup_module(celltype->str);
if (!module)
module = lookup_module("$abstract" + celltype->str);
if (!module) {
if (celltype->str.at(0) != '$')
reprocess_after(celltype->str);
return nullptr;
}
// build a mapping from true param name to param value
size_t para_counter = 0;
dict<RTLIL::IdString, RTLIL::Const> cell_params_map;
for (AstNode *child : children) {
if (child->type != AST_PARASET)
continue;
if (child->str.empty() && para_counter >= module->avail_parameters.size())
return nullptr; // let hierarchy handle this error
IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str;
const AstNode *value = child->children[0];
if (value->type != AST_REALVALUE && value->type != AST_CONSTANT)
return nullptr; // let genrtlil handle this error
cell_params_map[paraname] = value->asParaConst();
}
// put the parameters in order and generate the derived module name
std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
for (RTLIL::IdString param : module->avail_parameters) {
auto it = cell_params_map.find(param);
if (it != cell_params_map.end())
named_parameters.emplace_back(it->first, it->second);
}
std::string modname = celltype->str;
if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams
modname = derived_module_name(celltype->str, named_parameters);
// try to find the resolved module
module = lookup_module(modname);
if (!module) {
reprocess_after(modname);
return nullptr;
}
return module;
}
// returns whether an expression contains an unbased unsized literal; does not
// check the literal exists in a self-determined context
static bool contains_unbased_unsized(const AstNode *node)
{
if (node->type == AST_CONSTANT)
return node->is_unsized;
for (const AstNode *child : node->children)
if (contains_unbased_unsized(child))
return true;
return false;
}
// adds a wire to the current module with the given name that matches the
// dimensions of the given wire reference
void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str)
{
AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true);
AstNode *right = AstNode::mkconst_int(ref->start_offset, true);
if (ref->upto)
std::swap(left, right);
AstNode *range = new AstNode(AST_RANGE, left, right);
AstNode *wire = new AstNode(AST_WIRE, range);
wire->is_signed = ref->is_signed;
wire->is_logic = true;
wire->str = str;
current_ast_mod->children.push_back(wire);
current_scope[str] = wire;
}
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@ -920,19 +1029,110 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
if (type == AST_ARGUMENT)
{
if (children.size() == 1 && children[0]->type == AST_CONSTANT)
{
// HACK: For port bindings using unbased unsized literals, mark them
// signed so they sign-extend. The hierarchy will still incorrectly
// generate a warning complaining about resizing the expression.
// This also doesn't handle the complex of something like a ternary
// expression bound to a port, where the actual size of the port is
// needed to resolve the expression correctly.
AstNode *arg = children[0];
if (arg->is_unsized)
arg->is_signed = true;
if (type == AST_CELL) {
bool lookup_suggested = false;
for (AstNode *child : children) {
// simplify any parameters to constants
if (child->type == AST_PARASET)
while (child->simplify(true, false, false, 1, -1, false, true)) { }
// look for patterns which _may_ indicate ambiguity requiring
// resolution of the underlying module
if (child->type == AST_ARGUMENT) {
if (child->children.size() != 1)
continue;
const AstNode *value = child->children[0];
if (value->type == AST_IDENTIFIER) {
const AstNode *elem = value->id2ast;
if (elem == nullptr) {
if (current_scope.count(value->str))
elem = current_scope.at(value->str);
else
continue;
}
if (elem->type == AST_MEMORY)
// need to determine is the is a read or wire
lookup_suggested = true;
else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty())
// this may be a fully sliced signed wire which needs
// to be indirected to produce an unsigned connection
lookup_suggested = true;
}
else if (contains_unbased_unsized(value))
// unbased unsized literals extend to width of the context
lookup_suggested = true;
}
}
const RTLIL::Module *module = nullptr;
if (lookup_suggested)
module = lookup_cell_module();
if (module) {
size_t port_counter = 0;
for (AstNode *child : children) {
if (child->type != AST_ARGUMENT)
continue;
// determine the full name of port this argument is connected to
RTLIL::IdString port_name;
if (child->str.size())
port_name = child->str;
else {
if (port_counter >= module->ports.size())
log_file_error(filename, location.first_line,
"Cell instance has more ports than the module!\n");
port_name = module->ports[port_counter++];
}
// find the port's wire in the underlying module
const RTLIL::Wire *ref = module->wire(port_name);
if (ref == nullptr)
log_file_error(filename, location.first_line,
"Cell instance refers to port %s which does not exist in module %s!.\n",
log_id(port_name), log_id(module->name));
// select the argument, if present
log_assert(child->children.size() <= 1);
if (child->children.empty())
continue;
AstNode *arg = child->children[0];
// plain identifiers never need indirection; this also prevents
// adding infinite levels of indirection
if (arg->type == AST_IDENTIFIER && arg->children.empty())
continue;
// only add indirection for standard inputs or outputs
if (ref->port_input == ref->port_output)
continue;
did_something = true;
// create the indirection wire
std::stringstream sstr;
sstr << "$indirect$" << ref->name.c_str() << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
std::string tmp_str = sstr.str();
add_wire_for_ref(ref, tmp_str);
AstNode *asgn = new AstNode(AST_ASSIGN);
current_ast_mod->children.push_back(asgn);
AstNode *ident = new AstNode(AST_IDENTIFIER);
ident->str = tmp_str;
child->children[0] = ident->clone();
if (ref->port_input && !ref->port_output) {
asgn->children.push_back(ident);
asgn->children.push_back(arg);
} else {
log_assert(!ref->port_input && ref->port_output);
asgn->children.push_back(arg);
asgn->children.push_back(ident);
}
}
}
}