From a0bd3131390567d1bd79c1ac19f663caff603445 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 11 Feb 2021 14:24:49 -0800 Subject: Add FPGA interchange XDC parser. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/arch.cc | 7 ++ fpga_interchange/arch.h | 5 +- fpga_interchange/family.cmake | 13 ++++ fpga_interchange/main.cc | 22 +++++- fpga_interchange/xdc.cc | 160 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 fpga_interchange/xdc.cc diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 583813f0..8fd4d9dc 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -558,6 +558,13 @@ 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 {} + +// ----------------------------------------------------------------------- + #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 static const std::vector 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..47bfb436 100644 --- a/fpga_interchange/family.cmake +++ b/fpga_interchange/family.cmake @@ -0,0 +1,13 @@ + +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}) + target_link_libraries(${target} LINK_PUBLIC capnp) + target_link_libraries(${target} LINK_PUBLIC kj) + target_link_libraries(${target} LINK_PUBLIC z) +endforeach() diff --git a/fpga_interchange/main.cc b/fpga_interchange/main.cc index 1f98b186..63e990e2 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(), "name of chip database binary"); - specific.add_options()("xdc", po::value>(), "XDC-style constraints file"); + specific.add_options()("xdc", po::value>(), "XDC-style constraints file to read"); + specific.add_options()("netlist", po::value(), "FPGA interchange logical netlist to read"); specific.add_options()("phys", po::value(), "FPGA interchange Physical netlist to write"); + specific.add_options()("package", po::value(), "Package to use"); return specific; } @@ -70,7 +72,23 @@ std::unique_ptr FpgaInterchangeCommandHandler::createContext(std::unord log_error("chip database binary must be provided\n"); } chipArgs.chipdb = vm["chipdb"].as(); - return std::unique_ptr(new Context(chipArgs)); + if (vm.count("package")) { + chipArgs.package = vm["package"].as(); + } + + auto ctx = std::unique_ptr(new Context(chipArgs)); + + if (vm.count("netlist")) { + ctx->read_logical_netlist(vm["netlist"].as()); + } + + if (vm.count("xdc")) { + for(auto & x : vm["xdc"].as>()) { + 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..64569dd2 --- /dev/null +++ b/fpga_interchange/xdc.cc @@ -0,0 +1,160 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah + * 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 "log.h" +#include "nextpnr.h" +#include + +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(objPtr->internalRep.twoPtrValue.ptr1); + PortInfo * port_info = static_cast(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(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 + if(objc != 4) { + Tcl_SetStringResult(interp, "Only simple 'set_property ' 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(object->internalRep.twoPtrValue.ptr1); + PortInfo * port_info = static_cast(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; +} + +void Arch::parse_xdc(const std::string &filename) { + Tcl_Interp *interp = Tcl_CreateInterp(); + NPNR_ASSERT(Tcl_Init(interp) == TCL_OK); + + Tcl_RegisterObjType(&port_object); + + Tcl_CreateObjCommand(interp, "get_ports", get_ports, getCtx(), nullptr); + Tcl_CreateObjCommand(interp, "set_property", set_property, getCtx(), nullptr); + auto result = Tcl_EvalFile(interp, filename.c_str()); + if(result != TCL_OK) { + log_error("Error in %s:%d => %s\n", filename.c_str(), Tcl_GetErrorLine(interp), Tcl_GetStringResult(interp)); + } + Tcl_DeleteInterp(interp); + Tcl_Finalize(); +} + + +NEXTPNR_NAMESPACE_END + + -- cgit v1.2.3