diff options
-rw-r--r-- | .cirrus.yml | 3 | ||||
-rw-r--r-- | common/command.cc | 2 | ||||
-rw-r--r-- | common/design_utils.cc | 4 | ||||
-rw-r--r-- | common/nextpnr.h | 15 | ||||
-rw-r--r-- | docs/archapi.md | 8 | ||||
-rw-r--r-- | docs/netlist.md | 1 | ||||
-rw-r--r-- | ecp5/arch.h | 2 | ||||
-rw-r--r-- | fpga_interchange/arch.cc | 18 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 8 | ||||
-rw-r--r-- | fpga_interchange/family.cmake | 9 | ||||
-rw-r--r-- | fpga_interchange/main.cc | 22 | ||||
-rw-r--r-- | fpga_interchange/xdc.cc | 152 | ||||
-rw-r--r-- | fpga_interchange/xdc.h | 33 | ||||
-rw-r--r-- | frontend/frontend_base.h | 44 | ||||
-rw-r--r-- | frontend/json_frontend.cc | 2 | ||||
-rw-r--r-- | generic/arch.cc | 5 | ||||
-rw-r--r-- | generic/arch.h | 4 | ||||
-rw-r--r-- | generic/arch_pybindings.cc | 7 | ||||
-rw-r--r-- | generic/examples/simple.py | 4 | ||||
-rw-r--r-- | ice40/arch.h | 2 | ||||
-rw-r--r-- | nexus/arch.h | 2 | ||||
m--------- | tests | 0 |
22 files changed, 306 insertions, 41 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 0c2d8b78..f7181fd3 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -5,12 +5,13 @@ task: memory: 20 dockerfile: .cirrus/Dockerfile.ubuntu20.04 - build_script: mkdir build && cd build && cmake .. -DARCH=all+alpha -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on -DBUILD_GUI=on && make -j3 submodule_script: git submodule sync --recursive && git submodule update --init --recursive + build_script: mkdir build && cd build && cmake .. -DARCH=all+alpha -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on -DBUILD_GUI=on && make -j3 test_generic_script: cd build && ./nextpnr-generic-test test_ice40_script: cd build && ./nextpnr-ice40-test smoketest_ice40_script: export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh test_ecp5_script: cd build && ./nextpnr-ecp5-test + test_fpga_interchange_script: cd build && ./nextpnr-fpga_interchange-test smoketest_generic_script: export NEXTPNR=$(pwd)/build/nextpnr-generic && cd generic/examples && ./simple.sh && ./simtest.sh regressiontest_ice40_script: make -j $(nproc) -C tests/ice40/regressions NPNR=$(pwd)/build/nextpnr-ice40 regressiontest_ecp5_script: make -j $(nproc) -C tests/ecp5/regressions NPNR=$(pwd)/build/nextpnr-ecp5 diff --git a/common/command.cc b/common/command.cc index 23572e02..d3e8af8d 100644 --- a/common/command.cc +++ b/common/command.cc @@ -331,7 +331,7 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx) execute_python_file(filename.c_str()); } else #endif - if (vm.count("json")) { + if (ctx->design_loaded) { bool do_pack = vm.count("pack-only") != 0 || vm.count("no-pack") == 0; bool do_place = vm.count("pack-only") == 0 && vm.count("no-place") == 0; bool do_route = vm.count("pack-only") == 0 && vm.count("no-route") == 0; diff --git a/common/design_utils.cc b/common/design_utils.cc index 16cc2710..b81449b7 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -71,7 +71,9 @@ void print_utilisation(const Context *ctx) } std::map<IdString, int> available_types; for (auto bel : ctx->getBels()) { - available_types[ctx->getBelType(bel)]++; + if (!ctx->getBelHidden(bel)) { + available_types[ctx->getBelType(bel)]++; + } } log_break(); log_info("Device utilisation:\n"); diff --git a/common/nextpnr.h b/common/nextpnr.h index cb4dbc28..b6ee33fe 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -833,6 +833,7 @@ struct BaseCtx // Top-level ports std::unordered_map<IdString, PortInfo> ports; + std::unordered_map<IdString, CellInfo *> port_cells; // Floorplanning regions std::unordered_map<IdString, std::unique_ptr<Region>> region; @@ -842,6 +843,9 @@ struct BaseCtx Context *as_ctx = nullptr; + // Has the frontend loaded a design? + bool design_loaded; + BaseCtx() { idstring_str_to_idx = new std::unordered_map<std::string, int>; @@ -853,6 +857,8 @@ struct BaseCtx wildcard.id.index = 0; wildcard.type = TimingConstraintObject::ANYTHING; constraintObjects.push_back(wildcard); + + design_loaded = false; } virtual ~BaseCtx() @@ -1089,6 +1095,7 @@ template <typename R> struct ArchAPI : BaseCtx virtual CellInfo *getBoundBelCell(BelId bel) const = 0; virtual CellInfo *getConflictingBelCell(BelId bel) const = 0; virtual IdString getBelType(BelId bel) const = 0; + virtual bool getBelHidden(BelId bel) const = 0; virtual typename R::BelAttrsRangeT getBelAttrs(BelId bel) const = 0; virtual WireId getBelPinWire(BelId bel, IdString pin) const = 0; virtual PortType getBelPinType(BelId bel, IdString pin) const = 0; @@ -1204,7 +1211,7 @@ template <typename R> struct BaseArch : ArchAPI<R> // Basic config virtual IdString archId() const override { return this->id(STRINGIFY(ARCHNAME)); } - virtual IdString archArgsToId(typename R::ArchArgsT args) const { return IdString(); } + virtual IdString archArgsToId(typename R::ArchArgsT args) const override { return IdString(); } virtual int getTilePipDimZ(int x, int y) const override { return 1; } virtual char getNameDelimiter() const override { return ' '; } @@ -1231,6 +1238,8 @@ template <typename R> struct BaseArch : ArchAPI<R> this->refreshUiBel(bel); } + virtual bool getBelHidden(BelId bel) const override { return false; } + virtual bool getBelGlobalBuf(BelId bel) const override { return false; } virtual bool checkBelAvail(BelId bel) const override { return getBoundBelCell(bel) == nullptr; }; virtual CellInfo *getBoundBelCell(BelId bel) const override @@ -1298,7 +1307,7 @@ template <typename R> struct BaseArch : ArchAPI<R> virtual NetInfo *getConflictingWireNet(WireId wire) const override { return getBoundWireNet(wire); } // Pip methods - virtual IdString getPipType(PipId pip) const { return IdString(); } + virtual IdString getPipType(PipId pip) const override { return IdString(); } virtual typename R::PipAttrsRangeT getPipAttrs(PipId) const override { return empty_if_possible<typename R::PipAttrsRangeT>(); @@ -1375,7 +1384,7 @@ template <typename R> struct BaseArch : ArchAPI<R> // Decal methods virtual typename R::DecalGfxRangeT getDecalGraphics(DecalId decal) const override { - NPNR_ASSERT_FALSE("unreachable"); + return empty_if_possible<typename R::DecalGfxRangeT>(); }; virtual DecalXY getBelDecal(BelId bel) const override { return DecalXY(); } virtual DecalXY getWireDecal(WireId wire) const override { return DecalXY(); } diff --git a/docs/archapi.md b/docs/archapi.md index 2a38502c..0f0e6181 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -226,6 +226,12 @@ Return a list of all bels on the device. Return the type of a given bel. +### bool getBelHidden(BelId bel) const + +Should this bel be hidden from utilities? + +*BaseArch default: returns false* + ### BelAttrsRangeT getBelAttrs(BelId bel) const Return the attributes for that bel. Bel attributes are only informal. They are displayed by the GUI but are otherwise @@ -574,7 +580,7 @@ Return the graphic elements that make up a decal. The same decal must always produce the same list. If the graphics for a design element changes, that element must return another decal. -*BaseArch default: asserts false as unreachable due to there being no decals* +*BaseArch default: returns default-constructed range* ### DecalXY getBelDecal(BelId bel) const diff --git a/docs/netlist.md b/docs/netlist.md index 763f7d40..2e989a33 100644 --- a/docs/netlist.md +++ b/docs/netlist.md @@ -52,6 +52,7 @@ Relevant fields from a netlist point of view are: - `nets` is a map from net name to a `unique_ptr<NetInfo>` containing net data - `net_aliases` maps every alias for a net to its canonical name (i.e. index into `nets`) - net aliases often occur when a net has a name both inside a submodule and higher level module - `ports` is a list of top level ports, primarily used during JSON export (e.g. to produce a useful post-PnR simulation model). Unlike other ports, top level ports are _not_ added to the driver or users of any connected net. In this sense, nets connected to top-level ports are _dangling_. However, top level ports _can_ still see their connected net as part of their `PortInfo`. + - `port_cells` is a map of top level port cells. This is a subset of the `cells` maps containing only ports. Context also has a method `check()` that ensures all of the contracts met above are satisfied. It is strongly suggested to run this after any pass that may modify the netlist. diff --git a/ecp5/arch.h b/ecp5/arch.h index de8b225e..d5edd88e 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -601,7 +601,7 @@ struct Arch : BaseArch<ArchRanges> return range; } - std::vector<IdString> getBelPins(BelId bel) const; + std::vector<IdString> getBelPins(BelId bel) const override; // ------------------------------------------------- diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 583813f0..1abf6f30 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -33,6 +33,7 @@ #include "router2.h" #include "timing.h" #include "util.h" +#include "xdc.h" NEXTPNR_NAMESPACE_BEGIN @@ -558,6 +559,23 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port return info; } +// ----------------------------------------------------------------------- + +void Arch::read_logical_netlist(const std::string &filename) {} +void Arch::write_physical_netlist(const std::string &filename) const {} + +void Arch::parse_xdc(const std::string &filename) +{ + TclInterp interp(getCtx()); + auto result = Tcl_EvalFile(interp.interp, filename.c_str()); + if (result != TCL_OK) { + log_error("Error in %s:%d => %s\n", filename.c_str(), Tcl_GetErrorLine(interp.interp), + Tcl_GetStringResult(interp.interp)); + } +} + +// ----------------------------------------------------------------------- + #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index e5c7551b..886978f1 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -650,6 +650,7 @@ struct BelBucketRange struct ArchArgs { std::string chipdb; + std::string package; }; struct ArchRanges @@ -825,8 +826,7 @@ struct Arch : ArchAPI<ArchRanges> return false; } - // TODO: this needs to become part of the Arch API - bool getBelHidden(BelId bel) const { return bel_info(chip_info, bel).category != BEL_CATEGORY_LOGIC; } + bool getBelHidden(BelId bel) const override { return bel_info(chip_info, bel).category != BEL_CATEGORY_LOGIC; } IdString getBelType(BelId bel) const override { @@ -1305,7 +1305,9 @@ struct Arch : ArchAPI<ArchRanges> static const std::vector<std::string> availableRouters; // ------------------------------------------------- - void write_physical_netlist(const std::string &filename) const {} + void read_logical_netlist(const std::string &filename); + void write_physical_netlist(const std::string &filename) const; + void parse_xdc(const std::string &filename); }; NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/family.cmake b/fpga_interchange/family.cmake index e69de29b..c3fefaba 100644 --- a/fpga_interchange/family.cmake +++ b/fpga_interchange/family.cmake @@ -0,0 +1,9 @@ +find_package(TCL) +if(NOT ${TCL_FOUND}) + message(FATAL_ERROR "Tcl is required for FPGA interchange Arch.") +endif() + +foreach (target ${family_targets}) + target_link_libraries(${target} LINK_PUBLIC ${TCL_LIBRARY}) + include_directories (${TCL_INCLUDE_PATH}) +endforeach() diff --git a/fpga_interchange/main.cc b/fpga_interchange/main.cc index 1f98b186..48b07584 100644 --- a/fpga_interchange/main.cc +++ b/fpga_interchange/main.cc @@ -49,8 +49,10 @@ po::options_description FpgaInterchangeCommandHandler::getArchOptions() { po::options_description specific("Architecture specific options"); specific.add_options()("chipdb", po::value<std::string>(), "name of chip database binary"); - specific.add_options()("xdc", po::value<std::vector<std::string>>(), "XDC-style constraints file"); + specific.add_options()("xdc", po::value<std::vector<std::string>>(), "XDC-style constraints file to read"); + specific.add_options()("netlist", po::value<std::string>(), "FPGA interchange logical netlist to read"); specific.add_options()("phys", po::value<std::string>(), "FPGA interchange Physical netlist to write"); + specific.add_options()("package", po::value<std::string>(), "Package to use"); return specific; } @@ -70,7 +72,23 @@ std::unique_ptr<Context> FpgaInterchangeCommandHandler::createContext(std::unord log_error("chip database binary must be provided\n"); } chipArgs.chipdb = vm["chipdb"].as<std::string>(); - return std::unique_ptr<Context>(new Context(chipArgs)); + if (vm.count("package")) { + chipArgs.package = vm["package"].as<std::string>(); + } + + auto ctx = std::unique_ptr<Context>(new Context(chipArgs)); + + if (vm.count("netlist")) { + ctx->read_logical_netlist(vm["netlist"].as<std::string>()); + } + + if (vm.count("xdc")) { + for (auto &x : vm["xdc"].as<std::vector<std::string>>()) { + ctx->parse_xdc(x); + } + } + + return ctx; } void FpgaInterchangeCommandHandler::customAfterLoad(Context *ctx) {} diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc new file mode 100644 index 00000000..584a1777 --- /dev/null +++ b/fpga_interchange/xdc.cc @@ -0,0 +1,152 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah <david@symbioticeda.com> + * Copyright (C) 2021 Symbiflow Authors + * + * 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 "xdc.h" +#include <string> +#include "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +static int port_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; } + +static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s) +{ + NPNR_ASSERT(objPtr->bytes == nullptr); + // Need to have space for the end null byte. + objPtr->bytes = Tcl_Alloc(s.size() + 1); + + // Length is length of string, not including the end null byte. + objPtr->length = s.size(); + + std::copy(s.begin(), s.end(), objPtr->bytes); + objPtr->bytes[objPtr->length] = '\0'; +} + +static void port_update_string(Tcl_Obj *objPtr) +{ + const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1); + PortInfo *port_info = static_cast<PortInfo *>(objPtr->internalRep.twoPtrValue.ptr2); + + std::string port_name = port_info->name.str(ctx); + set_tcl_obj_string(objPtr, port_name); +} + +static void port_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) +{ + dupPtr->internalRep.twoPtrValue = srcPtr->internalRep.twoPtrValue; +} + +static void port_free(Tcl_Obj *objPtr) {} + +static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) +{ + char *copy = Tcl_Alloc(s.size() + 1); + std::copy(s.begin(), s.end(), copy); + copy[s.size()] = '\0'; + Tcl_SetResult(interp, copy, TCL_DYNAMIC); +} + +static Tcl_ObjType port_object = { + "port", port_free, port_dup, port_update_string, port_set_from_any, +}; + +static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const Context *ctx = static_cast<const Context *>(data); + if (objc == 1) { + // Return list of all ports. + Tcl_SetStringResult(interp, "Unimplemented"); + return TCL_ERROR; + } else if (objc == 2) { + const char *arg0 = Tcl_GetString(objv[1]); + IdString port_name = ctx->id(arg0); + + auto iter = ctx->ports.find(port_name); + if (iter == ctx->ports.end()) { + Tcl_SetStringResult(interp, "Could not find port " + port_name.str(ctx)); + return TCL_ERROR; + } + + Tcl_Obj *result = Tcl_NewObj(); + result->typePtr = &port_object; + result->internalRep.twoPtrValue.ptr1 = (void *)(ctx); + result->internalRep.twoPtrValue.ptr2 = (void *)(&iter->second); + + result->bytes = nullptr; + port_update_string(result); + + Tcl_SetObjResult(interp, result); + return TCL_OK; + } else { + return TCL_ERROR; + } +} + +static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + // set_property <property> <value> <object> + if (objc != 4) { + Tcl_SetStringResult(interp, "Only simple 'set_property <property> <value> <object>' is supported"); + return TCL_ERROR; + } + + const char *property = Tcl_GetString(objv[1]); + const char *value = Tcl_GetString(objv[2]); + const Tcl_Obj *object = objv[3]; + + if (object->typePtr != &port_object) { + Tcl_SetStringResult(interp, "Only port objects are handled right now!"); + return TCL_ERROR; + } + + const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); + PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2); + NPNR_ASSERT(port_info->net != nullptr); + CellInfo *cell = ctx->port_cells.at(port_info->name); + + cell->attrs[ctx->id(property)] = Property(value); + + return TCL_OK; +} + +TclInterp::TclInterp(Context *ctx) +{ + interp = Tcl_CreateInterp(); + NPNR_ASSERT(Tcl_Init(interp) == TCL_OK); + + Tcl_RegisterObjType(&port_object); + + NPNR_ASSERT(Tcl_Eval(interp, "rename unknown _original_unknown") == TCL_OK); + NPNR_ASSERT(Tcl_Eval(interp, "proc unknown args {\n" + " set result [scan [lindex $args 0] \"%d\" value]\n" + " if { $result == 1 && [llength $args] == 1 } {\n" + " return \\[$value\\]\n" + " } else {\n" + " uplevel 1 [list _original_unknown {*}$args]\n" + " }\n" + "}") == TCL_OK); + Tcl_CreateObjCommand(interp, "get_ports", get_ports, ctx, nullptr); + Tcl_CreateObjCommand(interp, "set_property", set_property, ctx, nullptr); +} + +TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); } + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/xdc.h b/fpga_interchange/xdc.h new file mode 100644 index 00000000..c6b80870 --- /dev/null +++ b/fpga_interchange/xdc.h @@ -0,0 +1,33 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 Symbiflow Authors + * + * 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 <tcl.h> +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct TclInterp +{ + TclInterp(Context *ctx); + ~TclInterp(); + + Tcl_Interp *interp; +}; + +NEXTPNR_NAMESPACE_END diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index e262c943..d39a8304 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -123,7 +123,7 @@ struct ModuleInfo template <typename FrontendType> struct GenericFrontend { - GenericFrontend(Context *ctx, const FrontendType &impl) : ctx(ctx), impl(impl) {} + GenericFrontend(Context *ctx, const FrontendType &impl, bool split_io) : ctx(ctx), impl(impl), split_io(split_io) {} void operator()() { // Find which module is top @@ -135,10 +135,13 @@ template <typename FrontendType> struct GenericFrontend ctx->top_module = top; // Do the actual import, starting from the top level module import_module(m, top.str(ctx), top.str(ctx), mod_refs.at(top)); + + ctx->design_loaded = true; } Context *ctx; const FrontendType &impl; + const bool split_io; using mod_dat_t = typename FrontendType::ModuleDataType; using mod_port_dat_t = typename FrontendType::ModulePortDataType; using cell_dat_t = typename FrontendType::CellDataType; @@ -146,7 +149,7 @@ template <typename FrontendType> struct GenericFrontend using bitvector_t = typename FrontendType::BitVectorDataType; std::unordered_map<IdString, ModuleInfo> mods; - std::unordered_map<IdString, const mod_dat_t &> mod_refs; + std::unordered_map<IdString, const mod_dat_t> mod_refs; IdString top; // Process the list of modules and determine @@ -583,22 +586,28 @@ template <typename FrontendType> struct GenericFrontend connect_port(ctx, net, iobuf, ctx->id("I")); } else if (dir == PORT_INOUT) { iobuf->type = ctx->id("$nextpnr_iobuf"); - iobuf->addInput(ctx->id("I")); - iobuf->addOutput(ctx->id("O")); - // Need to bifurcate the net to avoid multiple drivers and split - // the input/output parts of an inout - // Create a new net connecting only the current net's driver and the IOBUF input - // Then use the IOBUF output to drive all of the current net's users - NetInfo *split_iobuf_i = ctx->createNet(unique_name("", "$" + name + "$iobuf_i", true)); - auto drv = net->driver; - if (drv.cell != nullptr) { - disconnect_port(ctx, drv.cell, drv.port); - drv.cell->ports[drv.port].net = nullptr; - connect_port(ctx, split_iobuf_i, drv.cell, drv.port); + + if (split_io) { + iobuf->addInput(ctx->id("I")); + iobuf->addOutput(ctx->id("O")); + // Need to bifurcate the net to avoid multiple drivers and split + // the input/output parts of an inout + // Create a new net connecting only the current net's driver and the IOBUF input + // Then use the IOBUF output to drive all of the current net's users + NetInfo *split_iobuf_i = ctx->createNet(unique_name("", "$" + name + "$iobuf_i", true)); + auto drv = net->driver; + if (drv.cell != nullptr) { + disconnect_port(ctx, drv.cell, drv.port); + drv.cell->ports[drv.port].net = nullptr; + connect_port(ctx, split_iobuf_i, drv.cell, drv.port); + } + connect_port(ctx, split_iobuf_i, iobuf, ctx->id("I")); + NPNR_ASSERT(net->driver.cell == nullptr); + connect_port(ctx, net, iobuf, ctx->id("O")); + } else { + iobuf->addInout(ctx->id("IO")); + connect_port(ctx, net, iobuf, ctx->id("IO")); } - connect_port(ctx, split_iobuf_i, iobuf, ctx->id("I")); - NPNR_ASSERT(net->driver.cell == nullptr); - connect_port(ctx, net, iobuf, ctx->id("O")); } PortInfo pinfo; @@ -606,6 +615,7 @@ template <typename FrontendType> struct GenericFrontend pinfo.net = net; pinfo.type = dir; ctx->ports[pinfo.name] = pinfo; + ctx->port_cells[pinfo.name] = iobuf; return iobuf; } diff --git a/frontend/json_frontend.cc b/frontend/json_frontend.cc index 136786fc..52f7bfdc 100644 --- a/frontend/json_frontend.cc +++ b/frontend/json_frontend.cc @@ -197,7 +197,7 @@ bool parse_json(std::istream &in, const std::string &filename, Context *ctx) log_error("JSON file '%s' doesn't look like a netlist (doesn't contain \"modules\" key)\n", filename.c_str()); } - GenericFrontend<JsonFrontendImpl>(ctx, JsonFrontendImpl(root))(); + GenericFrontend<JsonFrontendImpl>(ctx, JsonFrontendImpl(root), /*split_io=*/true)(); return true; } diff --git a/generic/arch.cc b/generic/arch.cc index c51fbb84..7cd71179 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -91,7 +91,7 @@ void Arch::addPip(IdStringList name, IdString type, IdStringList srcWire, IdStri tilePipDimZ[loc.x][loc.y] = std::max(tilePipDimZ[loc.x][loc.y], loc.z + 1); } -void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb) +void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden) { NPNR_ASSERT(bels.count(name) == 0); NPNR_ASSERT(bel_by_loc.count(loc) == 0); @@ -102,6 +102,7 @@ void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb) bi.y = loc.y; bi.z = loc.z; bi.gb = gb; + bi.hidden = hidden; bel_ids.push_back(name); bel_by_loc[loc] = name; @@ -319,6 +320,8 @@ const std::vector<BelId> &Arch::getBels() const { return bel_ids; } IdString Arch::getBelType(BelId bel) const { return bels.at(bel).type; } +bool Arch::getBelHidden(BelId bel) const { return bels.at(bel).hidden; } + const std::map<IdString, std::string> &Arch::getBelAttrs(BelId bel) const { return bels.at(bel).attrs; } WireId Arch::getBelPinWire(BelId bel, IdString pin) const diff --git a/generic/arch.h b/generic/arch.h index e7d204ef..2a0c7158 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -77,6 +77,7 @@ struct BelInfo DecalXY decalxy; int x, y, z; bool gb; + bool hidden; }; struct GroupInfo @@ -178,7 +179,7 @@ struct Arch : ArchAPI<ArchRanges> void addWire(IdStringList name, IdString type, int x, int y); void addPip(IdStringList name, IdString type, IdStringList srcWire, IdStringList dstWire, DelayInfo delay, Loc loc); - void addBel(IdStringList name, IdString type, Loc loc, bool gb); + void addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden); void addBelInput(IdStringList bel, IdString name, IdStringList wire); void addBelOutput(IdStringList bel, IdString name, IdStringList wire); void addBelInout(IdStringList bel, IdString name, IdStringList wire); @@ -238,6 +239,7 @@ struct Arch : ArchAPI<ArchRanges> CellInfo *getConflictingBelCell(BelId bel) const override; const std::vector<BelId> &getBels() const override; IdString getBelType(BelId bel) const override; + bool getBelHidden(BelId bel) const override; const std::map<IdString, std::string> &getBelAttrs(BelId bel) const override; WireId getBelPinWire(BelId bel, IdString pin) const override; PortType getBelPinType(BelId bel, IdString pin) const override; diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index 29e8bc53..3dc04206 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -162,10 +162,9 @@ void arch_wrap_python(py::module &m) pass_through<DelayInfo>, pass_through<Loc>>::def_wrap(ctx_cls, "addPip", "name"_a, "type"_a, "srcWire"_a, "dstWire"_a, "delay"_a, "loc"_a); - fn_wrapper_4a_v<Context, decltype(&Context::addBel), &Context::addBel, conv_from_str<IdStringList>, - conv_from_str<IdString>, pass_through<Loc>, pass_through<bool>>::def_wrap(ctx_cls, "addBel", - "name"_a, "type"_a, - "loc"_a, "gb"_a); + fn_wrapper_5a_v<Context, decltype(&Context::addBel), &Context::addBel, conv_from_str<IdStringList>, + conv_from_str<IdString>, pass_through<Loc>, pass_through<bool>, + pass_through<bool>>::def_wrap(ctx_cls, "addBel", "name"_a, "type"_a, "loc"_a, "gb"_a, "hidden"_a); fn_wrapper_3a_v<Context, decltype(&Context::addBelInput), &Context::addBelInput, conv_from_str<IdStringList>, conv_from_str<IdString>, conv_from_str<IdStringList>>::def_wrap(ctx_cls, "addBelInput", "bel"_a, "name"_a, "wire"_a); diff --git a/generic/examples/simple.py b/generic/examples/simple.py index 9379b505..4b7f4025 100644 --- a/generic/examples/simple.py +++ b/generic/examples/simple.py @@ -20,13 +20,13 @@ for x in range(X): if x == y: continue for z in range(2): - ctx.addBel(name="X%dY%d_IO%d" % (x, y, z), type="GENERIC_IOB", loc=Loc(x, y, z), gb=False) + ctx.addBel(name="X%dY%d_IO%d" % (x, y, z), type="GENERIC_IOB", loc=Loc(x, y, z), gb=False, hidden=False) ctx.addBelInput(bel="X%dY%d_IO%d" % (x, y, z), name="I", wire="X%dY%dZ%d_I0" % (x, y, z)) ctx.addBelInput(bel="X%dY%d_IO%d" % (x, y, z), name="EN", wire="X%dY%dZ%d_I1" % (x, y, z)) ctx.addBelOutput(bel="X%dY%d_IO%d" % (x, y, z), name="O", wire="X%dY%dZ%d_Q" % (x, y, z)) else: for z in range(N): - ctx.addBel(name="X%dY%d_SLICE%d" % (x, y, z), type="GENERIC_SLICE", loc=Loc(x, y, z), gb=False) + ctx.addBel(name="X%dY%d_SLICE%d" % (x, y, z), type="GENERIC_SLICE", loc=Loc(x, y, z), gb=False, hidden=False) ctx.addBelInput(bel="X%dY%d_SLICE%d" % (x, y, z), name="CLK", wire="X%dY%dZ%d_CLK" % (x, y, z)) for k in range(K): ctx.addBelInput(bel="X%dY%d_SLICE%d" % (x, y, z), name="I[%d]" % k, wire="X%dY%dZ%d_I%d" % (x, y, z, k)) diff --git a/ice40/arch.h b/ice40/arch.h index 5df072f9..30b5f871 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -849,7 +849,7 @@ struct Arch : BaseArch<ArchRanges> // Assign architecture-specific arguments to nets and cells, which must be // called between packing or further // netlist modifications, and validity checks - void assignArchInfo(); + void assignArchInfo() override; void assignCellInfo(CellInfo *cell); // ------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index d81605af..963b5b2f 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1118,7 +1118,7 @@ struct Arch : BaseArch<ArchRanges> WireId getPipSrcWire(PipId pip) const override { return canonical_wire(pip.tile, pip_data(pip).from_wire); } - WireId getPipDstWire(PipId pip) const { return canonical_wire(pip.tile, pip_data(pip).to_wire); } + WireId getPipDstWire(PipId pip) const override { return canonical_wire(pip.tile, pip_data(pip).to_wire); } DelayInfo getPipDelay(PipId pip) const override { diff --git a/tests b/tests -Subproject 8f93e7e0f897b1b5da469919c9a43ba28b623b2 +Subproject 31648368460b9e216479ce7c38e6fed883c380c |