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: [PATCH 1/7] 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 + + From d987bd2997e51e1cbe0681b791adfc685753955c Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 11 Feb 2021 14:53:29 -0800 Subject: [PATCH 2/7] Add unknown handles to convert [0] to "[0]". Tcl reads something like "set port [get_ports x[0]]" as "invoke proc 0 with zero arguments", rather than just "[0]". To prevent exposing non-Tcl users this, "[]" just return themselves. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/xdc.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc index 64569dd2..ee1d1cab 100644 --- a/fpga_interchange/xdc.cc +++ b/fpga_interchange/xdc.cc @@ -21,6 +21,7 @@ #include "log.h" #include "nextpnr.h" #include +#include NEXTPNR_NAMESPACE_BEGIN @@ -144,6 +145,16 @@ void Arch::parse_xdc(const std::string &filename) { 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, getCtx(), nullptr); Tcl_CreateObjCommand(interp, "set_property", set_property, getCtx(), nullptr); auto result = Tcl_EvalFile(interp, filename.c_str()); From c96d0f225c19059e2eb91b66934e7f01e5e5b43e Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 11 Feb 2021 15:29:53 -0800 Subject: [PATCH 3/7] Refactor XDC parser into a little class for testing purposes. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/arch.cc | 11 +++++++++++ fpga_interchange/xdc.cc | 23 +++++++++-------------- fpga_interchange/xdc.h | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 fpga_interchange/xdc.h diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 8fd4d9dc..9263f3c4 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 @@ -563,6 +564,16 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port 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 diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc index ee1d1cab..ece662f8 100644 --- a/fpga_interchange/xdc.cc +++ b/fpga_interchange/xdc.cc @@ -18,9 +18,9 @@ * */ +#include "xdc.h" #include "log.h" #include "nextpnr.h" -#include #include NEXTPNR_NAMESPACE_BEGIN @@ -139,8 +139,8 @@ static int set_property( return TCL_OK; } -void Arch::parse_xdc(const std::string &filename) { - Tcl_Interp *interp = Tcl_CreateInterp(); +TclInterp::TclInterp(Context *ctx) { + interp = Tcl_CreateInterp(); NPNR_ASSERT(Tcl_Init(interp) == TCL_OK); Tcl_RegisterObjType(&port_object); @@ -149,23 +149,18 @@ void Arch::parse_xdc(const std::string &filename) { 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" + " 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, 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(); + 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..b41c86f8 --- /dev/null +++ b/fpga_interchange/xdc.h @@ -0,0 +1,32 @@ +/* + * 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 "nextpnr.h" +#include + +NEXTPNR_NAMESPACE_BEGIN + +struct TclInterp { + TclInterp(Context *ctx); + ~TclInterp(); + + Tcl_Interp *interp; +}; + +NEXTPNR_NAMESPACE_END From 8a860857ea8a1a0fffd68762bcf4cd8432e59408 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 11 Feb 2021 15:36:16 -0800 Subject: [PATCH 4/7] Remove capnp and libz for XDC parser PR. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/family.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fpga_interchange/family.cmake b/fpga_interchange/family.cmake index 47bfb436..c3fefaba 100644 --- a/fpga_interchange/family.cmake +++ b/fpga_interchange/family.cmake @@ -1,4 +1,3 @@ - find_package(TCL) if(NOT ${TCL_FOUND}) message(FATAL_ERROR "Tcl is required for FPGA interchange Arch.") @@ -7,7 +6,4 @@ 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() From 82ab3c1aad0b046c0a97228ef9baceb38ab76e26 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 12 Feb 2021 08:14:27 -0800 Subject: [PATCH 5/7] Run "make clangformat". Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/arch.cc | 10 ++-- fpga_interchange/main.cc | 2 +- fpga_interchange/xdc.cc | 102 +++++++++++++++++---------------------- fpga_interchange/xdc.h | 5 +- 4 files changed, 53 insertions(+), 66 deletions(-) diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 9263f3c4..1abf6f30 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -564,13 +564,13 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port 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) { +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)); + if (result != TCL_OK) { + log_error("Error in %s:%d => %s\n", filename.c_str(), Tcl_GetErrorLine(interp.interp), + Tcl_GetStringResult(interp.interp)); } } diff --git a/fpga_interchange/main.cc b/fpga_interchange/main.cc index 63e990e2..48b07584 100644 --- a/fpga_interchange/main.cc +++ b/fpga_interchange/main.cc @@ -83,7 +83,7 @@ std::unique_ptr FpgaInterchangeCommandHandler::createContext(std::unord } if (vm.count("xdc")) { - for(auto & x : vm["xdc"].as>()) { + for (auto &x : vm["xdc"].as>()) { ctx->parse_xdc(x); } } diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc index ece662f8..584a1777 100644 --- a/fpga_interchange/xdc.cc +++ b/fpga_interchange/xdc.cc @@ -19,20 +19,19 @@ */ #include "xdc.h" +#include #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 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) { +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); + objPtr->bytes = Tcl_Alloc(s.size() + 1); // Length is length of string, not including the end null byte. objPtr->length = s.size(); @@ -41,63 +40,55 @@ static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s) { 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); +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) { +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 port_free(Tcl_Obj *objPtr) {} - -static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { - char * copy = Tcl_Alloc(s.size()+1); +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, + "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) { +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]); + } 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()) { + if (iter == ctx->ports.end()) { Tcl_SetStringResult(interp, "Could not find port " + port_name.str(ctx)); return TCL_ERROR; } - Tcl_Obj * result = Tcl_NewObj(); + Tcl_Obj *result = Tcl_NewObj(); result->typePtr = &port_object; - result->internalRep.twoPtrValue.ptr1 = (void*)(ctx); - result->internalRep.twoPtrValue.ptr2 = (void*)(&iter->second); + result->internalRep.twoPtrValue.ptr1 = (void *)(ctx); + result->internalRep.twoPtrValue.ptr2 = (void *)(&iter->second); result->bytes = nullptr; port_update_string(result); @@ -109,13 +100,10 @@ static int get_ports( } } -static int set_property( - ClientData data, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[]) { +static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ // set_property - if(objc != 4) { + if (objc != 4) { Tcl_SetStringResult(interp, "Only simple 'set_property ' is supported"); return TCL_ERROR; } @@ -124,43 +112,41 @@ static int set_property( const char *value = Tcl_GetString(objv[2]); const Tcl_Obj *object = objv[3]; - if(object->typePtr != &port_object) { + 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); + 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); + CellInfo *cell = ctx->port_cells.at(port_info->name); cell->attrs[ctx->id(property)] = Property(value); return TCL_OK; } -TclInterp::TclInterp(Context *ctx) { +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); + 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); -} +TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); } NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/xdc.h b/fpga_interchange/xdc.h index b41c86f8..c6b80870 100644 --- a/fpga_interchange/xdc.h +++ b/fpga_interchange/xdc.h @@ -17,12 +17,13 @@ * */ -#include "nextpnr.h" #include +#include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN -struct TclInterp { +struct TclInterp +{ TclInterp(Context *ctx); ~TclInterp(); From 5312945757b078ca55519a256b452ce6cb3620d6 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 12 Feb 2021 08:14:58 -0800 Subject: [PATCH 6/7] Update tests to include XDC unit test. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests b/tests index 8f93e7e0..31648368 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit 8f93e7e0f897b1b5da469919c9a43ba28b623b2a +Subproject commit 31648368460b9e216479ce7c38e6fed883c380c4 From 033cc6731b18c89ee0ffbd0be9eab4d2d157980f Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 12 Feb 2021 12:51:32 -0800 Subject: [PATCH 7/7] Add FPGA interchange tests to CI. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- .cirrus.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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