diff --git a/lib/database.cc b/lib/database.cc index e9d767bf..defa3325 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -9,7 +9,11 @@ */ #include +#ifdef _WIN32 +#include +#else #include +#endif #include @@ -23,6 +27,25 @@ std::vector> Database::segbits() const { std::vector> segbits; +#ifdef _WIN32 + const std::string pattern = + absl::StrCat(db_path_, "/", kSegbitsGlobPattern); + WIN32_FIND_DATAA find_data; + HANDLE handle = FindFirstFileA(pattern.c_str(), &find_data); + if (handle == INVALID_HANDLE_VALUE) { + return {}; + } + + do { + auto this_segbit = SegbitsFileReader::InitWithFile( + absl::StrCat(db_path_, "/", find_data.cFileName)); + if (this_segbit) { + segbits.emplace_back(std::move(this_segbit)); + } + } while (FindNextFileA(handle, &find_data)); + + FindClose(handle); +#else glob_t segbits_glob_results; int ret = glob(absl::StrCat(db_path_, "/", kSegbitsGlobPattern).c_str(), GLOB_NOSORT | GLOB_TILDE, NULL, &segbits_glob_results); @@ -39,6 +62,7 @@ std::vector> Database::segbits() } globfree(&segbits_glob_results); +#endif return segbits; } diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h index 0a883547..4721f8db 100644 --- a/lib/include/prjxray/xilinx/configuration.h +++ b/lib/include/prjxray/xilinx/configuration.h @@ -75,6 +75,44 @@ class Configuration { FrameMap frames_; }; +// The explicit specializations below are defined in configuration.cc. +// They must be declared before use in every translation unit +// ([temp.expl.spec]): without these declarations, a TU that calls +// createType2ConfigurationPacketData instantiates the primary +// template defined further down in this header, and that COMDAT copy +// collides with the strong definition from configuration.cc when linking +// with mingw-w64 ld ("multiple definition"); ELF linkers silently +// resolve the collision in favour of the strong symbol. +template <> +Configuration::PacketData +Configuration::createType2ConfigurationPacketData( + const Frames::Frames2Data& frames, + absl::optional& part); + +template <> +void Configuration::createConfigurationPackage( + Spartan6::ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& part); + +template <> +void Configuration::createConfigurationPackage( + Series7::ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& part); + +template <> +void Configuration::createConfigurationPackage( + UltraScale::ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& part); + +template <> +void Configuration::createConfigurationPackage( + UltraScalePlus::ConfigurationPackage& out_packets, + const PacketData& packet_data, + absl::optional& part); + template typename Configuration::PacketData Configuration::createType2ConfigurationPacketData( diff --git a/lib/memory_mapped_file.cc b/lib/memory_mapped_file.cc index 0600c9b2..a5be18be 100644 --- a/lib/memory_mapped_file.cc +++ b/lib/memory_mapped_file.cc @@ -9,16 +9,56 @@ */ #include +#ifdef _WIN32 +#include +#else #include #include #include #include #include +#endif namespace prjxray { std::unique_ptr MemoryMappedFile::InitWithFile( const std::string& path) { +#ifdef _WIN32 + HANDLE file = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); + if (file == INVALID_HANDLE_VALUE) + return nullptr; + + LARGE_INTEGER file_size; + if (!GetFileSizeEx(file, &file_size)) { + CloseHandle(file); + return nullptr; + } + + // A zero-length file cannot be mapped; return an object (to indicate + // the file exists) with a nullptr and zero length. + if (file_size.QuadPart == 0) { + CloseHandle(file); + return std::unique_ptr( + new MemoryMappedFile(nullptr, 0)); + } + + HANDLE mapping = + CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); + // The view keeps the file/mapping alive, so the handles can be closed. + CloseHandle(file); + if (mapping == NULL) + return nullptr; + + void* file_map = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + CloseHandle(mapping); + if (file_map == NULL) + return nullptr; + + return std::unique_ptr(new MemoryMappedFile( + file_map, static_cast(file_size.QuadPart))); +#else int fd = open(path.c_str(), O_RDONLY, 0); if (fd == -1) return nullptr; @@ -51,10 +91,16 @@ std::unique_ptr MemoryMappedFile::InitWithFile( return std::unique_ptr( new MemoryMappedFile(file_map, statbuf.st_size)); +#endif } MemoryMappedFile::~MemoryMappedFile() { +#ifdef _WIN32 + if (data_) + UnmapViewOfFile(data_); +#else munmap(data_, size_); +#endif } } // namespace prjxray diff --git a/prjxray/util.py b/prjxray/util.py index 615bf726..1fc1ac04 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -8,7 +8,12 @@ # https://opensource.org/licenses/ISC # # SPDX-License-Identifier: ISC -import fcntl +try: + import fcntl +except ImportError: + # Windows: no fcntl (and no SIGALRM); OpenSafeFile degrades to a plain + # open without inter-process locking. + fcntl = None import math import os import random @@ -47,6 +52,8 @@ class OpenSafeFile: def lock_file(self): assert self.fd is not None + if fcntl is None: + return try: signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(self.timeout) @@ -58,6 +65,8 @@ class OpenSafeFile: def unlock_file(self): assert self.fd is not None + if fcntl is None: + return fcntl.flock(self.fd.fileno(), fcntl.LOCK_UN) diff --git a/utils/bit2fasm.py b/utils/bit2fasm.py index 859ec109..464c3de1 100755 --- a/utils/bit2fasm.py +++ b/utils/bit2fasm.py @@ -108,7 +108,12 @@ def main(): if args.bits_file: bits_file = stack.enter_context(open(args.bits_file, 'wb')) else: - bits_file = stack.enter_context(tempfile.NamedTemporaryFile()) + # On Windows an open NamedTemporaryFile cannot be re-opened by + # the bitread subprocess (sharing violation): create it closed + # and clean it up ourselves. + bits_file = tempfile.NamedTemporaryFile(delete=False) + bits_file.close() + stack.callback(os.unlink, bits_file.name) bit_to_bits( bitread=args.bitread, diff --git a/utils/fasm2frames.py b/utils/fasm2frames.py index 82ddc355..101744de 100755 --- a/utils/fasm2frames.py +++ b/utils/fasm2frames.py @@ -296,16 +296,16 @@ def main(): parser.add_argument('fn_in', help='Input FPGA assembly (.fasm) file') parser.add_argument( 'fn_out', - default='/dev/stdout', + default=None, nargs='?', - help='Output FPGA frame (.frm) file') + help='Output FPGA frame (.frm) file (default: stdout)') args = parser.parse_args() run( db_root=args.db_root, part=args.part, filename_in=args.fn_in, - f_out=open(args.fn_out, 'w'), + f_out=(open(args.fn_out, 'w') if args.fn_out else sys.stdout), sparse=args.sparse, roi=args.roi, debug=args.debug,