diff options
-rw-r--r-- | ecp5/arch.cc | 5 | ||||
-rw-r--r-- | ecp5/bitstream.cc | 4 | ||||
-rw-r--r-- | ecp5/pack.cc | 132 |
3 files changed, 140 insertions, 1 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 8b5962d2..ae9d1af0 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -574,6 +574,11 @@ bool Arch::place() PlacerHeapCfg cfg(getCtx()); cfg.criticalityExponent = 4; cfg.ioBufTypes.insert(id_TRELLIS_IO); + + cfg.cellGroups.emplace_back(); + cfg.cellGroups.back().insert(id_MULT18X18D); + cfg.cellGroups.back().insert(id_ALU54B); + if (!placer_heap(getCtx(), cfg)) return false; } else if (placer == "sa") { diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index af7c63f8..7db0e020 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -1171,7 +1171,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC")); tg.config.add_enum(dsp + ".MODE", "MULT18X18D"); - if (str_or_default(ci->params, ctx->id("REG_OUTPUT_CLK"), "NONE") == "NONE") + if (str_or_default(ci->params, ctx->id("REG_OUTPUT_CLK"), "NONE") == "NONE" && ci->constr_parent == nullptr) tg.config.add_enum(dsp + ".CIBOUT_BYP", "ON"); if (loc.z < 4) @@ -1209,6 +1209,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex str_or_default(ci->params, ctx->id("REG_OPCODEOP1_0_CLK"), "NONE")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_CLK", str_or_default(ci->params, ctx->id("REG_OPCODEOP0_1_CLK"), "NONE")); + tg.config.add_enum(dsp + ".REG_OPCODEOP1_1_CLK", + str_or_default(ci->params, ctx->id("REG_OPCODEOP1_1_CLK"), "NONE")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_CE", str_or_default(ci->params, ctx->id("REG_OPCODEOP0_1_CE"), "CE0")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_RST", diff --git a/ecp5/pack.cc b/ecp5/pack.cc index aa7fdd22..0de35be7 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1573,8 +1573,140 @@ class Ecp5Packer autocreate_empty_port(ci, ctx->id(port + std::to_string(i))); for (int i = 0; i < 11; i++) autocreate_empty_port(ci, ctx->id("OP" + std::to_string(i))); + + // Find the MULT18X18Ds feeding this ALU54B's MA and MB inputs. + CellInfo* mult_a = nullptr; + CellInfo* mult_b = nullptr; + for (auto port : {id_MA0, id_MB0}) { + CellInfo *mult = net_driven_by( + ctx, ci->ports.at(port).net, + [](const Context *ctx, const CellInfo *cell) { + return cell->type == id_MULT18X18D; + }, id_P0 + ); + + // We'll handle the mult not existing in check_alu below. + if(mult == nullptr) + break; + + // Set relative constraint depending on ALU port. + if(port == id_MA0) { + mult->constr_x = mult->constr_z = -3; + mult_a = mult; + } else if(port == id_MB0) { + mult->constr_x = mult->constr_z = -2; + mult_b = mult; + } + mult->constr_y = 0; + mult->constr_parent = ci; + ci->constr_children.push_back(mult); + log_info("DSP: Constraining MULT18X18D '%s' to ALU54B '%s' port %s\n", + mult->name.c_str(ctx), cell.first.c_str(ctx), ctx->nameOf(port)); + } + + // Check existance of, and connectivity to, each MULT. + check_alu(ci, mult_a, mult_b); + } + } + } + + // Check ALU54B is correctly connected to two MULT18X18Ds. + void check_alu(CellInfo* alu, CellInfo* mult_a, CellInfo* mult_b) + { + // MULT18X18Ds must be detected on both inputs. + if (mult_a == nullptr) { + log_error("No MULT18X18D found connected to ALU54B '%s' port A\n", + alu->name.c_str(ctx)); + } else if (mult_b == nullptr) { + log_error("No MULT18X18D found connected to ALU54B '%s' port B\n", + alu->name.c_str(ctx)); + } + + // Placement doesn't work if only one or the other of + // the ALU and MULTs have a BEL specified. + auto alu_has_bel = alu->attrs.count(ctx->id("BEL")); + for (auto mult : {mult_a, mult_b}) { + auto mult_has_bel = mult->attrs.count(ctx->id("BEL")); + if(alu_has_bel && !mult_has_bel) { + log_error("ALU54B '%s' has a fixed BEL specified, but connected " + "MULT18X18D '%s' does not, specify both or neither.\n", + alu->name.c_str(ctx), mult->name.c_str(ctx)); + } else if(!alu_has_bel && mult_has_bel) { + log_error("ALU54B '%s' does not have a fixed BEL specified, but " + "connected MULT18X18D '%s' does, specify both or neither.\n", + alu->name.c_str(ctx), mult->name.c_str(ctx)); + } + } + + // Cannot have MULT OUTPUT_CLK set when connected to an ALU unless + // MULT_BYPASS is also enabled. + for ( auto mult : {mult_a, mult_b}) { + if (str_or_default(mult->params, ctx->id("REG_OUTPUT_CLK"), "NONE") != "NONE" && + str_or_default(mult->params, ctx->id("MULT_BYPASS"), "DISABLED") != "ENABLED") + { + log_error("MULT18X18D '%s' REG_OUTPUT_CLK must be NONE when driving ALU without MULT_BYPASS\n", + mult->name.c_str(ctx)); } } + + // SIGNEDIA and SIGNEDIB inputs must be connected to SIGNEDP output. + NetInfo* net = alu->ports.at(id_SIGNEDIA).net; + if (net == nullptr || net->driver.cell != mult_a || net->driver.port != id_SIGNEDP) { + log_error("ALU54B '%s' input SIGNEDIA must be driven by SIGNEDP of" + " MULT18X18D '%s'\n", + alu->name.c_str(ctx), mult_a->name.c_str(ctx)); + } + net = alu->ports.at(id_SIGNEDIB).net; + if (net == nullptr || net->driver.cell != mult_b || net->driver.port != id_SIGNEDP) { + log_error("ALU54B '%s' input SIGNEDIB must be driven by SIGNEDP of" + " MULT18X18D '%s'\n", + alu->name.c_str(ctx), mult_b->name.c_str(ctx)); + } + + // All A and B inputs must be connected to ROA/ROB outputs, + // and all MA and MB inputs must be connected to P outputs. + for (int i = 0; i < 36; i++) { + IdString mult_port; + if (i < 18) + mult_port = ctx->id(std::string("ROA") + std::to_string(i)); + else + mult_port = ctx->id(std::string("ROB") + std::to_string(i - 18)); + + IdString alu_port = ctx->id(std::string("A") + std::to_string(i)); + net = alu->ports.at(alu_port).net; + if(net == nullptr || net->driver.cell != mult_a || net->driver.port != mult_port) { + log_error("ALU54B '%s' input %s must be driven by %s of MULT18X18D '%s'\n", + alu->name.c_str(ctx), alu_port.c_str(ctx), mult_port.c_str(ctx), + mult_a->name.c_str(ctx)); + } + + alu_port = ctx->id(std::string("B") + std::to_string(i)); + net = alu->ports.at(alu_port).net; + if(net == nullptr || net->driver.cell != mult_b || net->driver.port != mult_port) { + log_error("ALU54B '%s' input %s must be driven by %s of MULT18X18D '%s'\n", + alu->name.c_str(ctx), alu_port.c_str(ctx), mult_port.c_str(ctx), + mult_b->name.c_str(ctx)); + } + + mult_port = ctx->id(std::string("P") + std::to_string(i)); + + alu_port = ctx->id(std::string("MA") + std::to_string(i)); + net = alu->ports.at(alu_port).net; + if(net == nullptr || net->driver.cell != mult_a || net->driver.port != mult_port) { + log_error("ALU54B '%s' input %s must be driven by %s of MULT18X18D '%s'\n", + alu->name.c_str(ctx), alu_port.c_str(ctx), mult_port.c_str(ctx), + mult_a->name.c_str(ctx)); + } + + alu_port = ctx->id(std::string("MB") + std::to_string(i)); + net = alu->ports.at(alu_port).net; + if(net == nullptr || net->driver.cell != mult_b || net->driver.port != mult_port) { + log_error("ALU54B '%s' input %s must be driven by %s of MULT18X18D '%s'\n", + alu->name.c_str(ctx), alu_port.c_str(ctx), mult_port.c_str(ctx), + mult_b->name.c_str(ctx)); + } + } + } // "Pack" DCUs |