Skip to content

Commit

Permalink
Merge pull request #48 from ispras/dfcxx_dot_output_format
Browse files Browse the repository at this point in the history
DOT output format
  • Loading branch information
ssmolov authored Aug 19, 2024
2 parents 6ee7c2c + 186a5e6 commit eac27e3
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ The list of arguments for `hls`-mode is presented below:
* `--out-sv-lib <PATH>`: *optional* filesystem-path option; used to specify the output SystemVerilog file for generated operations library.
* `--out-dfcir <PATH>`: *optional* filesystem-path option; used to specify the output DFCIR file.
* `--out-firrtl <PATH>`: *optional* filesystem-path option; used to specify the output FIRRTL file.
* `--out-dot <PATH>`: *optional* filesystem-path option; used to specify the output DOT file.
* `-a` or `-l`: *required* flag; used to specify the chosen scheduling strategy - either as-soon-as-possible or linear programming. **Exactly one of these flags has to be specified**.

**At least one of the `out-*` options has to be specified.**
Expand Down
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"out_sv" : "",
"out_sv_lib" : "",
"out_dfcir" : "",
"out_firrtl" : ""
"out_firrtl" : "",
"out_dot" : ""
}
}
2 changes: 1 addition & 1 deletion src/model/dfcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
project(DFCXX LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)

find_package(CTemplate REQUIRED COMPONENTS nothreads)
add_subdirectory(include)
add_subdirectory(includeDev)
add_subdirectory(lib)
8 changes: 8 additions & 0 deletions src/model/dfcxx/include/dfcxx/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
#include <string_view>
#include <vector>

// This forward declaration is needed to avoid
// users having to include LLVM headers.
namespace llvm {
class raw_fd_ostream;
}

namespace dfcxx {

class DFCIRBuilder;
Expand All @@ -36,6 +42,8 @@ class Kernel {
TypeBuilder typeBuilder;
VarBuilder varBuilder;
Graph graph;

bool compileDot(llvm::raw_fd_ostream *stream);

protected:
IO io;
Expand Down
1 change: 1 addition & 0 deletions src/model/dfcxx/include/dfcxx/typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum class OutputFormatID : uint8_t {
SVLibrary,
DFCIR,
FIRRTL,
DOT,
// Utility value. Constains the number of elements in the enum.
COUNT
};
Expand Down
1 change: 1 addition & 0 deletions src/model/dfcxx/includeDev/dfcxx/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "dfcxx/typedefs.h"

#include "dfcir/conversions/DFCIRPasses.h"
#include "llvm/Support/raw_ostream.h"
#include "mlir/IR/BuiltinOps.h"

#include <string>
Expand Down
9 changes: 9 additions & 0 deletions src/model/dfcxx/lib/dfcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ target_include_directories(DFCXX
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/includeDev>
)

set(TEMPLATES_PATH "${PROJECT_SOURCE_DIR}/templates")

add_compile_definitions(
TEMPLATES_PATH="${TEMPLATES_PATH}"
DOT_TEMPLATE_PATH="${TEMPLATES_PATH}/dot.tpl"
)

## MLIRDFCIR is ensured to be compiled beforehand.
target_link_libraries(DFCXX
PRIVATE $<LINK_ONLY:MLIRPass>
Expand All @@ -49,6 +56,8 @@ target_link_libraries(DFCXX
PRIVATE $<LINK_ONLY:CIRCTFIRRTLToHW>
PRIVATE $<LINK_ONLY:CIRCTSeqToSV>
PRIVATE $<LINK_ONLY:CIRCTExportVerilog>
PRIVATE $<LINK_ONLY:CTemplate::nothreads>
PRIVATE $<LINK_ONLY:LLVMSupport>
)

add_library(Utopia::DFCXX ALIAS DFCXX)
108 changes: 105 additions & 3 deletions src/model/dfcxx/lib/dfcxx/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@
#include "dfcxx/converter.h"
#include "dfcxx/IRbuilders/builder.h"
#include "dfcxx/kernel.h"
#include "dfcxx/vars/constant.h"

#include "ctemplate/template.h"
#include "llvm/Support/raw_ostream.h"

#include <ctime>
#include <iostream>
#include <string>
#include <unordered_map>

namespace dfcxx {

Expand Down Expand Up @@ -40,6 +47,94 @@ DFType Kernel::dfBool() {
return DFType(storage.addType(type));
}

bool Kernel::compileDot(llvm::raw_fd_ostream *stream) {
using ctemplate::TemplateDictionary;

uint64_t counter = 0;
std::unordered_map<Node, std::string> idMapping;
auto getName = [&idMapping, &counter] (const Node &node) -> std::string {
// If the node is named - just return the name.
auto name = DFVariable(node.var).getName();
if (!name.empty()) {
return name.data();
}
// If the mapping contains the node name - return it.
auto it = idMapping.find(node);
if (it != idMapping.end()) {
return it->second;
}
// Otherwise create and return the new node name mapping.
return (idMapping[node] = "node" + std::to_string(counter++));
};

std::string result;
TemplateDictionary *dict = new TemplateDictionary("dot");
dict->SetValue("KERNEL_NAME", this->getName().data());
auto time = std::time(nullptr);
auto *localTime = std::localtime(&time);
dict->SetFormattedValue("GEN_TIME",
"%d-%d-%d %d:%d:%d",
localTime->tm_mday,
localTime->tm_mon + 1,
localTime->tm_year + 1900,
localTime->tm_hour,
localTime->tm_min,
localTime->tm_sec);

for (Node node : graph.nodes) {
TemplateDictionary *elem = dict->AddSectionDictionary("ELEMENTS");
auto name = getName(node);
elem->SetValue("NAME", name);
std::string shape = "box";
std::string label = name;
auto data = node.data;
switch (node.type) {
case OFFSET:
shape = "diamond";
label = std::to_string(data.offset);
break;
case IN: shape = "invtriangle"; break;
case OUT: shape = "triangle"; break;
case MUX: shape = "invtrapezium"; break;
case ADD: label = "+"; break;
case SUB: label = "-"; break;
case MUL: label = "*"; break;
case DIV: label = "/"; break;
case AND: label = "&"; break;
case OR: label = "|"; break;
case XOR: label = "^"; break;
case NOT: label = "!"; break;
case NEG: label = "-"; break;
case LESS: label = "<"; break;
case LESSEQ: label = "<="; break;
case GREATER: label = ">"; break;
case GREATEREQ: label = ">="; break;
case EQ: label = "=="; break;
case NEQ: label = "!="; break;
case SHL: label = "<< " + std::to_string(data.bitShift); break;
case SHR: label = ">> " + std::to_string(data.bitShift); break;
default: break; // Silences -Wswitch warning for "CONST".
}

elem->SetValue("SHAPE", shape);
elem->SetValue("LABEL", label);

unsigned i = 0;
for (Channel chan : graph.inputs[node]) {
TemplateDictionary *conn = elem->AddSectionDictionary("CONNECTIONS");
conn->SetValue("SRC_NAME", getName(chan.source));
conn->SetValue("TRG_NAME", name);
conn->SetValue("CON_LABEL", std::to_string(i++));
}
}

ctemplate::ExpandTemplate(DOT_TEMPLATE_PATH, ctemplate::DO_NOT_STRIP,
dict, &result);
delete dict;

*stream << result;
return true;
}

bool Kernel::compile(const DFLatencyConfig &config,
const std::vector<std::string> &outputPaths,
Expand All @@ -55,9 +150,16 @@ bool Kernel::compile(const DFLatencyConfig &config,
? new llvm::raw_fd_ostream(outputPaths[i], ec)
: nullptr;
}
bool result = DFCIRConverter(config).convertAndPrint(compiled,
outputStreams,
sched);
bool result = true;
// Compile the kernel to DOT if such stream is specified.
if (auto *stream = outputStreams[OUT_FORMAT_ID_INT(DOT)]) {
result &= compileDot(stream);
}
if (result) {
result &= DFCIRConverter(config).convertAndPrint(compiled,
outputStreams,
sched);
}
// Every created output stream has to be closed explicitly.
for (llvm::raw_fd_ostream *stream : outputStreams) {
if (stream) {
Expand Down
15 changes: 15 additions & 0 deletions src/model/dfcxx/templates/dot.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{{#LICENSE}}
{{!===---------------------------------------------------------------------===}}
{{! }}
{{! Part of the Utopia HLS Project, under the Apache License v2.0 }}
{{! SPDX-License-Identifier: Apache-2.0 }}
{{! Copyright 2024 ISP RAS (http://www.ispras.ru) }}
{{! }}
{{!===---------------------------------------------------------------------===}}
{{/LICENSE}}// This file has been automatically generated by Utopia HLS at {{GEN_TIME}}.
digraph {{KERNEL_NAME}} {
{{#ELEMENTS}}{{NAME}} [shape={{SHAPE}}, label="{{LABEL}}"]
{{#CONNECTIONS}}{{SRC_NAME}} -> {{TRG_NAME}} [label="{{CON_LABEL}}"]
{{/CONNECTIONS}}
{{/ELEMENTS}}
}
6 changes: 6 additions & 0 deletions src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#define OUT_SV_LIB_JSON "out_sv_lib"
#define OUT_DFCIR_JSON "out_dfcir"
#define OUT_FIRRTL_JSON "out_firrtl"
#define OUT_DOT_JSON "out_dot"

//===----------------------------------------------------------------------===//
// CLI args/flags definitions
Expand All @@ -53,6 +54,7 @@
#define OUT_SV_LIB_ARG CLI_ARG("out-sv-lib")
#define OUT_DFCIR_ARG CLI_ARG("out-dfcir")
#define OUT_FIRRTL_ARG CLI_ARG("out-firrtl")
#define OUT_DOT_ARG CLI_ARG("out-dot")

//===----------------------------------------------------------------------===//

Expand Down Expand Up @@ -195,6 +197,9 @@ struct HlsOptions final : public AppOptions {
outputGroup->add_option(OUT_FIRRTL_ARG,
outNames[OUT_FORMAT_ID_INT(FIRRTL)],
"Path to output scheduled FIRRTL");
outputGroup->add_option(OUT_DOT_ARG,
outNames[OUT_FORMAT_ID_INT(DOT)],
"Path to output a DFCxx kernel in DOT format.");
outputGroup->require_option();
}

Expand All @@ -206,6 +211,7 @@ struct HlsOptions final : public AppOptions {
get(json, OUT_SV_LIB_JSON, outNames[OUT_FORMAT_ID_INT(SVLibrary)]);
get(json, OUT_DFCIR_JSON, outNames[OUT_FORMAT_ID_INT(DFCIR)]);
get(json, OUT_FIRRTL_JSON, outNames[OUT_FORMAT_ID_INT(FIRRTL)]);
get(json, OUT_DOT_JSON, outNames[OUT_FORMAT_ID_INT(DOT)]);
}

std::string latConfigFile;
Expand Down
11 changes: 10 additions & 1 deletion test/model/dfcxx/output_formats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,22 @@ TEST(DFCxxOutputFormats, FIRRTL) {
EXPECT_EQ(kernel.compile(config, paths, dfcxx::ASAP), true);
}

TEST(DFCxxOutputFormats, DOT) {
Polynomial2 kernel;
DFOutputPaths paths = {
{dfcxx::OutputFormatID::DOT, NULLDEVICE}
};
EXPECT_EQ(kernel.compile(config, paths, dfcxx::ASAP), true);
}

TEST(DFCxxOutputFormats, All) {
Polynomial2 kernel;
DFOutputPaths paths = {
{dfcxx::OutputFormatID::SystemVerilog, NULLDEVICE},
{dfcxx::OutputFormatID::SVLibrary, NULLDEVICE},
{dfcxx::OutputFormatID::DFCIR, NULLDEVICE},
{dfcxx::OutputFormatID::FIRRTL, NULLDEVICE}
{dfcxx::OutputFormatID::FIRRTL, NULLDEVICE},
{dfcxx::OutputFormatID::DOT, NULLDEVICE}
};
EXPECT_EQ(kernel.compile(config, paths, dfcxx::ASAP), true);
}

0 comments on commit eac27e3

Please sign in to comment.