From 6edc11de4de20aa5194f5cf3705a320b1891a2d6 Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Mon, 5 Jul 2021 13:34:34 +0200 Subject: [PATCH 1/8] interchange: tests: add obuftds test Signed-off-by: Alessandro Comodi --- .../examples/tests/CMakeLists.txt | 1 + .../examples/tests/obuftds/CMakeLists.txt | 7 ++++ .../examples/tests/obuftds/basys3.xdc | 9 +++++ .../examples/tests/obuftds/obuftds.v | 37 +++++++++++++++++++ .../examples/tests/obuftds/run.tcl | 14 +++++++ fpga_interchange/macros.cc | 12 ++++++ 6 files changed, 80 insertions(+) create mode 100644 fpga_interchange/examples/tests/obuftds/CMakeLists.txt create mode 100644 fpga_interchange/examples/tests/obuftds/basys3.xdc create mode 100644 fpga_interchange/examples/tests/obuftds/obuftds.v create mode 100644 fpga_interchange/examples/tests/obuftds/run.tcl diff --git a/fpga_interchange/examples/tests/CMakeLists.txt b/fpga_interchange/examples/tests/CMakeLists.txt index 1d3dd72f..f8a52a41 100644 --- a/fpga_interchange/examples/tests/CMakeLists.txt +++ b/fpga_interchange/examples/tests/CMakeLists.txt @@ -6,4 +6,5 @@ add_subdirectory(ff) add_subdirectory(lut) add_subdirectory(lut_nexus) add_subdirectory(lutram) +add_subdirectory(obuftds) add_subdirectory(ram_nexus) diff --git a/fpga_interchange/examples/tests/obuftds/CMakeLists.txt b/fpga_interchange/examples/tests/obuftds/CMakeLists.txt new file mode 100644 index 00000000..0313c9bb --- /dev/null +++ b/fpga_interchange/examples/tests/obuftds/CMakeLists.txt @@ -0,0 +1,7 @@ +add_interchange_group_test( + name obuftds + family ${family} + board_list basys3 + tcl run.tcl + sources obuftds.v +) diff --git a/fpga_interchange/examples/tests/obuftds/basys3.xdc b/fpga_interchange/examples/tests/obuftds/basys3.xdc new file mode 100644 index 00000000..4b777233 --- /dev/null +++ b/fpga_interchange/examples/tests/obuftds/basys3.xdc @@ -0,0 +1,9 @@ +set_property PACKAGE_PIN V2 [get_ports sw[8] ] +set_property PACKAGE_PIN T3 [get_ports sw[9] ] +set_property PACKAGE_PIN T2 [get_ports sw[10]] +set_property PACKAGE_PIN R3 [get_ports sw[11]] + +set_property PACKAGE_PIN U19 [get_ports diff_p[0]] +set_property PACKAGE_PIN V19 [get_ports diff_n[0]] +set_property PACKAGE_PIN V13 [get_ports diff_p[1]] +set_property PACKAGE_PIN V14 [get_ports diff_n[1]] diff --git a/fpga_interchange/examples/tests/obuftds/obuftds.v b/fpga_interchange/examples/tests/obuftds/obuftds.v new file mode 100644 index 00000000..d4e9a603 --- /dev/null +++ b/fpga_interchange/examples/tests/obuftds/obuftds.v @@ -0,0 +1,37 @@ +module top( + input wire [11:8] sw, + + output wire [1:0] diff_p, + output wire [1:0] diff_n +); + +wire [1:0] buf_i; +wire [1:0] buf_t; + +OBUFTDS # ( + .IOSTANDARD("DIFF_SSTL135"), + .SLEW("FAST") +) obuftds_0 ( + .I(buf_i[0]), + .T(buf_t[0]), + .O(diff_p[0]), + .OB(diff_n[0]) +); + +OBUFTDS # ( + .IOSTANDARD("DIFF_SSTL135"), + .SLEW("FAST") +) obuftds_1 ( + .I(buf_i[1]), + .T(buf_t[1]), + .O(diff_p[1]), + .OB(diff_n[1]) +); + +assign buf_i[0] = sw[ 8]; +assign buf_t[0] = sw[ 9]; +assign buf_i[1] = sw[10]; +assign buf_t[1] = sw[11]; + +endmodule + diff --git a/fpga_interchange/examples/tests/obuftds/run.tcl b/fpga_interchange/examples/tests/obuftds/run.tcl new file mode 100644 index 00000000..b8d0df72 --- /dev/null +++ b/fpga_interchange/examples/tests/obuftds/run.tcl @@ -0,0 +1,14 @@ +yosys -import + +read_verilog $::env(SOURCES) + +synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean + +setundef -zero -params + +write_json $::env(OUT_JSON) diff --git a/fpga_interchange/macros.cc b/fpga_interchange/macros.cc index 42c8e1ba..762615c1 100644 --- a/fpga_interchange/macros.cc +++ b/fpga_interchange/macros.cc @@ -58,14 +58,24 @@ void Arch::expand_macros() std::vector next_cells; + bool first_iter = false; do { // Expand cells for (auto cell : cells) { // TODO: consult exception map const MacroExpansionPOD *exp = lookup_macro_rules(chip_info, cell->type); + + // Block infinite expansion loop due to a macro being expanded in the same primitive. + // E.g.: OBUFTDS expands into the following cells, with an infinite loop being generated: + // - 2 OBUFTDS + // - 1 INV + if (exp && first_iter) + continue; + const MacroPOD *macro = lookup_macro(chip_info, exp ? IdString(exp->macro_name) : cell->type); if (macro == nullptr) continue; + // Get the ultimate root of this macro expansion IdString parent = (cell->macro_parent == IdString()) ? cell->name : cell->macro_parent; // Create child instances @@ -158,6 +168,8 @@ void Arch::expand_macros() // The next iteration only needs to look at cells created in this iteration std::swap(next_cells, cells); next_cells.clear(); + + first_iter = true; } while (!cells.empty()); // Do this at the end, otherwise we might add cells that are later destroyed for (auto &cell : ctx->cells) From f64d06fa0287cede913942380ddf84c380b2e79b Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 6 Jul 2021 10:13:50 +0100 Subject: [PATCH 2/8] interchange: Improve search for PAD-attached bels Signed-off-by: gatecat --- fpga_interchange/arch.h | 3 +- fpga_interchange/arch_pack_io.cc | 70 ++++++++++++++------------------ 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 6e77054f..b71b1d03 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -708,7 +708,8 @@ struct Arch : ArchAPI // ------------------------------------------------- - void place_iobufs(WireId pad_wire, NetInfo *net, const pool &tightly_attached_bels, + void place_iobufs(WireId pad_wire, NetInfo *net, + const dict &tightly_attached_bels, pool *placed_cells); void pack_ports(); diff --git a/fpga_interchange/arch_pack_io.cc b/fpga_interchange/arch_pack_io.cc index 7b8e9f80..19d8cece 100644 --- a/fpga_interchange/arch_pack_io.cc +++ b/fpga_interchange/arch_pack_io.cc @@ -27,7 +27,7 @@ NEXTPNR_NAMESPACE_BEGIN namespace { -bool search_routing_for_placement(Arch *arch, WireId start_wire, CellInfo *cell, IdString cell_pin) +bool search_routing_for_placement(Arch *arch, WireId start_wire, CellInfo *cell, IdString cell_pin, bool downhill) { std::queue visit_queue; pool already_visited; @@ -51,54 +51,44 @@ bool search_routing_for_placement(Arch *arch, WireId start_wire, CellInfo *cell, // Bel pin doesn't match arch->unbindBel(bp.bel); } - for (auto pip : arch->getPipsDownhill(next)) { - WireId dst = arch->getPipDstWire(pip); + auto do_visit = [&](PipId pip) { + WireId dst = downhill ? arch->getPipDstWire(pip) : arch->getPipSrcWire(pip); if (already_visited.count(dst)) - continue; + return; if (!arch->is_site_wire(dst) && arch->get_wire_category(dst) == WIRE_CAT_GENERAL) - continue; // this pass only considers dedicated routing + return; // this pass only considers dedicated routing visit_queue.push(dst); already_visited.insert(dst); + }; + if (downhill) { + for (auto pip : arch->getPipsDownhill(next)) + do_visit(pip); + } else { + for (auto pip : arch->getPipsUphill(next)) + do_visit(pip); } } return false; } } // namespace -void Arch::place_iobufs(WireId pad_wire, NetInfo *net, const pool &tightly_attached_bels, +void Arch::place_iobufs(WireId pad_wire, NetInfo *net, + const dict &tightly_attached_bels, pool *placed_cells) { - for (BelPin bel_pin : getWireBelPins(pad_wire)) { - BelId bel = bel_pin.bel; - for (CellInfo *cell : tightly_attached_bels) { - if (isValidBelForCellType(cell->type, bel)) { - NPNR_ASSERT(cell->bel == BelId()); - NPNR_ASSERT(placed_cells->count(cell) == 0); - - bindBel(bel, cell, STRENGTH_FIXED); - placed_cells->emplace(cell); - - IdString cell_port; - for (auto pin_pair : cell->cell_bel_pins) { - for (IdString a_bel_pin : pin_pair.second) { - if (a_bel_pin == bel_pin.pin) { - NPNR_ASSERT(cell_port == IdString()); - cell_port = pin_pair.first; - } - } - } - NPNR_ASSERT(cell_port != IdString()); - - const PortInfo &port = cell->ports.at(cell_port); - NPNR_ASSERT(port.net == net); - } + Context *ctx = getCtx(); + for (auto cell_port : tightly_attached_bels) { + bool downhill = (cell_port.first->ports.at(cell_port.second).type != PORT_OUT); + if (search_routing_for_placement(this, pad_wire, cell_port.first, cell_port.second, downhill)) { + if (ctx->verbose) + log_info("Placed IO cell %s:%s at %s.\n", ctx->nameOf(cell_port.first), + ctx->nameOf(cell_port.first->type), ctx->nameOfBel(cell_port.first->bel)); } } // Also try, on a best-effort basis, to preplace other cells in the macro based on downstream routing. This is // needed for the split INBUF+IBUFCTRL arrangement in the UltraScale+, as just placing the INBUF will result in an // unrouteable site and illegal placement. - Context *ctx = getCtx(); std::queue place_queue; for (auto pc : *placed_cells) place_queue.push(pc); @@ -119,7 +109,7 @@ void Arch::place_iobufs(WireId pad_wire, NetInfo *net, const poolbel != BelId() || usr.cell->macro_parent != cursor->macro_parent) continue; // Try and place using dedicated routing - if (search_routing_for_placement(this, src_wire, usr.cell, usr.port)) { + if (search_routing_for_placement(this, src_wire, usr.cell, usr.port, true)) { // Successful placed_cells->insert(usr.cell); place_queue.push(usr.cell); @@ -200,34 +190,34 @@ void Arch::pack_ports() for (auto port_pair : port_cells) { IdString port_name = port_pair.first; CellInfo *port_cell = port_pair.second; - pool tightly_attached_bels; + dict tightly_attached_bels; for (auto port_pair : port_cell->ports) { const PortInfo &port_info = port_pair.second; const NetInfo *net = port_info.net; if (net->driver.cell) { - tightly_attached_bels.emplace(net->driver.cell); + tightly_attached_bels.emplace(net->driver.cell, net->driver.port); } for (const PortRef &port_ref : net->users) { if (port_ref.cell) { - tightly_attached_bels.emplace(port_ref.cell); + tightly_attached_bels.emplace(port_ref.cell, port_ref.port); } } } if (getCtx()->verbose) { log_info("Tightly attached BELs for port %s\n", port_name.c_str(getCtx())); - for (CellInfo *cell : tightly_attached_bels) { - log_info(" - %s : %s\n", cell->name.c_str(getCtx()), cell->type.c_str(getCtx())); + for (auto cell_port : tightly_attached_bels) { + log_info(" - %s : %s\n", cell_port.first->name.c_str(getCtx()), cell_port.first->type.c_str(getCtx())); } } NPNR_ASSERT(tightly_attached_bels.erase(port_cell) == 1); pool cell_types_in_io_group; - for (CellInfo *cell : tightly_attached_bels) { - NPNR_ASSERT(port_cells.find(cell->name) == port_cells.end()); - cell_types_in_io_group.emplace(cell->type); + for (auto cell_port : tightly_attached_bels) { + NPNR_ASSERT(port_cells.find(cell_port.first->name) == port_cells.end()); + cell_types_in_io_group.emplace(cell_port.first->type); } // Get possible placement locations for tightly coupled BELs with From 6fe071ad1d47c363f665995ae774edcd547e022d Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 6 Jul 2021 10:21:31 +0100 Subject: [PATCH 3/8] router2: Dump pre-bound routes when routing fails in debug mode Signed-off-by: gatecat --- common/router2.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/router2.cc b/common/router2.cc index a8eea5f9..a1fd8bef 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -856,10 +856,20 @@ struct Router2 int(a.first), int(a.second), ctx->nameOf(net)); auto res2 = route_arc(t, net, a.first, a.second, is_mt, false); // If this also fails, no choice but to give up - if (res2 != ARC_SUCCESS) + if (res2 != ARC_SUCCESS) { + if (ctx->debug) { + log_info("Pre-bound routing: \n"); + for (auto &wire_pair : net->wires) { + log(" %s", ctx->nameOfWire(wire_pair.first)); + if (wire_pair.second.pip != PipId()) + log(" %s", ctx->nameOfPip(wire_pair.second.pip)); + log("\n"); + } + } log_error("Failed to route arc %d.%d of net '%s', from %s to %s.\n", int(a.first), int(a.second), ctx->nameOf(net), ctx->nameOfWire(ctx->getNetinfoSourceWire(net)), ctx->nameOfWire(ctx->getNetinfoSinkWire(net, net->users.at(a.first), a.second))); + } } } } From 31abefc8e49edce55fb42c99ac99b81e948d9004 Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 6 Jul 2021 10:38:08 +0100 Subject: [PATCH 4/8] interchange: Allow pseudo pip wires to overlap with bound site wires on the same net Signed-off-by: gatecat --- common/router2.cc | 16 ++++++++-------- fpga_interchange/arch.cc | 11 +++-------- fpga_interchange/arch.h | 3 ++- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/common/router2.cc b/common/router2.cc index a1fd8bef..7bffc089 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -978,17 +978,17 @@ struct Router2 log_error("Internal error; incomplete route tree for arc %d of net %s.\n", usr_idx, ctx->nameOf(net)); } auto &p = wd.bound_nets.at(net->udata).second; - if (!ctx->checkPipAvail(p)) { + if (ctx->checkPipAvailForNet(p, net)) { NetInfo *bound_net = ctx->getBoundPipNet(p); - if (bound_net != net) { - if (ctx->verbose) { - log_info("Failed to bind pip %s to net %s\n", ctx->nameOfPip(p), net->name.c_str(ctx)); - } - success = false; - break; + if (bound_net == nullptr) { + to_bind.push_back(p); } } else { - to_bind.push_back(p); + if (ctx->verbose) { + log_info("Failed to bind pip %s to net %s\n", ctx->nameOfPip(p), net->name.c_str(ctx)); + } + success = false; + break; } cursor = ctx->getPipSrcWire(p); } diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index be40ddfd..901725d4 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -1518,11 +1518,6 @@ void Arch::remove_pip_pseudo_wires(PipId pip, NetInfo *net) // This wire is part of net->wires, make sure it has no pip, // but leave it alone. It will get cleaned up via // unbindWire. - if (wire_iter->second.pip != PipId() && wire_iter->second.pip != pip) { - log_error("Wire %s report source'd from pip %s, which is not %s\n", nameOfWire(wire), - nameOfPip(wire_iter->second.pip), nameOfPip(pip)); - } - NPNR_ASSERT(wire_iter->second.pip == PipId() || wire_iter->second.pip == pip); } else { // This wire is not in net->wires, update wire_to_net. #ifdef DEBUG_BINDING @@ -1756,12 +1751,12 @@ bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const NPNR_ASSERT(src != wire); NPNR_ASSERT(dst != wire); - NetInfo *net = getConflictingWireNet(wire); - if (net != nullptr) { + NetInfo *other_net = getConflictingWireNet(wire); + if (other_net != nullptr && other_net != net) { #ifdef DEBUG_BINDING if (getCtx()->verbose) { log_info("Pip %s is not available because wire %s is tied to net %s\n", getCtx()->nameOfPip(pip), - getCtx()->nameOfWire(wire), net->name.c_str(getCtx())); + getCtx()->nameOfWire(wire), other_net->name.c_str(getCtx())); } #endif return false; diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index b71b1d03..896a603a 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -576,7 +576,8 @@ struct Arch : ArchAPI const PipInfoPOD &pip_data = pip_info(chip_info, pip); for (int32_t wire_index : pip_data.pseudo_cell_wires) { wire.index = wire_index; - assign_net_to_wire(wire, net, "pseudo", /*require_empty=*/true); + if (getBoundWireNet(wire) != net) + assign_net_to_wire(wire, net, "pseudo", /*require_empty=*/true); } if (pip_data.pseudo_cell_wires.size() > 0) { From 96263058c3682debdbb8d6605373af8dd954fcd0 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Fri, 18 Jun 2021 09:20:24 +0200 Subject: [PATCH 5/8] add support for GW1NS-2 family Signed-off-by: Gwenhael Goavec-Merou --- gowin/CMakeLists.txt | 2 +- gowin/main.cc | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/gowin/CMakeLists.txt b/gowin/CMakeLists.txt index 695ef884..5d70cd32 100644 --- a/gowin/CMakeLists.txt +++ b/gowin/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) project(chipdb-gowin NONE) -set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9) +set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9 GW1NS-2) set(GOWIN_DEVICES ${ALL_GOWIN_DEVICES} CACHE STRING "Include support for these Gowin devices (available: ${ALL_GOWIN_DEVICES})") message(STATUS "Enabled Gowin devices: ${GOWIN_DEVICES}") diff --git a/gowin/main.cc b/gowin/main.cc index 95a7e2c1..01fcf25b 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -54,7 +54,7 @@ po::options_description GowinCommandHandler::getArchOptions() std::unique_ptr GowinCommandHandler::createContext(dict &values) { - std::regex devicere = std::regex("GW1N([A-Z]*)-(LV|UV)([0-9])([A-Z]{2}[0-9]+)(C[0-9]/I[0-9])"); + std::regex devicere = std::regex("GW1N([A-Z]*)-(LV|UV|UX)([0-9])(C?)([A-Z]{2}[0-9]+)(C[0-9]/I[0-9])"); std::smatch match; std::string device = vm["device"].as(); if (!std::regex_match(device, match, devicere)) { @@ -62,12 +62,13 @@ std::unique_ptr GowinCommandHandler::createContext(dict(new Context(chipArgs)); } From 3d0facf1192ca38e5326ff088c585dfc86b63dc2 Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 6 Jul 2021 11:34:14 +0100 Subject: [PATCH 6/8] design_utils: Fix memory error Signed-off-by: gatecat --- common/design_utils.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index a892feaa..da5decf9 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -161,7 +161,8 @@ void rename_net(Context *ctx, NetInfo *net, IdString new_name) if (net == nullptr) return; NPNR_ASSERT(!ctx->nets.count(new_name)); - std::swap(ctx->nets[net->name], ctx->nets[new_name]); + ctx->nets[new_name]; + std::swap(ctx->nets.at(net->name), ctx->nets.at(new_name)); ctx->nets.erase(net->name); net->name = new_name; } From 81c549549d88640ed77fc2b1f3da52a10b4f93ce Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 6 Jul 2021 11:45:27 +0100 Subject: [PATCH 7/8] ecp5: Add DCSC support Signed-off-by: gatecat --- ecp5/arch.cc | 12 ++++++++++++ ecp5/bitstream.cc | 5 +++++ ecp5/constids.inc | 4 ++++ ecp5/globals.cc | 45 ++++++++++++++++++++++++++++++++++----------- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 2c04105c..34bdfa1b 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -793,6 +793,12 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return true; } return false; + } else if (cell->type == id_DCSC) { + if ((fromPort == id_CLK0 || fromPort == id_CLK1) && toPort == id_DCSOUT) { + delay = DelayQuad(0); + return true; + } + return false; } else if (cell->type == id_DP16KD) { return false; } else if (cell->type == id_MULT18X18D) { @@ -866,6 +872,12 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in if (port == id_CLKO) return TMG_COMB_OUTPUT; return TMG_IGNORE; + } else if (cell->type == id_DCSC) { + if (port == id_CLK0 || port == id_CLK1) + return TMG_COMB_INPUT; + if (port == id_DCSOUT) + return TMG_COMB_OUTPUT; + return TMG_IGNORE; } else if (cell->type == id_DP16KD) { if (port == id_CLKA || port == id_CLKB) return TMG_CLOCK_INPUT; diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index c92de083..a544f2b7 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -1019,6 +1019,11 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum(std::string("DCC_") + belname[0] + belname.substr(4) + ".MODE", "DCCA"); cc.tilegroups.push_back(tg); } + } else if (ci->type == ctx->id("DCSC")) { + std::set dcs_tiles{"EBR_CMUX_LL", "EBR_CMUX_UL", "EBR_CMUX_LL_25K", "DSP_CMUX_UL"}; + std::string tile = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, dcs_tiles); + std::string dcs = ctx->loc_info(bel)->bel_data[bel.index].name.get(); + cc.tiles[tile].add_enum(dcs + ".DCSMODE", str_or_default(ci->attrs, ctx->id("DCSMODE"), "POS")); } else if (ci->type == ctx->id("DP16KD")) { TileGroup tg; Loc loc = ctx->getBelLocation(ci->bel); diff --git a/ecp5/constids.inc b/ecp5/constids.inc index e5ec1c3e..335f822a 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -1337,3 +1337,7 @@ X(IOLOGIC_MODE_ODDRX1F) X(IOLOGIC_MODE_ODDRX2F) X(IOLOGIC_MODE_OREG) X(IOLOGIC_MODE_TSREG) + +X(DCSC) +X(DCSOUT) +X(MODESEL) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 580d470a..8ee49c02 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -278,7 +278,7 @@ class Ecp5GlobalRouter bool route_onto_global(NetInfo *net, int network) { WireId glb_src; - NPNR_ASSERT(net->driver.cell->type == id_DCCA); + NPNR_ASSERT(net->driver.cell->type == id_DCCA || net->driver.cell->type == id_DCSC); glb_src = ctx->getNetinfoSourceWire(net); for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) { WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network); @@ -293,7 +293,7 @@ class Ecp5GlobalRouter // Get DCC wirelength based on source wirelen_t get_dcc_wirelen(CellInfo *dcc, bool &dedicated_routing) { - NetInfo *clki = dcc->ports.at(id_CLKI).net; + NetInfo *clki = dcc->ports.at((dcc->type == id_DCSC) ? id_CLK0 : id_CLKI).net; BelId drv_bel; const PortRef &drv = clki->driver; dedicated_routing = false; @@ -395,7 +395,7 @@ class Ecp5GlobalRouter } // Attempt to place a DCC - void place_dcc(CellInfo *dcc) + void place_dcc_dcs(CellInfo *dcc) { BelId best_bel; WireId best_bel_pclkcib; @@ -403,7 +403,7 @@ class Ecp5GlobalRouter wirelen_t best_wirelen = 9999999; bool dedicated_routing = false; for (auto bel : ctx->getBels()) { - if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) { + if (ctx->getBelType(bel) == dcc->type && ctx->checkBelAvail(bel)) { std::string belname = ctx->loc_info(bel)->bel_data[bel.index].name.get(); if (belname.at(0) == 'D' && using_ce) continue; // don't allow DCCs with CE at center @@ -414,7 +414,7 @@ class Ecp5GlobalRouter } wirelen_t wirelen = get_dcc_wirelen(dcc, dedicated_routing); if (wirelen < best_wirelen) { - if (dedicated_routing) { + if (dedicated_routing || dcc->type == id_DCSC) { best_bel_pclkcib = WireId(); } else { bool found_pclkcib = false; @@ -446,11 +446,11 @@ class Ecp5GlobalRouter } // Insert a DCC into a net to promote it to a global - NetInfo *insert_dcc(NetInfo *net) + NetInfo *insert_dcc(NetInfo *net, CellInfo *dcs_cell = nullptr) { NetInfo *glbptr = nullptr; CellInfo *dccptr = nullptr; - if (net->driver.cell != nullptr && net->driver.cell->type == id_DCCA) { + if (net->driver.cell != nullptr && (net->driver.cell->type == id_DCCA || net->driver.cell->type == id_DCSC)) { // Already have a DCC (such as clock gating) glbptr = net; dccptr = net->driver.cell; @@ -463,7 +463,10 @@ class Ecp5GlobalRouter dcc->ports[id_CLKO].net = glbnet.get(); std::vector keep_users; for (auto user : net->users) { - if (user.port == id_CLKFB) { + if (dcs_cell != nullptr && user.cell != dcs_cell) { + // DCS DCC insertion mode + keep_users.push_back(user); + } else if (user.port == id_CLKFB) { keep_users.push_back(user); } else if (net->driver.cell->type == id_EXTREFB && user.cell->type == id_DCUA) { keep_users.push_back(user); @@ -494,7 +497,7 @@ class Ecp5GlobalRouter } glbptr->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1; if (str_or_default(dccptr->attrs, ctx->id("BEL"), "") == "") - place_dcc(dccptr); + place_dcc_dcs(dccptr); return glbptr; } @@ -524,6 +527,20 @@ class Ecp5GlobalRouter else insert_dcc(clock); } + // Insert DCCs on DCS inputs, too + std::vector dcsc_cells; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type == id_DCSC) + dcsc_cells.push_back(ci); + } + for (auto ci : dcsc_cells) { + for (auto port : {id_CLK0, id_CLK1}) { + NetInfo *net = get_net_or_empty(ci, port); + if (net != nullptr) + insert_dcc(net, ci); + } + } } void route_globals() @@ -539,8 +556,8 @@ class Ecp5GlobalRouter dict clocks; for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_DCCA) { - NetInfo *clock = ci->ports.at(id_CLKO).net; + if (ci->type == id_DCCA || ci->type == id_DCSC) { + NetInfo *clock = ci->ports.at((ci->type == id_DCSC) ? id_DCSOUT : id_CLKO).net; NPNR_ASSERT(clock != nullptr); bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(), [this](const PortRef &port) { return !is_clock_port(port); }); @@ -571,6 +588,12 @@ class Ecp5GlobalRouter return global_route_priority(*a.first) < global_route_priority(*b.first); }); for (const auto &user : toroute) { + if (user.first->cell->type == id_DCSC && (user.first->port == id_CLK0 || user.first->port == id_CLK1)) { + // Special case, skips most of the typical global network + NetInfo *net = clocks.at(user.second); + simple_router(net, ctx->getNetinfoSourceWire(net), ctx->getNetinfoSinkWire(net, *(user.first), 0)); + continue; + } route_logic_tile_global(clocks.at(user.second), user.second, *user.first); } } From 027d54e771ec866e7e7815a4b68a18c6530ddbc4 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Tue, 6 Jul 2021 14:34:33 +0200 Subject: [PATCH 8/8] .cirrus/Dockerfile.ubuntu20.04: update apycula to 0.0.1a9 --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index 095d1d33..a6ad1c3b 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -65,4 +65,4 @@ RUN set -e -x ;\ PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide RUN set -e -x ;\ - pip3 install apycula==0.0.1a5 + pip3 install apycula==0.0.1a9