create capnp folder

This commit is contained in:
Lin 2024-09-25 18:42:04 +08:00
parent 4af766930b
commit faa222f2c1
11 changed files with 531 additions and 0 deletions

View File

@ -10,3 +10,4 @@ add_subdirectory(libpcf)
add_subdirectory(libbusgroup) add_subdirectory(libbusgroup)
add_subdirectory(libnamemanager) add_subdirectory(libnamemanager)
add_subdirectory(libtileconfig) add_subdirectory(libtileconfig)
add_subdirectory(libopenfpgacapnproto)

View File

@ -0,0 +1,90 @@
include(GNUInstallDirs)
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.
set(CAPNP_DEFS
gen/unique_blocks_uxsdcxx.capnp
)
capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS
${CAPNP_DEFS}
)
set(IC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../vtr-verilog-to-routing/libs/EXTERNAL/libinterchange/interchange)
set(CAPNPC_SRC_PREFIX ${IC_DIR})
find_program(WGET wget REQUIRED)
find_package(ZLIB REQUIRED)
# Add Java schema
set(JAVA_SCHEMA ${CMAKE_CURRENT_BINARY_DIR}/../../vtr-verilog-to-routing/libs/libvtrcapnproto/schema/capnp/java.capnp)
add_custom_command(
OUTPUT ${JAVA_SCHEMA}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/../../vtr-verilog-to-routing/libs/libvtrcapnproto/schema/capnp/
COMMAND ${WGET}
https://raw.githubusercontent.com/capnproto/capnproto-java/master/compiler/src/main/schema/capnp/java.capnp
-O ${JAVA_SCHEMA}
)
set(CAPNPC_IMPORT_DIRS)
list(APPEND CAPNPC_IMPORT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/../../vtr-verilog-to-routing/libs/libvtrcapnproto/schema)
set(IC_PROTOS)
set(IC_SRCS)
set(IC_HDRS)
foreach(PROTO ${IC_PROTOS})
capnp_generate_cpp(
IC_SRC
IC_HDR
${IC_DIR}/${PROTO}
)
list(APPEND IC_SRCS ${IC_SRC})
list(APPEND IC_HDRS ${IC_HDR})
list(APPEND CAPNP_DEFS ${IC_DIR}/${PROTO})
endforeach()
install(FILES ${CAPNP_DEFS} DESTINATION ${CMAKE_INSTALL_DATADIR}/openfpga)
add_library(libopenfpgacapnproto STATIC
${CAPNP_SRCS}
${IC_SRCS}
mmap_file.h
mmap_file.cpp
serdes_utils.h
serdes_utils.cpp
)
add_dependencies(libopenfpgacapnproto
get_java_capnp_schema)
target_include_directories(libopenfpgacapnproto PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/gen
)
target_link_libraries(libopenfpgacapnproto
libopenfpgautil
CapnProto::capnp
)

View File

@ -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
```

View File

@ -0,0 +1,4 @@
`rr_graph_uxsdcxx.capnp` is generated via uxsdcxx and is checked in to
avoid requiring python3 and the uxsdcxx depedencies to build VPR.
See `vpr/src/route/gen/SCHEMA_GENERATOR.md` for details.

View File

@ -0,0 +1,29 @@
# This file is generated by uxsdcap 0.1.0.
# https://github.com/duck2/uxsdcxx
# Modify only if your build process doesn't involve regenerating this file.
#
# Cmdline: /home/talaeikh/uxsdcxx/uxsdcap.py /home/talaeikh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd
# Input file: /home/talaeikh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd
# md5sum of input file: 9c14a0ddd3c6bc1e690ca6abf467bae6
@0xa136dddfdd48783b;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("uniqueblockcap");
enum BlockType {
cbx @0;
cby @1;
sb @2;
}
struct BlockInfo {
type @0: BlockType;
x @1: Int32;
y @2: Int32;
}
struct InstanceInfo {
x @0 :Int32;
y @1 :Int32;
}

View File

@ -0,0 +1,82 @@
//
// Created by amin on 1/17/23.
//
#ifndef VTR_INTRA_CLUSTER_SERDES_H
#define VTR_INTRA_CLUSTER_SERDES_H
#include <functional>
#include <vector>
#include <unordered_map>
#include "vtr_ndmatrix.h"
#include "vpr_error.h"
#include "matrix.capnp.h"
#include "map_lookahead.capnp.h"
#include "vpr_types.h"
#include "router_lookahead_map_utils.h"
template<typename CapElemType, typename ElemType>
void toVector(std::vector<ElemType>& vec_out,
const typename capnp::List<CapElemType>::Reader& m_in,
const std::function<void(std::vector<ElemType>&,
int,
const ElemType&)>& copy_fun) {
int size = m_in.size();
vec_out.resize(size);
for(int idx = 0; idx < size; idx++) {
copy_fun(vec_out, idx, m_in[idx]);
}
}
template<typename CapKeyType, typename CapValType, typename KeyType, typename CostType>
void toUnorderedMap(
std::unordered_map<KeyType, CostType>& map_in,
const int begin_flat_idx,
const int end_flat_idx,
const typename capnp::List<CapKeyType>::Reader& m_out_key,
const typename capnp::List<CapValType>::Reader& m_out_val,
const std::function<void(std::unordered_map<KeyType, CostType>&,
const KeyType&,
const typename CapValType::Reader&)>& copy_fun) {
for(int flat_idx = begin_flat_idx; flat_idx < end_flat_idx; flat_idx++) {
copy_fun(map_in, m_out_key[flat_idx], m_out_val[flat_idx]);
}
}
template<typename CapElemType, typename ElemType>
void fromVector(typename capnp::List<CapElemType>::Builder& m_out,
const std::vector<ElemType>& vec_in,
const std::function<void(typename capnp::List<CapElemType>::Builder&,
int,
const ElemType&)>& copy_fun) {
for(int idx = 0; idx < (int)vec_in.size(); idx++) {
copy_fun(m_out, idx, vec_in[idx]);
}
}
template<typename CapKeyType, typename CapValType, typename KeyType, typename CostType>
void FromUnorderedMap(
typename capnp::List<CapKeyType>::Builder& m_out_key,
typename capnp::List<CapValType>::Builder& m_out_val,
const KeyType out_offset,
const std::unordered_map<KeyType, CostType>& map_in,
const std::function<void(typename capnp::List<CapKeyType>::Builder&,
typename capnp::List<CapValType>::Builder&,
int,
const KeyType&,
const CostType&)>& copy_fun) {
int flat_idx = out_offset;
for (const auto& entry : map_in) {
copy_fun(m_out_key, m_out_val, flat_idx, entry.first, entry.second);
flat_idx++;
}
}
#endif //VTR_INTRA_CLUSTER_SERDES_H

View File

@ -0,0 +1,39 @@
#include "mmap_file.h"
#include "vtr_error.h"
#include "vtr_util.h"
#include <fcntl.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <sys/mman.h>
#endif
#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));
}

View File

@ -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_ */

View File

@ -0,0 +1,141 @@
#ifndef NDMATRIX_SERDES_H_
#define NDMATRIX_SERDES_H_
// Provide generic functions for serializing vtr::NdMatrix to and from Cap'n
// proto Matrix.
//
// Functions:
// ToNdMatrix - Converts Matrix capnproto message to vtr::NdMatrix
// FromNdMatrix - Converts vtr::NdMatrix to Matrix capnproto
//
// Example:
//
// Schema:
//
// using Matrix = import "matrix.capnp";
//
//
// struct Test {
// struct Vec2 {
// x @0 :Float32;
// y @1 :Float32;
// }
// vectors @0 :Matrix.Matrix(Vec2)
// }
//
// C++:
//
// struct Vec2 {
// float x;
// float y;
// };
//
// static void ToVec2(Vec2 *out, const Test::Vec2::Reader& in) {
// out->x = in.getX();
// out->y = in.getY();
// }
//
// static void FromVec2(Test::Vec2::Builder* out, const Vec2 &in) {
// out->setX(in.x);
// out->setY(in.y);
// }
//
// void example(std::string file) {
// vtr::NdMatrix<Vec2, 3> mat_in({3, 3, 3}, {2, 3});
//
// ::capnp::MallocMessageBuilder builder;
// auto test = builder.initRoot<Test>();
// auto vectors = test.getVectors();
//
// FromNdMatrix<3, Test::Vec2, Vec2>(&vectors, mat_in, FromVec2);
// writeMessageToFile(file, &builder);
//
// MmapFile f(file);
// ::capnp::FlatArrayMessageReader reader(f.getData());
// auto test = reader.getRoot<Test>();
//
// vtr::NdMatrix<Vec2, 3> mat_out;
// ToNdMatrix<3, Test::Vec2, Vec2>(&mat_out, test.getVectors(), FromVec2);
// }
#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_ */

View File

@ -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());
}
}

View File

@ -0,0 +1,20 @@
#ifndef SERDES_UTILS_H_
#define SERDES_UTILS_H_
#include <limits>
#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);
inline ::capnp::ReaderOptions default_large_capnp_opts() {
::capnp::ReaderOptions opts = ::capnp::ReaderOptions();
/* Remove traversal limit */
opts.traversalLimitInWords = std::numeric_limits<uint64_t>::max();
return opts;
}
#endif /* SERDES_UTILS_H_ */