aboutsummaryrefslogtreecommitdiffstats
path: root/frontends
diff options
context:
space:
mode:
authorClaire Wolf <clifford@clifford.at>2020-04-21 18:46:52 +0200
committerGitHub <noreply@github.com>2020-04-21 18:46:52 +0200
commit9e1afde7a02e6d3a65c106f74920dcad9e678a04 (patch)
treee521580ec3e49a3e5ead7c834bc8e9664a950d58 /frontends
parentabc8f1fcb65bb99ef4bf6fc6c6aa3126c333c68f (diff)
parentd32e56a3d1bdb36a77c0c3afad2eb4493292480b (diff)
downloadyosys-9e1afde7a02e6d3a65c106f74920dcad9e678a04.tar.gz
yosys-9e1afde7a02e6d3a65c106f74920dcad9e678a04.tar.bz2
yosys-9e1afde7a02e6d3a65c106f74920dcad9e678a04.zip
Merge pull request #1851 from YosysHQ/claire/bitselwrite
Improved rewrite code for writing to bit slice
Diffstat (limited to 'frontends')
-rw-r--r--frontends/ast/ast.cc5
-rw-r--r--frontends/ast/ast.h4
-rw-r--r--frontends/ast/genrtlil.cc131
-rw-r--r--frontends/ast/simplify.cc82
4 files changed, 207 insertions, 15 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index de741a506..9ddd538b5 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -218,6 +218,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
realvalue = 0;
id2ast = NULL;
basic_prep = false;
+ lookahead = false;
if (child1)
children.push_back(child1);
@@ -310,6 +311,10 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " reg");
if (is_signed)
fprintf(f, " signed");
+ if (basic_prep)
+ fprintf(f, " basic_prep");
+ if (lookahead)
+ fprintf(f, " lookahead");
if (port_id > 0)
fprintf(f, " port=%d", port_id);
if (range_valid || range_left != -1 || range_right != 0)
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 3dd40238f..3f6329112 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -202,6 +202,9 @@ namespace AST
// this is used by simplify to detect if basic analysis has been performed already on the node
bool basic_prep;
+ // this is used for ID references in RHS expressions that should use the "new" value for non-blocking assignments
+ bool lookahead;
+
// this is the original sourcecode location that resulted in this AST node
// it is automatically set by the constructor using AST::current_filename and
// the AST::get_line_num() callback function.
@@ -352,6 +355,7 @@ namespace AST_INTERNAL
extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
extern AST::AstModule *current_module;
extern bool current_always_clocked;
+ struct LookaheadRewriter;
struct ProcessGenerator;
}
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index ab368fdb0..ff3f5b84c 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -157,6 +157,126 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
return wire;
}
+// 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->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++);
+ wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire->is_logic = true;
+ while (wire->simplify(true, false, false, 1, -1, false, 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> ");
+ }
+};
+
// helper class for converting AST always nodes to RTLIL processes
struct AST_INTERNAL::ProcessGenerator
{
@@ -191,6 +311,9 @@ struct AST_INTERNAL::ProcessGenerator
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg)
{
+ // rewrite lookahead references
+ LookaheadRewriter la_rewriter(always);
+
// generate process and simple root case
proc = new RTLIL::Process;
proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column);
@@ -338,7 +461,7 @@ struct AST_INTERNAL::ProcessGenerator
return chunks;
}
- // recursively traverse the AST an collect all assigned signals
+ // recursively traverse the AST and collect all assigned signals
void collect_lvalues(RTLIL::SigSpec &reg, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true)
{
switch (ast->type)
@@ -1010,7 +1133,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
int add_undef_bits_msb = 0;
int add_undef_bits_lsb = 0;
- if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {
+ log_assert(id2ast != nullptr);
+
+ if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {
RTLIL::Wire *wire = current_module->addWire(str);
wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column);
wire->name = str;
@@ -1025,7 +1150,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk = RTLIL::Const(id2ast->children[0]->bits);
goto use_const_chunk;
}
- else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
+ 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);
if (current_wire->get_bool_attribute(ID::is_interface))
is_interface = true;
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 467afdd87..837c14ad7 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1754,8 +1754,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with
- // a big case block that selects the correct single-bit assignment.
- if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) {
+ // either a big case block that selects the correct single-bit assignment, or mask and
+ // shift operations.
+ if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)
+ {
if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0)
goto skip_dynamic_range_lvalue_expansion;
if (children[0]->children[0]->range_valid || did_something)
@@ -1764,10 +1766,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
goto skip_dynamic_range_lvalue_expansion;
if (!children[0]->id2ast->range_valid)
goto skip_dynamic_range_lvalue_expansion;
+
int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1;
int result_width = 1;
+
AstNode *shift_expr = NULL;
AstNode *range = children[0]->children[0];
+
if (range->children.size() == 1) {
shift_expr = range->children[0]->clone();
} else {
@@ -1780,19 +1785,72 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
}
- did_something = true;
- newNode = new AstNode(AST_CASE, shift_expr);
- for (int i = 0; i < source_width; i++) {
- int start_bit = children[0]->id2ast->range_right + i;
- AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
+
+ if (0)
+ {
+ // big case block
+
+ did_something = true;
+ newNode = new AstNode(AST_CASE, shift_expr);
+ for (int i = 0; i < source_width; i++) {
+ int start_bit = children[0]->id2ast->range_right + i;
+ AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
+ AstNode *lvalue = children[0]->clone();
+ lvalue->delete_children();
+ int end_bit = std::min(start_bit+result_width,source_width) - 1;
+ lvalue->children.push_back(new AstNode(AST_RANGE,
+ mkconst_int(end_bit, true), mkconst_int(start_bit, true)));
+ cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone())));
+ newNode->children.push_back(cond);
+ }
+ }
+ else
+ {
+ // mask and shift operations, disabled for now
+
+ AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true)));
+ wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", filename.c_str(), location.first_line, autoidx++);
+ wire_mask->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire_mask->is_logic = true;
+ while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { }
+ current_ast_mod->children.push_back(wire_mask);
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true)));
+ wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", filename.c_str(), location.first_line, autoidx++);
+ wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire_data->is_logic = true;
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
+ current_ast_mod->children.push_back(wire_data);
+
+ did_something = true;
+ newNode = new AstNode(AST_BLOCK);
+
AstNode *lvalue = children[0]->clone();
lvalue->delete_children();
- int end_bit = std::min(start_bit+result_width,source_width) - 1;
- lvalue->children.push_back(new AstNode(AST_RANGE,
- mkconst_int(end_bit, true), mkconst_int(start_bit, true)));
- cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone())));
- newNode->children.push_back(cond);
+
+ AstNode *ref_mask = new AstNode(AST_IDENTIFIER);
+ ref_mask->str = wire_mask->str;
+ ref_mask->id2ast = wire_mask;
+ ref_mask->was_checked = true;
+
+ AstNode *ref_data = new AstNode(AST_IDENTIFIER);
+ ref_data->str = wire_data->str;
+ ref_data->id2ast = wire_data;
+ ref_data->was_checked = true;
+
+ AstNode *old_data = lvalue->clone();
+ if (type == AST_ASSIGN_LE)
+ old_data->lookahead = true;
+
+ AstNode *shamt = shift_expr;
+
+ newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_mask->clone(),
+ new AstNode(AST_SHIFT_LEFT, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), shamt->clone())));
+ newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_data->clone(),
+ new AstNode(AST_SHIFT_LEFT, new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone()), shamt)));
+ newNode->children.push_back(new AstNode(type, lvalue, new AstNode(AST_BIT_OR, new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)), ref_data)));
}
+
goto apply_newNode;
}
skip_dynamic_range_lvalue_expansion:;