Major improvements in mem2reg and added "init" sync rules
authorClifford Wolf <clifford@clifford.at>
Thu, 21 Nov 2013 12:49:00 +0000 (13:49 +0100)
committerClifford Wolf <clifford@clifford.at>
Thu, 21 Nov 2013 12:49:00 +0000 (13:49 +0100)
backends/ilang/ilang_backend.cc
frontends/ast/ast.h
frontends/ast/genrtlil.cc
frontends/ast/simplify.cc
frontends/ilang/lexer.l
frontends/ilang/parser.y
kernel/rtlil.h
passes/proc/Makefile.inc
passes/proc/proc.cc
passes/proc/proc_init.cc [new file with mode: 0644]

index 9bf0fb0aefe6180a893ba39060f063d53fce4a47..513e57fa73bd6458cb892f9e9e55ddf7fdabeb9c 100644 (file)
@@ -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++) {
index 37f75454c7e74889919041424ae3441a96870a23..de32a2bb9d6e4fd4aefcb30fd50c001fa4b40acc 100644 (file)
@@ -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);
 
index 550f7245e26566b61401c05267d8661ec8860db1..e634a27a9928558b0247550b270ccc9d975b6c74 100644 (file)
@@ -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();
index 636dde48e33a99a9dbea612bedbf35ba4a64ca8c..f1cce397c5fc74b1354b1d29f036eba776a2677d 100644 (file)
@@ -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)) { }
+               if (children[0]->children[0]->type == AST_CONSTANT)
+               {
+                       int id = children[0]->children[0]->integer;
+                       str = stringf("%s[%d]", str.c_str(), id);
 
-               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;
+                       delete_children();
+                       range_valid = false;
+                       id2ast = NULL;
+               }
+               else
+               {
+                       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);
+                       }
 
-               AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
-               case_node->children[0]->str = id_addr;
+                       std::vector<RTLIL::State> x_bits;
+                       for (int i = 0; i < mem_width; i++)
+                               x_bits.push_back(RTLIL::State::Sx);
 
-               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));
+                       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;
-                       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);
+                       }
 
-               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;
                }
-
-               delete_children();
-               range_valid = false;
-               id2ast = NULL;
-               str = id_data;
        }
 
        assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
index 2a20f02a844d0f3ec655f5d06c4473d4d347ce89..73bc894b10825f20c508d309eae7c272bd064c86 100644 (file)
@@ -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; }
index b889a9989f2356d0f117b8a847bd16cfd5e3591e..dc39cf93f344c6ed8fb10f573257c52086966463 100644 (file)
@@ -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:
index 952cb59449ddea483018a0212f0246db5efd50d0..c8f6b370f352ad5eb8faef7fecd0ce66c78b9e16 100644 (file)
@@ -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;
index 68564e055aa4699933fabad9cc91f844f847f200..dfbc78eae12dec7146268874b0d140b5423abfaa 100644 (file)
@@ -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
index eb99ab005942d562978e21ccfbfdaca3db6fb8b4..36d4141bb5037d5997053846d66fcbba3ae6b4d0 100644 (file)
@@ -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
diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc
new file mode 100644 (file)
index 0000000..fca20fd
--- /dev/null
@@ -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;