2023-11-27 02:42:40 -06:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 N. Engelhardt <nak@yosyshq.com>
|
|
|
|
*
|
|
|
|
* 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/log.h"
|
|
|
|
#include "kernel/register.h"
|
|
|
|
#include "kernel/rtlil.h"
|
|
|
|
#include "kernel/sigtools.h"
|
|
|
|
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
PRIVATE_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct QlBramMergeWorker {
|
|
|
|
|
2023-08-14 09:20:36 -05:00
|
|
|
// can be used to record parameter values that have to match on both sides
|
|
|
|
typedef dict<RTLIL::IdString, RTLIL::Const> MergeableGroupKeyType;
|
|
|
|
|
|
|
|
RTLIL::Module *module;
|
|
|
|
dict<MergeableGroupKeyType, pool<RTLIL::Cell*>> mergeable_groups;
|
|
|
|
|
|
|
|
QlBramMergeWorker(RTLIL::Module* module) : module(module)
|
|
|
|
{
|
2024-01-02 04:26:48 -06:00
|
|
|
const RTLIL::IdString split_cell_type = ID($__QLF_TDP36K);
|
|
|
|
|
2023-08-14 09:20:36 -05:00
|
|
|
for (RTLIL::Cell* cell : module->selected_cells())
|
|
|
|
{
|
|
|
|
if(cell->type != split_cell_type) continue;
|
|
|
|
if(!cell->hasParam(ID(OPTION_SPLIT))) continue;
|
|
|
|
if(cell->getParam(ID(OPTION_SPLIT)) != RTLIL::Const(1, 32)) continue;
|
|
|
|
mergeable_groups[get_key(cell)].insert(cell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static MergeableGroupKeyType get_key(RTLIL::Cell* cell)
|
|
|
|
{
|
|
|
|
MergeableGroupKeyType key;
|
|
|
|
// For now, there are no restrictions on which cells can be merged
|
|
|
|
(void) cell;
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
const dict<RTLIL::IdString, RTLIL::IdString>& param_map(bool second)
|
|
|
|
{
|
|
|
|
static const dict<RTLIL::IdString, RTLIL::IdString> bram1_map = {
|
|
|
|
{ ID(INIT), ID(INIT1) },
|
|
|
|
{ ID(PORT_A_WIDTH), ID(PORT_A1_WIDTH) },
|
|
|
|
{ ID(PORT_B_WIDTH), ID(PORT_B1_WIDTH) },
|
|
|
|
{ ID(PORT_A_WR_BE_WIDTH), ID(PORT_A1_WR_BE_WIDTH) },
|
|
|
|
{ ID(PORT_B_WR_BE_WIDTH), ID(PORT_B1_WR_BE_WIDTH) }
|
|
|
|
};
|
|
|
|
static const dict<RTLIL::IdString, RTLIL::IdString> bram2_map = {
|
|
|
|
{ ID(INIT), ID(INIT2) },
|
|
|
|
{ ID(PORT_A_WIDTH), ID(PORT_A2_WIDTH) },
|
|
|
|
{ ID(PORT_B_WIDTH), ID(PORT_B2_WIDTH) },
|
|
|
|
{ ID(PORT_A_WR_BE_WIDTH), ID(PORT_A2_WR_BE_WIDTH) },
|
|
|
|
{ ID(PORT_B_WR_BE_WIDTH), ID(PORT_B2_WR_BE_WIDTH) }
|
|
|
|
};
|
|
|
|
|
|
|
|
if(second)
|
|
|
|
return bram2_map;
|
|
|
|
else
|
|
|
|
return bram1_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
const dict<RTLIL::IdString, RTLIL::IdString>& port_map(bool second)
|
|
|
|
{
|
|
|
|
static const dict<RTLIL::IdString, RTLIL::IdString> bram1_map = {
|
|
|
|
{ ID(PORT_A_CLK), ID(PORT_A1_CLK) },
|
|
|
|
{ ID(PORT_B_CLK), ID(PORT_B1_CLK) },
|
|
|
|
{ ID(PORT_A_CLK_EN), ID(PORT_A1_CLK_EN) },
|
|
|
|
{ ID(PORT_B_CLK_EN), ID(PORT_B1_CLK_EN) },
|
|
|
|
{ ID(PORT_A_ADDR), ID(PORT_A1_ADDR) },
|
|
|
|
{ ID(PORT_B_ADDR), ID(PORT_B1_ADDR) },
|
|
|
|
{ ID(PORT_A_WR_DATA), ID(PORT_A1_WR_DATA) },
|
|
|
|
{ ID(PORT_B_WR_DATA), ID(PORT_B1_WR_DATA) },
|
|
|
|
{ ID(PORT_A_WR_EN), ID(PORT_A1_WR_EN) },
|
|
|
|
{ ID(PORT_B_WR_EN), ID(PORT_B1_WR_EN) },
|
|
|
|
{ ID(PORT_A_WR_BE), ID(PORT_A1_WR_BE) },
|
|
|
|
{ ID(PORT_B_WR_BE), ID(PORT_B1_WR_BE) },
|
|
|
|
{ ID(PORT_A_RD_DATA), ID(PORT_A1_RD_DATA) },
|
|
|
|
{ ID(PORT_B_RD_DATA), ID(PORT_B1_RD_DATA) }
|
|
|
|
};
|
|
|
|
static const dict<RTLIL::IdString, RTLIL::IdString> bram2_map = {
|
|
|
|
{ ID(PORT_A_CLK), ID(PORT_A2_CLK) },
|
|
|
|
{ ID(PORT_B_CLK), ID(PORT_B2_CLK) },
|
|
|
|
{ ID(PORT_A_CLK_EN), ID(PORT_A2_CLK_EN) },
|
|
|
|
{ ID(PORT_B_CLK_EN), ID(PORT_B2_CLK_EN) },
|
|
|
|
{ ID(PORT_A_ADDR), ID(PORT_A2_ADDR) },
|
|
|
|
{ ID(PORT_B_ADDR), ID(PORT_B2_ADDR) },
|
|
|
|
{ ID(PORT_A_WR_DATA), ID(PORT_A2_WR_DATA) },
|
|
|
|
{ ID(PORT_B_WR_DATA), ID(PORT_B2_WR_DATA) },
|
|
|
|
{ ID(PORT_A_WR_EN), ID(PORT_A2_WR_EN) },
|
|
|
|
{ ID(PORT_B_WR_EN), ID(PORT_B2_WR_EN) },
|
|
|
|
{ ID(PORT_A_WR_BE), ID(PORT_A2_WR_BE) },
|
|
|
|
{ ID(PORT_B_WR_BE), ID(PORT_B2_WR_BE) },
|
|
|
|
{ ID(PORT_A_RD_DATA), ID(PORT_A2_RD_DATA) },
|
|
|
|
{ ID(PORT_B_RD_DATA), ID(PORT_B2_RD_DATA) }
|
|
|
|
};
|
|
|
|
|
|
|
|
if(second)
|
|
|
|
return bram2_map;
|
|
|
|
else
|
|
|
|
return bram1_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
void merge_brams(RTLIL::Cell* bram1, RTLIL::Cell* bram2)
|
|
|
|
{
|
2024-01-02 04:26:48 -06:00
|
|
|
const RTLIL::IdString merged_cell_type = ID($__QLF_TDP36K_MERGED);
|
2023-08-14 09:20:36 -05:00
|
|
|
|
|
|
|
// Create the new cell
|
|
|
|
RTLIL::Cell* merged = module->addCell(NEW_ID, merged_cell_type);
|
|
|
|
log_debug("Merging split BRAM cells %s and %s -> %s\n", log_id(bram1->name), log_id(bram2->name), log_id(merged->name));
|
|
|
|
|
|
|
|
for (auto &it : param_map(false))
|
|
|
|
{
|
|
|
|
if(bram1->hasParam(it.first))
|
|
|
|
merged->setParam(it.second, bram1->getParam(it.first));
|
|
|
|
}
|
|
|
|
for (auto &it : param_map(true))
|
|
|
|
{
|
|
|
|
if(bram2->hasParam(it.first))
|
|
|
|
merged->setParam(it.second, bram2->getParam(it.first));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &it : port_map(false))
|
|
|
|
{
|
|
|
|
if (bram1->hasPort(it.first))
|
|
|
|
merged->setPort(it.second, bram1->getPort(it.first));
|
|
|
|
else
|
|
|
|
log_error("Can't find port %s on cell %s!\n", log_id(it.first), log_id(bram1->name));
|
|
|
|
}
|
|
|
|
for (auto &it : port_map(true))
|
|
|
|
{
|
|
|
|
if (bram2->hasPort(it.first))
|
|
|
|
merged->setPort(it.second, bram2->getPort(it.first));
|
|
|
|
else
|
|
|
|
log_error("Can't find port %s on cell %s!\n", log_id(it.first), log_id(bram2->name));
|
|
|
|
}
|
|
|
|
merged->attributes = bram1->attributes;
|
|
|
|
for (auto attr: bram2->attributes)
|
|
|
|
if (!merged->has_attribute(attr.first))
|
|
|
|
merged->attributes.insert(attr);
|
|
|
|
|
|
|
|
// Remove the old cells
|
|
|
|
module->remove(bram1);
|
|
|
|
module->remove(bram2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void merge_bram_groups()
|
|
|
|
{
|
|
|
|
for (auto &it : mergeable_groups)
|
|
|
|
{
|
|
|
|
while (it.second.size() > 1)
|
|
|
|
{
|
|
|
|
merge_brams(it.second.pop(), it.second.pop());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-27 02:42:40 -06:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
struct QlBramMergePass : public Pass {
|
2023-08-14 09:20:36 -05:00
|
|
|
|
|
|
|
QlBramMergePass() : Pass("ql_bram_merge", "Infers QuickLogic k6n10f BRAM pairs that can operate independently") {}
|
|
|
|
|
|
|
|
void help() override
|
|
|
|
{
|
|
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
|
|
log("\n");
|
|
|
|
log(" ql_bram_merge [selection]\n");
|
|
|
|
log("\n");
|
|
|
|
log(" This pass identifies k6n10f 18K BRAM cells and packs pairs of them together\n");
|
|
|
|
log(" into a TDP36K cell operating in split mode\n");
|
|
|
|
log("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
|
|
|
{
|
|
|
|
log_header(design, "Executing QL_BRAM_MERGE pass.\n");
|
|
|
|
|
|
|
|
size_t argidx = 1;
|
|
|
|
extra_args(args, argidx, design);
|
|
|
|
|
|
|
|
for (RTLIL::Module* module : design->selected_modules())
|
|
|
|
{
|
|
|
|
QlBramMergeWorker worker(module);
|
|
|
|
worker.merge_bram_groups();
|
|
|
|
}
|
|
|
|
}
|
2023-11-27 02:42:40 -06:00
|
|
|
|
|
|
|
|
|
|
|
} QlBramMergePass;
|
|
|
|
|
|
|
|
PRIVATE_NAMESPACE_END
|