yosys/frontends/ast/genrtlil.cc

2248 lines
79 KiB
C++
Raw Normal View History

2013-01-05 04:13:26 -06:00
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
2015-07-02 04:14:30 -05:00
*
2013-01-05 04:13:26 -06:00
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
2015-07-02 04:14:30 -05:00
*
2013-01-05 04:13:26 -06:00
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ---
*
* This is the AST frontend library.
*
* The AST frontend library is not a frontend on it's own but provides a
* generic abstract syntax tree (AST) abstraction for HDL code and can be
* used by HDL frontends. See "ast.h" for an overview of the API and the
* Verilog frontend for an usage example.
*
*/
#include "kernel/log.h"
2014-08-16 17:57:24 -05:00
#include "kernel/utils.h"
#include "kernel/binding.h"
#include "libs/sha1/sha1.h"
2013-01-05 04:13:26 -06:00
#include "ast.h"
#include "ast_binding.h"
2013-01-05 04:13:26 -06:00
#include <sstream>
#include <stdarg.h>
#include <algorithm>
2013-01-05 04:13:26 -06:00
YOSYS_NAMESPACE_BEGIN
2013-01-05 04:13:26 -06:00
using namespace AST;
using namespace AST_INTERNAL;
// helper function for creating RTLIL code for unary operations
static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true)
2013-01-05 04:13:26 -06:00
{
2022-08-08 09:13:33 -05:00
IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++);
RTLIL::Cell *cell = current_module->addCell(name, type);
set_src_attr(cell, that);
2013-01-05 04:13:26 -06:00
RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);
set_src_attr(wire, that);
wire->is_signed = that->is_signed;
2013-01-05 04:13:26 -06:00
if (gen_attributes)
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
cell->parameters[ID::A_SIGNED] = RTLIL::Const(that->children[0]->is_signed);
cell->parameters[ID::A_WIDTH] = RTLIL::Const(arg.size());
2020-03-12 14:57:01 -05:00
cell->setPort(ID::A, arg);
2013-01-05 04:13:26 -06:00
cell->parameters[ID::Y_WIDTH] = result_width;
2020-03-12 14:57:01 -05:00
cell->setPort(ID::Y, wire);
return wire;
2013-01-05 04:13:26 -06:00
}
// helper function for extending bit width (preferred over SigSpec::extend() because of correct undef propagation in ConstEval)
2014-09-03 19:07:52 -05:00
static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed)
{
if (width <= sig.size()) {
sig.extend_u0(width, is_signed);
return;
}
2022-08-08 09:13:33 -05:00
IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++);
RTLIL::Cell *cell = current_module->addCell(name, ID($pos));
set_src_attr(cell, that);
RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width);
set_src_attr(wire, that);
wire->is_signed = that->is_signed;
if (that != NULL)
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
cell->parameters[ID::A_SIGNED] = RTLIL::Const(is_signed);
cell->parameters[ID::A_WIDTH] = RTLIL::Const(sig.size());
2020-03-12 14:57:01 -05:00
cell->setPort(ID::A, sig);
cell->parameters[ID::Y_WIDTH] = width;
2020-03-12 14:57:01 -05:00
cell->setPort(ID::Y, wire);
sig = wire;
}
2013-01-05 04:13:26 -06:00
// helper function for creating RTLIL code for binary operations
static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
2013-01-05 04:13:26 -06:00
{
2022-08-08 09:13:33 -05:00
IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++);
RTLIL::Cell *cell = current_module->addCell(name, type);
set_src_attr(cell, that);
2013-01-05 04:13:26 -06:00
RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width);
set_src_attr(wire, that);
wire->is_signed = that->is_signed;
2013-01-05 04:13:26 -06:00
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
cell->parameters[ID::A_SIGNED] = RTLIL::Const(that->children[0]->is_signed);
cell->parameters[ID::B_SIGNED] = RTLIL::Const(that->children[1]->is_signed);
2013-01-05 04:13:26 -06:00
cell->parameters[ID::A_WIDTH] = RTLIL::Const(left.size());
cell->parameters[ID::B_WIDTH] = RTLIL::Const(right.size());
2013-01-05 04:13:26 -06:00
2020-03-12 14:57:01 -05:00
cell->setPort(ID::A, left);
cell->setPort(ID::B, right);
2013-01-05 04:13:26 -06:00
cell->parameters[ID::Y_WIDTH] = result_width;
2020-03-12 14:57:01 -05:00
cell->setPort(ID::Y, wire);
return wire;
2013-01-05 04:13:26 -06:00
}
// helper function for creating RTLIL code for multiplexers
static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
{
2014-07-28 04:08:55 -05:00
log_assert(cond.size() == 1);
2013-01-05 04:13:26 -06:00
std::stringstream sstr;
2022-08-08 09:13:33 -05:00
sstr << "$ternary$" << RTLIL::encode_filename(that->filename) << ":" << that->location.first_line << "$" << (autoidx++);
2013-01-05 04:13:26 -06:00
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux));
set_src_attr(cell, that);
2013-01-05 04:13:26 -06:00
RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size());
set_src_attr(wire, that);
wire->is_signed = that->is_signed;
2013-01-05 04:13:26 -06:00
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
cell->parameters[ID::WIDTH] = RTLIL::Const(left.size());
2013-01-05 04:13:26 -06:00
2020-03-12 14:57:01 -05:00
cell->setPort(ID::A, right);
cell->setPort(ID::B, left);
cell->setPort(ID::S, cond);
cell->setPort(ID::Y, wire);
2013-01-05 04:13:26 -06:00
return wire;
2013-01-05 04:13:26 -06:00
}
// helper class for rewriting simple lookahead references in AST always blocks
struct AST_INTERNAL::LookaheadRewriter
{
dict<IdString, pair<AstNode*, AstNode*>> lookaheadids;
void collect_lookaheadids(AstNode *node)
{
if (node->lookahead) {
log_assert(node->type == AST_IDENTIFIER);
if (!lookaheadids.count(node->str)) {
AstNode *wire = new AstNode(AST_WIRE);
for (auto c : node->id2ast->children)
wire->children.push_back(c->clone());
wire->fixup_hierarchy_flags();
wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++);
wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire->is_logic = true;
while (wire->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire);
lookaheadids[node->str] = make_pair(node->id2ast, wire);
wire->genRTLIL();
}
}
for (auto child : node->children)
collect_lookaheadids(child);
}
bool has_lookaheadids(AstNode *node)
{
if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) != 0)
return true;
for (auto child : node->children)
if (has_lookaheadids(child))
return true;
return false;
}
bool has_nonlookaheadids(AstNode *node)
{
if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) == 0)
return true;
for (auto child : node->children)
if (has_nonlookaheadids(child))
return true;
return false;
}
void rewrite_lookaheadids(AstNode *node, bool lhs = false)
{
if (node->type == AST_ASSIGN_LE)
{
if (has_lookaheadids(node->children[0]))
{
if (has_nonlookaheadids(node->children[0]))
log_error("incompatible mix of lookahead and non-lookahead IDs in LHS expression.\n");
rewrite_lookaheadids(node->children[0], true);
node->type = AST_ASSIGN_EQ;
}
rewrite_lookaheadids(node->children[1], lhs);
return;
}
if (node->type == AST_IDENTIFIER && (node->lookahead || lhs)) {
AstNode *newwire = lookaheadids.at(node->str).second;
node->str = newwire->str;
node->id2ast = newwire;
lhs = false;
}
for (auto child : node->children)
rewrite_lookaheadids(child, lhs);
}
LookaheadRewriter(AstNode *top)
{
// top->dumpAst(NULL, "REWRITE-BEFORE> ");
// top->dumpVlog(NULL, "REWRITE-BEFORE> ");
AstNode *block = nullptr;
for (auto c : top->children)
if (c->type == AST_BLOCK) {
log_assert(block == nullptr);
block = c;
}
log_assert(block != nullptr);
collect_lookaheadids(block);
rewrite_lookaheadids(block);
for (auto it : lookaheadids)
{
AstNode *ref_orig = new AstNode(AST_IDENTIFIER);
ref_orig->str = it.second.first->str;
ref_orig->id2ast = it.second.first;
ref_orig->was_checked = true;
AstNode *ref_temp = new AstNode(AST_IDENTIFIER);
ref_temp->str = it.second.second->str;
ref_temp->id2ast = it.second.second;
ref_temp->was_checked = true;
AstNode *init_assign = new AstNode(AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone());
AstNode *final_assign = new AstNode(AST_ASSIGN_LE, ref_orig, ref_temp);
block->children.insert(block->children.begin(), init_assign);
block->children.push_back(final_assign);
}
// top->dumpAst(NULL, "REWRITE-AFTER> ");
// top->dumpVlog(NULL, "REWRITE-AFTER> ");
}
};
2013-01-05 04:13:26 -06:00
// helper class for converting AST always nodes to RTLIL processes
struct AST_INTERNAL::ProcessGenerator
{
// input and output structures
AstNode *always;
RTLIL::SigSpec initSyncSignals;
2013-01-05 04:13:26 -06:00
RTLIL::Process *proc;
RTLIL::SigSpec outputSignals;
2013-01-05 04:13:26 -06:00
2015-08-14 03:56:05 -05:00
// This always points to the RTLIL::CaseRule being filled at the moment
2013-01-05 04:13:26 -06:00
RTLIL::CaseRule *current_case;
// This map contains the replacement pattern to be used in the right hand side
2013-01-05 04:13:26 -06:00
// of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right
// hand side of the 2nd assignment needs to be replace with the temporary signal holding
2015-08-14 03:56:05 -05:00
// the value assigned in the first assignment. So when the first assignment is processed
2013-01-05 04:13:26 -06:00
// the according information is appended to subst_rvalue_from and subst_rvalue_to.
2014-08-16 17:57:24 -05:00
stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_rvalue_map;
2013-01-05 04:13:26 -06:00
// This map contains the replacement pattern to be used in the left hand side
2013-01-05 04:13:26 -06:00
// of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar
// should not be connected to the signal foo. Instead it must be connected to the temporary
// signal that is used as input for the register that drives the signal foo.
2014-08-16 17:57:24 -05:00
stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_lvalue_map;
2013-01-05 04:13:26 -06:00
2015-08-14 03:56:05 -05:00
// The code here generates a number of temporary signal for each output register. This
2013-01-05 04:13:26 -06:00
// map helps generating nice numbered names for all this temporary signals.
std::map<RTLIL::Wire*, int> new_temp_count;
// Buffer for generating the init action
RTLIL::SigSpec init_lvalue, init_rvalue;
2023-06-27 20:51:31 -05:00
// The most recently assigned $print cell \PRIORITY.
int last_print_priority;
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_print_priority(0)
2013-01-05 04:13:26 -06:00
{
// rewrite lookahead references
LookaheadRewriter la_rewriter(always);
2013-01-05 04:13:26 -06:00
// generate process and simple root case
2022-08-08 09:13:33 -05:00
proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(always->filename).c_str(), always->location.first_line, autoidx++));
set_src_attr(proc, always);
2013-01-05 04:13:26 -06:00
for (auto &attr : always->attributes) {
if (attr.second->type != AST_CONSTANT)
always->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
proc->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
current_case = &proc->root_case;
// create initial temporary signal for all output registers
RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to;
2013-01-05 04:13:26 -06:00
collect_lvalues(subst_lvalue_from, always, true, true);
subst_lvalue_to = new_temp_signal(subst_lvalue_from);
subst_lvalue_map = subst_lvalue_from.to_sigbit_map(subst_lvalue_to);
2013-01-05 04:13:26 -06:00
bool found_global_syncs = false;
2013-01-05 04:13:26 -06:00
bool found_anyedge_syncs = false;
for (auto child : always->children)
{
if ((child->type == AST_POSEDGE || child->type == AST_NEGEDGE) && GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER &&
2020-03-12 14:57:01 -05:00
child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute(ID::gclk)) {
found_global_syncs = true;
}
if (child->type == AST_EDGE) {
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock")
found_global_syncs = true;
else
found_anyedge_syncs = true;
}
}
2013-01-05 04:13:26 -06:00
if (found_anyedge_syncs) {
if (found_global_syncs)
always->input_error("Found non-synthesizable event list!\n");
log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string().c_str());
2013-01-05 04:13:26 -06:00
log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");
log("use of @* instead of @(...) for better match of synthesis and simulation.\n");
}
// create syncs for the process
bool found_clocked_sync = false;
for (auto child : always->children)
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) {
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast &&
2020-03-12 14:57:01 -05:00
child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute(ID::gclk))
continue;
2013-01-05 04:13:26 -06:00
found_clocked_sync = true;
if (found_global_syncs || found_anyedge_syncs)
always->input_error("Found non-synthesizable event list!\n");
2013-01-05 04:13:26 -06:00
RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
syncrule->signal = child->children[0]->genRTLIL();
if (GetSize(syncrule->signal) != 1)
always->input_error("Found posedge/negedge event on a signal that is not 1 bit wide!\n");
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
2013-01-05 04:13:26 -06:00
proc->syncs.push_back(syncrule);
}
if (proc->syncs.empty()) {
RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
syncrule->type = found_global_syncs ? RTLIL::STg : RTLIL::STa;
2013-01-05 04:13:26 -06:00
syncrule->signal = RTLIL::SigSpec();
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
2013-01-05 04:13:26 -06:00
proc->syncs.push_back(syncrule);
}
// create initial assignments for the temporary signals
if ((flag_nolatches || always->get_bool_attribute(ID::nolatches) || current_module->get_bool_attribute(ID::nolatches)) && !found_clocked_sync) {
2014-12-28 12:24:24 -06:00
subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from)));
2013-01-05 04:13:26 -06:00
} else {
addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
}
// process the AST
for (auto child : always->children)
if (child->type == AST_BLOCK)
processAst(child);
for (auto sync: proc->syncs)
processMemWrites(sync);
if (initSyncSignals.size() > 0)
{
RTLIL::SyncRule *sync = new RTLIL::SyncRule;
sync->type = RTLIL::SyncType::STi;
proc->syncs.push_back(sync);
2014-07-28 04:08:55 -05:00
log_assert(init_lvalue.size() == init_rvalue.size());
int offset = 0;
for (auto &init_lvalue_c : init_lvalue.chunks()) {
RTLIL::SigSpec lhs = init_lvalue_c;
RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width);
remove_unwanted_lvalue_bits(lhs, rhs);
sync->actions.push_back(RTLIL::SigSig(lhs, rhs));
offset += lhs.size();
}
}
outputSignals = RTLIL::SigSpec(subst_lvalue_from);
2013-01-05 04:13:26 -06:00
}
void remove_unwanted_lvalue_bits(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs)
{
RTLIL::SigSpec new_lhs, new_rhs;
log_assert(GetSize(lhs) == GetSize(rhs));
for (int i = 0; i < GetSize(lhs); i++) {
if (lhs[i].wire == nullptr)
continue;
new_lhs.append(lhs[i]);
new_rhs.append(rhs[i]);
}
lhs = new_lhs;
rhs = new_rhs;
}
2013-01-05 04:13:26 -06:00
// create new temporary signals
RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig)
{
std::vector<RTLIL::SigChunk> chunks = sig.chunks();
for (int i = 0; i < GetSize(chunks); i++)
2013-01-05 04:13:26 -06:00
{
RTLIL::SigChunk &chunk = chunks[i];
2013-01-05 04:13:26 -06:00
if (chunk.wire == NULL)
continue;
std::string wire_name;
2013-01-05 04:13:26 -06:00
do {
wire_name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++,
2013-01-05 04:13:26 -06:00
chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);;
if (chunk.wire->name.str().find('$') != std::string::npos)
wire_name += stringf("$%d", autoidx++);
} while (current_module->wires_.count(wire_name) > 0);
RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width);
set_src_attr(wire, always);
2013-01-05 04:13:26 -06:00
chunk.wire = wire;
chunk.offset = 0;
}
return chunks;
2013-01-05 04:13:26 -06:00
}
// recursively traverse the AST and collect all assigned signals
2013-01-05 04:13:26 -06:00
void collect_lvalues(RTLIL::SigSpec &reg, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true)
{
switch (ast->type)
{
case AST_CASE:
for (auto child : ast->children)
if (child != ast->children[0]) {
log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
2013-01-05 04:13:26 -06:00
collect_lvalues(reg, child, type_eq, type_le, false);
}
break;
case AST_COND:
case AST_CONDX:
case AST_CONDZ:
2013-01-05 04:13:26 -06:00
case AST_ALWAYS:
case AST_INITIAL:
2013-01-05 04:13:26 -06:00
for (auto child : ast->children)
if (child->type == AST_BLOCK)
collect_lvalues(reg, child, type_eq, type_le, false);
break;
case AST_BLOCK:
for (auto child : ast->children) {
if (child->type == AST_ASSIGN_EQ && type_eq)
reg.append(child->children[0]->genRTLIL());
if (child->type == AST_ASSIGN_LE && type_le)
reg.append(child->children[0]->genRTLIL());
if (child->type == AST_CASE || child->type == AST_BLOCK)
collect_lvalues(reg, child, type_eq, type_le, false);
}
break;
default:
2014-07-28 04:08:55 -05:00
log_abort();
2013-01-05 04:13:26 -06:00
}
if (run_sort_and_unify) {
std::set<RTLIL::SigBit> sorted_reg;
for (auto bit : reg)
if (bit.wire)
sorted_reg.insert(bit);
reg = RTLIL::SigSpec(sorted_reg);
}
2013-01-05 04:13:26 -06:00
}
// remove all assignments to the given signal pattern in a case and all its children.
// e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this
// function is called to clean up the first two assignments as they are overwritten by
2013-01-05 04:13:26 -06:00
// the third assignment.
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
2013-01-05 04:13:26 -06:00
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
it->first.remove2(pattern, &it->second);
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
removeSignalFromCaseTree(pattern, *it2);
}
// add an assignment (aka "action") but split it up in chunks. this way huge assignments
// are avoided and the generated $mux cells have a more "natural" size.
void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false)
2013-01-05 04:13:26 -06:00
{
if (inSyncRule && initSyncSignals.size() > 0) {
init_lvalue.append(lvalue.extract(initSyncSignals));
init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue));
lvalue.remove2(initSyncSignals, &rvalue);
}
2014-07-28 04:08:55 -05:00
log_assert(lvalue.size() == rvalue.size());
2013-01-05 04:13:26 -06:00
int offset = 0;
for (auto &lvalue_c : lvalue.chunks()) {
RTLIL::SigSpec lhs = lvalue_c;
RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue_c.width);
if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute(ID::nosync))
rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size());
remove_unwanted_lvalue_bits(lhs, rhs);
2013-01-05 04:13:26 -06:00
actions.push_back(RTLIL::SigSig(lhs, rhs));
offset += lhs.size();
2013-01-05 04:13:26 -06:00
}
}
// recursively process the AST and fill the RTLIL::Process
void processAst(AstNode *ast)
{
switch (ast->type)
{
case AST_BLOCK:
for (auto child : ast->children)
processAst(child);
break;
case AST_ASSIGN_EQ:
case AST_ASSIGN_LE:
{
RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue;
RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), true, &subst_rvalue_map.stdmap());
pool<SigBit> lvalue_sigbits;
for (int i = 0; i < GetSize(lvalue); i++) {
if (lvalue_sigbits.count(lvalue[i]) > 0) {
unmapped_lvalue.remove(i);
lvalue.remove(i);
rvalue.remove(i--);
} else
lvalue_sigbits.insert(lvalue[i]);
}
2014-08-16 17:57:24 -05:00
lvalue.replace(subst_lvalue_map.stdmap());
2013-01-05 04:13:26 -06:00
if (ast->type == AST_ASSIGN_EQ) {
for (int i = 0; i < GetSize(unmapped_lvalue); i++)
2014-08-16 17:57:24 -05:00
subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]);
2013-01-05 04:13:26 -06:00
}
removeSignalFromCaseTree(lvalue, current_case);
remove_unwanted_lvalue_bits(lvalue, rvalue);
2013-01-05 04:13:26 -06:00
current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
}
break;
case AST_CASE:
{
int width_hint;
bool sign_hint;
ast->detectSignWidth(width_hint, sign_hint);
2013-01-05 04:13:26 -06:00
RTLIL::SwitchRule *sw = new RTLIL::SwitchRule;
set_src_attr(sw, ast);
sw->signal = ast->children[0]->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap());
2013-01-05 04:13:26 -06:00
current_case->switches.push_back(sw);
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
ast->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
sw->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
RTLIL::SigSpec this_case_eq_lvalue;
collect_lvalues(this_case_eq_lvalue, ast, true, false);
RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue);
RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue;
2014-08-16 17:57:24 -05:00
this_case_eq_rvalue.replace(subst_rvalue_map.stdmap());
2013-01-05 04:13:26 -06:00
RTLIL::CaseRule *default_case = NULL;
2013-01-05 04:13:26 -06:00
RTLIL::CaseRule *last_generated_case = NULL;
for (auto child : ast->children)
{
if (child == ast->children[0])
2013-01-05 04:13:26 -06:00
continue;
log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
2013-01-05 04:13:26 -06:00
2014-08-16 17:57:24 -05:00
subst_lvalue_map.save();
subst_rvalue_map.save();
2013-01-05 04:13:26 -06:00
for (int i = 0; i < GetSize(this_case_eq_lvalue); i++)
2014-08-16 17:57:24 -05:00
subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
2013-01-05 04:13:26 -06:00
RTLIL::CaseRule *backup_case = current_case;
current_case = new RTLIL::CaseRule;
set_src_attr(current_case, child);
2013-01-05 04:13:26 -06:00
last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
for (auto node : child->children) {
if (node->type == AST_DEFAULT)
default_case = current_case;
else if (node->type == AST_BLOCK)
2013-01-05 04:13:26 -06:00
processAst(node);
else
current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap()));
2013-01-05 04:13:26 -06:00
}
if (default_case != current_case)
sw->cases.push_back(current_case);
else
log_assert(current_case->compare.size() == 0);
2013-01-05 04:13:26 -06:00
current_case = backup_case;
2014-08-16 17:57:24 -05:00
subst_lvalue_map.restore();
subst_rvalue_map.restore();
2013-01-05 04:13:26 -06:00
}
2020-03-12 14:57:01 -05:00
if (last_generated_case != NULL && ast->get_bool_attribute(ID::full_case) && default_case == NULL) {
#if 0
// this is a valid transformation, but as optimization it is premature.
// better: add a default case that assigns 'x' to everything, and let later
// optimizations take care of the rest
2013-01-05 04:13:26 -06:00
last_generated_case->compare.clear();
#else
default_case = new RTLIL::CaseRule;
addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)));
sw->cases.push_back(default_case);
#endif
} else {
if (default_case == NULL) {
default_case = new RTLIL::CaseRule;
addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
}
2013-01-05 04:13:26 -06:00
sw->cases.push_back(default_case);
}
for (int i = 0; i < GetSize(this_case_eq_lvalue); i++)
2014-08-16 17:57:24 -05:00
subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
2013-01-05 04:13:26 -06:00
2014-08-16 17:57:24 -05:00
this_case_eq_lvalue.replace(subst_lvalue_map.stdmap());
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
2013-01-05 04:13:26 -06:00
addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
}
break;
case AST_WIRE:
ast->input_error("Found reg declaration in block without label!\n");
break;
case AST_ASSIGN:
ast->input_error("Found continous assignment in always/initial block!\n");
break;
case AST_PARAMETER:
case AST_LOCALPARAM:
ast->input_error("Found parameter declaration in block without label!\n");
break;
2013-01-05 04:13:26 -06:00
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));
set_src_attr(cell, ast);
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;
2023-06-27 20:51:31 -05:00
cell->parameters[ID::PRIORITY] = --last_print_priority;
cell->setPort(ID::TRG, triggers);
Wire *wire = current_module->addWire(sstr.str() + "_EN", 1);
set_src_attr(wire, ast);
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<VerilogFmtArg> 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;
2023-06-27 20:51:20 -05:00
} else if (node->type == AST_IDENTIFIER && node->str == "$time") {
arg.type = VerilogFmtArg::TIME;
} else if (node->type == AST_IDENTIFIER && node->str == "$realtime") {
arg.type = VerilogFmtArg::TIME;
arg.realtime = true;
} else {
arg.type = VerilogFmtArg::INTEGER;
arg.sig = node->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap());
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:
2013-01-05 04:13:26 -06:00
case AST_FOR:
break;
default:
// ast->dumpAst(NULL, "ast> ");
// current_ast_mod->dumpAst(NULL, "mod> ");
2014-06-06 15:55:02 -05:00
log_abort();
2013-01-05 04:13:26 -06:00
}
}
void processMemWrites(RTLIL::SyncRule *sync)
{
// Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array.
dict<std::pair<std::string, int>, int> port_map;
for (auto child : always->children)
if (child->type == AST_MEMWR)
{
std::string memid = child->str;
int portid = child->children[3]->asInt(false);
int cur_idx = GetSize(sync->mem_write_actions);
RTLIL::MemWriteAction action;
set_src_attr(&action, child);
action.memid = memid;
action.address = child->children[0]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap());
action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, true, &subst_rvalue_map.stdmap());
action.enable = child->children[2]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap());
RTLIL::Const orig_priority_mask = child->children[4]->bitsAsConst();
RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx);
for (int i = 0; i < portid; i++) {
int new_bit = port_map[std::make_pair(memid, i)];
priority_mask.bits[new_bit] = orig_priority_mask.bits[i];
}
action.priority_mask = priority_mask;
sync->mem_write_actions.push_back(action);
port_map[std::make_pair(memid, portid)] = cur_idx;
}
}
2013-01-05 04:13:26 -06:00
};
// Generate RTLIL for a bind construct
//
// The AST node will have one or more AST_IDENTIFIER children, which were added
// by bind_target_instance in the parser. After these, it will have one or more
// cells, as parsed by single_cell. These have type AST_CELL.
//
// If there is more than one AST_IDENTIFIER, the first one should be considered
// a module identifier. If there is only one AST_IDENTIFIER, we can't tell at
// this point whether it's a module/interface name or the name of an instance
// because the correct interpretation depends on what's visible at elaboration
// time. For now, we just treat it as a target instance with unknown type, and
// we'll deal with the corner case in the hierarchy pass.
//
// To simplify downstream code, RTLIL::Binding only has a single target and
// single bound instance. If we see the syntax that allows more than one of
// either, we split it into multiple Binding objects.
std::vector<RTLIL::Binding *> AstNode::genBindings() const
{
// Partition children into identifiers and cells
int num_ids = 0;
for (int i = 0; i < GetSize(children); ++i) {
if (children[i]->type != AST_IDENTIFIER) {
log_assert(i > 0);
num_ids = i;
break;
}
}
// We should have found at least one child that's not an identifier
log_assert(num_ids > 0);
// Make sense of the identifiers, extracting a possible type name and a
// list of hierarchical IDs. We represent an unknown type with an empty
// string.
RTLIL::IdString tgt_type;
int first_tgt_inst = 0;
if (num_ids > 1) {
tgt_type = children[0]->str;
first_tgt_inst = 1;
}
std::vector<RTLIL::Binding *> ret;
// At this point, we know that children with index >= first_tgt_inst and
// index < num_ids are (hierarchical?) names of target instances. Make a
// binding object for each of them, and fill in the generated instance
// cells each time.
for (int i = first_tgt_inst; i < num_ids; ++i) {
const AstNode &tgt_child = *children[i];
for (int j = num_ids; j < GetSize(children); ++j) {
const AstNode &cell_child = *children[j];
log_assert(cell_child.type == AST_CELL);
ret.push_back(new AST::Binding(tgt_type, tgt_child.str,
cell_child));
}
}
return ret;
}
// detect sign and width of an expression
void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real)
{
std::string type_name;
bool sub_sign_hint = true;
int sub_width_hint = -1;
int this_width = 0;
AstNode *range = NULL;
AstNode *id_ast = NULL;
bool local_found_real = false;
if (found_real == NULL)
found_real = &local_found_real;
switch (type)
{
case AST_NONE:
// unallocated enum, ignore
break;
case AST_CONSTANT:
width_hint = max(width_hint, int(bits.size()));
if (!is_signed)
sign_hint = false;
break;
case AST_REALVALUE:
*found_real = true;
width_hint = max(width_hint, 32);
break;
case AST_IDENTIFIER:
id_ast = id2ast;
if (!id_ast) {
if (current_scope.count(str))
id_ast = current_scope[str];
else {
std::string alt = try_pop_module_prefix();
if (current_scope.count(alt))
id_ast = current_scope[alt];
}
}
if (!id_ast)
input_error("Failed to resolve identifier %s for width detection!\n", str.c_str());
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) {
if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) {
this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
} else {
if (id_ast->children[0]->type != AST_CONSTANT)
while (id_ast->simplify(true, 1, -1, false)) { }
if (id_ast->children[0]->type == AST_CONSTANT)
this_width = id_ast->children[0]->bits.size();
else
input_error("Failed to detect width for parameter %s!\n", str.c_str());
}
if (children.size() != 0)
range = children[0];
} else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) {
if (!id_ast->range_valid) {
if (id_ast->type == AST_AUTOWIRE)
this_width = 1;
else {
// current_ast_mod->dumpAst(NULL, "mod> ");
// log("---\n");
// id_ast->dumpAst(NULL, "decl> ");
// dumpAst(NULL, "ref> ");
input_error("Failed to detect width of signal access `%s'!\n", str.c_str());
}
} else {
this_width = id_ast->range_left - id_ast->range_right + 1;
if (children.size() != 0)
range = children[0];
}
} else if (id_ast->type == AST_GENVAR) {
this_width = 32;
} else if (id_ast->type == AST_MEMORY) {
if (!id_ast->children[0]->range_valid)
input_error("Failed to detect width of memory access `%s'!\n", str.c_str());
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
if (children.size() > 1)
range = children[1];
} else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) {
AstNode *tmp_range = make_struct_member_range(this, id_ast);
this_width = tmp_range->range_left - tmp_range->range_right + 1;
delete tmp_range;
} else
input_error("Failed to detect width for identifier %s!\n", str.c_str());
if (range) {
if (range->children.size() == 1)
this_width = 1;
else if (!range->range_valid) {
AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, 1, -1, false)) { }
while (right_at_zero_ast->simplify(true, 1, -1, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
delete left_at_zero_ast;
delete right_at_zero_ast;
} else
this_width = range->range_left - range->range_right + 1;
sign_hint = false;
2014-07-28 08:19:34 -05:00
}
width_hint = max(width_hint, this_width);
if (!id_ast->is_signed)
sign_hint = false;
break;
case AST_TO_BITS:
while (children[0]->simplify(true, 1, -1, false) == true) { }
if (children[0]->type != AST_CONSTANT)
input_error("Left operand of tobits expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
width_hint = max(width_hint, children[0]->bitsAsConst().as_int());
break;
case AST_TO_SIGNED:
children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint);
break;
case AST_TO_UNSIGNED:
children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint);
sign_hint = false;
break;
case AST_SELFSZ:
sub_width_hint = 0;
children.at(0)->detectSignWidthWorker(sub_width_hint, sign_hint);
break;
case AST_CAST_SIZE:
while (children.at(0)->simplify(true, 1, -1, false)) { }
if (children.at(0)->type != AST_CONSTANT)
input_error("Static cast with non constant expression!\n");
children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
this_width = children.at(0)->bitsAsConst().as_int();
width_hint = max(width_hint, this_width);
if (width_hint <= 0)
input_error("Static cast with zero or negative size!\n");
break;
case AST_CONCAT:
for (auto child : children) {
sub_width_hint = 0;
sub_sign_hint = true;
child->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
this_width += sub_width_hint;
}
width_hint = max(width_hint, this_width);
sign_hint = false;
break;
case AST_REPLICATE:
while (children[0]->simplify(true, 1, -1, false) == true) { }
if (children[0]->type != AST_CONSTANT)
input_error("Left operand of replicate expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint);
sign_hint = false;
break;
case AST_NEG:
case AST_BIT_NOT:
case AST_POS:
children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
case AST_BIT_AND:
case AST_BIT_OR:
case AST_BIT_XOR:
case AST_BIT_XNOR:
for (auto child : children)
child->detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
case AST_REDUCE_AND:
case AST_REDUCE_OR:
case AST_REDUCE_XOR:
case AST_REDUCE_XNOR:
case AST_REDUCE_BOOL:
width_hint = max(width_hint, 1);
sign_hint = false;
break;
case AST_SHIFT_LEFT:
case AST_SHIFT_RIGHT:
case AST_SHIFT_SLEFT:
case AST_SHIFT_SRIGHT:
case AST_SHIFTX:
case AST_SHIFT:
case AST_POW:
children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
case AST_LT:
case AST_LE:
case AST_EQ:
case AST_NE:
case AST_EQX:
case AST_NEX:
case AST_GE:
case AST_GT:
width_hint = max(width_hint, 1);
sign_hint = false;
break;
case AST_ADD:
case AST_SUB:
case AST_MUL:
case AST_DIV:
case AST_MOD:
for (auto child : children)
child->detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
case AST_LOGIC_AND:
case AST_LOGIC_OR:
case AST_LOGIC_NOT:
width_hint = max(width_hint, 1);
sign_hint = false;
break;
case AST_TERNARY:
children.at(1)->detectSignWidthWorker(width_hint, sign_hint, found_real);
children.at(2)->detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
case AST_MEMRD:
if (!id2ast->is_signed)
sign_hint = false;
if (!id2ast->children[0]->range_valid)
input_error("Failed to detect width of memory access `%s'!\n", str.c_str());
this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1;
width_hint = max(width_hint, this_width);
break;
case AST_CASE:
{
// This detects the _overall_ sign and width to be used for comparing
// the case expression with the case item expressions. The case
// expression and case item expressions are extended to the maximum
// width among them, and are only interpreted as signed if all of them
// are signed.
width_hint = -1;
sign_hint = true;
auto visit_case_expr = [&width_hint, &sign_hint] (AstNode *node) {
int sub_width_hint = -1;
bool sub_sign_hint = true;
node->detectSignWidth(sub_width_hint, sub_sign_hint);
width_hint = max(width_hint, sub_width_hint);
sign_hint &= sub_sign_hint;
};
visit_case_expr(children[0]);
for (size_t i = 1; i < children.size(); i++) {
AstNode *child = children[i];
for (AstNode *v : child->children)
if (v->type != AST_DEFAULT && v->type != AST_BLOCK)
visit_case_expr(v);
}
break;
}
case AST_PREFIX:
// Prefix nodes always resolve to identifiers in generate loops, so we
// can simply perform the resolution to determine the sign and width.
simplify(true, 1, -1, false);
log_assert(type == AST_IDENTIFIER);
detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
2016-07-27 08:41:22 -05:00
case AST_FCALL:
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
2016-07-27 08:41:22 -05:00
if (GetSize(children) == 1) {
while (children[0]->simplify(true, 1, -1, false) == true) { }
2016-07-27 08:41:22 -05:00
if (children[0]->type != AST_CONSTANT)
input_error("System function %s called with non-const argument!\n",
RTLIL::unescape_id(str).c_str());
2016-07-27 08:41:22 -05:00
width_hint = max(width_hint, int(children[0]->asInt(true)));
}
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);
2022-05-24 10:18:53 -05:00
sign_hint &= sub_sign_hint;
}
break;
}
if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") {
width_hint = max(width_hint, 32);
break;
}
if (current_scope.count(str))
{
// This width detection is needed for function calls which are
// unelaborated, which currently applies to calls to functions
// reached via unevaluated ternary branches or used in case or case
// item expressions.
const AstNode *func = current_scope.at(str);
if (func->type != AST_FUNCTION)
input_error("Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str());
const AstNode *wire = nullptr;
for (const AstNode *child : func->children)
if (child->str == func->str) {
wire = child;
break;
}
log_assert(wire && wire->type == AST_WIRE);
sign_hint &= wire->is_signed;
int result_width = 1;
if (!wire->children.empty())
{
log_assert(wire->children.size() == 1);
const AstNode *range = wire->children.at(0);
log_assert(range->type == AST_RANGE && range->children.size() == 2);
AstNode *left = range->children.at(0)->clone();
AstNode *right = range->children.at(1)->clone();
left->set_in_param_flag(true);
right->set_in_param_flag(true);
while (left->simplify(true, 1, -1, false)) { }
while (right->simplify(true, 1, -1, false)) { }
if (left->type != AST_CONSTANT || right->type != AST_CONSTANT)
input_error("Function %s has non-constant width!",
RTLIL::unescape_id(str).c_str());
result_width = abs(int(left->asInt(true) - right->asInt(true)));
delete left;
delete right;
}
width_hint = max(width_hint, result_width);
break;
}
YS_FALLTHROUGH
2016-07-27 08:41:22 -05:00
// everything should have been handled above -> print error if not.
default:
AstNode *current_scope_ast = current_ast_mod == nullptr ? current_ast : current_ast_mod;
for (auto f : log_files)
current_scope_ast->dumpAst(f, "verilog-ast> ");
input_error("Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
}
if (*found_real)
sign_hint = true;
}
// detect sign and width of an expression
void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real)
{
width_hint = -1;
sign_hint = true;
if (found_real)
*found_real = false;
detectSignWidthWorker(width_hint, sign_hint, found_real);
constexpr int kWidthLimit = 1 << 24;
if (width_hint >= kWidthLimit)
input_error("Expression width %d exceeds implementation limit of %d!\n",
width_hint, kWidthLimit);
}
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
const AstNode *node, const char *to_add_kind)
{
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
std::string src = existing->get_string_attribute(ID::src);
std::string location_str = "earlier";
if (!src.empty())
location_str = "at " + src;
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
};
if (const RTLIL::Wire *wire = module->wire(id))
already_exists(wire, "signal");
if (const RTLIL::Cell *cell = module->cell(id))
already_exists(cell, "cell");
if (module->processes.count(id))
already_exists(module->processes.at(id), "process");
if (module->memories.count(id))
already_exists(module->memories.at(id), "memory");
}
2013-01-05 04:13:26 -06:00
// create RTLIL from an AST node
// all generated cells, wires and processes are added to the module pointed to by 'current_module'
// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
//
// note that this function is influenced by a number of global variables that might be set when
// called from genWidthRTLIL(). also note that this function recursively calls itself to transform
// larger expressions into a netlist of cells.
RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
2013-01-05 04:13:26 -06:00
{
// in the following big switch() statement there are some uses of
// Clifford's Device (http://www.clifford.at/cfun/cliffdev/). In this
// cases this variable is used to hold the type of the cell that should
2015-08-14 03:56:05 -05:00
// be instantiated for this type of AST node.
IdString type_name;
2013-01-05 04:13:26 -06:00
current_filename = filename;
switch (type)
{
// simply ignore this nodes.
2015-08-14 03:56:05 -05:00
// they are either leftovers from simplify() or are referenced by other nodes
2013-01-05 04:13:26 -06:00
// and are only accessed here thru this references
2016-08-21 06:23:58 -05:00
case AST_NONE:
2013-01-05 04:13:26 -06:00
case AST_TASK:
case AST_FUNCTION:
case AST_DPI_FUNCTION:
2013-01-05 04:13:26 -06:00
case AST_AUTOWIRE:
case AST_DEFPARAM:
2013-01-05 04:13:26 -06:00
case AST_GENVAR:
case AST_GENFOR:
case AST_GENBLOCK:
2013-01-05 04:13:26 -06:00
case AST_GENIF:
case AST_GENCASE:
case AST_PACKAGE:
case AST_ENUM:
case AST_MODPORT:
case AST_MODPORTMEMBER:
case AST_TYPEDEF:
2020-05-08 08:40:49 -05:00
case AST_STRUCT:
case AST_UNION:
2013-01-05 04:13:26 -06:00
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::IdString id = str;
check_unique_id(current_module, id, this, "interface port");
RTLIL::Wire *wire = current_module->addWire(id, 1);
set_src_attr(wire, this);
wire->start_offset = 0;
wire->port_id = port_id;
wire->port_input = true;
wire->port_output = true;
2020-03-12 14:57:01 -05:00
wire->set_bool_attribute(ID::is_interface);
if (children.size() > 0) {
for(size_t i=0; i<children.size();i++) {
if(children[i]->type == AST_INTERFACEPORTTYPE) {
std::pair<std::string,std::string> res = AST::split_modport_from_type(children[i]->str);
wire->attributes[ID::interface_type] = res.first;
if (res.second != "")
wire->attributes[ID::interface_modport] = res.second;
break;
}
}
}
wire->upto = 0;
}
break;
case AST_INTERFACEPORTTYPE:
break;
2013-01-05 04:13:26 -06:00
// remember the parameter, needed for example in techmap
case AST_PARAMETER:
current_module->avail_parameters(str);
if (GetSize(children) >= 1 && children[0]->type == AST_CONSTANT) {
current_module->parameter_default_values[str] = children[0]->asParaConst();
}
YS_FALLTHROUGH
case AST_LOCALPARAM:
if (flag_pwires)
{
if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT)
input_error("Parameter `%s' with non-constant value!\n", str.c_str());
RTLIL::Const val = children[0]->bitsAsConst();
RTLIL::IdString id = str;
check_unique_id(current_module, id, this, "pwire");
RTLIL::Wire *wire = current_module->addWire(id, GetSize(val));
current_module->connect(wire, val);
wire->is_signed = children[0]->is_signed;
set_src_attr(wire, this);
wire->attributes[type == AST_PARAMETER ? ID::parameter : ID::localparam] = 1;
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
wire->attributes[attr.first] = attr.second->asAttrConst();
}
}
break;
2013-01-05 04:13:26 -06:00
// create an RTLIL::Wire for an AST_WIRE node
case AST_WIRE: {
if (!range_valid)
input_error("Signal `%s' with non-constant width!\n", str.c_str());
2013-01-05 04:13:26 -06:00
if (!(range_left + 1 >= range_right))
input_error("Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1);
2013-01-05 04:13:26 -06:00
RTLIL::IdString id = str;
check_unique_id(current_module, id, this, "signal");
RTLIL::Wire *wire = current_module->addWire(id, range_left - range_right + 1);
set_src_attr(wire, this);
2013-01-05 04:13:26 -06:00
wire->start_offset = range_right;
wire->port_id = port_id;
wire->port_input = is_input;
wire->port_output = is_output;
wire->upto = range_swapped;
wire->is_signed = is_signed;
2013-01-05 04:13:26 -06:00
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
wire->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
2019-05-23 03:16:41 -05:00
2020-03-12 14:57:01 -05:00
if (is_wand) wire->set_bool_attribute(ID::wand);
if (is_wor) wire->set_bool_attribute(ID::wor);
2013-01-05 04:13:26 -06:00
}
break;
// create an RTLIL::Memory for an AST_MEMORY node
case AST_MEMORY: {
2014-07-28 04:08:55 -05:00
log_assert(children.size() >= 2);
log_assert(children[0]->type == AST_RANGE);
log_assert(children[1]->type == AST_RANGE);
2013-01-05 04:13:26 -06:00
if (!children[0]->range_valid || !children[1]->range_valid)
input_error("Memory `%s' with non-constant width or size!\n", str.c_str());
2013-01-05 04:13:26 -06:00
RTLIL::Memory *memory = new RTLIL::Memory;
set_src_attr(memory, this);
2013-01-05 04:13:26 -06:00
memory->name = str;
memory->width = children[0]->range_left - children[0]->range_right + 1;
2015-01-01 05:56:01 -06:00
if (children[1]->range_right < children[1]->range_left) {
memory->start_offset = children[1]->range_right;
memory->size = children[1]->range_left - children[1]->range_right + 1;
} else {
memory->start_offset = children[1]->range_left;
memory->size = children[1]->range_right - children[1]->range_left + 1;
}
check_unique_id(current_module, memory->name, this, "memory");
2013-01-05 04:13:26 -06:00
current_module->memories[memory->name] = memory;
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
memory->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
}
break;
// simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node
case AST_CONSTANT:
case AST_REALVALUE:
2013-01-05 04:13:26 -06:00
{
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
is_signed = sign_hint;
2013-01-05 04:13:26 -06:00
if (type == AST_CONSTANT) {
if (is_unsized) {
return RTLIL::SigSpec(bitsAsUnsizedConst(width_hint));
} else {
return RTLIL::SigSpec(bitsAsConst());
}
}
RTLIL::SigSpec sig = realAsConst(width_hint);
log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));
return sig;
}
2013-01-05 04:13:26 -06:00
// simply return the corresponding RTLIL::SigSpec for an AST_IDENTIFIER node
// for identifiers with dynamic bit ranges (e.g. "foo[bar]" or "foo[bar+3:bar]") a
// shifter cell is created and the output signal of this cell is returned
case AST_IDENTIFIER:
{
RTLIL::Wire *wire = NULL;
RTLIL::SigChunk chunk;
bool is_interface = false;
AST::AstNode *member_node = NULL;
int add_undef_bits_msb = 0;
int add_undef_bits_lsb = 0;
log_assert(id2ast != nullptr);
if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {
RTLIL::Wire *wire = current_module->addWire(str);
set_src_attr(wire, this);
2013-01-05 04:13:26 -06:00
wire->name = str;
// If we are currently processing a bind directive which wires up
// signals or parameters explicitly, rather than with .*, then
// current_module will start out empty and we don't want to warn the
// user about it: we'll spot broken wiring later, when we run the
// hierarchy pass.
if (dynamic_cast<RTLIL::Binding*>(current_module)) {
/* nothing to do here */
} else if (flag_autowire)
log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str());
else
input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
2013-01-05 04:13:26 -06:00
}
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM || id2ast->type == AST_ENUM_ITEM) {
if (id2ast->children[0]->type != AST_CONSTANT)
input_error("Parameter %s does not evaluate to constant value!\n", str.c_str());
chunk = RTLIL::Const(id2ast->children[0]->bits);
goto use_const_chunk;
}
else if ((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);
2020-03-12 14:57:01 -05:00
if (current_wire->get_bool_attribute(ID::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 {
input_error("Identifier `%s' doesn't map to any signal!\n", str.c_str());
}
2013-01-05 04:13:26 -06:00
if (id2ast->type == AST_MEMORY)
input_error("Identifier `%s' does map to an unexpanded memory!\n", str.c_str());
2013-01-05 04:13:26 -06:00
// 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) {
IdString dummy_wire_name = stringf("$dummywireforinterface%s", str.c_str());
RTLIL::Wire *dummy_wire = current_module->wire(dummy_wire_name);
if (!dummy_wire) {
dummy_wire = current_module->addWire(dummy_wire_name);
2020-03-12 14:57:01 -05:00
dummy_wire->set_bool_attribute(ID::is_interface);
}
return dummy_wire;
}
wire = current_module->wires_[str];
2013-01-05 04:13:26 -06:00
chunk.wire = wire;
chunk.width = wire->width;
chunk.offset = 0;
if ((member_node = get_struct_member(this))) {
// Clamp wire chunk to range of member within struct/union.
chunk.width = member_node->range_left - member_node->range_right + 1;
chunk.offset = member_node->range_right;
}
use_const_chunk:
2013-01-05 04:13:26 -06:00
if (children.size() != 0) {
2018-10-02 02:44:23 -05:00
if (children[0]->type != AST_RANGE)
input_error("Single range expected.\n");
2014-07-28 09:45:26 -05:00
int source_width = id2ast->range_left - id2ast->range_right + 1;
int source_offset = id2ast->range_right;
int chunk_left = source_width - 1;
int chunk_right = 0;
if (member_node) {
// Clamp wire chunk to range of member within struct/union.
log_assert(!source_offset && !id2ast->range_swapped);
chunk_left = chunk.offset + chunk.width - 1;
chunk_right = chunk.offset;
}
2013-01-05 04:13:26 -06:00
if (!children[0]->range_valid) {
AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, 1, -1, false)) { }
while (right_at_zero_ast->simplify(true, 1, -1, false)) { }
2013-01-05 04:13:26 -06:00
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
2013-01-05 04:13:26 -06:00
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
children[0]->children[1]->clone() : children[0]->children[0]->clone());
fake_ast->children[0]->delete_children();
if (member_node)
fake_ast->children[0]->set_attribute(ID::wiretype, member_node->clone());
int fake_ast_width = 0;
bool fake_ast_sign = true;
fake_ast->children[1]->detectSignWidth(fake_ast_width, fake_ast_sign);
RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(fake_ast_width, fake_ast_sign);
if (source_offset != 0) {
shift_val = current_module->Sub(NEW_ID, shift_val, source_offset, fake_ast_sign);
fake_ast->children[1]->is_signed = true;
}
if (id2ast->range_swapped) {
shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast_sign);
fake_ast->children[1]->is_signed = true;
}
if (GetSize(shift_val) >= 32)
fake_ast->children[1]->is_signed = true;
RTLIL::SigSpec sig = binop2rtlil(fake_ast, ID($shiftx), width, fake_ast->children[0]->genRTLIL(), shift_val);
2013-01-05 04:13:26 -06:00
delete left_at_zero_ast;
delete right_at_zero_ast;
delete fake_ast;
return sig;
} else {
chunk.width = children[0]->range_left - children[0]->range_right + 1;
chunk.offset += children[0]->range_right - source_offset;
if (id2ast->range_swapped)
chunk.offset = source_width - (chunk.offset + chunk.width);
if (chunk.offset > chunk_left || chunk.offset + chunk.width < chunk_right) {
if (chunk.width == 1)
log_file_warning(filename, location.first_line, "Range select out of bounds on signal `%s': Setting result bit to undef.\n",
str.c_str());
else
log_file_warning(filename, location.first_line, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n",
children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width);
chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
} else {
if (chunk.offset + chunk.width - 1 > chunk_left) {
add_undef_bits_msb = (chunk.offset + chunk.width - 1) - chunk_left;
chunk.width -= add_undef_bits_msb;
}
if (chunk.offset < chunk_right) {
add_undef_bits_lsb = chunk_right - chunk.offset;
chunk.width -= add_undef_bits_lsb;
chunk.offset += add_undef_bits_lsb;
}
if (add_undef_bits_lsb)
log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n",
children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb);
if (add_undef_bits_msb)
log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n",
children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb);
}
2013-01-05 04:13:26 -06:00
}
}
RTLIL::SigSpec sig = { RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_msb), chunk, RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_lsb) };
2013-01-05 04:13:26 -06:00
if (genRTLIL_subst_ptr)
sig.replace(*genRTLIL_subst_ptr);
2013-01-05 04:13:26 -06:00
is_signed = children.size() > 0 ? false : id2ast->is_signed && sign_hint;
2013-01-05 04:13:26 -06:00
return sig;
}
2013-11-07 12:19:53 -06:00
// just pass thru the signal. the parent will evaluate the is_signed property and interpret the SigSpec accordingly
2013-01-05 04:13:26 -06:00
case AST_TO_SIGNED:
case AST_TO_UNSIGNED:
case AST_SELFSZ: {
RTLIL::SigSpec sig = children[0]->genRTLIL();
if (sig.size() < width_hint)
sig.extend_u0(width_hint, sign_hint);
2013-07-11 12:24:59 -05:00
is_signed = sign_hint;
2013-01-05 04:13:26 -06:00
return sig;
}
// changing the size of signal can be done directly using RTLIL::SigSpec
case AST_CAST_SIZE: {
RTLIL::SigSpec size = children[0]->genRTLIL();
if (!size.is_fully_const())
input_error("Static cast with non constant expression!\n");
int width = size.as_int();
if (width <= 0)
input_error("Static cast with zero or negative size!\n");
// determine the *signedness* of the expression
int sub_width_hint = -1;
bool sub_sign_hint = true;
children[1]->detectSignWidth(sub_width_hint, sub_sign_hint);
// generate the signal given the *cast's* size and the
// *expression's* signedness
RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint);
// context may effect this node's signedness, but not that of the
// casted expression
is_signed = sign_hint;
return sig;
}
2013-01-05 04:13:26 -06:00
// concatenation of signals can be done directly using RTLIL::SigSpec
case AST_CONCAT: {
RTLIL::SigSpec sig;
for (auto it = children.begin(); it != children.end(); it++)
sig.append((*it)->genRTLIL());
if (sig.size() < width_hint)
sig.extend_u0(width_hint, false);
2013-01-05 04:13:26 -06:00
return sig;
}
// replication of signals can be done directly using RTLIL::SigSpec
case AST_REPLICATE: {
RTLIL::SigSpec left = children[0]->genRTLIL();
RTLIL::SigSpec right = children[1]->genRTLIL();
if (!left.is_fully_const())
input_error("Left operand of replicate expression is not constant!\n");
2013-01-05 04:13:26 -06:00
int count = left.as_int();
RTLIL::SigSpec sig;
for (int i = 0; i < count; i++)
sig.append(right);
if (sig.size() < width_hint)
sig.extend_u0(width_hint, false);
2013-01-05 04:13:26 -06:00
is_signed = false;
return sig;
}
// generate cells for unary operations: $not, $pos, $neg
if (0) { case AST_BIT_NOT: type_name = ID($not); }
if (0) { case AST_POS: type_name = ID($pos); }
if (0) { case AST_NEG: type_name = ID($neg); }
2013-01-05 04:13:26 -06:00
{
RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint, sign_hint);
is_signed = children[0]->is_signed;
int width = arg.size();
if (width_hint > 0) {
2013-01-05 04:13:26 -06:00
width = width_hint;
2014-09-03 19:07:52 -05:00
widthExtend(this, arg, width, is_signed);
}
2013-01-05 04:13:26 -06:00
return uniop2rtlil(this, type_name, width, arg);
}
// generate cells for binary operations: $and, $or, $xor, $xnor
if (0) { case AST_BIT_AND: type_name = ID($and); }
if (0) { case AST_BIT_OR: type_name = ID($or); }
if (0) { case AST_BIT_XOR: type_name = ID($xor); }
if (0) { case AST_BIT_XNOR: type_name = ID($xnor); }
2013-01-05 04:13:26 -06:00
{
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
int width = max(left.size(), right.size());
if (width_hint > 0)
width = width_hint;
is_signed = children[0]->is_signed && children[1]->is_signed;
2013-01-05 04:13:26 -06:00
return binop2rtlil(this, type_name, width, left, right);
}
// generate cells for unary operations: $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor
if (0) { case AST_REDUCE_AND: type_name = ID($reduce_and); }
if (0) { case AST_REDUCE_OR: type_name = ID($reduce_or); }
if (0) { case AST_REDUCE_XOR: type_name = ID($reduce_xor); }
if (0) { case AST_REDUCE_XNOR: type_name = ID($reduce_xnor); }
2013-01-05 04:13:26 -06:00
{
RTLIL::SigSpec arg = children[0]->genRTLIL();
RTLIL::SigSpec sig = uniop2rtlil(this, type_name, max(width_hint, 1), arg);
2013-01-05 04:13:26 -06:00
return sig;
}
// generate cells for unary operations: $reduce_bool
2015-08-14 03:56:05 -05:00
// (this is actually just an $reduce_or, but for clarity a different cell type is used)
if (0) { case AST_REDUCE_BOOL: type_name = ID($reduce_bool); }
2013-01-05 04:13:26 -06:00
{
RTLIL::SigSpec arg = children[0]->genRTLIL();
RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, max(width_hint, 1), arg) : arg;
2013-01-05 04:13:26 -06:00
return sig;
}
// generate cells for binary operations: $shl, $shr, $sshl, $sshr
if (0) { case AST_SHIFT_LEFT: type_name = ID($shl); }
if (0) { case AST_SHIFT_RIGHT: type_name = ID($shr); }
if (0) { case AST_SHIFT_SLEFT: type_name = ID($sshl); }
if (0) { case AST_SHIFT_SRIGHT: type_name = ID($sshr); }
if (0) { case AST_SHIFTX: type_name = ID($shiftx); }
if (0) { case AST_SHIFT: type_name = ID($shift); }
2013-01-05 04:13:26 -06:00
{
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
// for $shift and $shiftx, the second operand can be negative
RTLIL::SigSpec right = children[1]->genRTLIL(-1, type == AST_SHIFT || type == AST_SHIFTX);
int width = width_hint > 0 ? width_hint : left.size();
is_signed = children[0]->is_signed;
2013-01-05 04:13:26 -06:00
return binop2rtlil(this, type_name, width, left, right);
}
2013-11-07 15:20:00 -06:00
// generate cells for binary operations: $pow
case AST_POW:
{
int right_width;
bool right_signed;
children[1]->detectSignWidth(right_width, right_signed);
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
RTLIL::SigSpec right = children[1]->genRTLIL(right_width, right_signed);
int width = width_hint > 0 ? width_hint : left.size();
2013-11-07 15:20:00 -06:00
is_signed = children[0]->is_signed;
if (!flag_noopt && left.is_fully_const() && left.as_int() == 2 && !right_signed)
return binop2rtlil(this, ID($shl), width, RTLIL::SigSpec(1, left.size()), right);
return binop2rtlil(this, ID($pow), width, left, right);
2013-11-07 15:20:00 -06:00
}
2013-01-05 04:13:26 -06:00
// generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt
if (0) { case AST_LT: type_name = ID($lt); }
if (0) { case AST_LE: type_name = ID($le); }
if (0) { case AST_EQ: type_name = ID($eq); }
if (0) { case AST_NE: type_name = ID($ne); }
if (0) { case AST_EQX: type_name = ID($eqx); }
if (0) { case AST_NEX: type_name = ID($nex); }
if (0) { case AST_GE: type_name = ID($ge); }
if (0) { case AST_GT: type_name = ID($gt); }
2013-01-05 04:13:26 -06:00
{
int width = max(width_hint, 1);
width_hint = -1, sign_hint = true;
children[0]->detectSignWidthWorker(width_hint, sign_hint);
children[1]->detectSignWidthWorker(width_hint, sign_hint);
RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
RTLIL::SigSpec sig = binop2rtlil(this, type_name, width, left, right);
2013-01-05 04:13:26 -06:00
return sig;
}
2013-11-07 15:20:00 -06:00
// generate cells for binary operations: $add, $sub, $mul, $div, $mod
if (0) { case AST_ADD: type_name = ID($add); }
if (0) { case AST_SUB: type_name = ID($sub); }
if (0) { case AST_MUL: type_name = ID($mul); }
if (0) { case AST_DIV: type_name = ID($div); }
if (0) { case AST_MOD: type_name = ID($mod); }
2013-01-05 04:13:26 -06:00
{
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
2013-11-07 15:20:00 -06:00
RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
2013-11-08 04:40:36 -06:00
#if 0
int width = max(left.size(), right.size());
2013-01-05 04:13:26 -06:00
if (width > width_hint && width_hint > 0)
width = width_hint;
if (width < width_hint) {
2013-08-09 10:09:24 -05:00
if (type == AST_ADD || type == AST_SUB || type == AST_DIV)
2013-01-05 04:13:26 -06:00
width++;
if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed))
2013-01-05 04:13:26 -06:00
width = width_hint;
if (type == AST_MUL)
width = min(left.size() + right.size(), width_hint);
2013-01-05 04:13:26 -06:00
}
2013-11-08 04:40:36 -06:00
#else
int width = max(max(left.size(), right.size()), width_hint);
2013-11-08 04:40:36 -06:00
#endif
is_signed = children[0]->is_signed && children[1]->is_signed;
2013-01-05 04:13:26 -06:00
return binop2rtlil(this, type_name, width, left, right);
}
// generate cells for binary operations: $logic_and, $logic_or
if (0) { case AST_LOGIC_AND: type_name = ID($logic_and); }
if (0) { case AST_LOGIC_OR: type_name = ID($logic_or); }
2013-01-05 04:13:26 -06:00
{
RTLIL::SigSpec left = children[0]->genRTLIL();
RTLIL::SigSpec right = children[1]->genRTLIL();
return binop2rtlil(this, type_name, max(width_hint, 1), left, right);
2013-01-05 04:13:26 -06:00
}
// generate cells for unary operations: $logic_not
case AST_LOGIC_NOT:
{
RTLIL::SigSpec arg = children[0]->genRTLIL();
return uniop2rtlil(this, ID($logic_not), max(width_hint, 1), arg);
2013-01-05 04:13:26 -06:00
}
// generate multiplexer for ternary operator (aka ?:-operator)
case AST_TERNARY:
{
2013-07-12 06:13:04 -05:00
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
is_signed = sign_hint;
2013-07-12 06:13:04 -05:00
2013-01-05 04:13:26 -06:00
RTLIL::SigSpec cond = children[0]->genRTLIL();
RTLIL::SigSpec sig;
if (cond.is_fully_def())
{
if (cond.as_bool()) {
sig = children[1]->genRTLIL(width_hint, sign_hint);
log_assert(is_signed == children[1]->is_signed);
} else {
sig = children[2]->genRTLIL(width_hint, sign_hint);
log_assert(is_signed == children[2]->is_signed);
}
widthExtend(this, sig, sig.size(), is_signed);
}
else
{
RTLIL::SigSpec val1 = children[1]->genRTLIL(width_hint, sign_hint);
RTLIL::SigSpec val2 = children[2]->genRTLIL(width_hint, sign_hint);
2013-01-05 04:13:26 -06:00
if (cond.size() > 1)
cond = uniop2rtlil(this, ID($reduce_bool), 1, cond, false);
2013-01-05 04:13:26 -06:00
int width = max(val1.size(), val2.size());
log_assert(is_signed == children[1]->is_signed);
log_assert(is_signed == children[2]->is_signed);
widthExtend(this, val1, width, is_signed);
widthExtend(this, val2, width, is_signed);
2013-01-05 04:13:26 -06:00
sig = mux2rtlil(this, cond, val1, val2);
}
if (sig.size() < width_hint)
sig.extend_u0(width_hint, sign_hint);
return sig;
2013-01-05 04:13:26 -06:00
}
// generate $memrd cells for memory read ports
case AST_MEMRD:
{
std::stringstream sstr;
2022-08-08 09:13:33 -05:00
sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
2013-01-05 04:13:26 -06:00
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd));
set_src_attr(cell, this);
2013-01-05 04:13:26 -06:00
RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width);
set_src_attr(wire, this);
2013-01-05 04:13:26 -06:00
int mem_width, mem_size, addr_bits;
is_signed = id2ast->is_signed;
wire->is_signed = is_signed;
id2ast->meminfo(mem_width, mem_size, addr_bits);
2013-01-05 04:13:26 -06:00
RTLIL::SigSpec addr_sig = children[0]->genRTLIL();
cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1));
cell->setPort(ID::EN, RTLIL::SigSpec(RTLIL::State::Sx, 1));
cell->setPort(ID::ADDR, addr_sig);
cell->setPort(ID::DATA, RTLIL::SigSpec(wire));
2013-01-05 04:13:26 -06:00
cell->parameters[ID::MEMID] = RTLIL::Const(str);
cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig));
cell->parameters[ID::WIDTH] = RTLIL::Const(wire->width);
2013-01-05 04:13:26 -06:00
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0);
cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0);
2013-01-05 04:13:26 -06:00
if (!sign_hint)
is_signed = false;
2013-01-05 04:13:26 -06:00
return RTLIL::SigSpec(wire);
}
// generate $meminit cells
case AST_MEMINIT:
2013-01-05 04:13:26 -06:00
{
std::stringstream sstr;
2022-08-08 09:13:33 -05:00
sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
2013-01-05 04:13:26 -06:00
SigSpec en_sig = children[2]->genRTLIL();
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit_v2));
set_src_attr(cell, this);
2013-01-05 04:13:26 -06:00
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
2013-01-05 04:13:26 -06:00
if (children[3]->type != AST_CONSTANT)
input_error("Memory init with non-constant word count!\n");
int num_words = int(children[3]->asInt(false));
cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
2015-07-31 03:40:09 -05:00
SigSpec addr_sig = children[0]->genRTLIL();
cell->setPort(ID::ADDR, addr_sig);
cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words, true));
cell->setPort(ID::EN, en_sig);
2013-01-05 04:13:26 -06:00
cell->parameters[ID::MEMID] = RTLIL::Const(str);
cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig));
cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width);
2013-01-05 04:13:26 -06:00
cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
2013-01-05 04:13:26 -06:00
}
break;
2014-01-19 07:03:40 -06:00
// generate $assert cells
case AST_ASSERT:
case AST_ASSUME:
case AST_LIVE:
case AST_FAIR:
case AST_COVER:
2014-01-19 07:03:40 -06:00
{
IdString celltype;
if (type == AST_ASSERT) celltype = ID($assert);
if (type == AST_ASSUME) celltype = ID($assume);
if (type == AST_LIVE) celltype = ID($live);
if (type == AST_FAIR) celltype = ID($fair);
if (type == AST_COVER) celltype = ID($cover);
2016-07-13 09:56:17 -05:00
2014-01-19 07:03:40 -06:00
log_assert(children.size() == 2);
RTLIL::SigSpec check = children[0]->genRTLIL();
if (GetSize(check) != 1)
check = current_module->ReduceBool(NEW_ID, check);
2014-01-19 07:03:40 -06:00
RTLIL::SigSpec en = children[1]->genRTLIL();
if (GetSize(en) != 1)
en = current_module->ReduceBool(NEW_ID, en);
2014-01-19 07:03:40 -06:00
IdString cellname;
if (str.empty())
2022-08-08 09:13:33 -05:00
cellname = stringf("%s$%s:%d$%d", celltype.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
else
cellname = str;
2014-01-19 07:03:40 -06:00
check_unique_id(current_module, cellname, this, "procedural assertion");
RTLIL::Cell *cell = current_module->addCell(cellname, celltype);
set_src_attr(cell, this);
2014-01-19 07:03:40 -06:00
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
2014-01-19 07:03:40 -06:00
cell->attributes[attr.first] = attr.second->asAttrConst();
}
2020-03-12 14:57:01 -05:00
cell->setPort(ID::A, check);
cell->setPort(ID::EN, en);
2014-01-19 07:03:40 -06:00
}
break;
2013-01-05 04:13:26 -06:00
// add entries to current_module->connections for assignments (outside of always blocks)
case AST_ASSIGN:
{
RTLIL::SigSpec left = children[0]->genRTLIL();
RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size(), true);
if (left.has_const()) {
RTLIL::SigSpec new_left, new_right;
for (int i = 0; i < GetSize(left); i++)
if (left[i].wire) {
2019-05-23 06:42:30 -05:00
new_left.append(left[i]);
new_right.append(right[i]);
2019-05-23 06:42:30 -05:00
}
log_file_warning(filename, location.first_line, "Ignoring assignment to constant bits:\n"
2019-05-23 06:42:30 -05:00
" old assignment: %s = %s\n new assignment: %s = %s.\n",
log_signal(left), log_signal(right),
log_signal(new_left), log_signal(new_right));
left = new_left;
right = new_right;
}
current_module->connect(RTLIL::SigSig(left, right));
2013-01-05 04:13:26 -06:00
}
break;
// create an RTLIL::Cell for an AST_CELL
case AST_CELL:
{
int port_counter = 0, para_counter = 0;
RTLIL::IdString id = str;
check_unique_id(current_module, id, this, "cell");
RTLIL::Cell *cell = current_module->addCell(id, "");
set_src_attr(cell, this);
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
cell->set_bool_attribute(ID::module_not_derived);
2013-01-05 04:13:26 -06:00
for (auto it = children.begin(); it != children.end(); it++) {
AstNode *child = *it;
if (child->type == AST_CELLTYPE) {
cell->type = child->str;
2019-08-07 14:20:08 -05:00
if (flag_icells && cell->type.begins_with("\\$"))
2014-01-28 17:59:28 -06:00
cell->type = cell->type.substr(1);
2013-01-05 04:13:26 -06:00
continue;
}
if (child->type == AST_PARASET) {
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
const AstNode *value = child->children[0];
if (value->type == AST_REALVALUE)
log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n",
log_id(cell), log_id(paraname), value->realvalue);
else if (value->type != AST_CONSTANT)
input_error("Parameter %s.%s with non-constant value!\n",
log_id(cell), log_id(paraname));
cell->parameters[paraname] = value->asParaConst();
2013-01-05 04:13:26 -06:00
continue;
}
if (child->type == AST_ARGUMENT) {
RTLIL::SigSpec sig;
if (child->children.size() > 0) {
AstNode *arg = child->children[0];
int local_width_hint = -1;
bool local_sign_hint = false;
// don't inadvertently attempt to detect the width of interfaces
if (arg->type != AST_IDENTIFIER || !arg->id2ast || arg->id2ast->type != AST_CELL)
arg->detectSignWidth(local_width_hint, local_sign_hint);
sig = arg->genRTLIL(local_width_hint, local_sign_hint);
log_assert(local_sign_hint == arg->is_signed);
if (sig.is_wire()) {
// if the resulting SigSpec is a wire, its
// signedness should match that of the AstNode
if (arg->type == AST_IDENTIFIER && arg->id2ast && arg->id2ast->is_signed && !arg->is_signed)
// fully-sliced signed wire will be resolved
// once the module becomes available
log_assert(attributes.count(ID::reprocess_after));
else
log_assert(arg->is_signed == sig.as_wire()->is_signed);
} else if (arg->is_signed) {
// non-trivial signed nodes are indirected through
// signed wires to enable sign extension
RTLIL::IdString wire_name = NEW_ID;
RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig));
wire->is_signed = true;
current_module->connect(wire, sig);
sig = wire;
}
}
2013-01-05 04:13:26 -06:00
if (child->str.size() == 0) {
char buf[100];
snprintf(buf, 100, "$%d", ++port_counter);
cell->setPort(buf, sig);
2013-01-05 04:13:26 -06:00
} else {
cell->setPort(child->str, sig);
2013-01-05 04:13:26 -06:00
}
continue;
}
2014-07-28 04:08:55 -05:00
log_abort();
2013-01-05 04:13:26 -06:00
}
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value.\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
2013-01-05 04:13:26 -06:00
}
if (cell->type == ID($specify2)) {
int src_width = GetSize(cell->getPort(ID::SRC));
int dst_width = GetSize(cell->getPort(ID::DST));
bool full = cell->getParam(ID::FULL).as_bool();
if (!full && src_width != dst_width)
input_error("Parallel specify SRC width does not match DST width.\n");
cell->setParam(ID::SRC_WIDTH, Const(src_width));
cell->setParam(ID::DST_WIDTH, Const(dst_width));
}
else if (cell->type == ID($specify3)) {
int dat_width = GetSize(cell->getPort(ID::DAT));
int dst_width = GetSize(cell->getPort(ID::DST));
2020-02-12 14:16:01 -06:00
if (dat_width != dst_width)
input_error("Specify DAT width does not match DST width.\n");
int src_width = GetSize(cell->getPort(ID::SRC));
cell->setParam(ID::SRC_WIDTH, Const(src_width));
cell->setParam(ID::DST_WIDTH, Const(dst_width));
2020-02-12 14:16:01 -06:00
}
else if (cell->type == ID($specrule)) {
int src_width = GetSize(cell->getPort(ID::SRC));
int dst_width = GetSize(cell->getPort(ID::DST));
cell->setParam(ID::SRC_WIDTH, Const(src_width));
cell->setParam(ID::DST_WIDTH, Const(dst_width));
}
2013-01-05 04:13:26 -06:00
}
break;
// use ProcessGenerator for always blocks
case AST_ALWAYS: {
2013-01-05 04:13:26 -06:00
AstNode *always = this->clone();
ProcessGenerator generator(always);
ignoreThisSignalsInInitial.append(generator.outputSignals);
delete always;
} break;
case AST_INITIAL: {
AstNode *always = this->clone();
ProcessGenerator generator(always, ignoreThisSignalsInInitial);
2013-01-05 04:13:26 -06:00
delete always;
} break;
case AST_TECALL: {
int sz = children.size();
if (str == "$info") {
if (sz > 0)
log_file_info(filename, location.first_line, "%s.\n", children[0]->str.c_str());
else
log_file_info(filename, location.first_line, "\n");
} else if (str == "$warning") {
if (sz > 0)
log_file_warning(filename, location.first_line, "%s.\n", children[0]->str.c_str());
else
log_file_warning(filename, location.first_line, "\n");
} else if (str == "$error") {
if (sz > 0)
input_error("%s.\n", children[0]->str.c_str());
else
input_error("\n");
} else if (str == "$fatal") {
// TODO: 1st parameter, if exists, is 0,1 or 2, and passed to $finish()
// if no parameter is given, default value is 1
// dollar_finish(sz ? children[0] : 1);
// perhaps create & use log_file_fatal()
if (sz > 0)
input_error("FATAL: %s.\n", children[0]->str.c_str());
else
input_error("FATAL.\n");
} else {
input_error("Unknown elabortoon system task '%s'.\n", str.c_str());
}
} break;
Add support for parsing the SystemVerilog 'bind' construct This doesn't do anything useful yet: the patch just adds support for the syntax to the lexer and parser and adds some tests to check the syntax parses properly. This generates AST nodes, but doesn't yet generate RTLIL. Since our existing hierarchical_identifier parser doesn't allow bit selects (so you can't do something like foo[1].bar[2].baz), I've also not added support for a trailing bit select (the "constant_bit_select" non-terminal in "bind_target_instance" in the spec). If we turn out to need this in future, we'll want to augment hierarchical_identifier and its other users too. Note that you can't easily use the BNF from the spec: bind_directive ::= "bind" bind_target_scope [ : bind_target_instance_list] bind_instantiation ; | "bind" bind_target_instance bind_instantiation ; even if you fix the lookahead problem, because code like this matches both branches in the BNF: bind a b b_i (.*); The problem is that 'a' could either be a module name or a degenerate hierarchical reference. This seems to be a genuine syntactic ambiguity, which the spec resolves (p739) by saying that we have to wait until resolution time (the hierarchy pass) and take whatever is defined, treating 'a' as an instance name if it names both an instance and a module. To keep the parser simple, it currently accepts this invalid syntax: bind a.b : c d e (.*); This is invalid because we're in the first branch of the BNF above, so the "a.b" term should match bind_target_scope: a module or interface identifier, not an arbitrary hierarchical identifier. This will fail in the hierarchy pass (when it's implemented in a future patch).
2020-05-21 11:36:29 -05:00
case AST_BIND: {
// Read a bind construct. This should have one or more cells as children.
for (RTLIL::Binding *binding : genBindings())
current_module->add(binding);
Add support for parsing the SystemVerilog 'bind' construct This doesn't do anything useful yet: the patch just adds support for the syntax to the lexer and parser and adds some tests to check the syntax parses properly. This generates AST nodes, but doesn't yet generate RTLIL. Since our existing hierarchical_identifier parser doesn't allow bit selects (so you can't do something like foo[1].bar[2].baz), I've also not added support for a trailing bit select (the "constant_bit_select" non-terminal in "bind_target_instance" in the spec). If we turn out to need this in future, we'll want to augment hierarchical_identifier and its other users too. Note that you can't easily use the BNF from the spec: bind_directive ::= "bind" bind_target_scope [ : bind_target_instance_list] bind_instantiation ; | "bind" bind_target_instance bind_instantiation ; even if you fix the lookahead problem, because code like this matches both branches in the BNF: bind a b b_i (.*); The problem is that 'a' could either be a module name or a degenerate hierarchical reference. This seems to be a genuine syntactic ambiguity, which the spec resolves (p739) by saying that we have to wait until resolution time (the hierarchy pass) and take whatever is defined, treating 'a' as an instance name if it names both an instance and a module. To keep the parser simple, it currently accepts this invalid syntax: bind a.b : c d e (.*); This is invalid because we're in the first branch of the BNF above, so the "a.b" term should match bind_target_scope: a module or interface identifier, not an arbitrary hierarchical identifier. This will fail in the hierarchy pass (when it's implemented in a future patch).
2020-05-21 11:36:29 -05:00
break;
}
2016-07-27 08:41:22 -05:00
case AST_FCALL: {
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
2016-07-27 08:41:22 -05:00
{
2016-08-30 12:09:56 -05:00
string myid = stringf("%s$%d", str.c_str() + 1, autoidx++);
2016-07-27 08:41:22 -05:00
int width = width_hint;
if (GetSize(children) > 1)
input_error("System function %s got %d arguments, expected 1 or 0.\n",
RTLIL::unescape_id(str).c_str(), GetSize(children));
2016-07-27 08:41:22 -05:00
if (GetSize(children) == 1) {
if (children[0]->type != AST_CONSTANT)
input_error("System function %s called with non-const argument!\n",
RTLIL::unescape_id(str).c_str());
2016-07-27 08:41:22 -05:00
width = children[0]->asInt(true);
}
if (width <= 0)
input_error("Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str());
2016-07-27 08:41:22 -05:00
Cell *cell = current_module->addCell(myid, str.substr(1));
set_src_attr(cell, this);
cell->parameters[ID::WIDTH] = width;
2016-07-27 08:41:22 -05:00
if (attributes.count(ID::reg)) {
auto &attr = attributes.at(ID::reg);
if (attr->type != AST_CONSTANT)
input_error("Attribute `reg' with non-constant value!\n");
cell->attributes[ID::reg] = attr->asAttrConst();
}
2016-07-27 08:41:22 -05:00
Wire *wire = current_module->addWire(myid + "_wire", width);
set_src_attr(wire, this);
2020-03-12 14:57:01 -05:00
cell->setPort(ID::Y, wire);
2016-07-27 08:41:22 -05:00
is_signed = sign_hint;
return SigSpec(wire);
}
}
YS_FALLTHROUGH
2016-07-27 08:41:22 -05:00
2013-01-05 04:13:26 -06:00
// everything should have been handled above -> print error if not.
default:
for (auto f : log_files)
current_ast_mod->dumpAst(f, "verilog-ast> ");
input_error("Don't know how to generate RTLIL code for %s node!\n", type2str(type).c_str());
2013-01-05 04:13:26 -06:00
}
return RTLIL::SigSpec();
}
// this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or
2015-08-14 03:56:05 -05:00
// signals must be substituted before being used as input values (used by ProcessGenerator)
2013-01-05 04:13:26 -06:00
// note that this is using some global variables to communicate this special settings to AstNode::genRTLIL().
RTLIL::SigSpec AstNode::genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)
2013-01-05 04:13:26 -06:00
{
2014-12-28 12:24:24 -06:00
const dict<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr;
2013-01-05 04:13:26 -06:00
if (new_subst_ptr)
genRTLIL_subst_ptr = new_subst_ptr;
2013-01-05 04:13:26 -06:00
bool sign_hint = sgn;
int width_hint = width;
detectSignWidthWorker(width_hint, sign_hint);
RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint);
2013-01-05 04:13:26 -06:00
genRTLIL_subst_ptr = backup_subst_ptr;
2013-01-05 04:13:26 -06:00
if (width >= 0)
2013-11-07 12:19:53 -06:00
sig.extend_u0(width, is_signed);
2013-01-05 04:13:26 -06:00
return sig;
}
YOSYS_NAMESPACE_END