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:
parent
bd16d01c0e
commit
e833c6a418
15 changed files with 397 additions and 42 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue