verilog: Use proc memory writes in the frontend.
authorMarcelina Kościelnicka <mwk@0x04.net>
Tue, 23 Feb 2021 15:48:29 +0000 (16:48 +0100)
committerMarcelina Kościelnicka <mwk@0x04.net>
Mon, 8 Mar 2021 19:16:29 +0000 (20:16 +0100)
frontends/ast/ast.cc
frontends/ast/ast.h
frontends/ast/genrtlil.cc
frontends/ast/simplify.cc
manual/CHAPTER_Verilog.tex

index af5e326adbe556fabac45477708228693413c237..3b631907180509492f1f8434ec834c9fccd70f9d 100644 (file)
@@ -54,6 +54,8 @@ namespace AST_INTERNAL {
        AstNode *current_always, *current_top_block, *current_block, *current_block_child;
        AstModule *current_module;
        bool current_always_clocked;
+       dict<std::string, int> current_memwr_count;
+       dict<std::string, pool<int>> current_memwr_visible;
 }
 
 // convert node types to string
index 1c9a6ee47c6bd002be61afa6b5ac4acfe3c5d186..1447bf568af4c87913097137bf144e875ef12342 100644 (file)
@@ -381,6 +381,8 @@ 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;
+       extern dict<std::string, int> current_memwr_count;
+       extern dict<std::string, pool<int>> current_memwr_visible;
        struct LookaheadRewriter;
        struct ProcessGenerator;
 }
index e0a52243054d7d76536b8b16f396de71089bfded..ad5814f1b124ed9811c3f94cca2cb542c136890a 100644 (file)
@@ -399,6 +399,9 @@ struct AST_INTERNAL::ProcessGenerator
                        if (child->type == AST_BLOCK)
                                processAst(child);
 
+               for (auto sync: proc->syncs)
+                       processMemWrites(sync);
+
                if (initSyncSignals.size() > 0)
                {
                        RTLIL::SyncRule *sync = new RTLIL::SyncRule;
@@ -698,6 +701,34 @@ struct AST_INTERNAL::ProcessGenerator
                        log_abort();
                }
        }
+
+       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, &subst_rvalue_map.stdmap());
+                               action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, &subst_rvalue_map.stdmap());
+                               action.enable = child->children[2]->genWidthRTLIL(-1, &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;
+                       }
+       }
 };
 
 // detect sign and width of an expression
@@ -1644,26 +1675,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                        return RTLIL::SigSpec(wire);
                }
 
-       // generate $memwr cells for memory write ports
-       case AST_MEMWR:
+       // generate $meminit cells
        case AST_MEMINIT:
                {
                        std::stringstream sstr;
-                       sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+                       sstr << "$meminit$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
 
-                       RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit));
+                       RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit));
                        set_src_attr(cell, this);
 
                        int mem_width, mem_size, addr_bits;
                        id2ast->meminfo(mem_width, mem_size, addr_bits);
 
-                       int num_words = 1;
-                       if (type == AST_MEMINIT) {
-                               if (children[2]->type != AST_CONSTANT)
-                                       log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
-                               num_words = int(children[2]->asInt(false));
-                               cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
-                       }
+                       if (children[2]->type != AST_CONSTANT)
+                               log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
+                       int num_words = int(children[2]->asInt(false));
+                       cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
 
                        SigSpec addr_sig = children[0]->genRTLIL();
 
@@ -1674,13 +1701,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                        cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig));
                        cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width);
 
-                       if (type == AST_MEMWR) {
-                               cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1));
-                               cell->setPort(ID::EN, children[2]->genRTLIL());
-                               cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0);
-                               cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0);
-                       }
-
                        cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
                }
                break;
index e7f897b3c8d556f93277b07c205d0ec5111e87dc..e0ac58f205da822d1aa842e46e48561b90ce057a 100644 (file)
@@ -1217,6 +1217,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                }
        }
 
+       dict<std::string, pool<int>> backup_memwr_visible;
+       dict<std::string, pool<int>> final_memwr_visible;
+
+       if (type == AST_CASE && stage == 2) {
+               backup_memwr_visible = current_memwr_visible;
+               final_memwr_visible = current_memwr_visible;
+       }
+
        // simplify all children first
        // (iterate by index as e.g. auto wires can add new children in the process)
        for (size_t i = 0; i < children.size(); i++) {
@@ -1279,11 +1287,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                }
                flag_autowire = backup_flag_autowire;
                unevaluated_tern_branch = backup_unevaluated_tern_branch;
+               if (stage == 2 && type == AST_CASE) {
+                       for (auto &x : current_memwr_visible) {
+                               for (int y : x.second)
+                                       final_memwr_visible[x.first].insert(y);
+                       }
+                       current_memwr_visible = backup_memwr_visible;
+               }
        }
        for (auto &attr : attributes) {
                while (attr.second->simplify(true, false, false, stage, -1, false, true))
                        did_something = true;
        }
+       if (type == AST_CASE && stage == 2) {
+               current_memwr_visible = final_memwr_visible;
+       }
+       if (type == AST_ALWAYS && stage == 2) {
+               current_memwr_visible.clear();
+               current_memwr_count.clear();
+       }
 
        if (reset_width_after_children) {
                width_hint = backup_width_hint;
@@ -2570,12 +2592,12 @@ skip_dynamic_range_lvalue_expansion:;
                        current_scope[wire_addr->str] = wire_addr;
                        while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
 
-                       AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
+                       AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
                        assign_addr->children[0]->str = id_addr;
                        assign_addr->children[0]->was_checked = true;
                        defNode->children.push_back(assign_addr);
 
-                       assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
+                       assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
                        assign_addr->children[0]->str = id_addr;
                        assign_addr->children[0]->was_checked = true;
                        newNode->children.push_back(assign_addr);
@@ -2596,7 +2618,7 @@ skip_dynamic_range_lvalue_expansion:;
                        current_scope[wire_data->str] = wire_data;
                        while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
 
-                       AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
+                       AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
                        assign_data->children[0]->str = id_data;
                        assign_data->children[0]->was_checked = true;
                        defNode->children.push_back(assign_data);
@@ -2616,7 +2638,7 @@ skip_dynamic_range_lvalue_expansion:;
                        current_scope[wire_en->str] = wire_en;
                        while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
 
-                       AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
+                       AstNode *assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
                        assign_en->children[0]->str = id_en;
                        assign_en->children[0]->was_checked = true;
                        defNode->children.push_back(assign_en);
@@ -2642,7 +2664,7 @@ skip_dynamic_range_lvalue_expansion:;
 
                                std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
 
-                               assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+                               assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
                                                new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
                                assign_data->children[0]->str = id_data;
                                assign_data->children[0]->was_checked = true;
@@ -2650,7 +2672,7 @@ skip_dynamic_range_lvalue_expansion:;
                                if (current_always->type != AST_INITIAL) {
                                        for (int i = 0; i < mem_width; i++)
                                                set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
-                                       assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+                                       assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
                                        assign_en->children[0]->str = id_en;
                                        assign_en->children[0]->was_checked = true;
                                }
@@ -2671,7 +2693,7 @@ skip_dynamic_range_lvalue_expansion:;
                                        log_file_error(filename, location.first_line, "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;
 
-                               assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+                               assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
                                                new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
                                assign_data->children[0]->str = id_data;
                                assign_data->children[0]->was_checked = true;
@@ -2679,7 +2701,7 @@ skip_dynamic_range_lvalue_expansion:;
                                if (current_always->type != AST_INITIAL) {
                                        for (int i = 0; i < mem_width; i++)
                                                set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
-                                       assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+                                       assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
                                                        new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
                                        assign_en->children[0]->str = id_en;
                                        assign_en->children[0]->was_checked = true;
@@ -2693,13 +2715,13 @@ skip_dynamic_range_lvalue_expansion:;
                else
                {
                        if (!(children[0]->children.size() == 1 && children[1]->isConst())) {
-                               assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
+                               assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone());
                                assign_data->children[0]->str = id_data;
                                assign_data->children[0]->was_checked = true;
                        }
 
                        if (current_always->type != AST_INITIAL) {
-                               assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+                               assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
                                assign_en->children[0]->str = id_en;
                                assign_en->children[0]->was_checked = true;
                        }
@@ -2712,7 +2734,21 @@ skip_dynamic_range_lvalue_expansion:;
                AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en);
                wrnode->str = children[0]->str;
                wrnode->id2ast = children[0]->id2ast;
-               current_ast_mod->children.push_back(wrnode);
+               wrnode->location = location;
+               if (wrnode->type == AST_MEMWR) {
+                       int portid = current_memwr_count[wrnode->str]++;
+                       wrnode->children.push_back(mkconst_int(portid, false));
+                       std::vector<RTLIL::State> priority_mask;
+                       for (int i = 0; i < portid; i++) {
+                               bool has_prio = current_memwr_visible[wrnode->str].count(i);
+                               priority_mask.push_back(State(has_prio));
+                       }
+                       wrnode->children.push_back(mkconst_bits(priority_mask, false));
+                       current_memwr_visible[wrnode->str].insert(portid);
+                       current_always->children.push_back(wrnode);
+               } else {
+                       current_ast_mod->children.push_back(wrnode);
+               }
 
                if (newNode->children.empty()) {
                        delete newNode;
index d4cc55647df3e38d55712462bc7d26fc5a0365d6..c1ecc0397b4ba1f3feeadb20c498dc117296be22 100644 (file)
@@ -503,6 +503,8 @@ signal to the temporary signal in its \lstinline[language=C++]{RTLIL::CaseRule}/
 \item Finally a \lstinline[language=C++]{RTLIL::SyncRule} is created for the \lstinline[language=C++]{RTLIL::Process} that
 assigns the temporary signals for the final values to the actual signals.
 %
+\item A process may also contain memory writes. A \lstinline[language=C++]{RTLIL::MemWriteAction} is created for each of them.
+%
 \item Calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} are generated for right hand sides as needed. When blocking
 assignments are used, \lstinline[language=C++]{AST::AstNode::genRTLIL()} is configured using global variables to use
 the temporary signals that hold the correct intermediate values whenever one of the previously assigned signals is used
@@ -821,6 +823,9 @@ the \C{RTLIL::SyncRule}s that describe the output registers.
 This pass replaces the \C{RTLIL::SyncRule}s to d-type flip-flops (with
 asynchronous resets if necessary).
 %
+\item {\tt proc\_dff} \\
+This pass replaces the \C{RTLIL::MemWriteActions}s with {\tt \$memwr} cells.
+%
 \item {\tt proc\_clean} \\
 A final call to {\tt proc\_clean} removes the now empty \C{RTLIL::Process} objects.
 \end{itemize}