From 5d191f8297e338e5ac678bdbdfcb176a2a7a8cc3 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 9 May 2021 13:16:22 +0100 Subject: mistral: Add IO packing Signed-off-by: gatecat --- mistral/arch.cc | 18 ++++++++-- mistral/arch.h | 5 ++- mistral/io.cc | 8 +++++ mistral/pack.cc | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- mistral/qsf.cc | 2 +- 5 files changed, 135 insertions(+), 8 deletions(-) (limited to 'mistral') diff --git a/mistral/arch.cc b/mistral/arch.cc index aa149a3c..592eddff 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -73,9 +73,6 @@ Arch::Arch(ArchArgs args) case CycloneV::block_type_t::LAB: create_lab(x, y); break; - case CycloneV::block_type_t::GPIO: - create_gpio(x, y); - break; default: continue; } @@ -83,6 +80,10 @@ Arch::Arch(ArchArgs args) } } + for (auto gpio_pos : cyclonev->gpio_get_pos()) { + create_gpio(CycloneV::pos2x(gpio_pos), CycloneV::pos2y(gpio_pos)); + } + // This import takes about 5s, perhaps long term we can speed it up, e.g. defer to Mistral more... log_info("Initialising routing graph...\n"); int pip_count = 0; @@ -251,6 +252,17 @@ BelBucketId Arch::getBelBucketForCellType(IdString cell_type) const return cell_type; } +BelId Arch::bel_by_block_idx(int x, int y, IdString type, int block_index) const +{ + auto &bels = bels_by_tile.at(pos2idx(x, y)); + for (size_t i = 0; i < bels.size(); i++) { + auto &bel_data = bels.at(i); + if (bel_data.type == type && bel_data.block_index == block_index) + return BelId(CycloneV::xy2pos(x, y), i); + } + return BelId(); +} + BelId Arch::add_bel(int x, int y, IdString name, IdString type) { auto &bels = bels_by_tile.at(pos2idx(x, y)); diff --git a/mistral/arch.h b/mistral/arch.h index b818f001..790c6782 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -316,6 +316,8 @@ struct Arch : BaseArch bool isBelLocationValid(BelId bel) const override; + BelId bel_by_block_idx(int x, int y, IdString type, int block_index) const; + // ------------------------------------------------- WireId getWireByName(IdStringList name) const override; @@ -403,7 +405,8 @@ struct Arch : BaseArch // ------------------------------------------------- - bool is_io_cell(IdString cell_type) const; // io.cc + bool is_io_cell(IdString cell_type) const; // io.cc + BelId get_io_pin_bel(const CycloneV::pin_info_t *pin) const; // io.cc // ------------------------------------------------- diff --git a/mistral/io.cc b/mistral/io.cc index f2517e5d..00918317 100644 --- a/mistral/io.cc +++ b/mistral/io.cc @@ -34,6 +34,7 @@ void Arch::create_gpio(int x, int y) add_bel_pin(bel, id_I, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAIN, 0)); add_bel_pin(bel, id_OE, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::OEIN, 0)); add_bel_pin(bel, id_O, PORT_OUT, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)); + bel_data(bel).block_index = z; } } @@ -50,4 +51,11 @@ bool Arch::is_io_cell(IdString cell_type) const } } +BelId Arch::get_io_pin_bel(const CycloneV::pin_info_t *pin) const +{ + auto pad = pin->pad; + CycloneV::pos_t pos = (pad & 0x3FFF); + return bel_by_block_idx(CycloneV::pos2x(pos), CycloneV::pos2y(pos), id_MISTRAL_IO, (pad >> 14)); +} + NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/mistral/pack.cc b/mistral/pack.cc index be0128fe..2c0ec80f 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -171,22 +171,126 @@ struct MistralPacker trim_design(); } + void prepare_io() + { + // Find the actual IO buffer corresponding to a port; and copy attributes across to it + // Note that this relies on Yosys to do IO buffer inference, to avoid tristate issues once we get to synthesised + // JSON. In all cases the nextpnr-inserted IO buffers are removed as redundant. + for (auto &port : sorted_ref(ctx->ports)) { + if (!ctx->cells.count(port.first)) + log_error("Port '%s' doesn't seem to have a corresponding top level IO\n", ctx->nameOf(port.first)); + CellInfo *ci = ctx->cells.at(port.first).get(); + + PortRef top_port; + top_port.cell = nullptr; + bool is_npnr_iob = false; + + if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an input buffer (IB etc) connected to it + is_npnr_iob = true; + NetInfo *o = get_net_or_empty(ci, id_O); + if (o == nullptr) + ; + else if (o->users.size() > 1) + log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first)); + else if (o->users.size() == 1) + top_port = o->users.at(0); + } + if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an output buffer (OB etc) connected to it + is_npnr_iob = true; + NetInfo *i = get_net_or_empty(ci, id_I); + if (i != nullptr && i->driver.cell != nullptr) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); + top_port = i->driver; + } + // Edge case of a bidirectional buffer driving an output pin + if (i->users.size() > 2) { + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + } else if (i->users.size() == 2) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + for (auto &usr : i->users) { + if (usr.cell->type == ctx->id("$nextpnr_obuf") || usr.cell->type == ctx->id("$nextpnr_iobuf")) + continue; + top_port = usr; + break; + } + } + } + if (!is_npnr_iob) + log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n", + ctx->nameOf(port.first)); + + if (top_port.cell == nullptr) { + log_info("Trimming port '%s' as it is unused.\n", ctx->nameOf(port.first)); + } else { + // Copy attributes to real IO buffer + if (ctx->io_attr.count(port.first)) { + for (auto &kv : ctx->io_attr.at(port.first)) { + top_port.cell->attrs[kv.first] = kv.second; + } + } + // Make sure that top level net is set correctly + port.second.net = top_port.cell->ports.at(top_port.port).net; + } + // Now remove the nextpnr-inserted buffer + disconnect_port(ctx, ci, id_I); + disconnect_port(ctx, ci, id_O); + ctx->cells.erase(port.first); + } + } + + void pack_io() + { + // Step 0: deal with top level inserted IO buffers + prepare_io(); + // Stage 1: apply constraints + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + // Iterate through all IO buffer primitives + if (!ctx->is_io_cell(ci->type)) + continue; + // We need all IO constrained at the moment, unconstrained IO are rare enough not to care + if (!ci->attrs.count(id_LOC)) + log_error("Found unconstrained IO '%s', these are currently unsupported\n", ctx->nameOf(ci)); + // Convert package pin constraint to bel constraint + std::string loc = ci->attrs.at(id_LOC).as_string(); + if (loc.compare(0, 4, "PIN_") != 0) + log_error("Expecting PIN_-prefixed pin for IO '%s', got '%s'\n", ctx->nameOf(ci), loc.c_str()); + auto pin_info = ctx->cyclonev->pin_find_name(loc.substr(4)); + if (pin_info == nullptr) + log_error("IO '%s' is constrained to invalid pin '%s'\n", ctx->nameOf(ci), loc.c_str()); + BelId bel = ctx->get_io_pin_bel(pin_info); + + if (bel == BelId()) { + log_error("IO '%s' is constrained to pin %s which is not a supported IO pin.\n", ctx->nameOf(ci), + loc.c_str()); + } else { + log_info("Constraining IO '%s' to pin %s (bel %s)\n", ctx->nameOf(ci), loc.c_str(), + ctx->nameOfBel(bel)); + ctx->bindBel(bel, ci, STRENGTH_LOCKED); + } + } + } + void run() { init_constant_nets(); pack_constants(); + pack_io(); } }; }; // namespace bool Arch::pack() { - // TODO: - // - Constrain IO - MistralPacker packer(getCtx()); packer.run(); + assignArchInfo(); + return true; } diff --git a/mistral/qsf.cc b/mistral/qsf.cc index 3f201aa1..0c95bf2e 100644 --- a/mistral/qsf.cc +++ b/mistral/qsf.cc @@ -51,7 +51,7 @@ void set_location_assignment_cmd(Context *ctx, const option_map_t &options, cons void set_instance_assignment_cmd(Context *ctx, const option_map_t &options, const std::vector &pos_args) { - ctx->io_attr[ctx->id(options.at("to").at(0))][id_LOC] = pos_args.at(0); + ctx->io_attr[ctx->id(options.at("to").at(0))][ctx->id(options.at("name").at(0))] = pos_args.at(0); } void set_global_assignment_cmd(Context *ctx, const option_map_t &options, const std::vector &pos_args) -- cgit v1.2.3