Merge pull request #1174 from YosysHQ/eddie/fix1173
[yosys.git] / passes / memory / memory_bram.cc
index e651a977b70bf68020de1ccd9ed88ecc80be1c76..ddc56d9b5ef338c42a32548a85dff5441ee7ac5f 100644 (file)
@@ -2,11 +2,11 @@
  *  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
@@ -32,6 +32,7 @@ struct rules_t
                SigSpec sig_addr, sig_data, sig_en;
                bool effective_clkpol;
                bool make_transp;
+               bool make_outreg;
                int mapped_port;
        };
 
@@ -85,6 +86,7 @@ struct rules_t
                                pi.clkpol = clkpol[i];
                                pi.mapped_port = -1;
                                pi.make_transp = false;
+                               pi.make_outreg = false;
                                pi.effective_clkpol = false;
                                portinfos.push_back(pi);
                        }
@@ -110,15 +112,15 @@ struct rules_t
                                if (ports[i] != other.ports[i])
                                        log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
                                if (wrmode[i] != other.wrmode[i])
-                                       variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1];
+                                       variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
                                if (enable[i] != other.enable[i])
-                                       variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1];
+                                       variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i];
                                if (transp[i] != other.transp[i])
-                                       variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1];
+                                       variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i];
                                if (clocks[i] != other.clocks[i])
-                                       variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1];
+                                       variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i];
                                if (clkpol[i] != other.clkpol[i])
-                                       variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1];
+                                       variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i];
                        }
                }
        };
@@ -126,7 +128,7 @@ struct rules_t
        struct match_t {
                IdString name;
                dict<string, int> min_limits, max_limits;
-               bool or_next_if_better, make_transp;
+               bool or_next_if_better, make_transp, make_outreg;
                char shuffle_enable;
        };
 
@@ -277,6 +279,7 @@ struct rules_t
                data.name = RTLIL::escape_id(tokens[1]);
                data.or_next_if_better = false;
                data.make_transp = false;
+               data.make_outreg = false;
                data.shuffle_enable = 0;
 
                while (next_line())
@@ -309,6 +312,12 @@ struct rules_t
                                continue;
                        }
 
+                       if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") {
+                               data.make_transp = true;
+                               data.make_outreg = true;
+                               continue;
+                       }
+
                        if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") {
                                data.or_next_if_better = true;
                                continue;
@@ -320,9 +329,7 @@ struct rules_t
 
        void parse(string filename)
        {
-               if (filename.substr(0, 2) == "+/")
-                       filename = proc_share_dirname() + filename.substr(1);
-
+               rewrite_filename(filename);
                infile.open(filename);
                linecount = 0;
 
@@ -380,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
                        if (pi.clkpol > 1)
                                clkpol_wr_ports.insert(pi.clkpol);
                }
-               clocks_max = std::max(clocks_max, pi.clocks);
-               clkpol_max = std::max(clkpol_max, pi.clkpol);
-               transp_max = std::max(transp_max, pi.transp);
+               clocks_max = max(clocks_max, pi.clocks);
+               clkpol_max = max(clkpol_max, pi.clkpol);
+               transp_max = max(transp_max, pi.transp);
        }
 
        log("    Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
@@ -422,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
        rd_clkpol.extend_u0(rd_ports);
        rd_transp.extend_u0(rd_ports);
 
+       SigSpec rd_en = cell->getPort("\\RD_EN");
        SigSpec rd_clk = cell->getPort("\\RD_CLK");
        SigSpec rd_data = cell->getPort("\\RD_DATA");
        SigSpec rd_addr = cell->getPort("\\RD_ADDR");
 
-       if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0)
+       if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
        {
                int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
                log("      Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
@@ -464,8 +472,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
                std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
                std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
                std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
+               std::vector<std::vector<State>> new_initdata;
                std::vector<int> shuffle_map;
 
+               if (cell_init)
+                       new_initdata.resize(mem_size);
+
                for (auto &it : en_order)
                {
                        auto &bits = bits_wr_en.at(it);
@@ -481,6 +493,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
                                }
                                for (int j = 0; j < rd_ports; j++)
                                        new_rd_data[j].append(old_rd_data[j][bits[i]]);
+                               if (cell_init) {
+                                       for (int j = 0; j < mem_size; j++)
+                                               new_initdata[j].push_back(initdata[j][bits[i]]);
+                               }
                                shuffle_map.push_back(bits[i]);
                        }
 
@@ -491,6 +507,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
                                }
                                for (int j = 0; j < rd_ports; j++)
                                        new_rd_data[j].append(State::Sx);
+                               if (cell_init) {
+                                       for (int j = 0; j < mem_size; j++)
+                                               new_initdata[j].push_back(State::Sx);
+                               }
                                shuffle_map.push_back(-1);
                        }
                }
@@ -514,10 +534,15 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
 
                for (int i = 0; i < rd_ports; i++)
                        rd_data.replace(i*mem_width, new_rd_data[i]);
+
+               if (cell_init) {
+                       for (int i = 0; i < mem_size; i++)
+                               initdata[i] = Const(new_initdata[i]);
+               }
        }
 
        // assign write ports
-
+       pair<SigBit, bool> wr_clkdom;
        for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
        {
                bool clken = wr_clken[cell_port_i] == State::S1;
@@ -527,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
                pair<SigBit, bool> clkdom(clksig, clkpol);
                if (!clken)
                        clkdom = pair<SigBit, bool>(State::S1, false);
-
+               wr_clkdom = clkdom;
                log("      Write port #%d is in clock domain %s%s.\n",
                                cell_port_i, clkdom.second ? "" : "!",
                                clken ? log_signal(clkdom.first) : "~async~");
@@ -598,7 +623,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
        mapped_wr_port:;
        }
 
-       // houskeeping stuff for growing more read ports and restarting read port assignments
+       // housekeeping stuff for growing more read ports and restarting read port assignments
 
        int grow_read_ports_cursor = -1;
        bool try_growing_more_read_ports = false;
@@ -615,6 +640,8 @@ grow_read_ports:;
                                pi.sig_addr = SigSpec();
                                pi.sig_data = SigSpec();
                                pi.sig_en = SigSpec();
+                               pi.make_outreg = false;
+                               pi.make_transp = false;
                        }
                        new_portinfos.push_back(pi);
                        if (pi.dupidx == dup_count-1) {
@@ -648,6 +675,9 @@ grow_read_ports:;
                bool transp = rd_transp[cell_port_i] == State::S1;
                SigBit clksig = rd_clk[cell_port_i];
 
+               if (wr_ports == 0)
+                       transp = false;
+
                pair<SigBit, bool> clkdom(clksig, clkpol);
                if (!clken)
                        clkdom = pair<SigBit, bool>(State::S1, false);
@@ -666,6 +696,10 @@ grow_read_ports:;
 
                        if (clken) {
                                if (pi.clocks == 0) {
+                                       if (match.make_outreg) {
+                                               pi.make_outreg = true;
+                                               goto skip_bram_rport_clkcheck;
+                                       }
                                        log("        Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
                                        goto skip_bram_rport;
                                }
@@ -677,12 +711,23 @@ grow_read_ports:;
                                        log("        Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
                                        goto skip_bram_rport;
                                }
+                               if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) {
+                                       log("        Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+                                       goto skip_bram_rport;
+                               }
+                       skip_bram_rport_clkcheck:
                                if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
                                        if (match.make_transp && wr_ports <= 1) {
                                                pi.make_transp = true;
-                                               enable_make_transp = true;
+                                               if (pi.clocks != 0) {
+                                                       if (wr_ports == 1 && wr_clkdom != clkdom) {                                                             
+                                                               log("        Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+                                                               goto skip_bram_rport;
+                                                       }
+                                                       enable_make_transp = true;
+                                               }
                                        } else {
-                                               log("        Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+                                               log("        Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
                                                goto skip_bram_rport;
                                        }
                                }
@@ -699,8 +744,10 @@ grow_read_ports:;
                        if (clken) {
                                clock_domains[pi.clocks] = clkdom;
                                clock_polarities[pi.clkpol] = clkdom.second;
-                               read_transp[pi.transp] = transp;
+                               if (!pi.make_transp)
+                                       read_transp[pi.transp] = transp;
                                pi.sig_clock = clkdom.first;
+                               pi.sig_en = rd_en[cell_port_i];
                                pi.effective_clkpol = clkdom.second;
                        }
 
@@ -808,7 +855,7 @@ grow_read_ports:;
                                        State padding = State::Sx;
                                        for (int j = 0; j < bram.dbits; j++)
                                                if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
-                                                       padding = initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
+                                                       initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
                                                else
                                                        initparam[i*bram.dbits+j] = padding;
                                }
@@ -841,8 +888,11 @@ grow_read_ports:;
                                if (pi.enable)
                                {
                                        SigSpec sig_en = pi.sig_en;
-                                       sig_en.extend_u0((grid_d+1) * pi.enable);
-                                       sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+
+                                       if (pi.wrmode == 1) {
+                                               sig_en.extend_u0((grid_d+1) * pi.enable);
+                                               sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+                                       }
 
                                        if (!addr_ok.empty())
                                                sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
@@ -871,9 +921,18 @@ grow_read_ports:;
                                } else {
                                        SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
                                        c->setPort(stringf("\\%sDATA", pf), bram_dout);
-
-                                       if (pi.make_transp)
-                                       {
+                                       if (pi.make_outreg && pi.make_transp) {
+                                               log("        Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+                                               SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
+                                               module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
+                                               c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
+                                       } else if (pi.make_outreg) {
+                                               SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
+                                               if (!pi.sig_en.empty())
+                                                       bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
+                                               module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
+                                               bram_dout = bram_dout_q;
+                                       } else if (pi.make_transp) {
                                                log("        Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
 
                                                SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
@@ -897,8 +956,10 @@ grow_read_ports:;
                                                }
 
                                        SigSpec addr_ok_q = addr_ok;
-                                       if (pi.clocks && !addr_ok.empty()) {
+                                       if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
                                                addr_ok_q = module->addWire(NEW_ID);
+                                               if (!pi.sig_en.empty())
+                                                       addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
                                                module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
                                        }
 
@@ -948,7 +1009,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
        log("\n");
 
        pool<pair<IdString, int>> failed_brams;
-       dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache;
+       dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
 
        for (int i = 0; i < GetSize(rules.matches); i++)
        {
@@ -1049,7 +1110,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
                                }
 
                                log("      Storing for later selection.\n");
-                               best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
+                               best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
 
                next_match_rule:
                                if (or_next_if_better || best_rule_cache.empty())
@@ -1088,7 +1149,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
 
 struct MemoryBramPass : public Pass {
        MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
-       virtual void help()
+       void help() YS_OVERRIDE
        {
                //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
                log("\n");
@@ -1108,8 +1169,8 @@ struct MemoryBramPass : public Pass {
                log("      groups 2           # number of port groups\n");
                log("      ports  1 1         # number of ports in each group\n");
                log("      wrmode 1 0         # set to '1' if this groups is write ports\n");
-               log("      enable 4 0         # number of enable bits (for write ports)\n");
-               log("      transp 0 2         # transparatent (for read ports)\n");
+               log("      enable 4 1         # number of enable bits\n");
+               log("      transp 0 2         # transparent (for read ports)\n");
                log("      clocks 1 2         # clock configuration\n");
                log("      clkpol 2 2         # clock polarity configuration\n");
                log("    endbram\n");
@@ -1127,7 +1188,7 @@ struct MemoryBramPass : public Pass {
                log("greater than 1 share the same configuration bit.\n");
                log("\n");
                log("Using the same bram name in different bram blocks will create different variants\n");
-               log("of the bram. Verilog configration parameters for the bram are created as needed.\n");
+               log("of the bram. Verilog configuration parameters for the bram are created as needed.\n");
                log("\n");
                log("It is also possible to create variants by repeating statements in the bram block\n");
                log("and appending '@<label>' to the individual statements.\n");
@@ -1160,7 +1221,7 @@ struct MemoryBramPass : public Pass {
                log("    dcells  .......  number of cells in 'data-direction'\n");
                log("    cells  ........  total number of cells (acells*dcells*dups)\n");
                log("\n");
-               log("The interface for the created bram instances is dervived from the bram\n");
+               log("The interface for the created bram instances is derived from the bram\n");
                log("description. Use 'techmap' to convert the created bram instances into\n");
                log("instances of the actual bram cells of your target architecture.\n");
                log("\n");
@@ -1171,15 +1232,18 @@ struct MemoryBramPass : public Pass {
                log("A match containing the command 'make_transp' will add external circuitry\n");
                log("to simulate 'transparent read', if necessary.\n");
                log("\n");
+               log("A match containing the command 'make_outreg' will add external flip-flops\n");
+               log("to implement synchronous read ports, if necessary.\n");
+               log("\n");
                log("A match containing the command 'shuffle_enable A' will re-organize\n");
                log("the data bits to accommodate the enable pattern of port A.\n");
                log("\n");
        }
-       virtual void execute(vector<string> args, Design *design)
+       void execute(vector<string> args, Design *design) YS_OVERRIDE
        {
                rules_t rules;
 
-               log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
+               log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
 
                size_t argidx;
                for (argidx = 1; argidx < args.size(); argidx++) {