mirror of https://github.com/YosysHQ/yosys.git
Major improvements in mem2reg and added "init" sync rules
This commit is contained in:
parent
84ced2bb8e
commit
09471846c5
|
@ -225,6 +225,7 @@ void ILANG_BACKEND::dump_proc_sync(FILE *f, std::string indent, const RTLIL::Syn
|
|||
fprintf(f, "\n");
|
||||
break;
|
||||
case RTLIL::STa: fprintf(f, "always\n"); break;
|
||||
case RTLIL::STi: fprintf(f, "init\n"); break;
|
||||
}
|
||||
|
||||
for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) {
|
||||
|
|
|
@ -162,12 +162,31 @@ namespace AST
|
|||
void delete_children();
|
||||
~AstNode();
|
||||
|
||||
enum mem2reg_flags
|
||||
{
|
||||
/* status flags */
|
||||
MEM2REG_FL_ALL = 0x00000001,
|
||||
MEM2REG_FL_ASYNC = 0x00000002,
|
||||
MEM2REG_FL_INIT = 0x00000004,
|
||||
|
||||
/* candidate flags */
|
||||
MEM2REG_FL_FORCED = 0x00000100,
|
||||
MEM2REG_FL_SET_INIT = 0x00000200,
|
||||
MEM2REG_FL_SET_ELSE = 0x00000400,
|
||||
MEM2REG_FL_SET_ASYNC = 0x00000800,
|
||||
MEM2REG_FL_EQ2 = 0x00001000,
|
||||
|
||||
/* proc flags */
|
||||
MEM2REG_FL_EQ1 = 0x01000000,
|
||||
};
|
||||
|
||||
// simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
|
||||
// it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
|
||||
bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint);
|
||||
void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map);
|
||||
void replace_ids(std::map<std::string, std::string> &rules);
|
||||
void mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg);
|
||||
void mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places,
|
||||
std::map<AstNode*, uint32_t> &mem2reg_flags, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);
|
||||
void mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block);
|
||||
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
|
||||
|
||||
|
|
|
@ -234,7 +234,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
{
|
||||
// input and output structures
|
||||
AstNode *always;
|
||||
RTLIL::SigSpec skipSyncSignals;
|
||||
RTLIL::SigSpec initSyncSignals;
|
||||
RTLIL::Process *proc;
|
||||
const RTLIL::SigSpec &outputSignals;
|
||||
|
||||
|
@ -258,7 +258,10 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
// map helps generating nice numbered names for all this temporary signals.
|
||||
std::map<RTLIL::Wire*, int> new_temp_count;
|
||||
|
||||
ProcessGenerator(AstNode *always, RTLIL::SigSpec skipSyncSignalsArg = RTLIL::SigSpec()) : always(always), skipSyncSignals(skipSyncSignalsArg), outputSignals(subst_lvalue_from)
|
||||
// Buffer for generating the init action
|
||||
RTLIL::SigSpec init_lvalue, init_rvalue;
|
||||
|
||||
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), outputSignals(subst_lvalue_from)
|
||||
{
|
||||
// generate process and simple root case
|
||||
proc = new RTLIL::Process;
|
||||
|
@ -321,6 +324,25 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
for (auto child : always->children)
|
||||
if (child->type == AST_BLOCK)
|
||||
processAst(child);
|
||||
|
||||
if (initSyncSignals.width > 0)
|
||||
{
|
||||
RTLIL::SyncRule *sync = new RTLIL::SyncRule;
|
||||
sync->type = RTLIL::SyncType::STi;
|
||||
proc->syncs.push_back(sync);
|
||||
|
||||
assert(init_lvalue.width == init_rvalue.width);
|
||||
init_lvalue.optimize();
|
||||
init_rvalue.optimize();
|
||||
|
||||
int offset = 0;
|
||||
for (size_t i = 0; i < init_lvalue.chunks.size(); i++) {
|
||||
RTLIL::SigSpec lhs = init_lvalue.chunks[i];
|
||||
RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue.chunks[i].width);
|
||||
sync->actions.push_back(RTLIL::SigSig(lhs, rhs));
|
||||
offset += lhs.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create new temporary signals
|
||||
|
@ -406,8 +428,11 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
// 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)
|
||||
{
|
||||
if (inSyncRule)
|
||||
lvalue.remove2(skipSyncSignals, &rvalue);
|
||||
if (inSyncRule && initSyncSignals.width > 0) {
|
||||
init_lvalue.append(lvalue.extract(initSyncSignals));
|
||||
init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue));
|
||||
lvalue.remove2(initSyncSignals, &rvalue);
|
||||
}
|
||||
assert(lvalue.width == rvalue.width);
|
||||
lvalue.optimize();
|
||||
rvalue.optimize();
|
||||
|
|
|
@ -56,8 +56,46 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
|
||||
if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg"))
|
||||
{
|
||||
std::set<AstNode*> mem2reg_set, mem2reg_candidates;
|
||||
mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, false, false, flag_mem2reg);
|
||||
std::map<AstNode*, std::set<std::string>> mem2reg_places;
|
||||
std::map<AstNode*, uint32_t> mem2reg_candidates, dummy_proc_flags;
|
||||
uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0;
|
||||
mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags);
|
||||
|
||||
std::set<AstNode*> mem2reg_set;
|
||||
for (auto &it : mem2reg_candidates)
|
||||
{
|
||||
AstNode *mem = it.first;
|
||||
uint32_t memflags = it.second;
|
||||
assert((memflags & ~0x00ffff00) == 0);
|
||||
|
||||
if (mem->get_bool_attribute("\\nomem2reg"))
|
||||
continue;
|
||||
|
||||
if (memflags & AstNode::MEM2REG_FL_FORCED)
|
||||
goto silent_activate;
|
||||
|
||||
if (memflags & AstNode::MEM2REG_FL_EQ2)
|
||||
goto verbose_activate;
|
||||
|
||||
if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE))
|
||||
goto verbose_activate;
|
||||
|
||||
continue;
|
||||
|
||||
verbose_activate:
|
||||
if (mem2reg_set.count(mem) == 0) {
|
||||
log("Warning: Replacing memory %s with list of registers.", mem->str.c_str());
|
||||
bool first_element = true;
|
||||
for (auto &place : mem2reg_places[it.first]) {
|
||||
log("%s%s", first_element ? " See " : ", ", place.c_str());
|
||||
first_element = false;
|
||||
}
|
||||
log("\n");
|
||||
}
|
||||
|
||||
silent_activate:
|
||||
mem2reg_set.insert(mem);
|
||||
}
|
||||
|
||||
for (auto node : mem2reg_set)
|
||||
{
|
||||
|
@ -1249,36 +1287,102 @@ void AstNode::replace_ids(std::map<std::string, std::string> &rules)
|
|||
}
|
||||
|
||||
// find memories that should be replaced by registers
|
||||
void AstNode::mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg)
|
||||
void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places,
|
||||
std::map<AstNode*, uint32_t> &mem2reg_candidates, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &flags)
|
||||
{
|
||||
if ((type == AST_ASSIGN_LE && async_proc) || (type == AST_ASSIGN_EQ && (sync_proc || async_proc)))
|
||||
if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY &&
|
||||
!children[0]->id2ast->get_bool_attribute("\\nomem2reg")) {
|
||||
if (async_proc || mem2reg_candidates.count(children[0]->id2ast) > 0) {
|
||||
if (mem2reg_set.count(children[0]->id2ast) == 0)
|
||||
log("Warning: Replacing memory %s with list of registers because of assignment in line %s:%d.\n",
|
||||
children[0]->str.c_str(), filename.c_str(), linenum);
|
||||
mem2reg_set.insert(children[0]->id2ast);
|
||||
uint32_t children_flags = 0;
|
||||
int ignore_children_counter = 0;
|
||||
|
||||
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
|
||||
{
|
||||
if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY)
|
||||
{
|
||||
AstNode *mem = children[0]->id2ast;
|
||||
|
||||
// activate mem2reg if this is assigned in an async proc
|
||||
if (flags & AstNode::MEM2REG_FL_ASYNC) {
|
||||
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC))
|
||||
mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
|
||||
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC;
|
||||
}
|
||||
|
||||
// remember if this is assigned blocking (=)
|
||||
if (type == AST_ASSIGN_EQ) {
|
||||
if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1))
|
||||
mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
|
||||
proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1;
|
||||
}
|
||||
|
||||
// remember where this is
|
||||
if (flags & MEM2REG_FL_INIT) {
|
||||
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT))
|
||||
mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
|
||||
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT;
|
||||
} else {
|
||||
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE))
|
||||
mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
|
||||
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE;
|
||||
}
|
||||
mem2reg_candidates.insert(children[0]->id2ast);
|
||||
}
|
||||
|
||||
if (type == AST_MEMORY && (get_bool_attribute("\\mem2reg") || force_mem2reg))
|
||||
mem2reg_set.insert(this);
|
||||
|
||||
ignore_children_counter = 1;
|
||||
}
|
||||
|
||||
if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY)
|
||||
{
|
||||
AstNode *mem = id2ast;
|
||||
|
||||
// flag if used after blocking assignment (in same proc)
|
||||
if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
|
||||
mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum));
|
||||
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2;
|
||||
}
|
||||
}
|
||||
|
||||
// also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg'
|
||||
if (type == AST_MEMORY && (get_bool_attribute("\\mem2reg") || (flags & AstNode::MEM2REG_FL_ALL) || !is_reg))
|
||||
mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED;
|
||||
|
||||
if (type == AST_MODULE && get_bool_attribute("\\mem2reg"))
|
||||
force_mem2reg = true;
|
||||
children_flags |= AstNode::MEM2REG_FL_ALL;
|
||||
|
||||
std::map<AstNode*, uint32_t> *proc_flags_p = NULL;
|
||||
|
||||
if (type == AST_ALWAYS) {
|
||||
bool sync_proc = false;
|
||||
for (auto child : children) {
|
||||
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
|
||||
sync_proc = true;
|
||||
}
|
||||
async_proc = !sync_proc;
|
||||
if (!sync_proc)
|
||||
children_flags |= AstNode::MEM2REG_FL_ASYNC;
|
||||
proc_flags_p = new std::map<AstNode*, uint32_t>;
|
||||
}
|
||||
|
||||
if (type == AST_INITIAL) {
|
||||
children_flags |= AstNode::MEM2REG_FL_INIT;
|
||||
proc_flags_p = new std::map<AstNode*, uint32_t>;
|
||||
}
|
||||
|
||||
uint32_t backup_flags = flags;
|
||||
flags |= children_flags;
|
||||
assert((flags & ~0x000000ff) == 0);
|
||||
|
||||
for (auto child : children)
|
||||
child->mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, sync_proc, async_proc, force_mem2reg);
|
||||
if (ignore_children_counter > 0)
|
||||
ignore_children_counter--;
|
||||
else if (proc_flags_p)
|
||||
child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
|
||||
else
|
||||
child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
|
||||
|
||||
flags &= ~children_flags | backup_flags;
|
||||
|
||||
if (proc_flags_p) {
|
||||
for (auto it : *proc_flags_p)
|
||||
assert((it.second & ~0xff000000) == 0);
|
||||
delete proc_flags_p;
|
||||
}
|
||||
}
|
||||
|
||||
// actually replace memories with registers
|
||||
|
@ -1287,8 +1391,8 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
|
|||
if (type == AST_BLOCK)
|
||||
block = this;
|
||||
|
||||
if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL &&
|
||||
children[0]->id2ast && mem2reg_set.count(children[0]->id2ast) > 0)
|
||||
if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && children[0]->id2ast &&
|
||||
mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
|
||||
|
@ -1344,77 +1448,89 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
|
|||
|
||||
if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
|
||||
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
|
||||
|
||||
int mem_width, mem_size, addr_bits;
|
||||
id2ast->meminfo(mem_width, mem_size, addr_bits);
|
||||
|
||||
AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
|
||||
wire_addr->str = id_addr;
|
||||
wire_addr->is_reg = true;
|
||||
if (block)
|
||||
wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
mod->children.push_back(wire_addr);
|
||||
while (wire_addr->simplify(true, false, false, 1, -1, false)) { }
|
||||
|
||||
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
||||
wire_data->str = id_data;
|
||||
wire_data->is_reg = true;
|
||||
if (block)
|
||||
wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
mod->children.push_back(wire_data);
|
||||
while (wire_data->simplify(true, false, false, 1, -1, false)) { }
|
||||
|
||||
AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
|
||||
assign_addr->children[0]->str = id_addr;
|
||||
|
||||
AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
|
||||
case_node->children[0]->str = id_addr;
|
||||
|
||||
for (int i = 0; i < mem_size; i++) {
|
||||
if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i)
|
||||
continue;
|
||||
AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
|
||||
AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
|
||||
assign_reg->children[0]->str = id_data;
|
||||
assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
|
||||
cond_node->children[1]->children.push_back(assign_reg);
|
||||
case_node->children.push_back(cond_node);
|
||||
}
|
||||
|
||||
std::vector<RTLIL::State> x_bits;
|
||||
for (int i = 0; i < mem_width; i++)
|
||||
x_bits.push_back(RTLIL::State::Sx);
|
||||
|
||||
AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK));
|
||||
AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
|
||||
assign_reg->children[0]->str = id_data;
|
||||
cond_node->children[1]->children.push_back(assign_reg);
|
||||
case_node->children.push_back(cond_node);
|
||||
|
||||
if (block)
|
||||
if (children[0]->children[0]->type == AST_CONSTANT)
|
||||
{
|
||||
size_t assign_idx = 0;
|
||||
while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
|
||||
assign_idx++;
|
||||
assert(assign_idx < block->children.size());
|
||||
block->children.insert(block->children.begin()+assign_idx, case_node);
|
||||
block->children.insert(block->children.begin()+assign_idx, assign_addr);
|
||||
int id = children[0]->children[0]->integer;
|
||||
str = stringf("%s[%d]", str.c_str(), id);
|
||||
|
||||
delete_children();
|
||||
range_valid = false;
|
||||
id2ast = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
|
||||
proc->children[0]->children.push_back(case_node);
|
||||
mod->children.push_back(proc);
|
||||
mod->children.push_back(assign_addr);
|
||||
}
|
||||
std::stringstream sstr;
|
||||
sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
|
||||
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
|
||||
|
||||
delete_children();
|
||||
range_valid = false;
|
||||
id2ast = NULL;
|
||||
str = id_data;
|
||||
int mem_width, mem_size, addr_bits;
|
||||
id2ast->meminfo(mem_width, mem_size, addr_bits);
|
||||
|
||||
AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
|
||||
wire_addr->str = id_addr;
|
||||
wire_addr->is_reg = true;
|
||||
if (block)
|
||||
wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
mod->children.push_back(wire_addr);
|
||||
while (wire_addr->simplify(true, false, false, 1, -1, false)) { }
|
||||
|
||||
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
||||
wire_data->str = id_data;
|
||||
wire_data->is_reg = true;
|
||||
if (block)
|
||||
wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
mod->children.push_back(wire_data);
|
||||
while (wire_data->simplify(true, false, false, 1, -1, false)) { }
|
||||
|
||||
AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
|
||||
assign_addr->children[0]->str = id_addr;
|
||||
|
||||
AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
|
||||
case_node->children[0]->str = id_addr;
|
||||
|
||||
for (int i = 0; i < mem_size; i++) {
|
||||
if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i)
|
||||
continue;
|
||||
AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
|
||||
AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
|
||||
assign_reg->children[0]->str = id_data;
|
||||
assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
|
||||
cond_node->children[1]->children.push_back(assign_reg);
|
||||
case_node->children.push_back(cond_node);
|
||||
}
|
||||
|
||||
std::vector<RTLIL::State> x_bits;
|
||||
for (int i = 0; i < mem_width; i++)
|
||||
x_bits.push_back(RTLIL::State::Sx);
|
||||
|
||||
AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK));
|
||||
AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
|
||||
assign_reg->children[0]->str = id_data;
|
||||
cond_node->children[1]->children.push_back(assign_reg);
|
||||
case_node->children.push_back(cond_node);
|
||||
|
||||
if (block)
|
||||
{
|
||||
size_t assign_idx = 0;
|
||||
while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
|
||||
assign_idx++;
|
||||
assert(assign_idx < block->children.size());
|
||||
block->children.insert(block->children.begin()+assign_idx, case_node);
|
||||
block->children.insert(block->children.begin()+assign_idx, assign_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
|
||||
proc->children[0]->children.push_back(case_node);
|
||||
mod->children.push_back(proc);
|
||||
mod->children.push_back(assign_addr);
|
||||
}
|
||||
|
||||
delete_children();
|
||||
range_valid = false;
|
||||
id2ast = NULL;
|
||||
str = id_data;
|
||||
}
|
||||
}
|
||||
|
||||
assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"negedge" { return TOK_NEGEDGE; }
|
||||
"edge" { return TOK_EDGE; }
|
||||
"always" { return TOK_ALWAYS; }
|
||||
"init" { return TOK_INIT; }
|
||||
"update" { return TOK_UPDATE; }
|
||||
"process" { return TOK_PROCESS; }
|
||||
"end" { return TOK_END; }
|
||||
|
|
|
@ -52,7 +52,7 @@ using namespace ILANG_FRONTEND;
|
|||
%token <integer> TOK_INT
|
||||
%token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
|
||||
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
|
||||
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS
|
||||
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_INIT
|
||||
%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
|
||||
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_AUTO TOK_MEMORY TOK_SIZE
|
||||
|
||||
|
@ -279,6 +279,12 @@ sync_list:
|
|||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_INIT TOK_EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STi;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
/* empty */;
|
||||
|
||||
sync_type:
|
||||
|
|
|
@ -44,7 +44,8 @@ namespace RTLIL
|
|||
STp = 2, // edge sensitive: posedge
|
||||
STn = 3, // edge sensitive: negedge
|
||||
STe = 4, // edge sensitive: both edges
|
||||
STa = 5 // always active
|
||||
STa = 5, // always active
|
||||
STi = 6 // init
|
||||
};
|
||||
|
||||
extern int autoidx;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
OBJS += passes/proc/proc.o
|
||||
OBJS += passes/proc/proc_clean.o
|
||||
OBJS += passes/proc/proc_rmdead.o
|
||||
OBJS += passes/proc/proc_init.o
|
||||
OBJS += passes/proc/proc_arst.o
|
||||
OBJS += passes/proc/proc_mux.o
|
||||
OBJS += passes/proc/proc_dff.o
|
||||
|
|
|
@ -34,6 +34,7 @@ struct ProcPass : public Pass {
|
|||
log("\n");
|
||||
log(" proc_clean\n");
|
||||
log(" proc_rmdead\n");
|
||||
log(" proc_init\n");
|
||||
log(" proc_arst\n");
|
||||
log(" proc_mux\n");
|
||||
log(" proc_dff\n");
|
||||
|
@ -67,6 +68,7 @@ struct ProcPass : public Pass {
|
|||
|
||||
Pass::call(design, "proc_clean");
|
||||
Pass::call(design, "proc_rmdead");
|
||||
Pass::call(design, "proc_init");
|
||||
if (global_arst.empty())
|
||||
Pass::call(design, "proc_arst");
|
||||
else
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule)
|
||||
{
|
||||
assert(rule.compare.size() == 0);
|
||||
|
||||
while (1) {
|
||||
sig.optimize();
|
||||
RTLIL::SigSpec tmp = sig;
|
||||
for (auto &it : rule.actions)
|
||||
tmp.replace(it.first, it.second);
|
||||
if (tmp == sig)
|
||||
break;
|
||||
sig = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
|
||||
{
|
||||
bool found_init = false;
|
||||
|
||||
for (auto &sync : proc->syncs)
|
||||
if (sync->type == RTLIL::SyncType::STi)
|
||||
{
|
||||
found_init = true;
|
||||
log("Found init rule in `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
|
||||
|
||||
for (auto &action : sync->actions)
|
||||
{
|
||||
RTLIL::SigSpec lhs = action.first;
|
||||
RTLIL::SigSpec rhs = action.second;
|
||||
|
||||
lhs.optimize();
|
||||
proc_get_const(rhs, proc->root_case);
|
||||
|
||||
if (!rhs.is_fully_const())
|
||||
log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs));
|
||||
|
||||
int offset = 0;
|
||||
for (size_t i = 0; i < lhs.chunks.size(); i++) {
|
||||
if (lhs.chunks[i].wire == NULL)
|
||||
continue;
|
||||
RTLIL::Wire *wire = lhs.chunks[i].wire;
|
||||
RTLIL::SigSpec value = rhs.extract(offset, lhs.chunks[i].width);
|
||||
if (value.width != wire->width)
|
||||
log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs.chunks[i]), log_signal(value));
|
||||
log(" Setting init value: %s = %s\n", log_signal(wire), log_signal(value));
|
||||
wire->attributes["\\init"] = value.as_const();
|
||||
offset += wire->width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_init) {
|
||||
std::vector<RTLIL::SyncRule*> new_syncs;
|
||||
for (auto &sync : proc->syncs)
|
||||
if (sync->type == RTLIL::SyncType::STi)
|
||||
delete sync;
|
||||
else
|
||||
new_syncs.push_back(sync);
|
||||
proc->syncs.swap(new_syncs);
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcInitPass : public Pass {
|
||||
ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" proc_init [selection]\n");
|
||||
log("\n");
|
||||
log("This pass extracts the 'init' actions from processes (generated from verilog\n");
|
||||
log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n");
|
||||
log("respective wire.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header("Executing PROC_INIT pass (extract init attributes).\n");
|
||||
|
||||
extra_args(args, 1, design);
|
||||
|
||||
for (auto &mod_it : design->modules)
|
||||
if (design->selected(mod_it.second))
|
||||
for (auto &proc_it : mod_it.second->processes)
|
||||
if (design->selected(mod_it.second, proc_it.second))
|
||||
proc_init(mod_it.second, proc_it.second);
|
||||
}
|
||||
} ProcInitPass;
|
||||
|
Loading…
Reference in New Issue