backends/verilog: Try to preserve mem write port priorities.
authorMarcelina Kościelnicka <mwk@0x04.net>
Tue, 25 May 2021 20:37:03 +0000 (22:37 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Tue, 25 May 2021 22:19:31 +0000 (00:19 +0200)
backends/verilog/verilog_backend.cc

index cfdc0316ad99f71a7b5c424a1b7535e39a5601d2..27a032586ad2e3ddd8b1f689fdddcd8a77bec667 100644 (file)
@@ -594,48 +594,100 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
                }
        }
 
-       // write ports
-       for (auto &port : mem.wr_ports)
+       // Write ports.  Those are messy because we try to preserve priority, as much as we can:
+       //
+       // 1. We split all ports into several disjoint processes.
+       // 2. If a port has priority over another port, the two ports need to share
+       //    a process, so that priority can be reconstructed on the other end.
+       // 3. We want each process to be as small as possible, to avoid extra
+       //    priorities inferred on the other end.
+       pool<int> wr_ports_done;
+       for (int ridx = 0; ridx < GetSize(mem.wr_ports); ridx++)
        {
+               if (wr_ports_done.count(ridx))
+                       continue;
+
+               auto &root = mem.wr_ports[ridx];
+
+               // Start from a root.
+               pool<int> wr_ports_now;
+               wr_ports_now.insert(ridx);
+
+               // Transitively fill list of ports in this process by following priority edges.
+               while (true)
                {
-                       std::ostringstream os;
-                       dump_sigspec(os, port.clk);
-                       clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str());
-                       if( clk_to_lof_body.count(clk_domain_str) == 0 )
-                               clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
+                       bool changed = false;
+
+                       for (int i = 0; i < GetSize(mem.wr_ports); i++)
+                       for (int j = 0; j < i; j++)
+                       if (mem.wr_ports[i].priority_mask[j])
+                       {
+                               if (wr_ports_now.count(i) && !wr_ports_now.count(j)) {
+                                       wr_ports_now.insert(j);
+                                       changed = true;
+                               }
+                               if (!wr_ports_now.count(i) && wr_ports_now.count(j)) {
+                                       wr_ports_now.insert(i);
+                                       changed = true;
+                               }
+                       }
+
+                       if (!changed)
+                               break;
                }
-               //   make something like:
-               //   always @(posedge clk)
-               //      if (wr_en_bit) memid[w_addr][??] <= w_data[??];
-               //   ...
-               for (int i = 0; i < GetSize(port.en); i++)
-               {
-                       int start_i = i, width = 1;
-                       SigBit wen_bit = port.en[i];
 
-                       while (i+1 < GetSize(port.en) && active_sigmap(port.en[i+1]) == active_sigmap(wen_bit))
-                               i++, width++;
+               f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", root.clk_polarity ? "pos" : "neg");
+               dump_sigspec(f, root.clk);
+               f << ") begin\n";
 
-                       if (wen_bit == State::S0)
+               for (int pidx = 0; pidx < GetSize(mem.wr_ports); pidx++)
+               {
+                       if (!wr_ports_now.count(pidx))
                                continue;
+                       wr_ports_done.insert(pidx);
+
+                       auto &port = mem.wr_ports[pidx];
+                       log_assert(port.clk_enable == root.clk_enable);
+                       if (port.clk_enable) {
+                               log_assert(port.clk == root.clk);
+                               log_assert(port.clk_polarity == root.clk_polarity);
+                       }
 
-                       std::ostringstream os;
-                       if (wen_bit != State::S1)
+                       //   make something like:
+                       //   always @(posedge clk)
+                       //      if (wr_en_bit) memid[w_addr][??] <= w_data[??];
+                       //   ...
+                       for (int i = 0; i < GetSize(port.en); i++)
                        {
-                               os << stringf("if (");
-                               dump_sigspec(os, wen_bit);
-                               os << stringf(") ");
+                               int start_i = i, width = 1;
+                               SigBit wen_bit = port.en[i];
+
+                               while (i+1 < GetSize(port.en) && active_sigmap(port.en[i+1]) == active_sigmap(wen_bit))
+                                       i++, width++;
+
+                               if (wen_bit == State::S0)
+                                       continue;
+
+                               f << stringf("%s%s", indent.c_str(), indent.c_str());
+                               if (wen_bit != State::S1)
+                               {
+                                       f << stringf("if (");
+                                       dump_sigspec(f, wen_bit);
+                                       f << stringf(")\n");
+                                       f << stringf("%s%s%s", indent.c_str(), indent.c_str(), indent.c_str());
+                               }
+                               f << stringf("%s[", mem_id.c_str());
+                               dump_sigspec(f, port.addr);
+                               if (width == GetSize(port.en))
+                                       f << stringf("] <= ");
+                               else
+                                       f << stringf("][%d:%d] <= ", i, start_i);
+                               dump_sigspec(f, port.data.extract(start_i, width));
+                               f << stringf(";\n");
                        }
-                       os << stringf("%s[", mem_id.c_str());
-                       dump_sigspec(os, port.addr);
-                       if (width == GetSize(port.en))
-                               os << stringf("] <= ");
-                       else
-                               os << stringf("][%d:%d] <= ", i, start_i);
-                       dump_sigspec(os, port.data.extract(start_i, width));
-                       os << stringf(";\n");
-                       clk_to_lof_body[clk_domain_str].push_back(os.str());
                }
+
+               f << stringf("%s" "end\n", indent.c_str());
        }
        // Output Verilog that looks something like this:
        // reg [..] _3_;