diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 55e05709d..e357579ad 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -278,6 +278,8 @@ namespace AST bool replace_variables(std::map &variables, AstNode *fcall, bool must_succeed); AstNode *eval_const_function(AstNode *fcall, bool must_succeed); bool is_simple_const_expr(); + + // helper for parsing format strings Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0); bool is_recursive_function() const; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 3aa19b706..40e71e8bd 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -693,8 +693,80 @@ struct AST_INTERNAL::ProcessGenerator ast->input_error("Found parameter declaration in block without label!\n"); break; - case AST_NONE: case AST_TCALL: + if (ast->str == "$display" || ast->str == "$displayb" || ast->str == "$displayh" || ast->str == "$displayo" || + ast->str == "$write" || ast->str == "$writeb" || ast->str == "$writeh" || ast->str == "$writeo") { + std::stringstream sstr; + sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++); + + RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print)); + cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, ast->location.first_column, ast->location.last_line, ast->location.last_column); + + RTLIL::SigSpec triggers; + RTLIL::Const polarity; + for (auto sync : proc->syncs) { + if (sync->type == RTLIL::STp) { + triggers.append(sync->signal); + polarity.bits.push_back(RTLIL::S1); + } else if (sync->type == RTLIL::STn) { + triggers.append(sync->signal); + polarity.bits.push_back(RTLIL::S0); + } + } + cell->parameters[ID::TRG_WIDTH] = triggers.size(); + cell->parameters[ID::TRG_ENABLE] = !triggers.empty(); + cell->parameters[ID::TRG_POLARITY] = polarity; + cell->setPort(ID::TRG, triggers); + + Wire *wire = current_module->addWire(sstr.str() + "_EN", 1); + wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, ast->location.first_column, ast->location.last_line, ast->location.last_column); + cell->setPort(ID::EN, wire); + + proc->root_case.actions.push_back(SigSig(wire, false)); + current_case->actions.push_back(SigSig(wire, true)); + + int default_base = 10; + if (ast->str.back() == 'b') + default_base = 2; + else if (ast->str.back() == 'o') + default_base = 8; + else if (ast->str.back() == 'h') + default_base = 16; + + std::vector args; + for (auto node : ast->children) { + int width; + bool is_signed; + node->detectSignWidth(width, is_signed, nullptr); + + VerilogFmtArg arg = {}; + arg.filename = node->filename; + arg.first_line = node->location.first_line; + if (node->type == AST_CONSTANT && node->is_string) { + arg.type = VerilogFmtArg::STRING; + arg.str = node->bitsAsConst().decode_string(); + // and in case this will be used as an argument... + arg.sig = node->bitsAsConst(); + arg.signed_ = false; + } else { + arg.type = VerilogFmtArg::INTEGER; + arg.sig = node->genRTLIL(); + arg.signed_ = is_signed; + } + args.push_back(arg); + } + + Fmt fmt = {}; + fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); + if (ast->str.substr(0, 8) == "$display") + fmt.append_string("\n"); + fmt.emit_rtlil(cell); + } else if (!ast->str.empty()) { + log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); + } + break; + + case AST_NONE: case AST_FOR: break; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 489ce255a..19e62668b 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -951,29 +951,33 @@ bool AstNode::simplify(bool const_fold, bool in_lvalue, int stage, int width_hin str = std::string(); } - if ((type == AST_TCALL) && (str.substr(0, 8) == "$display" || str.substr(0, 6) == "$write") && (!current_always || current_always->type != AST_INITIAL)) { - log_file_warning(filename, location.first_line, "System task `%s' outside initial block is unsupported.\n", str.c_str()); - delete_children(); - str = std::string(); - } - - // print messages if this a call to $display() or $write() family of functions if ((type == AST_TCALL) && (str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" || str == "$write" || str == "$writeb" || str == "$writeh" || str == "$writeo")) { - int default_base = 10; - if (str.back() == 'b') - default_base = 2; - else if (str.back() == 'o') - default_base = 8; - else if (str.back() == 'h') - default_base = 16; + if (!current_always) { + log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str()); + } else if (current_always->type == AST_INITIAL) { + int default_base = 10; + if (str.back() == 'b') + default_base = 2; + else if (str.back() == 'o') + default_base = 8; + else if (str.back() == 'h') + default_base = 16; - Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base); - if (str.substr(0, 8) == "$display") - fmt.append_string("\n"); - log("%s", fmt.render().c_str()); + // when $display()/$write() functions are used in an initial block, print them during synthesis + Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base); + if (str.substr(0, 8) == "$display") + fmt.append_string("\n"); + log("%s", fmt.render().c_str()); + } else { + // when $display()/$write() functions are used in an always block, simplify the expressions and + // convert them to a special cell later in genrtlil + for (auto node : children) + while (node->simplify(true, false, stage, -1, false, false)) {} + return false; + } delete_children(); str = std::string(); diff --git a/kernel/constids.inc b/kernel/constids.inc index 39211d0c7..08b0ecdc2 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -22,6 +22,8 @@ X(always_ff) X(always_latch) X(anyconst) X(anyseq) +X(ARGS) +X(ARGS_WIDTH) X(ARST) X(ARST_POLARITY) X(ARST_VALUE) @@ -86,6 +88,7 @@ X(equiv_merged) X(equiv_region) X(extract_order) X(F) +X(FORMAT) X(force_downto) X(force_upto) X(fsm_encoding) @@ -233,6 +236,10 @@ X(TRANS_NUM) X(TRANSPARENCY_MASK) X(TRANSPARENT) X(TRANS_TABLE) +X(TRG) +X(TRG_ENABLE) +X(TRG_POLARITY) +X(TRG_WIDTH) X(T_RISE_MAX) X(T_RISE_MIN) X(T_RISE_TYP) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 7011429ff..09fe08000 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1720,6 +1720,17 @@ namespace { return; } + if (cell->type == ID($print)) { + param(ID(FORMAT)); + param_bool(ID::TRG_ENABLE); + param(ID::TRG_POLARITY); + port(ID::EN, 1); + port(ID::TRG, param(ID::TRG_WIDTH)); + port(ID::ARGS, param(ID::ARGS_WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; }