ecp5: Add --out-of-context for building hard macros
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
90364fc3fa
commit
661237eb64
@ -294,6 +294,9 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
PipId pip = it->second.pip;
|
PipId pip = it->second.pip;
|
||||||
|
if (pip == PipId())
|
||||||
|
break;
|
||||||
|
|
||||||
delay += getPipDelay(pip).maxDelay();
|
delay += getPipDelay(pip).maxDelay();
|
||||||
delay += getWireDelay(cursor).maxDelay();
|
delay += getWireDelay(cursor).maxDelay();
|
||||||
cursor = getPipSrcWire(pip);
|
cursor = getPipSrcWire(pip);
|
||||||
@ -571,6 +574,16 @@ void BaseCtx::attributesToArchInfo()
|
|||||||
BelId b = getCtx()->getBelByName(id(val->second.as_string()));
|
BelId b = getCtx()->getBelByName(id(val->second.as_string()));
|
||||||
getCtx()->bindBel(b, ci, strength);
|
getCtx()->bindBel(b, ci, strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val = ci->attrs.find(id("CONSTR_PARENT"));
|
||||||
|
if (val != ci->attrs.end()) {
|
||||||
|
auto parent = cells.find(id(val->second.str));
|
||||||
|
if (parent != cells.end())
|
||||||
|
ci->constr_parent = parent->second.get();
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
val = ci->attrs.find(id("CONSTR_X"));
|
val = ci->attrs.find(id("CONSTR_X"));
|
||||||
if (val != ci->attrs.end())
|
if (val != ci->attrs.end())
|
||||||
ci->constr_x = val->second.as_int64();
|
ci->constr_x = val->second.as_int64();
|
||||||
@ -599,7 +612,8 @@ void BaseCtx::attributesToArchInfo()
|
|||||||
auto children = val->second.as_string();
|
auto children = val->second.as_string();
|
||||||
boost::split(strs, children, boost::is_any_of(";"));
|
boost::split(strs, children, boost::is_any_of(";"));
|
||||||
for (auto val : strs) {
|
for (auto val : strs) {
|
||||||
ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get());
|
if (cells.count(id(val.c_str())))
|
||||||
|
ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -524,9 +524,15 @@ bool Arch::place()
|
|||||||
} else {
|
} else {
|
||||||
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
|
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
permute_luts();
|
permute_luts();
|
||||||
|
|
||||||
|
// In out-of-context mode, create a locked macro
|
||||||
|
if (bool_or_default(settings, id("arch.ooc")))
|
||||||
|
for (auto &cell : cells)
|
||||||
|
cell.second->belStrength = STRENGTH_LOCKED;
|
||||||
|
|
||||||
getCtx()->settings[getCtx()->id("place")] = 1;
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
|
|
||||||
archInfoToAttributes();
|
archInfoToAttributes();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,10 @@ void Arch::permute_luts()
|
|||||||
inputs.emplace_back(crit, i);
|
inputs.emplace_back(crit, i);
|
||||||
}
|
}
|
||||||
// Least critical first (A input is slowest)
|
// Least critical first (A input is slowest)
|
||||||
std::sort(inputs.begin(), inputs.end());
|
|
||||||
|
// Avoid permuting locked LUTs (e.g. from an OOC submodule)
|
||||||
|
if (ci->belStrength <= STRENGTH_STRONG)
|
||||||
|
std::sort(inputs.begin(), inputs.end());
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
IdString p = port_names.at(i);
|
IdString p = port_names.at(i);
|
||||||
// log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first);
|
// log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first);
|
||||||
|
@ -789,15 +789,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
|
|
||||||
if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "CCU2") {
|
if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "CCU2") {
|
||||||
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0",
|
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0",
|
||||||
str_or_default(ci->params, ctx->id("INJECT1_0"), "YES"));
|
str_or_default(ci->params, ctx->id("CCU2_INJECT1_0"), "YES"));
|
||||||
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
|
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
|
||||||
str_or_default(ci->params, ctx->id("INJECT1_1"), "YES"));
|
str_or_default(ci->params, ctx->id("CCU2_INJECT1_1"), "YES"));
|
||||||
} else {
|
} else {
|
||||||
// Don't interfere with cascade mux wiring
|
// Don't interfere with cascade mux wiring
|
||||||
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0",
|
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", "_NONE_");
|
||||||
str_or_default(ci->params, ctx->id("INJECT1_0"), "_NONE_"));
|
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", "_NONE_");
|
||||||
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
|
|
||||||
str_or_default(ci->params, ctx->id("INJECT1_1"), "_NONE_"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "DPRAM" && slice == "SLICEA") {
|
if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "DPRAM" && slice == "SLICEA") {
|
||||||
|
@ -274,8 +274,8 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
|
|||||||
lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16));
|
lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16));
|
||||||
lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16));
|
lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16));
|
||||||
|
|
||||||
lc->params[ctx->id("INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES");
|
lc->params[ctx->id("CCU2_INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES");
|
||||||
lc->params[ctx->id("INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES");
|
lc->params[ctx->id("CCU2_INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES");
|
||||||
|
|
||||||
replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI"));
|
replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI"));
|
||||||
|
|
||||||
|
@ -431,11 +431,15 @@ class Ecp5GlobalRouter
|
|||||||
public:
|
public:
|
||||||
void promote_globals()
|
void promote_globals()
|
||||||
{
|
{
|
||||||
|
bool is_ooc = bool_or_default(ctx->settings, ctx->id("arch.ooc"));
|
||||||
log_info("Promoting globals...\n");
|
log_info("Promoting globals...\n");
|
||||||
auto clocks = get_clocks();
|
auto clocks = get_clocks();
|
||||||
for (auto clock : clocks) {
|
for (auto clock : clocks) {
|
||||||
log_info(" promoting clock net %s to global network\n", clock->name.c_str(ctx));
|
log_info(" promoting clock net %s to global network\n", clock->name.c_str(ctx));
|
||||||
insert_dcc(clock);
|
if (is_ooc) // Don't actually do anything in OOC mode, global routing will be done in the full design
|
||||||
|
clock->is_global = true;
|
||||||
|
else
|
||||||
|
insert_dcc(clock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,8 @@ po::options_description ECP5CommandHandler::getArchOptions()
|
|||||||
specific.add_options()("lpf", po::value<std::vector<std::string>>(), "LPF pin constraint file(s)");
|
specific.add_options()("lpf", po::value<std::vector<std::string>>(), "LPF pin constraint file(s)");
|
||||||
specific.add_options()("lpf-allow-unconstrained", "don't require LPF file(s) to constrain all IO");
|
specific.add_options()("lpf-allow-unconstrained", "don't require LPF file(s) to constrain all IO");
|
||||||
|
|
||||||
|
specific.add_options()("out-of-context", "disable IO buffer insertion and global promotion/routing, for building pre-routed blocks (experimental)");
|
||||||
|
|
||||||
return specific;
|
return specific;
|
||||||
}
|
}
|
||||||
void ECP5CommandHandler::validate()
|
void ECP5CommandHandler::validate()
|
||||||
@ -91,6 +93,9 @@ void ECP5CommandHandler::customBitstream(Context *ctx)
|
|||||||
basecfg = vm["basecfg"].as<std::string>();
|
basecfg = vm["basecfg"].as<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bool_or_default(ctx->settings, ctx->id("arch.ooc")) && vm.count("textcfg"))
|
||||||
|
log_error("bitstream generation is not available in out-of-context mode (use --write to create a post-PnR JSON design)\n");
|
||||||
|
|
||||||
std::string textcfg;
|
std::string textcfg;
|
||||||
if (vm.count("textcfg"))
|
if (vm.count("textcfg"))
|
||||||
textcfg = vm["textcfg"].as<std::string>();
|
textcfg = vm["textcfg"].as<std::string>();
|
||||||
@ -228,6 +233,8 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext(std::unordered_map<st
|
|||||||
ctx->settings[ctx->id(val.first)] = val.second;
|
ctx->settings[ctx->id(val.first)] = val.second;
|
||||||
ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package;
|
ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package;
|
||||||
ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed);
|
ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed);
|
||||||
|
if (vm.count("out-of-context"))
|
||||||
|
ctx->settings[ctx->id("arch.ooc")] = 1;
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +356,12 @@ class Ecp5Packer
|
|||||||
ionet = ci->ports.at(ctx->id("I")).net;
|
ionet = ci->ports.at(ctx->id("I")).net;
|
||||||
trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci);
|
trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci);
|
||||||
}
|
}
|
||||||
if (trio != nullptr) {
|
if (bool_or_default(ctx->settings, ctx->id("arch.ooc"))) {
|
||||||
|
// No IO buffer insertion in out-of-context mode, just remove the nextpnr buffer
|
||||||
|
// and leave the top level port
|
||||||
|
for (auto &port : ci->ports)
|
||||||
|
disconnect_port(ctx, ci, port.first);
|
||||||
|
} else if (trio != nullptr) {
|
||||||
// Trivial case, TRELLIS_IO used. Just destroy the net and the
|
// Trivial case, TRELLIS_IO used. Just destroy the net and the
|
||||||
// iobuf
|
// iobuf
|
||||||
log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx),
|
log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx),
|
||||||
@ -673,6 +678,7 @@ class Ecp5Packer
|
|||||||
CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional<PortRef> chain_next = boost::optional<PortRef>())
|
CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional<PortRef> chain_next = boost::optional<PortRef>())
|
||||||
{
|
{
|
||||||
std::unique_ptr<CellInfo> feedout = create_ecp5_cell(ctx, ctx->id("CCU2C"));
|
std::unique_ptr<CellInfo> feedout = create_ecp5_cell(ctx, ctx->id("CCU2C"));
|
||||||
|
|
||||||
feedout->params[ctx->id("INIT0")] = Property(0, 16);
|
feedout->params[ctx->id("INIT0")] = Property(0, 16);
|
||||||
feedout->params[ctx->id("INIT1")] = Property(10, 16); // LUT4 = 0; LUT2 = A
|
feedout->params[ctx->id("INIT1")] = Property(10, 16); // LUT4 = 0; LUT2 = A
|
||||||
feedout->params[ctx->id("INJECT1_0")] = std::string("NO");
|
feedout->params[ctx->id("INJECT1_0")] = std::string("NO");
|
||||||
|
Loading…
Reference in New Issue
Block a user