Initial place matrix serialization/deserialization support.
- Adds library for handling capnp files and generic NdMatrix support.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9fffea5..78dfaa0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,6 +38,7 @@
#Allow the user to decide weather to compile the graphics library
set(VPR_USE_EZGL "auto" CACHE STRING "Specify whether vpr uses the graphics library")
set_property(CACHE VPR_USE_EZGL PROPERTY STRINGS auto off on)
+option(VTR_ENABLE_CAPNPROTO "Enable capnproto binary serialization support in VPR." ON)
option(WITH_BLIFEXPLORER "Enable build with blifexplorer" OFF)
option(WITH_LIBRTLNUMBER "Enable build with librtlnumber" OFF)
@@ -276,8 +277,13 @@
#
# Set final flags
#
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARN_FLAGS} ${SANITIZE_FLAGS} ${PROFILING_FLAGS} ${COVERAGE_FLAGS} ${LOGGING_FLAGS} ${COLORED_COMPILE}")
-message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
+separate_arguments(
+ ADDITIONAL_FLAGS UNIX_COMMAND "${SANITIZE_FLAGS} ${PROFILING_FLAGS} ${COVERAGE_FLAGS} ${LOGGING_FLAGS} ${COLORED_COMPILE}"
+ )
+add_compile_options(${ADDITIONAL_FLAGS})
+separate_arguments(
+ WARN_FLAGS UNIX_COMMAND "${WARN_FLAGS}"
+ )
#
@@ -331,6 +337,9 @@
#Add the various sub-projects
add_subdirectory(libs)
+
+# Only add warn flags for VPR internal libraries.
+add_compile_options(${WARN_FLAGS})
add_subdirectory(vpr)
add_subdirectory(abc)
add_subdirectory(ODIN_II)
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index 22ee5a2..f95ac8d 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -1,9 +1,14 @@
-#VTR developed libraries
+#Externally developed libraries
+add_subdirectory(EXTERNAL)
+
+# VTR developed libraries
+# Only add warn flags for VPR internal libraries.
+add_compile_options(${WARN_FLAGS})
add_subdirectory(libarchfpga)
add_subdirectory(libvtrutil)
add_subdirectory(liblog)
add_subdirectory(libpugiutil)
add_subdirectory(librtlnumber)
-
-#Externally developed libraries
-add_subdirectory(EXTERNAL)
+if(${VTR_ENABLE_CAPNPROTO})
+ add_subdirectory(libvtrcapnproto)
+endif()
diff --git a/libs/EXTERNAL/CMakeLists.txt b/libs/EXTERNAL/CMakeLists.txt
index a2118ef..cb96998 100644
--- a/libs/EXTERNAL/CMakeLists.txt
+++ b/libs/EXTERNAL/CMakeLists.txt
@@ -13,3 +13,20 @@
if(VPR_USE_EZGL STREQUAL "on")
add_subdirectory(libezgl)
endif()
+
+if(${VTR_ENABLE_CAPNPROTO})
+ # Override default policy for capnproto (CMake policy version 3.1)
+ # Enable new IPO variables
+ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
+
+ # Enable option overrides via variables
+ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
+
+ # Re-enable CXX extensions for capnproto.
+ set(CMAKE_CXX_EXTENSIONS ON)
+
+ # Disable capnproto tests
+ set(BUILD_TESTING OFF)
+
+ add_subdirectory(capnproto EXCLUDE_FROM_ALL)
+endif()
diff --git a/libs/libvtrcapnproto/CMakeLists.txt b/libs/libvtrcapnproto/CMakeLists.txt
new file mode 100644
index 0000000..ccf3b6b
--- /dev/null
+++ b/libs/libvtrcapnproto/CMakeLists.txt
@@ -0,0 +1,39 @@
+if(NOT MSCV)
+ # These flags generate noisy but non-bug warnings when using lib kj,
+ # supress them.
+ set(WARN_FLAGS_TO_DISABLE
+ -Wno-undef
+ -Wno-non-virtual-dtor
+ )
+ foreach(flag ${WARN_FLAGS_TO_DISABLE})
+ CHECK_CXX_COMPILER_FLAG(${flag} CXX_COMPILER_SUPPORTS_${flag})
+ if(CXX_COMPILER_SUPPORTS_${flag})
+ #Flag supported, so enable it
+ add_compile_options(${flag})
+ endif()
+ endforeach()
+endif()
+
+# Create generated headers from capnp schema files
+#
+# Each schema used should appear here.
+capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS
+ place_delay_model.capnp
+ matrix.capnp
+ )
+
+add_library(libvtrcapnproto STATIC
+ ${CAPNP_SRCS}
+ mmap_file.h
+ mmap_file.cpp
+ serdes_utils.h
+ serdes_utils.cpp
+ )
+target_include_directories(libvtrcapnproto PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ )
+target_link_libraries(libvtrcapnproto
+ libvtrutil
+ CapnProto::capnp
+ )
diff --git a/libs/libvtrcapnproto/README.md b/libs/libvtrcapnproto/README.md
new file mode 100644
index 0000000..38187e0
--- /dev/null
+++ b/libs/libvtrcapnproto/README.md
@@ -0,0 +1,84 @@
+Capnproto usage in VTR
+======================
+
+Capnproto is a data serialization framework designed for portabliity and speed.
+In VPR, capnproto is used to provide binary formats for internal data
+structures that can be computed once, and used many times. Specific examples:
+ - rrgraph
+ - Router lookahead data
+ - Place matrix delay estimates
+
+What is capnproto?
+==================
+
+capnproto can be broken down into 3 parts:
+ - A schema language
+ - A code generator
+ - A library
+
+The schema language is used to define messages. Each message must have an
+explcit capnproto schema, which are stored in files suffixed with ".capnp".
+The capnproto documentation for how to write these schema files can be found
+here: https://capnproto.org/language.html
+
+The schema by itself is not especially useful. In order to read and write
+messages defined by the schema in a target language (e.g. C++), a code
+generation step is required. Capnproto provides a cmake function for this
+purpose, `capnp_generate_cpp`. This generates C++ source and header files.
+These source and header files combined with the capnproto C++ library, enables
+C++ code to read and write the messages matching a particular schema. The C++
+library API can be found here: https://capnproto.org/cxx.html
+
+Contents of libvtrcapnproto
+===========================
+
+libvtrcapnproto should contain two elements:
+ - Utilities for working capnproto messages in VTR
+ - Generate source and header files of all capnproto messages used in VTR
+
+I/O Utilities
+-------------
+
+Capnproto does not provide IO support, instead it works from arrays (or file
+descriptors). To avoid re-writing this code, libvtrcapnproto provides two
+utilities that should be used whenever reading or writing capnproto message to
+disk:
+ - `serdes_utils.h` provides the writeMessageToFile function - Writes a
+ capnproto message to disk.
+ - `mmap_file.h` provides MmapFile object - Maps a capnproto message from the
+ disk as a flat array.
+
+NdMatrix Utilities
+------------------
+
+A common datatype which appears in many data structures that VPR might want to
+serialize is the generic type `vtr::NdMatrix`. `ndmatrix_serdes.h` provides
+generic functions ToNdMatrix and FromNdMatrix, which can be used to generically
+convert between the provideid capnproto message `Matrix` and `vtr::NdMatrix`.
+
+Capnproto schemas
+-----------------
+
+libvtrcapnproto should contain all capnproto schema definitions used within
+VTR. To add a new schema:
+1. Add the schema to git in `libs/libvtrcapnproto/`
+2. Add the schema file name to `capnp_generate_cpp` invocation in
+ `libs/libvtrcapnproto/CMakeLists.txt`.
+
+The schema will be available in the header file `schema filename>.h`. The
+actual header file will appear in the CMake build directory
+`libs/libvtrcapnproto` after `libvtrcapnproto` has been rebuilt.
+
+Writing capnproto binary files to text
+======================================
+
+The `capnp` tool (found in the CMake build directiory
+`libs/EXTERNAL/capnproto/c++/src/capnp`) can be used to convert from a binary
+capnp message to a textual form.
+
+Example converting VprOverrideDelayModel from binary to text:
+
+```
+capnp convert binary:text place_delay_model.capnp VprOverrideDelayModel \
+ < place_delay.bin > place_delay.txt
+```
diff --git a/libs/libvtrcapnproto/matrix.capnp b/libs/libvtrcapnproto/matrix.capnp
new file mode 100644
index 0000000..14250a4
--- /dev/null
+++ b/libs/libvtrcapnproto/matrix.capnp
@@ -0,0 +1,9 @@
+@0xafffc9c1f309bc00;
+
+struct Matrix(Value) {
+ struct Entry {
+ value @0 :Value;
+ }
+ dims @0 :List(Int64);
+ data @1 :List(Entry);
+}
diff --git a/libs/libvtrcapnproto/mmap_file.cpp b/libs/libvtrcapnproto/mmap_file.cpp
new file mode 100644
index 0000000..57d29b5
--- /dev/null
+++ b/libs/libvtrcapnproto/mmap_file.cpp
@@ -0,0 +1,37 @@
+#include "mmap_file.h"
+#include "vtr_error.h"
+#include "vtr_util.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "kj/filesystem.h"
+
+MmapFile::MmapFile(const std::string& file)
+ : size_(0) {
+ try {
+ auto fs = kj::newDiskFilesystem();
+ auto path = fs->getCurrentPath().evalNative(file);
+
+ const auto& dir = fs->getRoot();
+ auto stat = dir.lstat(path);
+ auto f = dir.openFile(path);
+ size_ = stat.size;
+ data_ = f->mmap(0, stat.size);
+ } catch (kj::Exception& e) {
+ throw vtr::VtrError(e.getDescription().cStr(), e.getFile(), e.getLine());
+ }
+}
+
+const kj::ArrayPtr<const ::capnp::word> MmapFile::getData() const {
+ if ((size_ % sizeof(::capnp::word)) != 0) {
+ throw vtr::VtrError(
+ vtr::string_fmt("size_ %d is not a multiple of capnp::word", size_),
+ __FILE__, __LINE__);
+ }
+
+ return kj::arrayPtr(reinterpret_cast<const ::capnp::word*>(data_.begin()),
+ size_ / sizeof(::capnp::word));
+}
diff --git a/libs/libvtrcapnproto/mmap_file.h b/libs/libvtrcapnproto/mmap_file.h
new file mode 100644
index 0000000..ee1a93e
--- /dev/null
+++ b/libs/libvtrcapnproto/mmap_file.h
@@ -0,0 +1,19 @@
+#ifndef MMAP_FILE_H_
+#define MMAP_FILE_H_
+
+#include <string>
+#include "capnp/message.h"
+#include "kj/array.h"
+
+// Platform independent mmap, useful for reading large capnp's.
+class MmapFile {
+ public:
+ explicit MmapFile(const std::string& file);
+ const kj::ArrayPtr<const ::capnp::word> getData() const;
+
+ private:
+ size_t size_;
+ kj::Array<const kj::byte> data_;
+};
+
+#endif /* MMAP_FILE_H_ */
diff --git a/libs/libvtrcapnproto/ndmatrix_serdes.h b/libs/libvtrcapnproto/ndmatrix_serdes.h
new file mode 100644
index 0000000..5525c0b
--- /dev/null
+++ b/libs/libvtrcapnproto/ndmatrix_serdes.h
@@ -0,0 +1,87 @@
+#ifndef NDMATRIX_SERDES_H_
+#define NDMATRIX_SERDES_H_
+
+#include <functional>
+#include "vtr_ndmatrix.h"
+#include "vpr_error.h"
+#include "matrix.capnp.h"
+
+// Generic function to convert from Matrix capnproto message to vtr::NdMatrix.
+//
+// Template arguments:
+// N = Number of matrix dimensions, must be fixed.
+// CapType = Source capnproto message type that is a single element the
+// Matrix capnproto message.
+// CType = Target C++ type that is a single element of vtr::NdMatrix.
+//
+// Arguments:
+// m_out = Target vtr::NdMatrix.
+// m_in = Source capnproto message reader.
+// copy_fun = Function to convert from CapType to CType.
+//
+template<size_t N, typename CapType, typename CType>
+void ToNdMatrix(
+ vtr::NdMatrix<CType, N>* m_out,
+ const typename Matrix<CapType>::Reader& m_in,
+ const std::function<void(CType*, const typename CapType::Reader&)>& copy_fun) {
+ const auto& dims = m_in.getDims();
+ if (N != dims.size()) {
+ VPR_THROW(VPR_ERROR_OTHER,
+ "Wrong dimension of template N = %zu, m_in.getDims() = %zu",
+ N, dims.size());
+ }
+
+ std::array<size_t, N> dim_sizes;
+ size_t required_elements = 1;
+ for (size_t i = 0; i < N; ++i) {
+ dim_sizes[i] = dims[i];
+ required_elements *= dims[i];
+ }
+ m_out->resize(dim_sizes);
+
+ const auto& data = m_in.getData();
+ if (data.size() != required_elements) {
+ VPR_THROW(VPR_ERROR_OTHER,
+ "Wrong number of elements, expected %zu, actual %zu",
+ required_elements, data.size());
+ }
+
+ for (size_t i = 0; i < required_elements; ++i) {
+ copy_fun(&m_out->get(i), data[i].getValue());
+ }
+}
+
+// Generic function to convert from vtr::NdMatrix to Matrix capnproto message.
+//
+// Template arguments:
+// N = Number of matrix dimensions, must be fixed.
+// CapType = Target capnproto message type that is a single element the
+// Matrix capnproto message.
+// CType = Source C++ type that is a single element of vtr::NdMatrix.
+//
+// Arguments:
+// m_out = Target capnproto message builder.
+// m_in = Source vtr::NdMatrix.
+// copy_fun = Function to convert from CType to CapType.
+//
+template<size_t N, typename CapType, typename CType>
+void FromNdMatrix(
+ typename Matrix<CapType>::Builder* m_out,
+ const vtr::NdMatrix<CType, N>& m_in,
+ const std::function<void(typename CapType::Builder*, const CType&)>& copy_fun) {
+ size_t elements = 1;
+ auto dims = m_out->initDims(N);
+ for (size_t i = 0; i < N; ++i) {
+ dims.set(i, m_in.dim_size(i));
+ elements *= dims[i];
+ }
+
+ auto data = m_out->initData(elements);
+
+ for (size_t i = 0; i < elements; ++i) {
+ auto elem = data[i].getValue();
+ copy_fun(&elem, m_in.get(i));
+ }
+}
+
+#endif /* NDMATRIX_SERDES_H_ */
diff --git a/libs/libvtrcapnproto/place_delay_model.capnp b/libs/libvtrcapnproto/place_delay_model.capnp
new file mode 100644
index 0000000..df2a674
--- /dev/null
+++ b/libs/libvtrcapnproto/place_delay_model.capnp
@@ -0,0 +1,26 @@
+@0xfee98b707b92aa09;
+
+using Matrix = import "matrix.capnp";
+
+struct VprFloatEntry {
+ value @0 :Float32;
+}
+struct VprDeltaDelayModel {
+ delays @0 :Matrix.Matrix(VprFloatEntry);
+}
+
+struct VprOverrideEntry {
+ fromType @0 :Int16;
+ toType @1 :Int16;
+ fromClass @2 :Int16;
+ toClass @3 :Int16;
+ deltaX @4 :Int16;
+ deltaY @5 :Int16;
+
+ delay @6 :Float32;
+}
+
+struct VprOverrideDelayModel {
+ delays @0 :Matrix.Matrix(VprFloatEntry);
+ delayOverrides @1 :List(VprOverrideEntry);
+}
diff --git a/libs/libvtrcapnproto/serdes_utils.cpp b/libs/libvtrcapnproto/serdes_utils.cpp
new file mode 100644
index 0000000..2201fa5
--- /dev/null
+++ b/libs/libvtrcapnproto/serdes_utils.cpp
@@ -0,0 +1,22 @@
+#include "serdes_utils.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "vtr_error.h"
+#include "kj/filesystem.h"
+
+void writeMessageToFile(const std::string& file, ::capnp::MessageBuilder* builder) {
+ try {
+ auto fs = kj::newDiskFilesystem();
+ auto path = fs->getCurrentPath().evalNative(file);
+
+ const auto& dir = fs->getRoot();
+ auto f = dir.openFile(path, kj::WriteMode::CREATE | kj::WriteMode::MODIFY);
+ f->truncate(0);
+ auto f_app = kj::newFileAppender(std::move(f));
+ capnp::writeMessage(*f_app, *builder);
+ } catch (kj::Exception& e) {
+ throw vtr::VtrError(e.getDescription().cStr(), e.getFile(), e.getLine());
+ }
+}
diff --git a/libs/libvtrcapnproto/serdes_utils.h b/libs/libvtrcapnproto/serdes_utils.h
new file mode 100644
index 0000000..55e3299
--- /dev/null
+++ b/libs/libvtrcapnproto/serdes_utils.h
@@ -0,0 +1,11 @@
+#ifndef SERDES_UTILS_H_
+#define SERDES_UTILS_H_
+
+#include <string>
+#include "capnp/serialize.h"
+
+// Platform indepedent way to file message to a file on disk.
+void writeMessageToFile(const std::string& file,
+ ::capnp::MessageBuilder* builder);
+
+#endif /* SERDES_UTILS_H_ */
diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt
index 5c99091..df5a7eb 100644
--- a/vpr/CMakeLists.txt
+++ b/vpr/CMakeLists.txt
@@ -41,6 +41,10 @@
file(GLOB_RECURSE LIB_HEADERS src/*/*.h)
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
+if(${VTR_ENABLE_CAPNPROTO})
+ add_definitions("-DVTR_ENABLE_CAPNPROTO")
+endif()
+
#Create the library
add_library(libvpr STATIC
${LIB_HEADERS}
@@ -98,6 +102,10 @@
target_compile_definitions(libvpr PUBLIC ${GRAPHICS_DEFINES})
+if(${VTR_ENABLE_CAPNPROTO})
+ target_link_libraries(libvpr libvtrcapnproto)
+endif()
+
add_executable(vpr ${EXEC_SOURCES})
target_link_libraries(vpr libvpr)
diff --git a/vpr/src/place/place_delay_model.cpp b/vpr/src/place/place_delay_model.cpp
index ee015eb..9756c45 100644
--- a/vpr/src/place/place_delay_model.cpp
+++ b/vpr/src/place/place_delay_model.cpp
@@ -8,6 +8,15 @@
#include "vtr_log.h"
#include "vtr_math.h"
+#include "vpr_error.h"
+
+#ifdef VTR_ENABLE_CAPNPROTO
+# include "capnp/serialize.h"
+# include "place_delay_model.capnp.h"
+# include "ndmatrix_serdes.h"
+# include "mmap_file.h"
+# include "serdes_utils.h"
+#endif /* VTR_ENABLE_CAPNPROTO */
/*
* DeltaDelayModel
@@ -110,3 +119,178 @@
vtr::fclose(f);
}
+
+float OverrideDelayModel::get_delay_override(int from_type, int from_class, int to_type, int to_class, int delta_x, int delta_y) const {
+ t_override key;
+ key.from_type = from_type;
+ key.from_class = from_class;
+ key.to_type = to_type;
+ key.to_class = to_class;
+ key.delta_x = delta_x;
+ key.delta_y = delta_y;
+
+ auto iter = delay_overrides_.find(key);
+ if (iter == delay_overrides_.end()) {
+ VPR_THROW(VPR_ERROR_PLACE, "Key not found.");
+ }
+ return iter->second;
+}
+
+const DeltaDelayModel* OverrideDelayModel::base_delay_model() const {
+ return base_delay_model_.get();
+}
+
+void OverrideDelayModel::set_base_delay_model(std::unique_ptr<DeltaDelayModel> base_delay_model) {
+ base_delay_model_ = std::move(base_delay_model);
+}
+
+// When writing capnp targetted serialization, always allow compilation when
+// VTR_ENABLE_CAPNPROTO=OFF. Generally this means throwing an exception
+// instead.
+//
+#ifndef VTR_ENABLE_CAPNPROTO
+
+# define DISABLE_ERROR \
+ "is disable because VTR_ENABLE_CAPNPROTO=OFF." \
+ "Re-compile with CMake option VTR_ENABLE_CAPNPROTO=ON to enable."
+
+void DeltaDelayModel::read(const std::string& /*file*/) {
+ VPR_THROW(VPR_ERROR_PLACE, "DeltaDelayModel::read " DISABLE_ERROR);
+}
+
+void DeltaDelayModel::write(const std::string& /*file*/) const {
+ VPR_THROW(VPR_ERROR_PLACE, "DeltaDelayModel::write " DISABLE_ERROR);
+}
+
+void OverrideDelayModel::read(const std::string& /*file*/) {
+ VPR_THROW(VPR_ERROR_PLACE, "OverrideDelayModel::read " DISABLE_ERROR);
+}
+
+void OverrideDelayModel::write(const std::string& /*file*/) const {
+ VPR_THROW(VPR_ERROR_PLACE, "OverrideDelayModel::write " DISABLE_ERROR);
+}
+
+#else /* VTR_ENABLE_CAPNPROTO */
+
+static void ToFloat(float* out, const VprFloatEntry::Reader& in) {
+ // Getting a scalar field is always "get<field name>()".
+ *out = in.getValue();
+}
+
+static void FromFloat(VprFloatEntry::Builder* out, const float& in) {
+ // Setting a scalar field is always "set<field name>(value)".
+ out->setValue(in);
+}
+
+void DeltaDelayModel::read(const std::string& file) {
+ // MmapFile object creates an mmap of the specified path, and will munmap
+ // when the object leaves scope.
+ MmapFile f(file);
+
+ // FlatArrayMessageReader is used to read the message from the data array
+ // provided by MmapFile.
+ ::capnp::FlatArrayMessageReader reader(f.getData());
+
+ // When reading capnproto files the Reader object to use is named
+ // <schema name>::Reader.
+ //
+ // Initially this object is an empty VprDeltaDelayModel.
+ VprDeltaDelayModel::Reader model;
+
+ // The reader.getRoot performs a cast from the generic capnproto to fit
+ // with the specified schema.
+ //
+ // Note that capnproto does not validate that the incoming data matches the
+ // schema. If this property is required, some form of check would be
+ // required.
+ model = reader.getRoot<VprDeltaDelayModel>();
+
+ // ToNdMatrix is a generic function for converting a Matrix capnproto
+ // to a vtr::NdMatrix.
+ //
+ // The use must supply the matrix dimension (2 in this case), the source
+ // capnproto type (VprFloatEntry),
+ // target C++ type (flat), and a function to convert from the source capnproto
+ // type to the target C++ type (ToFloat).
+ //
+ // The second argument should be of type Matrix<X>::Reader where X is the
+ // capnproto element type.
+ ToNdMatrix<2, VprFloatEntry, float>(&delays_, model.getDelays(), ToFloat);
+}
+
+void DeltaDelayModel::write(const std::string& file) const {
+ // MallocMessageBuilder object is the generate capnproto message builder,
+ // using malloc for buffer allocation.
+ ::capnp::MallocMessageBuilder builder;
+
+ // initRoot<X> returns a X::Builder object that can be used to set the
+ // fields in the message.
+ auto model = builder.initRoot<VprDeltaDelayModel>();
+
+ // FromNdMatrix is a generic function for converting a vtr::NdMatrix to a
+ // Matrix message. It is the mirror function of ToNdMatrix described in
+ // read above.
+ auto delays = model.getDelays();
+ FromNdMatrix<2, VprFloatEntry, float>(&delays, delays_, FromFloat);
+
+ // writeMessageToFile writes message to the specified file.
+ writeMessageToFile(file, &builder);
+}
+
+void OverrideDelayModel::read(const std::string& file) {
+ MmapFile f(file);
+ ::capnp::FlatArrayMessageReader reader(f.getData());
+
+ vtr::Matrix<float> delays;
+ auto model = reader.getRoot<VprOverrideDelayModel>();
+ ToNdMatrix<2, VprFloatEntry, float>(&delays, model.getDelays(), ToFloat);
+
+ base_delay_model_ = std::make_unique<DeltaDelayModel>(delays);
+
+ // Reading non-scalar capnproto fields is roughly equivilant to using
+ // a std::vector of the field type. Actual type is capnp::List<X>::Reader.
+ auto overrides = model.getDelayOverrides();
+ std::vector<std::pair<t_override, float> > overrides_arr(overrides.size());
+ for (size_t i = 0; i < overrides.size(); ++i) {
+ const auto& elem = overrides[i];
+ overrides_arr[i].first.from_type = elem.getFromType();
+ overrides_arr[i].first.to_type = elem.getToType();
+ overrides_arr[i].first.from_class = elem.getFromClass();
+ overrides_arr[i].first.to_class = elem.getToClass();
+ overrides_arr[i].first.delta_x = elem.getDeltaX();
+ overrides_arr[i].first.delta_y = elem.getDeltaY();
+
+ overrides_arr[i].second = elem.getDelay();
+ }
+
+ delay_overrides_ = vtr::make_flat_map2(std::move(overrides_arr));
+}
+
+void OverrideDelayModel::write(const std::string& file) const {
+ ::capnp::MallocMessageBuilder builder;
+ auto model = builder.initRoot<VprOverrideDelayModel>();
+
+ auto delays = model.getDelays();
+ FromNdMatrix<2, VprFloatEntry, float>(&delays, base_delay_model_->delays(), FromFloat);
+
+ // Non-scalar capnproto fields should be first initialized with
+ // init<field name>(count), and then accessed from the returned
+ // std::vector-like Builder object (specifically capnp::List<X>::Builder).
+ auto overrides = model.initDelayOverrides(delay_overrides_.size());
+ auto dst_iter = overrides.begin();
+ for (const auto& src : delay_overrides_) {
+ auto elem = *dst_iter++;
+ elem.setFromType(src.first.from_type);
+ elem.setToType(src.first.to_type);
+ elem.setFromClass(src.first.from_class);
+ elem.setToClass(src.first.to_class);
+ elem.setDeltaX(src.first.delta_x);
+ elem.setDeltaY(src.first.delta_y);
+
+ elem.setDelay(src.second);
+ }
+
+ writeMessageToFile(file, &builder);
+}
+
+#endif
diff --git a/vpr/src/place/place_delay_model.h b/vpr/src/place/place_delay_model.h
index f2412b1..9235c29 100644
--- a/vpr/src/place/place_delay_model.h
+++ b/vpr/src/place/place_delay_model.h
@@ -41,9 +41,8 @@
class DeltaDelayModel : public PlaceDelayModel {
public:
DeltaDelayModel() {}
- DeltaDelayModel(vtr::Matrix<float> delta_delays, t_router_opts router_opts)
- : delays_(std::move(delta_delays))
- , router_opts_(router_opts) {}
+ DeltaDelayModel(vtr::Matrix<float> delta_delays)
+ : delays_(std::move(delta_delays)) {}
void compute(
const RouterDelayProfiler& router,
@@ -53,16 +52,14 @@
float delay(int from_x, int from_y, int /*from_pin*/, int to_x, int to_y, int /*to_pin*/) const override;
void dump_echo(std::string filepath) const override;
- void read(const std::string& /*file*/) override {
- VPR_THROW(VPR_ERROR_ROUTE, "DeltaDelayModel::read unimplemented");
- }
- void write(const std::string& /*file*/) const override {
- VPR_THROW(VPR_ERROR_ROUTE, "DeltaDelayModel::write unimplemented");
+ void read(const std::string& file) override;
+ void write(const std::string& file) const override;
+ const vtr::Matrix<float>& delays() const {
+ return delays_;
}
private:
vtr::Matrix<float> delays_;
- t_router_opts router_opts_;
};
class OverrideDelayModel : public PlaceDelayModel {
@@ -75,19 +72,20 @@
float delay(int from_x, int from_y, int from_pin, int to_x, int to_y, int to_pin) const override;
void dump_echo(std::string filepath) const override;
- void read(const std::string& /*file*/) override {
- VPR_THROW(VPR_ERROR_ROUTE, "OverrideDelayModel::read unimplemented");
- }
- void write(const std::string& /*file*/) const override {
- VPR_THROW(VPR_ERROR_ROUTE, "OverrideDelayModel::write unimplemented");
- }
+ void read(const std::string& file) override;
+ void write(const std::string& file) const override;
public: //Mutators
- private:
- std::unique_ptr<PlaceDelayModel> base_delay_model_;
-
+ void set_base_delay_model(std::unique_ptr<DeltaDelayModel> base_delay_model);
+ const DeltaDelayModel* base_delay_model() const;
+ float get_delay_override(int from_type, int from_class, int to_type, int to_class, int delta_x, int delta_y) const;
void set_delay_override(int from_type, int from_class, int to_type, int to_class, int delta_x, int delta_y, float delay);
- void compute_override_delay_model(const RouterDelayProfiler& router);
+
+ private:
+ std::unique_ptr<DeltaDelayModel> base_delay_model_;
+
+ void compute_override_delay_model(const RouterDelayProfiler& router,
+ const t_router_opts& router_opts);
struct t_override {
short from_type;
@@ -104,7 +102,6 @@
};
vtr::flat_map2<t_override, float> delay_overrides_;
- t_router_opts router_opts_;
};
#endif
diff --git a/vpr/src/place/timing_place_lookup.cpp b/vpr/src/place/timing_place_lookup.cpp
index c3c4fac..48dee35 100644
--- a/vpr/src/place/timing_place_lookup.cpp
+++ b/vpr/src/place/timing_place_lookup.cpp
@@ -183,7 +183,6 @@
const t_placer_opts& placer_opts,
const t_router_opts& router_opts,
int longest_length) {
- router_opts_ = router_opts;
delays_ = compute_delta_delay_model(
route_profiler,
placer_opts, router_opts, /*measure_directconnect=*/true,
@@ -195,15 +194,14 @@
const t_placer_opts& placer_opts,
const t_router_opts& router_opts,
int longest_length) {
- router_opts_ = router_opts;
auto delays = compute_delta_delay_model(
route_profiler,
placer_opts, router_opts, /*measure_directconnect=*/false,
longest_length);
- base_delay_model_ = std::make_unique<DeltaDelayModel>(delays, router_opts);
+ base_delay_model_ = std::make_unique<DeltaDelayModel>(delays);
- compute_override_delay_model(route_profiler);
+ compute_override_delay_model(route_profiler, router_opts);
}
/******* File Accessible Functions **********/
@@ -855,8 +853,10 @@
return true;
}
-void OverrideDelayModel::compute_override_delay_model(const RouterDelayProfiler& route_profiler) {
- t_router_opts router_opts2 = router_opts_;
+void OverrideDelayModel::compute_override_delay_model(
+ const RouterDelayProfiler& route_profiler,
+ const t_router_opts& router_opts) {
+ t_router_opts router_opts2 = router_opts;
router_opts2.astar_fac = 0.;
//Look at all the direct connections that exist, and add overrides to delay model
diff --git a/vpr/test/test_place_delay_model_serdes.cpp b/vpr/test/test_place_delay_model_serdes.cpp
new file mode 100644
index 0000000..1db5124
--- /dev/null
+++ b/vpr/test/test_place_delay_model_serdes.cpp
@@ -0,0 +1,87 @@
+#include "catch.hpp"
+
+#include "place_delay_model.h"
+
+namespace {
+
+#ifdef VTR_ENABLE_CAPNPROTO
+static constexpr const char kDeltaDelayBin[] = "test_delta_delay.bin";
+static constexpr const char kOverrideDelayBin[] = "test_override_delay.bin";
+
+TEST_CASE("round_trip_delta_delay_model", "[vpr]") {
+ constexpr size_t kDimX = 10;
+ constexpr size_t kDimY = 10;
+ vtr::Matrix<float> delays;
+ delays.resize({kDimX, kDimY});
+
+ for (size_t x = 0; x < kDimX; ++x) {
+ for (size_t y = 0; y < kDimY; ++y) {
+ delays[x][y] = (x + 1) * (y + 1);
+ }
+ }
+ DeltaDelayModel model(std::move(delays));
+ const auto& delays1 = model.delays();
+
+ model.write(kDeltaDelayBin);
+
+ DeltaDelayModel model2;
+ model2.read(kDeltaDelayBin);
+
+ const auto& delays2 = model2.delays();
+
+ REQUIRE(delays1.size() == delays2.size());
+ REQUIRE(delays1.ndims() == delays2.ndims());
+ for (size_t dim = 0; dim < delays1.ndims(); ++dim) {
+ REQUIRE(delays1.dim_size(dim) == delays2.dim_size(dim));
+ }
+
+ for (size_t x = 0; x < kDimX; ++x) {
+ for (size_t y = 0; y < kDimY; ++y) {
+ CHECK(delays1[x][y] == delays2[x][y]);
+ }
+ }
+}
+
+TEST_CASE("round_trip_override_delay_model", "[vpr]") {
+ constexpr size_t kDimX = 10;
+ constexpr size_t kDimY = 10;
+ vtr::Matrix<float> delays;
+ delays.resize({kDimX, kDimY});
+
+ for (size_t x = 0; x < kDimX; ++x) {
+ for (size_t y = 0; y < kDimY; ++y) {
+ delays[x][y] = (x + 1) * (y + 1);
+ }
+ }
+ OverrideDelayModel model;
+ auto base_model = std::make_unique<DeltaDelayModel>(delays);
+ model.set_base_delay_model(std::move(base_model));
+ model.set_delay_override(1, 2, 3, 4, 5, 6, -1);
+ model.set_delay_override(2, 2, 3, 4, 5, 6, -2);
+
+ model.write(kOverrideDelayBin);
+
+ OverrideDelayModel model2;
+ model2.read(kOverrideDelayBin);
+
+ const auto& delays1 = model.base_delay_model()->delays();
+ const auto& delays2 = model2.base_delay_model()->delays();
+
+ REQUIRE(delays1.size() == delays2.size());
+ REQUIRE(delays1.ndims() == delays2.ndims());
+ for (size_t dim = 0; dim < delays1.ndims(); ++dim) {
+ REQUIRE(delays1.dim_size(dim) == delays2.dim_size(dim));
+ }
+
+ for (size_t x = 0; x < kDimX; ++x) {
+ for (size_t y = 0; y < kDimY; ++y) {
+ CHECK(delays1[x][y] == delays2[x][y]);
+ }
+ }
+
+ CHECK(model2.get_delay_override(1, 2, 3, 4, 5, 6) == -1);
+ CHECK(model2.get_delay_override(2, 2, 3, 4, 5, 6) == -2);
+}
+#endif
+
+} // namespace