Skip to content

Commit

Permalink
Merge pull request #1847 from lnis-uofu/bin_format
Browse files Browse the repository at this point in the history
Bin format
  • Loading branch information
tangxifan authored Oct 9, 2024
2 parents 9cb9394 + fc5c0f6 commit 7c3de4c
Show file tree
Hide file tree
Showing 38 changed files with 709 additions and 187 deletions.
1 change: 1 addition & 0 deletions libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ add_subdirectory(libpcf)
add_subdirectory(libbusgroup)
add_subdirectory(libnamemanager)
add_subdirectory(libtileconfig)
add_subdirectory(libopenfpgacapnproto)
67 changes: 67 additions & 0 deletions libs/libopenfpgacapnproto/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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
set(CAPNP_DEFS
gen/unique_blocks_uxsdcxx.capnp
)

capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS
${CAPNP_DEFS}
)



add_library(libopenfpgacapnproto STATIC
${CAPNP_SRCS}
${IC_SRCS}
)


add_dependencies(libopenfpgacapnproto
generate_unique_block_capnp
)


target_include_directories(libopenfpgacapnproto PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/gen
)
target_link_libraries(libopenfpgacapnproto
libopenfpgautil
libvtrcapnproto
)


add_custom_target(
generate_unique_block_capnp
COMMAND ${CMAKE_COMMAND} -E remove_directory unique_blocks_capnproto_generate
COMMAND ${CMAKE_COMMAND} -E make_directory unique_blocks_capnproto_generate
COMMAND ${CMAKE_COMMAND} -E chdir unique_blocks_capnproto_generate git clone https://github.com/duck2/uxsdcxx
COMMAND python3 -mpip install --user -r unique_blocks_capnproto_generate/uxsdcxx/requirements.txt
COMMAND ${CMAKE_COMMAND} -E chdir unique_blocks_capnproto_generate python3 uxsdcxx/uxsdcxx.py ${CMAKE_CURRENT_SOURCE_DIR}/gen/unique_blocks.xsd
COMMAND ${CMAKE_COMMAND} -E chdir unique_blocks_capnproto_generate python3 uxsdcxx/uxsdcap.py ${CMAKE_CURRENT_SOURCE_DIR}/gen/unique_blocks.xsd
unique_blocks_capnproto_generate/unique_blocks_uxsdcxx.h
unique_blocks_capnproto_generate/unique_blocks_uxsdcxx_capnp.h
unique_blocks_capnproto_generate/unique_blocks_uxsdcxx_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/gen
COMMAND ${CMAKE_COMMAND} -E copy unique_blocks_capnproto_generate/unique_blocks_uxsdcxx.capnp ${CMAKE_CURRENT_SOURCE_DIR}/gen
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gen/unique_blocks.xsd
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
74 changes: 74 additions & 0 deletions libs/libopenfpgacapnproto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Capnproto usage in Openfpga
======================

Capnproto is a data serialization framework designed for portabliity and speed.
In Openfpga, capnproto is used to provide binary formats for internal data
structures that can be computed once, and used many times. Specific examples:
- preload unique blocks

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 libopenfpgacapnproto
===========================

libopenfpgacapnproto should contain two elements:
- Utilities for working capnproto messages in Openfpga
- Generate source and header files of all capnproto messages used in Openfpga

I/O Utilities
-------------

Capnproto does not provide IO support, instead it works from arrays (or file
descriptors). To avoid re-writing this code, libopenfpgacapnproto provides two
utilities that should be used whenever reading or writing capnproto message to
disk. These two files are copied :
- `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.

Capnproto schemas
-----------------

libopenfpgacapnproto should contain all capnproto schema definitions used within
Openfpga. To add a new schema:
1. Add the schema to git in `libs/libopenfpgacapnproto/`
2. Add the schema file name to `capnp_generate_cpp` invocation in
`libs/libopenfpgacapnproto/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/libopenfpgacapnproto` after `libopenfpgacapnproto` has been rebuilt.

Writing capnproto binary files to text
======================================

The `capnp` tool (found in the CMake build directiory
`/vtr-verilog-to-routing/libs/EXTERNAL/capnproto/c++/src/capnp`) can be used to convert from a binary
capnp message to a textual form.

Example converting UniqueBlockCompactInfo from binary to text:

```
capnp convert binary:text unique_blocks_uxsdcxx.capnp UniqueBlockCompactInfo \
< test.bin > test.txt
```
4 changes: 4 additions & 0 deletions libs/libopenfpgacapnproto/gen/README.gen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
`unique_blocks_uxsdcxx.capnp` is generated via uxsdcxx and is checked in to
avoid requiring python3 and the uxsdcxx depedencies to build Openfpga.


38 changes: 38 additions & 0 deletions libs/libopenfpgacapnproto/gen/unique_blocks.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<!-- Enumeration for BlockType -->
<xs:simpleType name="type">
<xs:restriction base="xs:string">
<xs:enumeration value="cbx"/>
<xs:enumeration value="cby"/>
<xs:enumeration value="sb"/>
</xs:restriction>
</xs:simpleType>

<!-- InstanceInfo Structure (using attributes for x and y) -->
<xs:complexType name="instance">
<xs:attribute name="x" type="xs:unsignedInt" use="required"/>
<xs:attribute name="y" type="xs:unsignedInt" use="required"/>
</xs:complexType>

<!-- BlockInfo Structure (using attributes for type, x, and y, and instances as children) -->
<xs:complexType name="block">
<xs:sequence>
<xs:element name="instance" type="instance" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="type" type="type" use="required" />
<xs:attribute name="x" type="xs:unsignedInt" use="required" />
<xs:attribute name="y" type="xs:unsignedInt" use="required" />
</xs:complexType>

<!-- Root element definition -->
<xs:element name="unique_blocks">
<xs:complexType>
<xs:sequence>
<xs:element name="block" type="block" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>
34 changes: 34 additions & 0 deletions libs/libopenfpgacapnproto/gen/unique_blocks_uxsdcxx.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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: uxsdcxx/uxsdcap.py /home/jrlin/add_feature/bin_format/OpenFPGA/libs/libopenfpgacapnproto/gen/unique_blocks.xsd unique_blocks_capnproto_generate/unique_blocks_uxsdcxx.h unique_blocks_capnproto_generate/unique_blocks_uxsdcxx_capnp.h unique_blocks_capnproto_generate/unique_blocks_uxsdcxx_interface.h /home/jrlin/add_feature/bin_format/OpenFPGA/libs/libopenfpgacapnproto/gen
# Input file: /home/jrlin/add_feature/bin_format/OpenFPGA/libs/libopenfpgacapnproto/gen/unique_blocks.xsd
# md5sum of input file: 1db9d740309076fa51f61413bae1e072

@0xc5f2ef95c322aac3;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ucap");

enum Type {
uxsdInvalid @0;
cbx @1;
cby @2;
sb @3;
}

struct Instance {
x @0 :UInt32;
y @1 :UInt32;
}

struct Block {
type @0 :Type;
x @1 :UInt32;
y @2 :UInt32;
instances @3 :List(Instance);
}

struct UniqueBlocks {
blocks @0 :List(Block);
}
4 changes: 3 additions & 1 deletion openfpga/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ target_link_libraries(libopenfpga
libnamemanager
libtileconfig
libpugixml
libvpr)
libvpr
libopenfpgacapnproto
)

#Create the test executable
add_executable(openfpga ${EXEC_SOURCE})
Expand Down
12 changes: 9 additions & 3 deletions openfpga/src/annotation/device_rr_gsb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ std::vector<vtr::Point<size_t>> DeviceRRGSB::get_sb_unique_block_instance_coord(
sb_unique_module_id_[location_x][location_y];
if (unique_module_id_instance == unique_module_id) {
vtr::Point<size_t> instance_coord(location_x, location_y);
instance_map.push_back(instance_coord);
if (instance_coord != unique_block_coord) {
instance_map.push_back(instance_coord);
}
}
}
}
Expand Down Expand Up @@ -144,7 +146,9 @@ DeviceRRGSB::get_cbx_unique_block_instance_coord(
cbx_unique_module_id_[location_x][location_y];
if (unique_module_id_instance == unique_module_id) {
vtr::Point<size_t> instance_coord(location_x, location_y);
instance_map.push_back(instance_coord);
if (instance_coord != unique_block_coord) {
instance_map.push_back(instance_coord);
}
}
}
}
Expand Down Expand Up @@ -172,7 +176,9 @@ DeviceRRGSB::get_cby_unique_block_instance_coord(
cby_unique_module_id_[location_x][location_y];
if (unique_module_id_instance == unique_module_id) {
vtr::Point<size_t> instance_coord(location_x, location_y);
instance_map.push_back(instance_coord);
if (instance_coord != unique_block_coord) {
instance_map.push_back(instance_coord);
}
}
}
}
Expand Down
103 changes: 103 additions & 0 deletions openfpga/src/annotation/read_unique_blocks_bin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include <capnp/message.h>
#include <capnp/serialize.h>
#include <kj/io.h>

#include <string>
/* Headers from pugi XML library */
#include "pugixml.hpp"
#include "pugixml_util.hpp"

/* Headers from vtr util library */
#include "vtr_assert.h"
#include "vtr_log.h"
#include "vtr_time.h"

/* Headers from libarchfpga */
#include "arch_error.h"
#include "command_exit_codes.h"
#include "device_rr_gsb_utils.h"
#include "mmap_file.h"
#include "openfpga_digest.h"
#include "read_unique_blocks_bin.h"
#include "read_unique_blocks_xml.h"
#include "read_xml_util.h"
#include "rr_gsb.h"
#include "unique_blocks_uxsdcxx.capnp.h"
#include "write_xml_utils.h"

/********************************************************************
* This file includes the top-level functions of this library
* which includes:
* -- reads a bin file of unique blocks to the associated
* data structures: device_rr_gsb
*******************************************************************/
namespace openfpga {

/*read the instances' coordinate of a unique block from a bin file*/
std::vector<vtr::Point<size_t>> read_bin_unique_instance_coords(
const ucap::Block::Reader& unique_block) {
std::vector<vtr::Point<size_t>> instance_coords;
if (unique_block.hasInstances()) {
auto instance_list = unique_block.getInstances();
for (auto instance : instance_list) {
int instance_x = instance.getX();
int instance_y = instance.getY();
vtr::Point<size_t> instance_coordinate(instance_x, instance_y);
instance_coords.push_back(instance_coordinate);
}
}
return instance_coords;
}

/*read the unique block coordinate from a bin file */
vtr::Point<size_t> read_bin_unique_block_coord(
const ucap::Block::Reader& unique_block, ucap::Type& type) {
int block_x = unique_block.getX();
int block_y = unique_block.getY();
type = unique_block.getType();
vtr::Point<size_t> block_coordinate(block_x, block_y);
return block_coordinate;
}

/*top-level function to read unique blocks from bin file*/
int read_bin_unique_blocks(DeviceRRGSB& device_rr_gsb, const char* file_name,
bool verbose_output) {
/* clear unique modules & reserve memory to relavant vectors */
device_rr_gsb.clear_unique_modules();
device_rr_gsb.reserve_unique_modules();
MmapFile f(file_name);
::capnp::FlatArrayMessageReader reader(f.getData());
auto root = reader.getRoot<ucap::UniqueBlocks>();
if (root.hasBlocks()) {
auto block_list = root.getBlocks();
for (auto unique_block : block_list) {
ucap::Type type;
vtr::Point<size_t> block_coordinate = read_bin_unique_block_coord(
unique_block, type); /*get block coordinate and type*/
std::vector<vtr::Point<size_t>> instance_coords =
read_bin_unique_instance_coords(
unique_block); /* get a list of instance coordinates*/
/* get block coordinate and instance coordinate, try to setup
* device_rr_gsb */
if (type == ucap::Type::SB) {
device_rr_gsb.preload_unique_sb_module(block_coordinate,
instance_coords);
} else if (type == ucap::Type::CBY) {
device_rr_gsb.preload_unique_cby_module(block_coordinate,
instance_coords);
} else if (type == ucap::Type::CBX) {
device_rr_gsb.preload_unique_cbx_module(block_coordinate,
instance_coords);
} else if (type == ucap::Type::UXSD_INVALID) {
VTR_LOG_ERROR("Invalid block type!");
return CMD_EXEC_FATAL_ERROR;
}
}
}
device_rr_gsb.build_gsb_unique_module();
if (verbose_output) {
report_unique_module_status_read(device_rr_gsb, true);
}
return CMD_EXEC_SUCCESS;
}
} // namespace openfpga
Loading

0 comments on commit 7c3de4c

Please sign in to comment.