diff options
author | gatecat <gatecat@ds0.me> | 2021-02-12 22:40:41 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-12 22:40:41 +0000 |
commit | f1ccc0e20531f63355e3da7c6c5f4f39a684fa3f (patch) | |
tree | 09175eb6facecba7890beadcd5cccac2e6b50417 /fpga_interchange | |
parent | cede6825859217e15a0d41cd4cc51b6129902aad (diff) | |
parent | 033cc6731b18c89ee0ffbd0be9eab4d2d157980f (diff) | |
download | nextpnr-f1ccc0e20531f63355e3da7c6c5f4f39a684fa3f.tar.gz nextpnr-f1ccc0e20531f63355e3da7c6c5f4f39a684fa3f.tar.bz2 nextpnr-f1ccc0e20531f63355e3da7c6c5f4f39a684fa3f.zip |
Merge pull request #582 from litghost/add_xdc_parser
Add XDC parser to FPGA interchange
Diffstat (limited to 'fpga_interchange')
-rw-r--r-- | fpga_interchange/arch.cc | 18 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 5 | ||||
-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 |
6 files changed, 236 insertions, 3 deletions
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 5964a38f..435bb93d 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 @@ -1301,7 +1302,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 |