diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/ast/genrtlil.cc | 10 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 133 | ||||
-rw-r--r-- | frontends/liberty/liberty.cc | 78 |
3 files changed, 218 insertions, 3 deletions
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d00738ecd..3c57162aa 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -765,6 +765,16 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun } break; } + if (str == "\\$past") { + if (GetSize(children) > 0) { + sub_width_hint = 0; + sub_sign_hint = true; + children.at(0)->detectSignWidthWorker(sub_width_hint, sub_sign_hint); + width_hint = max(width_hint, sub_width_hint); + sign_hint = false; + } + break; + } /* fall through */ // everything should have been handled above -> print error if not. diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 8387c11c6..57aa648ce 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -63,7 +63,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, #if 0 log("-------------\n"); - log("AST simplify[%d] depth %d at %s:%d,\n", stage, recursion_counter, filename.c_str(), linenum); + log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), linenum, type2str(type).c_str(), this); log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); // dumpAst(NULL, "> "); @@ -522,6 +522,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children_are_self_determined = true; break; + case AST_FCALL: + case AST_TCALL: + children_are_self_determined = true; + break; + default: width_hint = -1; sign_hint = false; @@ -537,6 +542,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, detectSignWidth(width_hint, sign_hint); } + if (type == AST_FCALL && str == "\\$past") + detectSignWidth(width_hint, sign_hint); + if (type == AST_TERNARY) { int width_hint_left, width_hint_right; bool sign_hint_left, sign_hint_right; @@ -1682,9 +1690,128 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (str == "\\$past") + { + if (width_hint <= 0) + goto replace_fcall_later; + + int num_steps = 1; + + if (GetSize(children) != 1 && GetSize(children) != 2) + log_error("System function %s got %d arguments, expected 1 or 2 at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); + + if (!current_always_clocked) + log_error("System function %s is only allowed in clocked blocks at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); + + if (GetSize(children) == 2) + { + AstNode *buf = children[1]->clone(); + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (buf->type != AST_CONSTANT) + log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + num_steps = buf->asInt(true); + delete buf; + } + + AstNode *block = nullptr; + + for (auto child : current_always->children) + if (child->type == AST_BLOCK) + block = child; + + log_assert(block != nullptr); + + int myidx = autoidx++; + AstNode *outreg = nullptr; + + for (int i = 0; i < num_steps; i++) + { + AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, + mkconst_int(width_hint-1, true), mkconst_int(0, true))); + + reg->str = stringf("$past$%s:%d$%d$%d", filename.c_str(), linenum, myidx, i); + reg->is_reg = true; + + current_ast_mod->children.push_back(reg); + + while (reg->simplify(true, false, false, 1, -1, false, false)) { } + + AstNode *regid = new AstNode(AST_IDENTIFIER); + regid->str = reg->str; + regid->id2ast = reg; + + AstNode *rhs = nullptr; + + if (outreg == nullptr) { + rhs = children.at(0)->clone(); + } else { + rhs = new AstNode(AST_IDENTIFIER); + rhs->str = outreg->str; + rhs->id2ast = outreg; + } + + block->children.push_back(new AstNode(AST_ASSIGN_LE, regid, rhs)); + outreg = reg; + } + + newNode = new AstNode(AST_IDENTIFIER); + newNode->str = outreg->str; + newNode->id2ast = outreg; + goto apply_newNode; + } + + if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell") + { + if (GetSize(children) != 1) + log_error("System function %s got %d arguments, expected 1 at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); + + if (!current_always_clocked) + log_error("System function %s is only allowed in clocked blocks at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); + + AstNode *present = children.at(0)->clone(); + AstNode *past = clone(); + past->str = "\\$past"; + + if (str == "\\$stable") + newNode = new AstNode(AST_EQ, past, present); + + else if (str == "\\$rose") + newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, past), present); + + else if (str == "\\$fell") + newNode = new AstNode(AST_LOGIC_AND, past, new AstNode(AST_LOGIC_NOT, present)); + + else + log_abort(); + + goto apply_newNode; + } + + if (str == "\\$rose" || str == "\\$fell") + { + if (GetSize(children) != 1) + log_error("System function %s got %d arguments, expected 1 at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); + + if (!current_always_clocked) + log_error("System function %s is only allowed in clocked blocks at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); + + newNode = new AstNode(AST_EQ, children.at(0)->clone(), clone()); + newNode->children.at(1)->str = "\\$past"; + goto apply_newNode; + } + // $anyconst is mapped in AstNode::genRTLIL() - if (str == "\\$anyconst") + if (str == "\\$anyconst") { + recursion_counter--; return false; + } if (str == "\\$clog2") { @@ -2092,6 +2219,8 @@ skip_dynamic_range_lvalue_expansion:; did_something = true; } +replace_fcall_later:; + // perform const folding when activated if (const_fold) { diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 0be58b6da..73d927fab 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -469,6 +469,46 @@ struct LibertyFrontend : public Frontend { LibertyParser parser(*f); int cell_count = 0; + std::map<std::string, std::tuple<int, int, bool>> type_map; + + for (auto type_node : parser.ast->children) + { + if (type_node->id != "type" || type_node->args.size() != 1) + continue; + + std::string type_name = type_node->args.at(0); + int bit_width = -1, bit_from = -1, bit_to = -1; + bool upto = false; + + for (auto child : type_node->children) + { + if (child->id == "base_type" && child->value != "array") + goto next_type; + + if (child->id == "data_type" && child->value != "bit") + goto next_type; + + if (child->id == "bit_width") + bit_width = atoi(child->value.c_str()); + + if (child->id == "bit_from") + bit_from = atoi(child->value.c_str()); + + if (child->id == "bit_to") + bit_to = atoi(child->value.c_str()); + + if (child->id == "downto" && (child->value == "0" || child->value == "false" || child->value == "FALSE")) + upto = true; + } + + if (bit_width != (std::max(bit_from, bit_to) - std::min(bit_from, bit_to) + 1)) + log_error("Incompatible array type '%s': bit_width=%d, bit_from=%d, bit_to=%d.\n", + type_name.c_str(), bit_width, bit_from, bit_to); + + type_map[type_name] = std::tuple<int, int, bool>(bit_width, std::min(bit_from, bit_to), upto); + next_type:; + } + for (auto cell : parser.ast->children) { if (cell->id != "cell" || cell->args.size() != 1) @@ -494,13 +534,14 @@ struct LibertyFrontend : public Frontend { module->attributes[attr] = 1; for (auto node : cell->children) + { if (node->id == "pin" && node->args.size() == 1) { LibertyAst *dir = node->find("direction"); if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) { if (!flag_ignore_miss_dir) { - log_error("Missing or invalid direction for pin %s of cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); + log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); } else { log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str()); delete module; @@ -511,6 +552,41 @@ struct LibertyFrontend : public Frontend { module->addWire(RTLIL::escape_id(node->args.at(0))); } + if (node->id == "bus" && node->args.size() == 1) + { + if (!flag_lib) + log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name)); + + LibertyAst *dir = node->find("direction"); + + if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) + log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); + + if (dir->value == "internal") + continue; + + LibertyAst *bus_type_node = node->find("bus_type"); + + if (!bus_type_node || !type_map.count(bus_type_node->value)) + log_error("Unkown or unsupported type for bus interface %s on cell %s.\n", + node->args.at(0).c_str(), log_id(cell_name)); + + int bus_type_width = std::get<0>(type_map.at(bus_type_node->value)); + int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value)); + bool bus_type_upto = std::get<2>(type_map.at(bus_type_node->value)); + + Wire *wire = module->addWire(RTLIL::escape_id(node->args.at(0)), bus_type_width); + wire->start_offset = bus_type_offset; + wire->upto = bus_type_upto; + + if (dir->value == "input" || dir->value == "inout") + wire->port_input = true; + + if (dir->value == "output" || dir->value == "inout") + wire->port_output = true; + } + } + for (auto node : cell->children) { if (!flag_lib) { |