diff --git a/README.md b/README.md index 6913873..dfb5484 100644 --- a/README.md +++ b/README.md @@ -211,12 +211,12 @@ The compiled Utopia HLS executable has a CLI to accept a number of parameters af For the executable there are three general arguments: `-h,--help`, `-H,--help-all` and `-v,--version` for printing simple and extended help-messages, and printing current executable's version respectively. -Unless neither of the three arguments is used, first argument is the mode which the executable has to function in. Currently there is only one available mode `hls`. +Unless neither of the three arguments is used, first argument is the mode which the executable has to function in. Currently there are only two available modes: `hls` and `sim`. -The list of arguments for `hls`-mode is presented below: +`hls` mode is used to perform the high-level synthesis of digital hardware description from the input DFCxx kernel. The list of arguments for `hls`-mode is presented below: * `-h,--help`: *optional* flag; used to print the help-message about other arguments. -* `--config `: *required* filesystem-path option; used to specify the file for a JSON latency configuration file. Its format is presented in *JSON Configuration* section. +* `--config `: *required* filesystem-path option; used to specify the file for a JSON latency configuration file. Its format is presented in `docs/latency_config.md`. * `--out-sv `: *optional* filesystem-path option; used to specify the output SystemVerilog file. * `--out-sv-lib `: *optional* filesystem-path option; used to specify the output SystemVerilog file for generated operations library. * `--out-dfcir `: *optional* filesystem-path option; used to specify the output DFCIR file. @@ -232,43 +232,13 @@ Here is an example of an Utopia HLS CLI call: umain hls --config ~/utopia-user/config.json --out-sv ~/outFile.sv --out-dfcir ~/outFile2.mlir -a ``` -### JSON Configuration +`sim` mode is used to simulate the input DFCxx kernel. The list of arguments for `sim`-mode is presented below: -Latency configuration for each computational operation (number of pipeline stages) used in a DFCxx kernel is provided via a JSON file. - -Currently each operation has two specifications: for integer values (`INT`) and floating point (`FLOAT`) values. - -The list of all computational operations is provided below: - -* `ADD` - Addition -* `SUB` - Subtraction -* `MUL` - Multiplication -* `DIV` - Division -* `AND` - Logical conjunction -* `OR` - Logical disjunction -* `XOR` - Exclusive logical disjunction -* `NOT` - Logical inversion -* `NEG` - Negation -* `LESS` - "less" comparison -* `LESSEQ` - "less or equal" comparison -* `GREATER` - "greater" comparison -* `GREATEREQ` - "greater or equal" comparison -* `EQ` - "equal" comparison -* `NEQ` - "not equal" comparison - -JSON configuration structure states that for every operation with a specific configuration (each pair is represented as a separate JSON-field with `_` between pair's elements) present in the kernel, operation's latency will be provided. - -Here is an example of a JSON configuration file, containing latencies for multiplication, addition and subtraction of integer numbers: - -```json -{ - "MUL_INT": 3, - "ADD_INT": 1, - "SUB_INT": 1 -} -``` +* `-h,--help`: *optional* flag; used to print the help-message about other arguments. +* `--in `: *optional* filesystem-path option; used to specify the input file for simulation data (default: `sim.txt`). Its format is presented in `docs/simulation.md`. +* `--out `: *optional* filesystem-path option; used to specify the output VCD file (default: `sim_out.vcd`). -## Examples +### Examples Root subdirectory `examples` contains different examples of DFCxx kernels, `start`-function definitions and JSON configuration files. @@ -280,9 +250,17 @@ cmake --build build build/src/umain hls --config examples/polynomial2/add_int_2_mul_int3.json -a --out-sv output ``` -The execution command is going to pass a JSON configuration file (with 2 and 3 pipeline stages for integer addition +The execution command is going to pass a JSON configuration file (with latencies 2 and 3 for integer addition and multiplication respectively) to Utopia HLS, resulting in the creation of the file `output`, containing a SystemVerilog -module for Polynomial2 kernel with a greedy ASAP-scheduling. +module for `Polynomial2` kernel with a greedy ASAP-scheduling. + +The same kernel can be simulated with: + +```bash +build/src/umain sim --in examples/polynomial2/sim.txt --out output.vcd +``` + +This command uses the simulation data from `sim.txt` file and stores the simulation trace in the output file `output.vcd`. ## DFCxx Documentation diff --git a/config.json b/config.json index 4504a0d..2ac439d 100644 --- a/config.json +++ b/config.json @@ -8,5 +8,9 @@ "out_dfcir" : "", "out_firrtl" : "", "out_dot" : "" + }, + "sim": { + "in" : "sim.txt", + "out" : "sim_out.vcd" } } diff --git a/docs/latency_config.md b/docs/latency_config.md new file mode 100644 index 0000000..63f9b76 --- /dev/null +++ b/docs/latency_config.md @@ -0,0 +1,35 @@ +## Latency Configuration + +Latency configuration is a JSON-based file describing characteristics of computational operations for the specific DFCxx kernel. + +Currently each operation has two specifications based on the types of its arguments: for integer values (`INT`) and floating point (`FLOAT`) values respectively. + +All the supported computational operations are listed below: + +* `ADD` - Addition +* `SUB` - Subtraction +* `MUL` - Multiplication +* `DIV` - Division +* `AND` - Logical conjunction +* `OR` - Logical disjunction +* `XOR` - Exclusive logical disjunction +* `NOT` - Logical inversion +* `NEG` - Negation +* `LESS` - "less" comparison +* `LESSEQ` - "less or equal" comparison +* `GREATER` - "greater" comparison +* `GREATEREQ` - "greater or equal" comparison +* `EQ` - "equal" comparison +* `NEQ` - "not equal" comparison + +JSON configuration structure states that for every operation with a specific configuration (each pair is represented as a separate JSON-field with `_` between pair's elements) present in the kernel, operation's latency has to be provided. + +Here is an example of a JSON configuration file, containing latencies for multiplication, addition and subtraction of integer numbers: + +```json +{ + "MUL_INT": 3, + "ADD_INT": 1, + "SUB_INT": 1 +} +``` diff --git a/docs/simulation.md b/docs/simulation.md new file mode 100644 index 0000000..d4cdc56 --- /dev/null +++ b/docs/simulation.md @@ -0,0 +1,32 @@ +## DFCxx Simulation + +DFCxx kernels can be simulated to check that they describe computations as expected. The simulation doesn't take scheduling into account and requires every computational node to use **and** accept some values at every simulation tick. + +### Input format + +The format to provide simulation input data is the following: + +* input data is divided into blocks separated with a newline character (`\n`) - one block for each simulation step +* every block has a number of lines, each of which has the case-sensitive name of some **input** stream/scalar variable and its hex-value (these values do not have an explicit type - they will be casted to the types of related computational nodes) +* stream/scalar value name and the hex-value are separated with a single space character (` `) +* the provided value must be a valid hex-value: with or without `0x`, with either lower- or uppercase letters +* if some stream/scalar hex-value is present twice or more in the same block - its latest described value is used + +Here is an example of an input simulation file for `MuxMul` kernel, which has two input streams: `x` (unsigned 32-bit integer values) and `ctrl` (unsigned 1-bit integer values). + +```txt +x 0x32 +ctrl 0x1 + +x 0x45 +ctrl 0x0 + +x 0x56 +ctrl 0x1 +``` + +In this example, `x` accepts values `50` (`0x32`), `69` (`0x45`) and `86` (`0x56`), while `ctrl` accepts `1`, `0` and `1`. This means that 3 simulation ticks will be performed for the provided kernel. + +### Not Supported Constructions + +* *offsets* \ No newline at end of file diff --git a/examples/addconst/sim.txt b/examples/addconst/sim.txt new file mode 100644 index 0000000..1e7620b --- /dev/null +++ b/examples/addconst/sim.txt @@ -0,0 +1,5 @@ +x 0x32 + +x 0x45 + +x 0x56 diff --git a/examples/idct/idct.h b/examples/idct/idct.h index 56ad875..3d48be3 100644 --- a/examples/idct/idct.h +++ b/examples/idct/idct.h @@ -53,17 +53,17 @@ class IDCT : public dfcxx::Kernel { DFVariable x7 = (values[kDIM * i + 3]); DFVariable x8 = (x4 + x5) * W7; - x4 = x8 + (W1 - W7) * x4; - x5 = x8 - (W1 - W7) * x5; - x8 = W3 * (x6 + x7); - x6 = x8 - (W3 - W5) * x6; - x7 = x8 - (W3 + W5) * x7; + x4 = x8 + x4 * (W1 - W7); + x5 = x8 - x5 * (W1 + W7); + x8 = (x6 + x7) * W3; + x6 = x8 - x6 * (W3 - W5); + x7 = x8 - x7 * (W3 + W5); x8 = x0 + x1; x0 = x0 - x1; x1 = (x3 + x2) * W6; - x2 = x1 - (W2 + W6) * x2; - x3 = x1 + (W2 - W6) * x3; + x2 = x1 - x2 * (W2 + W6); + x3 = x1 + x3 * (W2 - W6); x1 = x4 + x6; x4 = x4 - x6; x6 = x5 + x7; @@ -97,17 +97,17 @@ class IDCT : public dfcxx::Kernel { DFVariable x7 = values[kDIM * 3 + i]; DFVariable x8 = ((x4 + x5) * W7) + const4; - x4 = (x8 + (W1 - W7) * x4) >> 3; - x5 = (x8 - (W1 - W7) * x5) >> 3; - x8 = (W3 * (x6 + x7)) + const4; - x6 = (x8 - (W3 - W5) * x6) >> 3; - x7 = (x8 - (W3 + W5) * x7) >> 3; + x4 = (x8 + x4 * (W1 - W7)) >> 3; + x5 = (x8 - x5 * (W1 + W7)) >> 3; + x8 = ((x6 + x7) * W3) + const4; + x6 = (x8 - x6 * (W3 - W5)) >> 3; + x7 = (x8 - x7 * (W3 + W5)) >> 3; x8 = x0 + x1; x0 = x0 - x1; x1 = ((x3 + x2) * W6) + const4; - x2 = (x1 - (W2 + W6) * x2) >> 3; - x3 = (x1 + (W2 - W6) * x3) >> 3; + x2 = (x1 - x2 * (W2 + W6)) >> 3; + x3 = (x1 + x3 * (W2 - W6)) >> 3; x1 = x4 + x6; x4 = x4 - x6; x6 = x5 + x7; @@ -119,7 +119,7 @@ class IDCT : public dfcxx::Kernel { x0 = x0 - x2; x2 = (((x4 + x5) * const181) + const128) >> 8; x4 = (((x4 - x5) * const181) + const128) >> 8; - + values[kDIM * 0 + i] = (x7 + x1) >> 14; values[kDIM * 1 + i] = (x3 + x2) >> 14; values[kDIM * 2 + i] = (x0 + x4) >> 14; diff --git a/examples/idct/sim.txt b/examples/idct/sim.txt new file mode 100644 index 0000000..339e8fd --- /dev/null +++ b/examples/idct/sim.txt @@ -0,0 +1,64 @@ +x0 0x0 +x1 0x1 +x2 0x2 +x3 0x3 +x4 0x4 +x5 0x5 +x6 0x6 +x7 0x7 +x8 0x8 +x9 0x9 +x10 0xA +x11 0xB +x12 0xC +x13 0xD +x14 0xE +x15 0xF +x16 0x10 +x17 0x11 +x18 0x12 +x19 0x13 +x20 0x14 +x21 0x15 +x22 0x16 +x23 0x17 +x24 0x18 +x25 0x19 +x26 0x1A +x27 0x1B +x28 0x1C +x29 0x1D +x30 0x1E +x31 0x1F +x32 0x20 +x33 0x21 +x34 0x22 +x35 0x23 +x36 0x24 +x37 0x25 +x38 0x26 +x39 0x27 +x40 0x28 +x41 0x29 +x42 0x2A +x43 0x2B +x44 0x2C +x45 0x2D +x46 0x2E +x47 0x2F +x48 0x30 +x49 0x31 +x50 0x32 +x51 0x33 +x52 0x34 +x53 0x35 +x54 0x36 +x55 0x37 +x56 0x38 +x57 0x39 +x58 0x3A +x59 0x3B +x60 0x3C +x61 0x3D +x62 0x3E +x63 0x3F \ No newline at end of file diff --git a/examples/matrixmul2/sim.txt b/examples/matrixmul2/sim.txt new file mode 100644 index 0000000..0c3a5fa --- /dev/null +++ b/examples/matrixmul2/sim.txt @@ -0,0 +1,8 @@ +x11 0x32 +x12 0x64 +x21 0x48 +x22 0x99 +y11 0x1 +y12 0x0 +y21 0x0 +y22 0x1 diff --git a/examples/muxmul/sim.txt b/examples/muxmul/sim.txt new file mode 100644 index 0000000..4edb971 --- /dev/null +++ b/examples/muxmul/sim.txt @@ -0,0 +1,8 @@ +x 0x32 +ctrl 0x1 + +x 0x45 +ctrl 0x0 + +x 0x56 +ctrl 0x1 diff --git a/examples/polynomial2/sim.txt b/examples/polynomial2/sim.txt new file mode 100644 index 0000000..1e7620b --- /dev/null +++ b/examples/polynomial2/sim.txt @@ -0,0 +1,5 @@ +x 0x32 + +x 0x45 + +x 0x56 diff --git a/examples/scalar3/sim.txt b/examples/scalar3/sim.txt new file mode 100644 index 0000000..db2cb18 --- /dev/null +++ b/examples/scalar3/sim.txt @@ -0,0 +1,6 @@ +x1 0x2 +y1 0x32 +x2 0x3 +y2 0x32 +x3 0x4 +y3 0x32 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9fa311b..79cf2eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,29 +19,42 @@ INITIALIZE_EASYLOGGINGPP +// User-defined function to specify functional behaviour of top-level kernel. +std::unique_ptr start(); + //===-----------------------------------------------------------------------===/ // High-Level Synthesis //===-----------------------------------------------------------------------===/ struct HlsContext { - HlsContext(const HlsOptions &options): - options(options) - {} + HlsContext(const HlsOptions &options) : options(options) {} const HlsOptions &options; }; -// User-defined function to specify functional behaviour of top-level kernel. -std::unique_ptr start(); +//===-----------------------------------------------------------------------===/ +// DFCxx Simulation. +//===-----------------------------------------------------------------------===/ + +struct SimContext { + SimContext(const SimOptions &options): options(options) {} -int hlsMain(HlsContext &context) { + const SimOptions &options; +}; + +int hlsMain(const HlsContext &context) { auto kernel = start(); bool useASAP = context.options.asapScheduler; - kernel->compile(context.options.latConfig, - context.options.outNames, - (useASAP) ? dfcxx::Scheduler::ASAP - : dfcxx::Scheduler::Linear); - return 0; + return !kernel->compile(context.options.latencyCfg, + context.options.outNames, + (useASAP) ? dfcxx::Scheduler::ASAP + : dfcxx::Scheduler::Linear); +} + +int simMain(const SimContext &context) { + auto kernel = start(); + return !kernel->simulate(context.options.inFilePath, + context.options.outFilePath); } int main(int argc, char **argv) { @@ -63,7 +76,10 @@ int main(int argc, char **argv) { catch(const CLI::ParseError &e) { return options.exit(e); } - - HlsContext context(options.hls); - return hlsMain(context); + + if (options.hls) { + return hlsMain(HlsContext(options.hls)); + } else { + return simMain(SimContext(options.sim)); + } } diff --git a/src/model/dfcxx/include/dfcxx/DFCXX.h b/src/model/dfcxx/include/dfcxx/DFCXX.h index 9e714bb..8b37b6c 100644 --- a/src/model/dfcxx/include/dfcxx/DFCXX.h +++ b/src/model/dfcxx/include/dfcxx/DFCXX.h @@ -9,7 +9,9 @@ #ifndef DFCXX_H #define DFCXX_H -#include "kernel.h" -#include "typedefs.h" +#include "dfcxx/kernel.h" +#include "dfcxx/typedefs.h" +#include "dfcxx/types/types.h" +#include "dfcxx/vars/vars.h" #endif // DFCXX_H diff --git a/src/model/dfcxx/include/dfcxx/channel.h b/src/model/dfcxx/include/dfcxx/channel.h index dd682f7..3477f12 100644 --- a/src/model/dfcxx/include/dfcxx/channel.h +++ b/src/model/dfcxx/include/dfcxx/channel.h @@ -17,7 +17,8 @@ struct Channel { Node source; Node target; unsigned opInd; - + + Channel() = default; Channel(Node source, Node target, unsigned opInd); bool operator==(const Channel &channel) const; diff --git a/src/model/dfcxx/include/dfcxx/constant.h b/src/model/dfcxx/include/dfcxx/constant.h index e145e6b..4947f39 100644 --- a/src/model/dfcxx/include/dfcxx/constant.h +++ b/src/model/dfcxx/include/dfcxx/constant.h @@ -9,9 +9,7 @@ #ifndef DFCXX_CONSTANT_H #define DFCXX_CONSTANT_H -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" namespace dfcxx { @@ -21,13 +19,9 @@ class Constant { friend Kernel; private: - Graph &graph; - GraphHelper helper; - VarBuilder &varBuilder; - KernStorage &storage; + KernMeta &meta; - Constant(Graph &graph, TypeBuilder &typeBuilder, - VarBuilder &varBuilder, KernStorage &storage); + Constant(KernMeta &meta); public: DFVariable var(const DFType &type, int64_t value); diff --git a/src/model/dfcxx/include/dfcxx/control.h b/src/model/dfcxx/include/dfcxx/control.h index 97f938c..47cd240 100644 --- a/src/model/dfcxx/include/dfcxx/control.h +++ b/src/model/dfcxx/include/dfcxx/control.h @@ -9,9 +9,7 @@ #ifndef DFCXX_CONTROL_H #define DFCXX_CONTROL_H -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" #include @@ -23,13 +21,9 @@ class Control { friend Kernel; private: - Graph &graph; - GraphHelper helper; - VarBuilder &varBuilder; - KernStorage &storage; + KernMeta &meta; - Control(Graph &graph, TypeBuilder &typeBuilder, - VarBuilder &varBuilder, KernStorage &storage); + Control(KernMeta &meta); public: DFVariable mux(DFVariable ctrl, std::initializer_list args); diff --git a/src/model/dfcxx/include/dfcxx/graph.h b/src/model/dfcxx/include/dfcxx/graph.h index 7bc2951..c41ffa1 100644 --- a/src/model/dfcxx/include/dfcxx/graph.h +++ b/src/model/dfcxx/include/dfcxx/graph.h @@ -11,7 +11,6 @@ #include "dfcxx/channel.h" #include "dfcxx/node.h" -#include "dfcxx/typebuilders/builder.h" #include "dfcxx/vars/var.h" #include @@ -20,33 +19,7 @@ namespace dfcxx { -class GraphHelper; - -class Kernel; - -class IO; - -class Offset; - -class Constant; - -class Control; - -class DFCIRBuilder; - -class VarBuilder; - -class KernStorage; - class Graph { - friend GraphHelper; - friend Kernel; - friend IO; - friend Offset; - friend Constant; - friend Control; - friend DFCIRBuilder; - private: std::unordered_set nodes; std::unordered_set startNodes; @@ -54,47 +27,23 @@ class Graph { std::unordered_map> outputs; std::unordered_map connections; - Graph() = default; - - Node findNode(DFVariableImpl *var); - - void addNode(DFVariableImpl *var, OpType type, NodeData data); +public: + const std::unordered_set &getNodes() const; - void addNode(const DFVariable &var, OpType type, NodeData data); + const std::unordered_set &getStartNodes() const; - void addChannel(DFVariableImpl *source, DFVariableImpl *target, - unsigned opInd, bool connect); + const std::unordered_map> &getInputs() const; - void addChannel(const DFVariable &source, const DFVariable &target, - unsigned opInd, bool connect); -}; - -class GraphHelper { - friend IO; - friend Offset; - friend Constant; - friend Control; + const std::unordered_map> &getOutputs() const; -private: - Graph &graph; + const std::unordered_map &getConnections() const; - GraphHelper(Graph &graph, TypeBuilder &typeBuilder, - VarBuilder &varBuilder, KernStorage &storage); - -public: - TypeBuilder &typeBuilder; - VarBuilder &varBuilder; - KernStorage &storage; + Node findNode(DFVariableImpl *var); void addNode(DFVariableImpl *var, OpType type, NodeData data); - void addNode(const DFVariable &var, OpType type, NodeData data); - void addChannel(DFVariableImpl *source, DFVariableImpl *target, unsigned opInd, bool connect); - - void addChannel(const DFVariable &source, const DFVariable &target, - unsigned opInd, bool connect); }; } // namespace dfcxx diff --git a/src/model/dfcxx/include/dfcxx/io.h b/src/model/dfcxx/include/dfcxx/io.h index 24c5191..5ec0e66 100644 --- a/src/model/dfcxx/include/dfcxx/io.h +++ b/src/model/dfcxx/include/dfcxx/io.h @@ -9,10 +9,7 @@ #ifndef DFCXX_IO_H #define DFCXX_IO_H -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/typebuilders/builder.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" namespace dfcxx { @@ -22,13 +19,9 @@ class IO { friend Kernel; private: - Graph &graph; - GraphHelper helper; - VarBuilder &varBuilder; - KernStorage &storage; + KernMeta &meta; - IO(Graph &graph, TypeBuilder &typeBuilder, - VarBuilder &builder, KernStorage &storage); + IO(KernMeta &meta); public: DFVariable input(const std::string &name, const DFType &type); diff --git a/src/model/dfcxx/include/dfcxx/kernel.h b/src/model/dfcxx/include/dfcxx/kernel.h index d9e2c09..a64fe11 100644 --- a/src/model/dfcxx/include/dfcxx/kernel.h +++ b/src/model/dfcxx/include/dfcxx/kernel.h @@ -11,16 +11,15 @@ #include "dfcxx/constant.h" #include "dfcxx/control.h" -#include "dfcxx/graph.h" #include "dfcxx/io.h" -#include "dfcxx/kernstorage.h" +#include "dfcxx/kernmeta.h" #include "dfcxx/offset.h" -#include "dfcxx/typebuilders/builder.h" #include "dfcxx/typedefs.h" -#include "dfcxx/types/types.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/types/type.h" #include "dfcxx/vars/var.h" +#include +#include #include #include @@ -32,16 +31,9 @@ namespace llvm { namespace dfcxx { -class DFCIRBuilder; - class Kernel { - friend DFCIRBuilder; - private: - KernStorage storage; - TypeBuilder typeBuilder; - VarBuilder varBuilder; - Graph graph; + KernMeta meta; bool compileDot(llvm::raw_fd_ostream *stream); @@ -65,6 +57,8 @@ class Kernel { virtual ~Kernel() = default; virtual std::string_view getName() = 0; + + const Graph &getGraph() const; bool compile(const DFLatencyConfig &config, const std::vector &outputPaths, @@ -74,6 +68,9 @@ class Kernel { const DFOutputPaths &outputPaths, const Scheduler &sched); + bool simulate(const std::string &inDataPath, + const std::string &outFilePath); + }; } // namespace dfcxx diff --git a/src/model/dfcxx/include/dfcxx/kernmeta.h b/src/model/dfcxx/include/dfcxx/kernmeta.h new file mode 100644 index 0000000..a1d3715 --- /dev/null +++ b/src/model/dfcxx/include/dfcxx/kernmeta.h @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// 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) +// +//===----------------------------------------------------------------------===// + +#ifndef DFCXX_KERNMETA_H +#define DFCXX_KERNMETA_H + +#include "dfcxx/graph.h" +#include "dfcxx/kernstorage.h" +#include "dfcxx/typebuilders/builder.h" +#include "dfcxx/varbuilders/builder.h" + +namespace dfcxx { + +struct KernMeta { + Graph graph; + KernStorage storage; + TypeBuilder typeBuilder; + VarBuilder varBuilder; + + KernMeta() = default; + KernMeta(const KernMeta &) = delete; + ~KernMeta() = default; +}; + +} // namespace dfcxx + +#endif // DFCXX_KERNMETA_H diff --git a/src/model/dfcxx/include/dfcxx/kernstorage.h b/src/model/dfcxx/include/dfcxx/kernstorage.h index 444d820..3fc4331 100644 --- a/src/model/dfcxx/include/dfcxx/kernstorage.h +++ b/src/model/dfcxx/include/dfcxx/kernstorage.h @@ -24,12 +24,8 @@ class KernStorage { public: DFTypeImpl *addType(DFTypeImpl *type); - DFType addType(const DFType &type); - DFVariableImpl *addVariable(DFVariableImpl *var); - DFVariable addVariable(const DFVariable &var); - ~KernStorage(); }; diff --git a/src/model/dfcxx/include/dfcxx/node.h b/src/model/dfcxx/include/dfcxx/node.h index 1e4e4d2..9c7eb85 100644 --- a/src/model/dfcxx/include/dfcxx/node.h +++ b/src/model/dfcxx/include/dfcxx/node.h @@ -48,7 +48,8 @@ struct Node { DFVariableImpl *var; OpType type; NodeData data; - + + Node() = default; Node(DFVariableImpl *var, OpType type, NodeData data); bool operator==(const Node &node) const; diff --git a/src/model/dfcxx/include/dfcxx/offset.h b/src/model/dfcxx/include/dfcxx/offset.h index 216f5ce..aef3c68 100644 --- a/src/model/dfcxx/include/dfcxx/offset.h +++ b/src/model/dfcxx/include/dfcxx/offset.h @@ -9,9 +9,7 @@ #ifndef DFCXX_OFFSET_H #define DFCXX_OFFSET_H -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" namespace dfcxx { @@ -21,12 +19,9 @@ class Offset { friend Kernel; private: - Graph &graph; - GraphHelper helper; - VarBuilder &varBuilder; - KernStorage &storage; + KernMeta &meta; - Offset(Graph &graph, TypeBuilder &typeBuilder, VarBuilder &builder, KernStorage &storage); + Offset(KernMeta &meta); public: DFVariable operator()(DFVariable &stream, int64_t offset); diff --git a/src/model/dfcxx/include/dfcxx/typebuilders/builder.h b/src/model/dfcxx/include/dfcxx/typebuilders/builder.h index 390fe6f..bf8bab7 100644 --- a/src/model/dfcxx/include/dfcxx/typebuilders/builder.h +++ b/src/model/dfcxx/include/dfcxx/typebuilders/builder.h @@ -15,15 +15,14 @@ namespace dfcxx { class TypeBuilder { public: - DFTypeImpl *buildFixed(SignMode mode, uint8_t intBits, uint8_t fracBits); + DFTypeImpl *buildFixed(FixedType::SignMode mode, uint8_t intBits, + uint8_t fracBits); DFTypeImpl *buildBool(); DFTypeImpl *buildFloat(uint8_t expBits, uint8_t fracBits); - DFTypeImpl *buildShiftedType(DFTypeImpl &type, int8_t shift); - - DFType buildShiftedType(const DFType &type, int8_t shift); + DFTypeImpl *buildShiftedType(DFTypeImpl *type, int8_t shift); }; } // namespace dfcxx diff --git a/src/model/dfcxx/include/dfcxx/types/fixed.h b/src/model/dfcxx/include/dfcxx/types/fixed.h index 0616cc7..3572399 100644 --- a/src/model/dfcxx/include/dfcxx/types/fixed.h +++ b/src/model/dfcxx/include/dfcxx/types/fixed.h @@ -13,16 +13,17 @@ namespace dfcxx { -enum SignMode { - UNSIGNED = 0, - SIGNED -}; - class TypeBuilder; class FixedType : DFTypeImpl { friend TypeBuilder; +public: + enum SignMode { + UNSIGNED = 0, + SIGNED + }; + private: SignMode mode; uint8_t intBits; diff --git a/src/model/dfcxx/include/dfcxx/types/type.h b/src/model/dfcxx/include/dfcxx/types/type.h index 53b577e..1cfbbd8 100644 --- a/src/model/dfcxx/include/dfcxx/types/type.h +++ b/src/model/dfcxx/include/dfcxx/types/type.h @@ -13,17 +13,7 @@ namespace dfcxx { -class DFType; -class TypeBuilder; -class DFCIRTypeConverter; -class DFCIRBuilder; - class DFTypeImpl { - friend DFType; - friend TypeBuilder; - friend DFCIRTypeConverter; - friend DFCIRBuilder; - public: virtual ~DFTypeImpl() = default; @@ -44,10 +34,12 @@ class DFType { public: DFType(DFTypeImpl *impl); + + operator DFTypeImpl*() const; DFType(const DFType &) = default; - DFTypeImpl *getImpl() const; + DFTypeImpl *getImpl(); uint16_t getTotalBits() const; diff --git a/src/model/dfcxx/include/dfcxx/varbuilders/builder.h b/src/model/dfcxx/include/dfcxx/varbuilders/builder.h index c26b7dd..eb9c1c7 100644 --- a/src/model/dfcxx/include/dfcxx/varbuilders/builder.h +++ b/src/model/dfcxx/include/dfcxx/varbuilders/builder.h @@ -13,27 +13,23 @@ namespace dfcxx { +struct KernMeta; // Forward declaration to omit cyclic dependency. + class VarBuilder { public: - DFVariableImpl * buildStream(const std::string &name, IODirection direction, - GraphHelper &helper, DFTypeImpl &type); - - DFVariableImpl * buildScalar(const std::string &name, IODirection direction, - GraphHelper &helper, DFTypeImpl &type); - - DFVariableImpl * buildConstant(GraphHelper &helper, DFTypeImpl &type, - ConstantTypeKind kind, ConstantValue value); - - DFVariable buildStream(const std::string &name, IODirection direction, - GraphHelper &helper, const DFType &type); + DFVariableImpl *buildStream(const std::string &name, + DFVariableImpl::IODirection direction, + KernMeta &meta, DFTypeImpl *type); - DFVariable buildScalar(const std::string &name, IODirection direction, - GraphHelper &helper, const DFType &type); + DFVariableImpl *buildScalar(const std::string &name, + DFVariableImpl::IODirection direction, + KernMeta &meta, DFTypeImpl *type); - DFVariable buildConstant(GraphHelper &helper, const DFType &type, - ConstantTypeKind kind, ConstantValue value); + DFVariableImpl *buildConstant(KernMeta &meta, + DFTypeImpl *type, + DFConstant::Value value); - DFVariable buildMuxCopy(const DFVariable &var, GraphHelper &helper); + DFVariableImpl *buildClone(DFVariableImpl *var); }; } // namespace dfcxx diff --git a/src/model/dfcxx/include/dfcxx/vars/constant.h b/src/model/dfcxx/include/dfcxx/vars/constant.h index 4d06198..9fce62e 100644 --- a/src/model/dfcxx/include/dfcxx/vars/constant.h +++ b/src/model/dfcxx/include/dfcxx/vars/constant.h @@ -13,80 +13,79 @@ namespace dfcxx { -enum ConstantTypeKind : uint8_t { - INT = 0, - UINT, - FLOAT -}; - -union ConstantValue { - int64_t int_; - uint64_t uint_; - double double_; -}; - class VarBuilder; -class DFCIRBuilder; class DFConstant : DFVariableImpl { friend VarBuilder; - friend DFCIRBuilder; + +public: + enum TypeKind : uint8_t { + INT = 0, + UINT, + FLOAT + }; + + union Value { + int64_t int_; + uint64_t uint_; + double double_; + }; private: DFTypeImpl &type; - ConstantTypeKind kind; - ConstantValue value; + TypeKind kind; + Value value; + + DFConstant(KernMeta &meta, DFTypeImpl *type, Value value); - DFConstant(GraphHelper &helper, DFTypeImpl &type, - ConstantTypeKind kind, ConstantValue value); + DFVariableImpl *clone() const override; public: ~DFConstant() override = default; + + int64_t getInt() const; -protected: - DFTypeImpl &getType() override; + uint64_t getUInt() const; - DFVariableImpl &operator+(DFVariableImpl &rhs) override; + double getDouble() const; - DFVariableImpl &operator-(DFVariableImpl &rhs) override; + DFTypeImpl *getType() override; - DFVariableImpl &operator*(DFVariableImpl &rhs) override; + DFVariableImpl *operator+(DFVariableImpl &rhs) override; - DFVariableImpl &operator/(DFVariableImpl &rhs) override; + DFVariableImpl *operator-(DFVariableImpl &rhs) override; - DFVariableImpl &operator&(DFVariableImpl &rhs) override; + DFVariableImpl *operator*(DFVariableImpl &rhs) override; - DFVariableImpl &operator|(DFVariableImpl &rhs) override; + DFVariableImpl *operator/(DFVariableImpl &rhs) override; - DFVariableImpl &operator^(DFVariableImpl &rhs) override; + DFVariableImpl *operator&(DFVariableImpl &rhs) override; - DFVariableImpl &operator!() override; + DFVariableImpl *operator|(DFVariableImpl &rhs) override; - DFVariableImpl &operator-() override; + DFVariableImpl *operator^(DFVariableImpl &rhs) override; - DFVariableImpl &operator<(DFVariableImpl &rhs) override; + DFVariableImpl *operator!() override; - DFVariableImpl &operator<=(DFVariableImpl &rhs) override; + DFVariableImpl *operator-() override; - DFVariableImpl &operator>(DFVariableImpl &rhs) override; + DFVariableImpl *operator<(DFVariableImpl &rhs) override; - DFVariableImpl &operator>=(DFVariableImpl &rhs) override; + DFVariableImpl *operator<=(DFVariableImpl &rhs) override; - DFVariableImpl &operator==(DFVariableImpl &rhs) override; + DFVariableImpl *operator>(DFVariableImpl &rhs) override; - DFVariableImpl &operator!=(DFVariableImpl &rhs) override; + DFVariableImpl *operator>=(DFVariableImpl &rhs) override; - DFVariableImpl &operator<<(uint8_t bits) override; + DFVariableImpl *operator==(DFVariableImpl &rhs) override; - DFVariableImpl &operator>>(uint8_t bits) override; + DFVariableImpl *operator!=(DFVariableImpl &rhs) override; - ConstantTypeKind getKind() const; + DFVariableImpl *operator<<(uint8_t bits) override; - int64_t getInt() const; + DFVariableImpl *operator>>(uint8_t bits) override; - uint64_t getUInt() const; - - double getDouble() const; + TypeKind getKind() const; bool isConstant() const override; }; diff --git a/src/model/dfcxx/include/dfcxx/vars/scalar.h b/src/model/dfcxx/include/dfcxx/vars/scalar.h index 195b313..7ea6e16 100644 --- a/src/model/dfcxx/include/dfcxx/vars/scalar.h +++ b/src/model/dfcxx/include/dfcxx/vars/scalar.h @@ -14,57 +14,56 @@ namespace dfcxx { class VarBuilder; -class DFCIRBuilder; class DFScalar : DFVariableImpl { friend VarBuilder; - friend DFCIRBuilder; private: DFTypeImpl &type; DFScalar(const std::string &name, IODirection direction, - GraphHelper &helper, DFTypeImpl &type); + KernMeta &meta, DFTypeImpl *type); + + DFVariableImpl *clone() const override; public: ~DFScalar() override = default; -protected: - DFTypeImpl &getType() override; + DFTypeImpl *getType() override; - DFVariableImpl &operator+(DFVariableImpl &rhs) override; + DFVariableImpl *operator+(DFVariableImpl &rhs) override; - DFVariableImpl &operator-(DFVariableImpl &rhs) override; + DFVariableImpl *operator-(DFVariableImpl &rhs) override; - DFVariableImpl &operator*(DFVariableImpl &rhs) override; + DFVariableImpl *operator*(DFVariableImpl &rhs) override; - DFVariableImpl &operator/(DFVariableImpl &rhs) override; + DFVariableImpl *operator/(DFVariableImpl &rhs) override; - DFVariableImpl &operator&(DFVariableImpl &rhs) override; + DFVariableImpl *operator&(DFVariableImpl &rhs) override; - DFVariableImpl &operator|(DFVariableImpl &rhs) override; + DFVariableImpl *operator|(DFVariableImpl &rhs) override; - DFVariableImpl &operator^(DFVariableImpl &rhs) override; + DFVariableImpl *operator^(DFVariableImpl &rhs) override; - DFVariableImpl &operator!() override; + DFVariableImpl *operator!() override; - DFVariableImpl &operator-() override; + DFVariableImpl *operator-() override; - DFVariableImpl &operator<(DFVariableImpl &rhs) override; + DFVariableImpl *operator<(DFVariableImpl &rhs) override; - DFVariableImpl &operator<=(DFVariableImpl &rhs) override; + DFVariableImpl *operator<=(DFVariableImpl &rhs) override; - DFVariableImpl &operator>(DFVariableImpl &rhs) override; + DFVariableImpl *operator>(DFVariableImpl &rhs) override; - DFVariableImpl &operator>=(DFVariableImpl &rhs) override; + DFVariableImpl *operator>=(DFVariableImpl &rhs) override; - DFVariableImpl &operator==(DFVariableImpl &rhs) override; + DFVariableImpl *operator==(DFVariableImpl &rhs) override; - DFVariableImpl &operator!=(DFVariableImpl &rhs) override; + DFVariableImpl *operator!=(DFVariableImpl &rhs) override; - DFVariableImpl &operator<<(uint8_t bits) override; + DFVariableImpl *operator<<(uint8_t bits) override; - DFVariableImpl &operator>>(uint8_t bits) override; + DFVariableImpl *operator>>(uint8_t bits) override; bool isScalar() const override; }; diff --git a/src/model/dfcxx/include/dfcxx/vars/stream.h b/src/model/dfcxx/include/dfcxx/vars/stream.h index 013b5e3..5ace679 100644 --- a/src/model/dfcxx/include/dfcxx/vars/stream.h +++ b/src/model/dfcxx/include/dfcxx/vars/stream.h @@ -14,58 +14,56 @@ namespace dfcxx { class VarBuilder; -class DFCIRBuilder; class DFStream : DFVariableImpl { friend VarBuilder; - friend DFCIRBuilder; private: DFTypeImpl &type; DFStream(const std::string &name, IODirection direction, - GraphHelper &helper, DFTypeImpl &type); + KernMeta &meta, DFTypeImpl *type); + + DFVariableImpl *clone() const override; public: ~DFStream() override = default; -protected: - - DFTypeImpl &getType() override; + DFTypeImpl *getType() override; - DFVariableImpl &operator+(DFVariableImpl &rhs) override; + DFVariableImpl *operator+(DFVariableImpl &rhs) override; - DFVariableImpl &operator-(DFVariableImpl &rhs) override; + DFVariableImpl *operator-(DFVariableImpl &rhs) override; - DFVariableImpl &operator*(DFVariableImpl &rhs) override; + DFVariableImpl *operator*(DFVariableImpl &rhs) override; - DFVariableImpl &operator/(DFVariableImpl &rhs) override; + DFVariableImpl *operator/(DFVariableImpl &rhs) override; - DFVariableImpl &operator&(DFVariableImpl &rhs) override; + DFVariableImpl *operator&(DFVariableImpl &rhs) override; - DFVariableImpl &operator|(DFVariableImpl &rhs) override; + DFVariableImpl *operator|(DFVariableImpl &rhs) override; - DFVariableImpl &operator^(DFVariableImpl &rhs) override; + DFVariableImpl *operator^(DFVariableImpl &rhs) override; - DFVariableImpl &operator!() override; + DFVariableImpl *operator!() override; - DFVariableImpl &operator-() override; + DFVariableImpl *operator-() override; - DFVariableImpl &operator<(DFVariableImpl &rhs) override; + DFVariableImpl *operator<(DFVariableImpl &rhs) override; - DFVariableImpl &operator<=(DFVariableImpl &rhs) override; + DFVariableImpl *operator<=(DFVariableImpl &rhs) override; - DFVariableImpl &operator>(DFVariableImpl &rhs) override; + DFVariableImpl *operator>(DFVariableImpl &rhs) override; - DFVariableImpl &operator>=(DFVariableImpl &rhs) override; + DFVariableImpl *operator>=(DFVariableImpl &rhs) override; - DFVariableImpl &operator==(DFVariableImpl &rhs) override; + DFVariableImpl *operator==(DFVariableImpl &rhs) override; - DFVariableImpl &operator!=(DFVariableImpl &rhs) override; + DFVariableImpl *operator!=(DFVariableImpl &rhs) override; - DFVariableImpl &operator<<(uint8_t bits) override; + DFVariableImpl *operator<<(uint8_t bits) override; - DFVariableImpl &operator>>(uint8_t bits) override; + DFVariableImpl *operator>>(uint8_t bits) override; bool isStream() const override; }; diff --git a/src/model/dfcxx/include/dfcxx/vars/var.h b/src/model/dfcxx/include/dfcxx/vars/var.h index e67b1a1..6ab7533 100644 --- a/src/model/dfcxx/include/dfcxx/vars/var.h +++ b/src/model/dfcxx/include/dfcxx/vars/var.h @@ -9,36 +9,37 @@ #ifndef DFCXX_VAR_H #define DFCXX_VAR_H -#include "dfcxx/types/type.h" +#include "dfcxx/types/types.h" #include #include namespace dfcxx { -enum IODirection{ - NONE = 0, - INPUT, - OUTPUT -}; - -class GraphHelper; -class DFVariable; class VarBuilder; -class DFCIRTypeConverter; -class DFCIRBuilder; +struct KernMeta; // Forward declaration to omit cyclic dependency. class DFVariableImpl { - friend DFVariable; friend VarBuilder; - friend DFCIRTypeConverter; - friend DFCIRBuilder; -private: +public: + enum IODirection { + NONE = 0, + INPUT, + OUTPUT + }; + +protected: std::string name; IODirection direction; + KernMeta &meta; + + virtual DFVariableImpl *clone() const = 0; public: + DFVariableImpl(const std::string &name, IODirection direction, + KernMeta &meta); + virtual ~DFVariableImpl() = default; virtual bool isStream() const; @@ -47,53 +48,49 @@ class DFVariableImpl { virtual bool isConstant() const; -protected: - GraphHelper &helper; - - DFVariableImpl(const std::string &name, IODirection direction, - GraphHelper &helper); - std::string_view getName() const; IODirection getDirection() const; - virtual DFTypeImpl &getType() = 0; + const KernMeta &getMeta() const; - virtual DFVariableImpl &operator+(DFVariableImpl &rhs) = 0; + virtual DFTypeImpl *getType() = 0; - virtual DFVariableImpl &operator-(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator+(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator*(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator-(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator/(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator*(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator&(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator/(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator|(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator&(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator^(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator|(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator!() = 0; + virtual DFVariableImpl *operator^(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator-() = 0; + virtual DFVariableImpl *operator!() = 0; - virtual DFVariableImpl &operator<(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator-() = 0; - virtual DFVariableImpl &operator<=(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator<(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator>(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator<=(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator>=(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator>(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator==(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator>=(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator!=(DFVariableImpl &rhs) = 0; + virtual DFVariableImpl *operator==(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator<<(uint8_t bits) = 0; + virtual DFVariableImpl *operator!=(DFVariableImpl &rhs) = 0; - virtual DFVariableImpl &operator>>(uint8_t bits) = 0; + virtual DFVariableImpl *operator<<(uint8_t bits) = 0; - void connect(DFVariableImpl &connectee); + virtual DFVariableImpl *operator>>(uint8_t bits) = 0; + + void connect(DFVariableImpl *connectee); }; class DFVariable { @@ -103,13 +100,17 @@ class DFVariable { public: DFVariable(DFVariableImpl *impl); + operator DFVariableImpl*() const; + DFVariable(const DFVariable &) = default; DFVariableImpl *getImpl() const; std::string_view getName() const; - IODirection getDirection() const; + DFVariableImpl::IODirection getDirection() const; + + const KernMeta &getMeta() const; DFType getType() const; diff --git a/src/model/dfcxx/includeDev/dfcxx/IRbuilders/builder.h b/src/model/dfcxx/includeDev/dfcxx/IRbuilders/builder.h index f22fc2c..6ab2dd7 100644 --- a/src/model/dfcxx/includeDev/dfcxx/IRbuilders/builder.h +++ b/src/model/dfcxx/includeDev/dfcxx/IRbuilders/builder.h @@ -17,32 +17,23 @@ #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" -#include - namespace dfcxx { class DFCIRBuilder { private: mlir::MLIRContext ctx; - const DFLatencyConfig &config; - mlir::OpBuilder builder; - std::unordered_map map; mlir::OwningOpRef module; DFCIRTypeConverter conv; - std::unordered_map> muxMap; - void buildKernelBody(Graph *graph, mlir::OpBuilder &builder); + void translate(Node node, const Graph &graph, mlir::OpBuilder &builder, + std::unordered_map &map); + + void buildKernelBody(const Graph &graph, mlir::OpBuilder &builder); mlir::dfcir::KernelOp buildKernel(Kernel *kern, mlir::OpBuilder &builder); - mlir::ModuleOp buildModule(Kernel *kern, mlir::OpBuilder &builder); - - std::stack topSortNodes(Graph *graph); - - void translate(Node node, Graph *graph, mlir::OpBuilder &builder); - public: - explicit DFCIRBuilder(const DFLatencyConfig &config); + DFCIRBuilder(); mlir::ModuleOp buildModule(Kernel *kern); }; diff --git a/src/model/dfcxx/includeDev/dfcxx/simulator.h b/src/model/dfcxx/includeDev/dfcxx/simulator.h new file mode 100644 index 0000000..7b8d55a --- /dev/null +++ b/src/model/dfcxx/includeDev/dfcxx/simulator.h @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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) +// +//===----------------------------------------------------------------------===// + +#ifndef DFCXX_SIMULATOR_H +#define DFCXX_SIMULATOR_H + +#include "dfcxx/graph.h" +#include "dfcxx/node.h" + +#include +#include +#include +#include +#include +#include + +// This forward declaration is needed to avoid +// users having to include CTemplate headers. +namespace ctemplate { + class TemplateDictionary; +} + +namespace dfcxx { + +typedef uint64_t SimValue; + +// Buffer size for reading from and writing to simulation data files. +#define SIM_DATA_BUF_SIZE 200 + +typedef std::map> IOVars; + +typedef std::unordered_map> Inputs; + +typedef std::unordered_map RecordedValues; + +typedef bool (*OpSimulationFunc)(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind); + +typedef std::unordered_map OpSimulationFuncs; + +class DFCXXSimulator { +public: + DFCXXSimulator(std::vector &nodes, + const Inputs &inputs); + bool simulate(std::ifstream &in, std::ofstream &out); + +private: + uint64_t readInput(std::ifstream &in, IOVars &inData); + bool runSim(RecordedValues &vals, IOVars &inData, uint64_t iter); + + bool processOp(RecordedValues &vals, const Node &node, + const IOVars &inData, uint64_t ind); + + void genHeader(ctemplate::TemplateDictionary *dict, + const RecordedValues &vals, + std::unordered_map &idMap, + uint64_t &counter); + + void writeOutput(ctemplate::TemplateDictionary *dict, + const RecordedValues &vals, + uint64_t startInd, + uint64_t iter, + const std::unordered_map &idMap); + + std::vector &nodes; + const Inputs &inputs; + const OpSimulationFuncs funcs; +}; + +} // namespace dfcxx + +#endif // DFCXX_SIMULATOR_H diff --git a/src/model/dfcxx/includeDev/dfcxx/utils.h b/src/model/dfcxx/includeDev/dfcxx/utils.h new file mode 100644 index 0000000..7b00a2d --- /dev/null +++ b/src/model/dfcxx/includeDev/dfcxx/utils.h @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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) +// +//===----------------------------------------------------------------------===// + +#ifndef DFCXX_UTILS_H +#define DFCXX_UTILS_H + +#include "dfcxx/graph.h" + +#include +#include +#include +#include + +namespace dfcxx { + +std::vector topSort(const Graph &graph); + +} // namespace dfcxx + +#endif // DFCXX_UTILS_H diff --git a/src/model/dfcxx/lib/dfcxx/CMakeLists.txt b/src/model/dfcxx/lib/dfcxx/CMakeLists.txt index d8e1be7..2d75803 100644 --- a/src/model/dfcxx/lib/dfcxx/CMakeLists.txt +++ b/src/model/dfcxx/lib/dfcxx/CMakeLists.txt @@ -26,6 +26,8 @@ set(SOURCES graph.cpp node.cpp channel.cpp + utils.cpp + simulator.cpp ) add_library(DFCXX STATIC @@ -45,6 +47,7 @@ set(TEMPLATES_PATH "${PROJECT_SOURCE_DIR}/templates") add_compile_definitions( TEMPLATES_PATH="${TEMPLATES_PATH}" DOT_TEMPLATE_PATH="${TEMPLATES_PATH}/dot.tpl" + VCD_TEMPLATE_PATH="${TEMPLATES_PATH}/vcd.tpl" ) ## MLIRDFCIR is ensured to be compiled beforehand. diff --git a/src/model/dfcxx/lib/dfcxx/IRbuilders/CMakeLists.txt b/src/model/dfcxx/lib/dfcxx/IRbuilders/CMakeLists.txt index 8bed545..83b5ac2 100644 --- a/src/model/dfcxx/lib/dfcxx/IRbuilders/CMakeLists.txt +++ b/src/model/dfcxx/lib/dfcxx/IRbuilders/CMakeLists.txt @@ -1,6 +1,5 @@ set(IR_BUILDERS_SOURCES IRbuilders/builder.cpp - IRbuilders/utils.cpp IRbuilders/converter.cpp PARENT_SCOPE ) diff --git a/src/model/dfcxx/lib/dfcxx/IRbuilders/builder.cpp b/src/model/dfcxx/lib/dfcxx/IRbuilders/builder.cpp index 24a6773..2dffc35 100644 --- a/src/model/dfcxx/lib/dfcxx/IRbuilders/builder.cpp +++ b/src/model/dfcxx/lib/dfcxx/IRbuilders/builder.cpp @@ -7,60 +7,286 @@ //===----------------------------------------------------------------------===// #include "dfcxx/IRbuilders/builder.h" +#include "dfcxx/utils.h" -#include "circt/Dialect/FIRRTL/FIRRTLDialect.h" -#include "circt/Dialect/SV/SVDialect.h" #include "mlir/Parser/Parser.h" +#include + namespace dfcxx { -DFCIRBuilder::DFCIRBuilder(const DFLatencyConfig &config) : ctx(), - config(config), - builder(&ctx), - conv(&ctx) { - // We are allowed to initialize 'builder'-field before loading - // dialects as OpBuilder only stores the pointer to MLIRContext - // and doesn't check any of its state. +DFCIRBuilder::DFCIRBuilder() : ctx(), conv(&ctx) { ctx.getOrLoadDialect(); - ctx.getOrLoadDialect(); - ctx.getOrLoadDialect(); } -void DFCIRBuilder::buildKernelBody(Graph *graph, mlir::OpBuilder &builder) { - std::stack sorted = topSortNodes(graph); +void DFCIRBuilder::buildKernelBody(const Graph &graph, mlir::OpBuilder &builder) { + std::vector sorted = topSort(graph); - while (!sorted.empty()) { - Node node = sorted.top(); - sorted.pop(); - translate(node, graph, builder); + std::unordered_map map; + for (Node node : sorted) { + translate(node, graph, builder, map); } } -mlir::dfcir::KernelOp -DFCIRBuilder::buildKernel(dfcxx::Kernel *kern, mlir::OpBuilder &builder) { +mlir::dfcir::KernelOp DFCIRBuilder::buildKernel(Kernel *kern, + mlir::OpBuilder &builder) { auto kernel = builder.create(builder.getUnknownLoc(), kern->getName()); builder.setInsertionPointToStart(&kernel.getBody().emplaceBlock()); - buildKernelBody(&kern->graph, builder); + buildKernelBody(kern->getGraph(), builder); return kernel; } -mlir::ModuleOp -DFCIRBuilder::buildModule(dfcxx::Kernel *kern, mlir::OpBuilder &builder) { - auto module = builder.create(builder.getUnknownLoc()); - builder.setInsertionPointToStart(module.getBody()); - buildKernel(kern, builder); - return module; -} - -mlir::ModuleOp DFCIRBuilder::buildModule(dfcxx::Kernel *kern) { +mlir::ModuleOp DFCIRBuilder::buildModule(Kernel *kern) { assert(ctx.getLoadedDialect() != nullptr); mlir::OpBuilder builder(&ctx); - module = buildModule(kern, builder); + module = builder.create(builder.getUnknownLoc()); + builder.setInsertionPointToStart(module->getBody()); + buildKernel(kern, builder); return module.get(); } +#define MLIR_INT_ATTR_WIDTH 64 + +void DFCIRBuilder::translate(Node node, const Graph &graph, + mlir::OpBuilder &builder, + std::unordered_map &map) { + auto loc = builder.getUnknownLoc(); + + const auto &ins = graph.getInputs().at(node); + + auto nameAttr = mlir::StringAttr::get(&ctx, node.var->getName()); + + mlir::Operation *newOp = nullptr; + + switch (node.type) { + case OFFSET: { + Node in = ins[0].source; + auto type = mlir::IntegerType::get(builder.getContext(), + MLIR_INT_ATTR_WIDTH, + mlir::IntegerType::Signless); + auto attr = mlir::IntegerAttr::get(type, node.data.offset); + newOp = builder.create(loc, conv[in.var], + map[in], attr); + break; + } + case IN: { + if (node.var->isStream()) { + newOp = builder.create(loc, conv[node.var], + nameAttr, nullptr); + } else { + newOp = builder.create(loc, + conv[node.var], + nameAttr); + } + break; + } + case OUT: { + if (node.var->isStream()) { + newOp = builder.create(loc, conv[node.var], + nameAttr, nullptr, + nullptr); + } else { + newOp = builder.create(loc, + conv[node.var], + nameAttr, + nullptr); + } + break; + } + case CONST: { + auto constant = (DFConstant *) (node.var); + int64_t val; + mlir::IntegerType attrType; + unsigned width = constant->getType()->getTotalBits(); + switch (constant->getKind()) { + case DFConstant::TypeKind::INT: + val = constant->getInt(); + attrType = mlir::IntegerType::get(builder.getContext(), width, + mlir::IntegerType::Signed); + break; + case DFConstant::TypeKind::UINT: { + auto tmpU = constant->getUInt(); + memcpy(&val, &tmpU, sizeof(val)); + attrType = mlir::IntegerType::get(builder.getContext(), width, + mlir::IntegerType::Unsigned); + break; + } + case DFConstant::TypeKind::FLOAT: { + auto tmpD = constant->getDouble(); + memcpy(&val, &tmpD, sizeof(val)); + attrType = mlir::IntegerType::get(builder.getContext(), width, + mlir::IntegerType::Signless); + break; + } + } + auto attr = mlir::IntegerAttr::get(attrType, val); + newOp = builder.create(loc, conv[node.var], + attr); + break; + } + case MUX: { + Node ctrl = ins[node.data.muxId].source; + llvm::SmallVector mux; + uint64_t size = ins.size(); + for (uint64_t i = 0; i < size; ++i) { + // To produce correct FIRRTL/SystemVerilog code + // multiplexer inputs have to be reversed. + uint64_t ind = size - 1 - i; + if (ind != node.data.muxId) { + mux.push_back(map[ins[ind].source]); + } + } + + newOp = builder.create(loc, conv[node.var], + map[ctrl], mux); + break; + } + case ADD: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case SUB: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case MUL: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case DIV: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case AND: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case OR: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case XOR: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case NOT: { + Node first = ins[0].source; + newOp = builder.create(loc, conv[node.var], + map[first]); + break; + } + case NEG: { + Node first = ins[0].source; + newOp = builder.create(loc, conv[node.var], + map[first]); + break; + } + case LESS: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], map[second]); + break; + } + case LESSEQ: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], + map[second]); + break; + } + case GREATER: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], + map[second]); + break; + } + case GREATEREQ: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], + map[second]); + break; + } + case EQ: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], + map[second]); + break; + } + case NEQ: { + Node first = ins[0].source; + Node second = ins[1].source; + newOp = builder.create(loc, conv[node.var], + map[first], + map[second]); + break; + } + case SHL: { + Node first = ins[0].source; + auto attrType = mlir::IntegerType::get(builder.getContext(), 32, + mlir::IntegerType::Signless); + auto attr = mlir::IntegerAttr::get(attrType, node.data.bitShift); + + newOp = builder.create(loc, conv[node.var], + map[first], attr); + break; + } + case SHR: { + Node first = ins[0].source; + auto attrType = mlir::IntegerType::get(builder.getContext(), 32, + mlir::IntegerType::Signless); + auto attr = mlir::IntegerAttr::get(attrType, node.data.bitShift); + + newOp = builder.create(loc, conv[node.var], + map[first], attr); + break; + } + default: { + // TODO: Add proper logging: https://github.com/ispras/utopia-hls/issues/13 + std::cout << "[ERROR] Unknown node type id: " << node.type << std::endl; + assert(false); + }; + } + + map[node] = newOp->getResult(0); + + auto &connections = graph.getConnections(); + if (connections.find(node) != connections.end()) { + auto conSrc = connections.at(node).source; + builder.create(loc, map[node], map[conSrc]); + } +} + } // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/IRbuilders/converter.cpp b/src/model/dfcxx/lib/dfcxx/IRbuilders/converter.cpp index d78f3e7..75c6bbf 100644 --- a/src/model/dfcxx/lib/dfcxx/IRbuilders/converter.cpp +++ b/src/model/dfcxx/lib/dfcxx/IRbuilders/converter.cpp @@ -13,18 +13,18 @@ namespace dfcxx { DFCIRTypeConverter::DFCIRTypeConverter(mlir::MLIRContext *ctx) : ctx(ctx) {} mlir::Type DFCIRTypeConverter::operator[](dfcxx::DFVariableImpl *var) { - const DFTypeImpl &type = var->getType(); + auto *type = var->getType(); mlir::Type newInnerType; - if (type.isFixed()) { - const FixedType &casted = (const FixedType &) (type); - newInnerType = mlir::dfcir::DFCIRFixedType::get(ctx, casted.isSigned(), - casted.getIntBits(), - casted.getFracBits()); - } else if (type.isFloat()) { - const FloatType &casted = (const FloatType &) (type); - newInnerType = mlir::dfcir::DFCIRFloatType::get(ctx, casted.getExpBits(), - casted.getFracBits()); + if (type->isFixed()) { + auto *casted = (FixedType *) (type); + newInnerType = mlir::dfcir::DFCIRFixedType::get(ctx, casted->isSigned(), + casted->getIntBits(), + casted->getFracBits()); + } else if (type->isFloat()) { + auto *casted = (FloatType *) (type); + newInnerType = mlir::dfcir::DFCIRFloatType::get(ctx, casted->getExpBits(), + casted->getFracBits()); } else { return nullptr; } diff --git a/src/model/dfcxx/lib/dfcxx/IRbuilders/utils.cpp b/src/model/dfcxx/lib/dfcxx/IRbuilders/utils.cpp deleted file mode 100644 index 72d0d44..0000000 --- a/src/model/dfcxx/lib/dfcxx/IRbuilders/utils.cpp +++ /dev/null @@ -1,295 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the Utopia HLS Project, under the Apache License v2.0 -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2021-2024 ISP RAS (http://www.ispras.ru) -// -//===----------------------------------------------------------------------===// - -#include "dfcxx/IRbuilders/builder.h" - -namespace dfcxx { - -std::stack DFCIRBuilder::topSortNodes(Graph *graph) { - std::stack result; - - std::unordered_map checked; - std::stack stack; - - for (Node node: graph->startNodes) { - stack.push(node); - checked[node] = 0; - } - - while (!stack.empty()) { - Node node = stack.top(); - size_t count = graph->outputs[node].size(); - size_t curr; - bool flag = true; - for (curr = checked[node]; flag && curr < count; ++curr) { - Channel next = graph->outputs[node][curr]; - if (!checked[next.target]) { - stack.push(next.target); - flag = false; - } - ++checked[node]; - } - - if (flag) { - stack.pop(); - result.push(node); - } - } - return result; -} - -void DFCIRBuilder::translate(dfcxx::Node node, dfcxx::Graph *graph, - mlir::OpBuilder &builder) { - auto loc = builder.getUnknownLoc(); - - const auto &ins = graph->inputs[node]; - - auto nameAttr = mlir::StringAttr::get(&ctx, node.var->getName()); - - switch (node.type) { - case OFFSET: { - Node in = ins[0].source; - auto type = mlir::IntegerType::get(builder.getContext(), 64, - mlir::IntegerType::Signless); - auto attr = mlir::IntegerAttr::get(type, node.data.offset); - auto newOp = builder.create(loc, conv[in.var], - map[in], attr); - map[node] = newOp.getResult(); - break; - } - case IN: { - if (node.var->isStream()) { - auto newOp = builder.create(loc, conv[node.var], - nameAttr, nullptr); - map[node] = newOp.getResult(); - } else { - auto newOp = builder.create(loc, - conv[node.var], - nameAttr); - map[node] = newOp.getResult(); - } - break; - } - case OUT: { - if (node.var->isStream()) { - auto newOp = builder.create(loc, conv[node.var], - nameAttr, nullptr, - nullptr); - map[node] = newOp.getResult(); - } else { - auto newOp = builder.create(loc, - conv[node.var], - nameAttr, - nullptr); - map[node] = newOp.getResult(); - } - break; - } - case CONST: { - auto constant = (DFConstant *) (node.var); - int64_t val; - mlir::IntegerType attrType; - unsigned width = constant->getType().getTotalBits(); - switch (constant->getKind()) { - case INT: - val = constant->getInt(); - attrType = mlir::IntegerType::get(builder.getContext(), width, - mlir::IntegerType::Signed); - break; - case UINT: { - auto tmpU = constant->getUInt(); - memcpy(&val, &tmpU, sizeof(val)); - attrType = mlir::IntegerType::get(builder.getContext(), width, - mlir::IntegerType::Unsigned); - break; - } - case FLOAT: { - auto tmpD = constant->getDouble(); - memcpy(&val, &tmpD, sizeof(val)); - attrType = mlir::IntegerType::get(builder.getContext(), width, - mlir::IntegerType::Signless); - break; - } - } - auto attr = mlir::IntegerAttr::get(attrType, val); - auto newOp = builder.create(loc, conv[node.var], - attr); - map[node] = newOp.getRes(); - break; - } - case MUX: { - Node ctrl = ins[node.data.muxId].source; - llvm::SmallVector mux; - uint64_t size = ins.size(); - for (uint64_t i = 0; i < size; ++i) { - // To produce correct FIRRTL/SystemVerilog code - // multiplexer inputs have to be reversed. - uint64_t ind = size - 1 - i; - if (ind != node.data.muxId) { - mux.push_back(map[ins[ind].source]); - } - } - muxMap[node] = mux; - auto newOp = builder.create(loc, conv[node.var], - map[ctrl], muxMap[node]); - map[node] = newOp.getRes(); - break; - } - case ADD: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case SUB: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case MUL: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case DIV: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case AND: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case OR: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case XOR: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case NOT: { - Node first = ins[0].source; - auto newOp = builder.create(loc, conv[node.var], - map[first]); - map[node] = newOp.getResult(); - break; - } - case NEG: { - Node first = ins[0].source; - auto newOp = builder.create(loc, conv[node.var], - map[first]); - map[node] = newOp.getResult(); - break; - } - case LESS: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], map[second]); - map[node] = newOp.getResult(); - break; - } - case LESSEQ: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], - map[second]); - map[node] = newOp.getResult(); - break; - } - case GREATER: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], - map[second]); - map[node] = newOp.getResult(); - break; - } - case GREATEREQ: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], - map[second]); - map[node] = newOp.getResult(); - break; - } - case EQ: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], - map[second]); - map[node] = newOp.getResult(); - break; - } - case NEQ: { - Node first = ins[0].source; - Node second = ins[1].source; - auto newOp = builder.create(loc, conv[node.var], - map[first], - map[second]); - map[node] = newOp.getResult(); - break; - } - case SHL: { - Node first = ins[0].source; - auto attrType = mlir::IntegerType::get(builder.getContext(), 32, - mlir::IntegerType::Signless); - auto attr = mlir::IntegerAttr::get(attrType, node.data.bitShift); - - auto newOp = builder.create(loc, conv[node.var], - map[first], attr); - map[node] = newOp.getResult(); - break; - } - case SHR: { - Node first = ins[0].source; - auto attrType = mlir::IntegerType::get(builder.getContext(), 32, - mlir::IntegerType::Signless); - auto attr = mlir::IntegerAttr::get(attrType, node.data.bitShift); - - auto newOp = builder.create(loc, conv[node.var], - map[first], attr); - map[node] = newOp.getResult(); - break; - } - } - if (graph->connections.find(node) != graph->connections.end()) { - auto conSrc = graph->connections.at(node).source; - builder.create(loc, map[node], map[conSrc]); - } -} - -} // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/constant.cpp b/src/model/dfcxx/lib/dfcxx/constant.cpp index 93c3ff2..bcf91b4 100644 --- a/src/model/dfcxx/lib/dfcxx/constant.cpp +++ b/src/model/dfcxx/lib/dfcxx/constant.cpp @@ -10,36 +10,35 @@ namespace dfcxx { -Constant::Constant(Graph &graph, TypeBuilder &typeBuilder, - VarBuilder &varBuilder, KernStorage &storage) : - graph(graph), - helper(graph, typeBuilder, varBuilder, storage), - varBuilder(varBuilder), storage(storage) {} +Constant::Constant(KernMeta &meta) : meta(meta) {} DFVariable Constant::var(const DFType &type, int64_t value) { - DFVariable var = varBuilder.buildConstant(helper, type, - ConstantTypeKind::INT, - ConstantValue{.int_ = value}); - storage.addVariable(var); - graph.addNode(var, OpType::CONST, NodeData{}); + auto *var = meta.varBuilder.buildConstant(meta, type, + DFConstant::Value { + .int_ = value + }); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::CONST, NodeData {}); return var; } DFVariable Constant::var(const DFType &type, uint64_t value) { - DFVariable var = varBuilder.buildConstant(helper, *(type.getImpl()), - ConstantTypeKind::UINT, - ConstantValue{.uint_ = value}); - storage.addVariable(var); - graph.addNode(var, OpType::CONST, NodeData{}); + auto *var = meta.varBuilder.buildConstant(meta, type, + DFConstant::Value { + .uint_ = value + }); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::CONST, NodeData {}); return var; } DFVariable Constant::var(const DFType &type, double value) { - DFVariable var = varBuilder.buildConstant(helper, *(type.getImpl()), - ConstantTypeKind::FLOAT, - ConstantValue{.double_ = value}); - storage.addVariable(var); - graph.addNode(var, OpType::CONST, NodeData{}); + auto *var = meta.varBuilder.buildConstant(meta, type, + DFConstant::Value { + .double_ = value + }); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::CONST, NodeData {}); return var; } diff --git a/src/model/dfcxx/lib/dfcxx/control.cpp b/src/model/dfcxx/lib/dfcxx/control.cpp index bf24260..364891d 100644 --- a/src/model/dfcxx/lib/dfcxx/control.cpp +++ b/src/model/dfcxx/lib/dfcxx/control.cpp @@ -11,23 +11,18 @@ namespace dfcxx { -Control::Control(Graph &graph, TypeBuilder &typeBuilder, VarBuilder &varBuilder, - KernStorage &storage) : graph(graph), - helper(graph, typeBuilder, - varBuilder, storage), - varBuilder(varBuilder), - storage(storage) {} +Control::Control(KernMeta &meta) : meta(meta) {} DFVariable Control::mux(DFVariable ctrl, std::initializer_list args) { const DFVariable *argsData = args.begin(); unsigned argsCount = args.size(); - DFVariable var = helper.varBuilder.buildMuxCopy(argsData[0], helper); - storage.addVariable(var); - graph.addNode(var, OpType::MUX, NodeData{.muxId = 0}); - graph.addChannel(ctrl, var, 0, false); + auto *var = meta.varBuilder.buildClone(argsData[0]); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::MUX, NodeData {.muxId = 0}); + meta.graph.addChannel(ctrl, var, 0, false); for (unsigned i = 0; i < argsCount; ++i) { - graph.addChannel(argsData[i], var, i + 1, false); + meta.graph.addChannel(argsData[i], var, i + 1, false); } return var; } diff --git a/src/model/dfcxx/lib/dfcxx/converter.cpp b/src/model/dfcxx/lib/dfcxx/converter.cpp index 714a8e2..73fbfc4 100644 --- a/src/model/dfcxx/lib/dfcxx/converter.cpp +++ b/src/model/dfcxx/lib/dfcxx/converter.cpp @@ -9,6 +9,8 @@ #include "dfcxx/converter.h" #include "circt/Conversion/Passes.h" +#include "circt/Dialect/FIRRTL/FIRRTLDialect.h" +#include "circt/Dialect/SV/SVDialect.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" @@ -51,6 +53,8 @@ bool DFCIRConverter::convertAndPrint(mlir::ModuleOp module, OutputStreams &outputStreams, const Scheduler &sched) { mlir::MLIRContext *context = module.getContext(); + context->getOrLoadDialect(); + context->getOrLoadDialect(); mlir::PassManager pm(context); // Dump DFCIR if the corresponding option is specified. diff --git a/src/model/dfcxx/lib/dfcxx/graph.cpp b/src/model/dfcxx/lib/dfcxx/graph.cpp index 6e49791..447a5e7 100644 --- a/src/model/dfcxx/lib/dfcxx/graph.cpp +++ b/src/model/dfcxx/lib/dfcxx/graph.cpp @@ -12,20 +12,41 @@ namespace dfcxx { +const std::unordered_set &Graph::getNodes() const { + return nodes; +} + +const std::unordered_set &Graph::getStartNodes() const { + return startNodes; +} + +const std::unordered_map> &Graph::getInputs() const { + return inputs; +} + +const std::unordered_map> &Graph::getOutputs() const { + return outputs; +} + +const std::unordered_map &Graph::getConnections() const { + return connections; +} + Node Graph::findNode(DFVariableImpl *var) { return *std::find_if(nodes.begin(), nodes.end(), [&](const Node &node) { return node.var == var; }); } void Graph::addNode(DFVariableImpl *var, OpType type, NodeData data) { - nodes.emplace(var, type, data); + auto node = nodes.emplace(var, type, data); if (type == IN || type == CONST) { startNodes.emplace(var, type, data); } -} - -void Graph::addNode(const DFVariable &var, OpType type, NodeData data) { - addNode(var.getImpl(), type, data); + // The following lines create empty channel vectors + // for new nodes. This allows to use .at() on unconnected + // nodes without getting an exception. + (void)inputs[*(node.first)]; + (void)outputs[*(node.first)]; } void Graph::addChannel(DFVariableImpl *source, DFVariableImpl *target, @@ -41,36 +62,4 @@ void Graph::addChannel(DFVariableImpl *source, DFVariableImpl *target, } } -void Graph::addChannel(const DFVariable &source, const DFVariable &target, - unsigned opInd, bool connect) { - addChannel(source.getImpl(), target.getImpl(), opInd, connect); -} - -GraphHelper::GraphHelper(Graph &graph, TypeBuilder &typeBuilder, - VarBuilder &varBuilder, - KernStorage &storage) : graph(graph), - typeBuilder(typeBuilder), - varBuilder(varBuilder), - storage(storage) {} - -void GraphHelper::addNode(DFVariableImpl *var, OpType type, NodeData data) { - graph.addNode(var, type, data); -} - -void GraphHelper::addNode(const DFVariable &var, OpType type, NodeData data) { - addNode(var.getImpl(), type, data); -} - -void GraphHelper::addChannel(DFVariableImpl *source, DFVariableImpl *target, - unsigned opInd, bool connect) { - graph.addChannel(source, target, opInd, connect); -} - -void GraphHelper::addChannel(const DFVariable &source, - const DFVariable &target, - unsigned opInd, - bool connect) { - graph.addChannel(source.getImpl(), target.getImpl(), opInd, connect); -} - } // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/io.cpp b/src/model/dfcxx/lib/dfcxx/io.cpp index f9262c7..1be9b88 100644 --- a/src/model/dfcxx/lib/dfcxx/io.cpp +++ b/src/model/dfcxx/lib/dfcxx/io.cpp @@ -10,53 +10,47 @@ namespace dfcxx { -using IODirection = dfcxx::IODirection; +using IODirection = dfcxx::DFVariableImpl::IODirection; -IO::IO(Graph &graph, TypeBuilder &typeBuilder, VarBuilder &varBuilder, - KernStorage &storage) : graph(graph), helper(graph, - typeBuilder, - varBuilder, - storage), - varBuilder(varBuilder), - storage(storage) {} +IO::IO(KernMeta &meta) : meta(meta) {} DFVariable IO::input(const std::string &name, const DFType &type) { - DFVariable var = varBuilder.buildStream(name, + auto *var = meta.varBuilder.buildStream(name, IODirection::INPUT, - helper, + meta, type); - storage.addVariable(var); - graph.addNode(var, OpType::IN, NodeData{}); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::IN, NodeData {}); return var; } DFVariable IO::inputScalar(const std::string &name, const DFType &type) { - DFVariable var = varBuilder.buildScalar(name, + auto *var = meta.varBuilder.buildScalar(name, IODirection::INPUT, - helper, + meta, type); - storage.addVariable(var); - graph.addNode(var, OpType::IN, NodeData{}); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::IN, NodeData {}); return var; } DFVariable IO::output(const std::string &name, const DFType &type) { - DFVariable var = varBuilder.buildStream(name, + auto *var = meta.varBuilder.buildStream(name, IODirection::OUTPUT, - helper, + meta, type); - storage.addVariable(var); - graph.addNode(var, OpType::OUT, NodeData{}); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::OUT, NodeData {}); return var; } DFVariable IO::outputScalar(const std::string &name, const DFType &type) { - DFVariable var = varBuilder.buildScalar(name, + auto *var = meta.varBuilder.buildScalar(name, IODirection::OUTPUT, - helper, + meta, type); - storage.addVariable(var); - graph.addNode(var, OpType::OUT, NodeData{}); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::OUT, NodeData {}); return var; } diff --git a/src/model/dfcxx/lib/dfcxx/kernel.cpp b/src/model/dfcxx/lib/dfcxx/kernel.cpp index fe81172..3cb639c 100644 --- a/src/model/dfcxx/lib/dfcxx/kernel.cpp +++ b/src/model/dfcxx/lib/dfcxx/kernel.cpp @@ -9,42 +9,50 @@ #include "dfcxx/converter.h" #include "dfcxx/IRbuilders/builder.h" #include "dfcxx/kernel.h" +#include "dfcxx/simulator.h" +#include "dfcxx/utils.h" #include "dfcxx/vars/constant.h" #include "ctemplate/template.h" #include "llvm/Support/raw_ostream.h" #include +#include #include #include #include namespace dfcxx { -Kernel::Kernel() : storage(), typeBuilder(), varBuilder(), - graph(), io(graph, typeBuilder, varBuilder, storage), - offset(graph, typeBuilder, varBuilder, storage), - constant(graph, typeBuilder, varBuilder, storage), - control(graph, typeBuilder, varBuilder, storage) {} +Kernel::Kernel() : meta(), io(meta), offset(meta), + constant(meta), control(meta) {} DFType Kernel::dfUInt(uint8_t bits) { - DFTypeImpl *type = typeBuilder.buildFixed(SignMode::UNSIGNED, bits, 0); - return DFType(storage.addType(type)); + auto *type = meta.typeBuilder.buildFixed(FixedType::SignMode::UNSIGNED, + bits, + 0); + return meta.storage.addType(type); } DFType Kernel::dfInt(uint8_t bits) { - DFTypeImpl *type = typeBuilder.buildFixed(SignMode::SIGNED, bits, 0); - return DFType(storage.addType(type)); + auto *type = meta.typeBuilder.buildFixed(FixedType::SignMode::SIGNED, + bits, + 0); + return meta.storage.addType(type); } DFType Kernel::dfFloat(uint8_t expBits, uint8_t fracBits) { - DFTypeImpl *type = typeBuilder.buildFloat(expBits, fracBits); - return DFType(storage.addType(type)); + auto *type = meta.typeBuilder.buildFloat(expBits, fracBits); + return meta.storage.addType(type); } DFType Kernel::dfBool() { - DFTypeImpl *type = typeBuilder.buildBool(); - return DFType(storage.addType(type)); + auto *type = meta.typeBuilder.buildBool(); + return meta.storage.addType(type); +} + +const Graph &Kernel::getGraph() const { + return meta.graph; } bool Kernel::compileDot(llvm::raw_fd_ostream *stream) { @@ -80,8 +88,11 @@ bool Kernel::compileDot(llvm::raw_fd_ostream *stream) { localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + + const auto &nodes = meta.graph.getNodes(); + const auto &inputs = meta.graph.getInputs(); - for (Node node : graph.nodes) { + for (const Node &node : nodes) { TemplateDictionary *elem = dict->AddSectionDictionary("ELEMENTS"); auto name = getName(node); elem->SetValue("NAME", name); @@ -120,7 +131,7 @@ bool Kernel::compileDot(llvm::raw_fd_ostream *stream) { elem->SetValue("LABEL", label); unsigned i = 0; - for (Channel chan : graph.inputs[node]) { + for (Channel chan : inputs.at(node)) { TemplateDictionary *conn = elem->AddSectionDictionary("CONNECTIONS"); conn->SetValue("SRC_NAME", getName(chan.source)); conn->SetValue("TRG_NAME", name); @@ -139,7 +150,7 @@ bool Kernel::compileDot(llvm::raw_fd_ostream *stream) { bool Kernel::compile(const DFLatencyConfig &config, const std::vector &outputPaths, const Scheduler &sched) { - DFCIRBuilder builder(config); + DFCIRBuilder builder; auto compiled = builder.buildModule(this); size_t count = outputPaths.size(); std::vector outputStreams(count); @@ -180,4 +191,17 @@ bool Kernel::compile(const DFLatencyConfig &config, return compile(config, outPathsStrings, sched); } + +bool Kernel::simulate(const std::string &inDataPath, + const std::string &outFilePath) { + std::vector sorted = topSort(meta.graph); + DFCXXSimulator sim(sorted, meta.graph.getInputs()); + std::ifstream input(inDataPath, std::ios::in); + if (!input || input.bad() || input.eof() || input.fail() || !input.is_open()) { + return false; + } + std::ofstream output(outFilePath, std::ios::out); + return sim.simulate(input, output); +} + } // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/kernstorage.cpp b/src/model/dfcxx/lib/dfcxx/kernstorage.cpp index ab6f0c1..f94071b 100644 --- a/src/model/dfcxx/lib/dfcxx/kernstorage.cpp +++ b/src/model/dfcxx/lib/dfcxx/kernstorage.cpp @@ -25,17 +25,10 @@ dfcxx::DFTypeImpl *KernStorage::addType(dfcxx::DFTypeImpl *type) { } } -DFType KernStorage::addType(const DFType &type) { - return DFType(addType(type.getImpl())); -} - DFVariableImpl *KernStorage::addVariable(DFVariableImpl *var) { return *(variables.insert(var).first); } -DFVariable KernStorage::addVariable(const DFVariable &var) { - return DFVariable(addVariable(var.getImpl())); -} KernStorage::~KernStorage() { for (DFTypeImpl *type: types) { diff --git a/src/model/dfcxx/lib/dfcxx/offset.cpp b/src/model/dfcxx/lib/dfcxx/offset.cpp index 130d60f..fdc47be 100644 --- a/src/model/dfcxx/lib/dfcxx/offset.cpp +++ b/src/model/dfcxx/lib/dfcxx/offset.cpp @@ -11,19 +11,16 @@ namespace dfcxx { -Offset::Offset(Graph &graph, TypeBuilder &typeBuilder, VarBuilder &varBuilder, - KernStorage &storage) : graph(graph), helper(graph, typeBuilder, - varBuilder, storage), - varBuilder(varBuilder), - storage(storage) {} +Offset::Offset(KernMeta &meta) : meta(meta) {} DFVariable Offset::operator()(DFVariable &stream, int64_t offset) { if (!stream.isStream()) { throw std::exception(); } - DFVariable var = varBuilder.buildStream("", IODirection::NONE, - helper, stream.getType()); - storage.addVariable(var); - graph.addNode(var, OpType::OFFSET, NodeData{.offset = offset}); - graph.addChannel(stream, var, 0, false); + auto *var = meta.varBuilder.buildStream("", + DFVariableImpl::IODirection::NONE, + meta, stream.getType()); + meta.storage.addVariable(var); + meta.graph.addNode(var, OpType::OFFSET, NodeData{.offset = offset}); + meta.graph.addChannel(stream, var, 0, false); return var; } diff --git a/src/model/dfcxx/lib/dfcxx/simulator.cpp b/src/model/dfcxx/lib/dfcxx/simulator.cpp new file mode 100644 index 0000000..477169b --- /dev/null +++ b/src/model/dfcxx/lib/dfcxx/simulator.cpp @@ -0,0 +1,370 @@ +//===----------------------------------------------------------------------===// +// +// 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) +// +//===----------------------------------------------------------------------===// + +#include "dfcxx/simulator.h" +#include "dfcxx/types/types.h" +#include "dfcxx/vars/vars.h" + +#include "ctemplate/template.h" + +#include +#include +#include + +namespace dfcxx { + +#define HEX_BASE 16 + +uint64_t DFCXXSimulator::readInput(std::ifstream &in, + IOVars &inData) { + uint64_t ind = 0; + std::string line; + bool atLeastOne = false; + while (std::getline(in, line) && ind < SIM_DATA_BUF_SIZE) { + // An empty line is treated as a data block delimiter. + if (line.empty()) { + ++ind; + continue; + } + size_t spaceInd = line.find(' '); + if (spaceInd == line.npos || + spaceInd == 0 || + spaceInd == line.size() - 1) { + return 0; + } + atLeastOne = true; + inData[line.substr(0, spaceInd)][ind] = + std::stoul(line.substr(spaceInd + 1), 0, HEX_BASE); + } + // It is assumed that at least one data block exists. + return atLeastOne ? (ind + 1) : 0; +} + +static bool processInput(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + auto name = std::string(DFVariable(node.var).getName()); + vals[node] = inData.at(name)[ind]; + return true; +} + +static bool processOutput(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + auto name = std::string(DFVariable(node.var).getName()); + // Take output's only connection and assign the existing source value. + vals[node] = vals[inputs.at(node)[0].source]; + return true; +} + +static bool processConst(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + vals[node] = ((DFConstant *) node.var)->getUInt(); + return true; +} + +static bool processMux(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + auto muxedValue = vals[inputs.at(node)[node.data.muxId].source]; + vals[node] = vals[inputs.at(node)[muxedValue + 1].source]; + return true; +} + +// Generic name for a simulation function. +#define GENERIC_FUNC_NAME(OP_NAME) process##OP_NAME##Name + +// Casts the provided value to a concrete type in a system-dependent way. +#define CAST_SIM_VALUE_TO(TYPE, VALUE) *(reinterpret_cast(&VALUE)) + +// Generic procedure to perform the simulation +// for the concrete binary op and type of its operands. +#define GENERIC_BINARY_OP_SIM_WITH_TYPE(TYPE, VALS, INPUTS, NODE, OP) \ +TYPE left = CAST_SIM_VALUE_TO(TYPE, VALS[INPUTS.at(NODE)[0].source]); \ +TYPE right = CAST_SIM_VALUE_TO(TYPE, VALS[INPUTS.at(NODE)[1].source]); \ +TYPE result = left OP right; \ +VALS[NODE] = CAST_SIM_VALUE_TO(SimValue, result); + +// Generic procedure to perform the simulation +// for the concrete unary op and type of its operand. +#define GENERIC_UNARY_OP_SIM_WITH_TYPE(TYPE, VALS, INPUTS, NODE, OP) \ +TYPE left = CAST_SIM_VALUE_TO(TYPE, VALS[INPUTS.at(NODE)[0].source]); \ +TYPE result = OP left; \ +VALS[NODE] = CAST_SIM_VALUE_TO(SimValue, result); + +#define PROCESS_GENERIC_BINARY_OP_FUNC(OP_NAME, OP) \ +static bool GENERIC_FUNC_NAME(OP_NAME)(RecordedValues &vals, \ + const Node &node, \ + const Inputs &inputs, \ + const IOVars &inData, \ + uint64_t ind) { \ + DFTypeImpl *type = (DFVariable(node.var).getType()).getImpl(); \ + if (type->isFixed()) { \ + if (((FixedType*) type)->isSigned()) { \ + GENERIC_BINARY_OP_SIM_WITH_TYPE(int64_t, vals, inputs, node, OP) \ + } else { \ + GENERIC_BINARY_OP_SIM_WITH_TYPE(uint64_t, vals, inputs, node, OP) \ + } \ + } else if (type->isFloat()) { \ + GENERIC_BINARY_OP_SIM_WITH_TYPE(double, vals, inputs, node, OP) \ + } else { \ + return false; \ + } \ + return true; \ +} + +PROCESS_GENERIC_BINARY_OP_FUNC(Add, +) + +PROCESS_GENERIC_BINARY_OP_FUNC(Sub, -) + +PROCESS_GENERIC_BINARY_OP_FUNC(Mul, *) + +PROCESS_GENERIC_BINARY_OP_FUNC(Div, /) + +PROCESS_GENERIC_BINARY_OP_FUNC(Less, <) + +PROCESS_GENERIC_BINARY_OP_FUNC(LessEq, <=) + +PROCESS_GENERIC_BINARY_OP_FUNC(Greater, >) + +PROCESS_GENERIC_BINARY_OP_FUNC(GreaterEq, >=) + +PROCESS_GENERIC_BINARY_OP_FUNC(Eq, ==) + +PROCESS_GENERIC_BINARY_OP_FUNC(Neq, !=) + +#define PROCESS_GENERIC_BITWISE_BINARY_OP_FUNC(OP_NAME, OP) \ +static bool GENERIC_FUNC_NAME(OP_NAME)(RecordedValues &vals, \ + const Node &node, \ + const Inputs &inputs, \ + const IOVars &inData, \ + uint64_t ind) { \ + vals[node] = \ + vals[inputs.at(node)[0].source] OP vals[inputs.at(node)[1].source]; \ + return true; \ +} + +PROCESS_GENERIC_BITWISE_BINARY_OP_FUNC(And, &) + +PROCESS_GENERIC_BITWISE_BINARY_OP_FUNC(Or, |) + +PROCESS_GENERIC_BITWISE_BINARY_OP_FUNC(Xor, ^) + +static bool processNotOp(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + vals[node] = ~(vals[inputs.at(node)[0].source]); + return true; +} + +static bool processNegOp(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + DFTypeImpl *type = (DFVariable(node.var).getType()).getImpl(); + if (type->isFixed()) { + if (((FixedType*) type)->isSigned()) { + GENERIC_UNARY_OP_SIM_WITH_TYPE(int64_t, vals, inputs, node, -) + } else { + GENERIC_UNARY_OP_SIM_WITH_TYPE(uint64_t, vals, inputs, node, -) + } + } else if (type->isFloat()) { + GENERIC_UNARY_OP_SIM_WITH_TYPE(double, vals, inputs, node, -) + } else { + return false; + } + return true; +} + +static bool processShiftLeftOp(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + vals[node] = vals[inputs.at(node)[0].source] << node.data.bitShift; + return true; +} + +static bool processShiftRightOp(RecordedValues &vals, const Node &node, + const Inputs &inputs, const IOVars &inData, + uint64_t ind) { + vals[node] = vals[inputs.at(node)[0].source] >> node.data.bitShift; + return true; +} + +bool DFCXXSimulator::processOp(RecordedValues &vals, const Node &node, + const IOVars &inData, uint64_t ind) { + if (funcs.find(node.type) == funcs.end()) { + return false; + } + return funcs.at(node.type)(vals, node, inputs, inData, ind); +} + +DFCXXSimulator::DFCXXSimulator(std::vector &nodes, + const Inputs &inputs) : + nodes(nodes), + inputs(inputs), + funcs({ + {OpType::IN, processInput}, + {OpType::OUT, processOutput}, + {OpType::CONST, processConst}, + {OpType::MUX, processMux}, + {OpType::ADD, GENERIC_FUNC_NAME(Add)}, + {OpType::SUB, GENERIC_FUNC_NAME(Sub)}, + {OpType::MUL, GENERIC_FUNC_NAME(Mul)}, + {OpType::DIV, GENERIC_FUNC_NAME(Div)}, + {OpType::AND, GENERIC_FUNC_NAME(And)}, + {OpType::OR, GENERIC_FUNC_NAME(Or)}, + {OpType::XOR, GENERIC_FUNC_NAME(Xor)}, + {OpType::NOT, processNotOp}, + {OpType::NEG, processNegOp}, + {OpType::LESS, GENERIC_FUNC_NAME(Less)}, + {OpType::LESSEQ, GENERIC_FUNC_NAME(LessEq)}, + {OpType::GREATER, GENERIC_FUNC_NAME(Greater)}, + { + OpType::GREATEREQ, + GENERIC_FUNC_NAME(GreaterEq)}, + {OpType::EQ, GENERIC_FUNC_NAME(Eq)}, + {OpType::NEQ, GENERIC_FUNC_NAME(Neq)}, + {OpType::SHL, processShiftLeftOp}, + {OpType::SHR, processShiftRightOp}}) { + // TODO: Add offset support in the future. +} + +bool DFCXXSimulator::runSim(RecordedValues &vals, + IOVars &inData, + uint64_t iter) { + // Node->value mapping is updated. This allows us + // to remember the relevant value for the operand node. + // With every single "clock" input nodes' mapping is updated + // with the value from the buffer. + for (Node &node : nodes) { + if (!processOp(vals, node, inData, iter)) { + return false; + } + } + return true; +} + +#define ASCII_ZERO_CHAR_NUM 48 + +static inline std::string valueToBinary(SimValue value, uint64_t width) { + std::stringstream stream; + uint64_t lastBitId = width - 1; + // Every iteration we take leftmost bit and convert it to a char. + for (uint64_t bitId = 0; bitId < width; ++bitId) { + stream << char(((value >> (lastBitId - bitId)) & 1) + ASCII_ZERO_CHAR_NUM); + } + return stream.str(); +} + +void +DFCXXSimulator::writeOutput(ctemplate::TemplateDictionary *dict, + const RecordedValues &vals, + uint64_t startInd, + uint64_t iter, + const std::unordered_map &idMap) { + ctemplate::TemplateDictionary *tick = + dict->AddSectionDictionary("TICKS"); + tick->SetValue("TICK", std::to_string(startInd + iter)); + for (const auto &kv : vals) { + ctemplate::TemplateDictionary *value = + tick->AddSectionDictionary("VALUES"); + value->SetValue("VALUE", + valueToBinary(kv.second, + kv.first.var->getType()->getTotalBits())); + value->SetValue("NAME", idMap.at(kv.first)); + } +} + +void DFCXXSimulator::genHeader(ctemplate::TemplateDictionary *dict, + const RecordedValues &vals, + std::unordered_map &idMap, + uint64_t &counter) { + 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); + + auto getName = [&idMap, &counter] (const Node &node) -> std::string { + // If the node is named - just save and return the name. + auto name = DFVariable(node.var).getName(); + if (!name.empty()) { + return (idMap[node] = name.data()); + } + // If the mapping contains the node name - return it. + auto it = idMap.find(node); + if (it != idMap.end()) { + return it->second; + } + // Otherwise create and return the new node name mapping. + return (idMap[node] = "node" + std::to_string(counter++)); + }; + + for (const auto &kv : vals) { + std::string name = getName(kv.first); + auto width = kv.first.var->getType()->getTotalBits(); + + ctemplate::TemplateDictionary *var = + dict->AddSectionDictionary("VARS"); + var->SetValue("WIDTH", + std::to_string(width)); + var->SetValue("NAME", name); + + ctemplate::TemplateDictionary *initVar = + dict->AddSectionDictionary("INIT_VARS"); + initVar->SetValue("INIT_VALUE", std::string(width, 'x')); + initVar->SetValue("NAME", name); + } +} + +bool DFCXXSimulator::simulate(std::ifstream &in, + std::ofstream &out) { + IOVars inData; + RecordedValues vals; + bool headerGenerated = false; + uint64_t startInd = 1; + uint64_t counter = 0; + std::unordered_map idMapping; + ctemplate::TemplateDictionary *dict = + new ctemplate::TemplateDictionary("vcd"); + + while (uint64_t count = readInput(in, inData)) { + for (uint64_t iter = 0; iter < count; ++iter) { + // If the simulation fails - return false. + if (!runSim(vals, inData, iter)) { + delete dict; + return false; + } + // If it's the first iteration - generate .vcd headers. + if (!headerGenerated) { + genHeader(dict, vals, idMapping, counter); + headerGenerated = true; + } + writeOutput(dict, vals, startInd, iter, idMapping); + } + startInd += count; + } + dict->SetValue("FINAL_TICK", std::to_string(startInd)); + std::string result; + ctemplate::ExpandTemplate(VCD_TEMPLATE_PATH, + ctemplate::DO_NOT_STRIP, + dict, + &result); + + out << result; + + delete dict; + return true; +} + +} // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/typebuilders/builder.cpp b/src/model/dfcxx/lib/dfcxx/typebuilders/builder.cpp index 8d1ce4d..a675646 100644 --- a/src/model/dfcxx/lib/dfcxx/typebuilders/builder.cpp +++ b/src/model/dfcxx/lib/dfcxx/typebuilders/builder.cpp @@ -10,35 +10,32 @@ namespace dfcxx { -DFTypeImpl *TypeBuilder::buildFixed(dfcxx::SignMode mode, uint8_t intBits, +DFTypeImpl *TypeBuilder::buildFixed(FixedType::SignMode mode, + uint8_t intBits, uint8_t fracBits) { return new FixedType(mode, intBits, fracBits); } DFTypeImpl *TypeBuilder::buildBool() { - return buildFixed(dfcxx::SignMode::UNSIGNED, 1, 0); + return buildFixed(FixedType::SignMode::UNSIGNED, 1, 0); } DFTypeImpl *TypeBuilder::buildFloat(uint8_t expBits, uint8_t fracBits) { return new FloatType(expBits, fracBits); } -DFTypeImpl *TypeBuilder::buildShiftedType(DFTypeImpl &type, int8_t shift) { - if (type.isFixed()) { - FixedType &casted = (FixedType &) (type); +DFTypeImpl *TypeBuilder::buildShiftedType(DFTypeImpl *type, int8_t shift) { + if (type->isFixed()) { + FixedType *casted = (FixedType *) (type); return buildFixed( - casted.getSign(), - uint8_t(int16_t(casted.getIntBits()) + shift), casted.getFracBits()); + casted->getSign(), + uint8_t(int16_t(casted->getIntBits()) + shift), casted->getFracBits()); } else { - FloatType &casted = (FloatType &) (type); + FloatType *casted = (FloatType *) (type); return buildFloat( - casted.getExpBits(), - uint8_t(uint16_t(casted.getFracBits()) + shift)); + casted->getExpBits(), + uint8_t(uint16_t(casted->getFracBits()) + shift)); } } -DFType TypeBuilder::buildShiftedType(const DFType &type, int8_t shift) { - return DFType(buildShiftedType(*(type.getImpl()), shift)); -} - } // namespace dfcxx \ No newline at end of file diff --git a/src/model/dfcxx/lib/dfcxx/types/fixed.cpp b/src/model/dfcxx/lib/dfcxx/types/fixed.cpp index 4532474..b9095b9 100644 --- a/src/model/dfcxx/lib/dfcxx/types/fixed.cpp +++ b/src/model/dfcxx/lib/dfcxx/types/fixed.cpp @@ -14,7 +14,7 @@ FixedType::FixedType(SignMode mode, uint8_t intBits, uint8_t fracBits) : mode(mode), intBits(intBits), fracBits(fracBits) {} -SignMode FixedType::getSign() const { +FixedType::SignMode FixedType::getSign() const { return mode; } diff --git a/src/model/dfcxx/lib/dfcxx/types/type.cpp b/src/model/dfcxx/lib/dfcxx/types/type.cpp index 1d4b6e7..112d550 100644 --- a/src/model/dfcxx/lib/dfcxx/types/type.cpp +++ b/src/model/dfcxx/lib/dfcxx/types/type.cpp @@ -24,7 +24,11 @@ bool DFTypeImpl::isFloat() const { DFType::DFType(DFTypeImpl *impl) : impl(impl) {} -DFTypeImpl *DFType::getImpl() const { +DFType::operator DFTypeImpl*() const { + return impl; +} + +DFTypeImpl *DFType::getImpl() { return impl; } diff --git a/src/model/dfcxx/lib/dfcxx/utils.cpp b/src/model/dfcxx/lib/dfcxx/utils.cpp new file mode 100644 index 0000000..01e078a --- /dev/null +++ b/src/model/dfcxx/lib/dfcxx/utils.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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) +// +//===----------------------------------------------------------------------===// + +#include "dfcxx/utils.h" + +namespace dfcxx { + +std::vector topSort(const Graph &graph) { + size_t nodesCount = graph.getNodes().size(); + auto &outs = graph.getOutputs(); + + std::vector result(nodesCount); + + std::unordered_map checked; + std::stack stack; + + for (Node node: graph.getStartNodes()) { + stack.push(node); + checked[node] = 0; + } + + size_t i = nodesCount - 1; + while (!stack.empty()) { + Node node = stack.top(); + size_t count = outs.at(node).size(); + size_t curr; + bool flag = true; + for (curr = checked[node]; flag && curr < count; ++curr) { + Channel next = outs.at(node)[curr]; + if (!checked[next.target]) { + stack.push(next.target); + flag = false; + } + ++checked[node]; + } + + if (flag) { + stack.pop(); + result[i--] = node; + } + } + return result; +} + +} // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/varbuilders/builder.cpp b/src/model/dfcxx/lib/dfcxx/varbuilders/builder.cpp index d9f7719..4d37bfd 100644 --- a/src/model/dfcxx/lib/dfcxx/varbuilders/builder.cpp +++ b/src/model/dfcxx/lib/dfcxx/varbuilders/builder.cpp @@ -10,55 +10,28 @@ namespace dfcxx { -DFVariableImpl * -VarBuilder::buildStream(const std::string &name, IODirection direction, - GraphHelper &helper, DFTypeImpl &type) { - return new DFStream(name, direction, helper, type); +DFVariableImpl *VarBuilder::buildStream(const std::string &name, + DFVariableImpl::IODirection direction, + KernMeta &meta, + DFTypeImpl *type) { + return new DFStream(name, direction, meta, type); } -DFVariableImpl * -VarBuilder::buildScalar(const std::string &name, IODirection direction, - GraphHelper &helper, DFTypeImpl &type) { - return new DFScalar(name, direction, helper, type); +DFVariableImpl *VarBuilder::buildScalar(const std::string &name, + DFVariableImpl::IODirection direction, + KernMeta &meta, + DFTypeImpl *type) { + return new DFScalar(name, direction, meta, type); } -DFVariableImpl * -VarBuilder::buildConstant(GraphHelper &helper, DFTypeImpl &type, - ConstantTypeKind kind, ConstantValue value) { - return new DFConstant(helper, type, kind, value); +DFVariableImpl *VarBuilder::buildConstant(KernMeta &meta, + DFTypeImpl *type, + DFConstant::Value value) { + return new DFConstant(meta, type, value); } -DFVariable -VarBuilder::buildStream(const std::string &name, IODirection direction, - GraphHelper &helper, const DFType &type) { - return DFVariable(buildStream(name, direction, helper, *(type.getImpl()))); -} - -DFVariable -VarBuilder::buildScalar(const std::string &name, IODirection direction, - GraphHelper &helper, const DFType &type) { - return DFVariable(buildScalar(name, direction, helper, *(type.getImpl()))); -} - -DFVariable -VarBuilder::buildConstant(GraphHelper &helper, const DFType &type, - ConstantTypeKind kind, ConstantValue value) { - return DFVariable(buildConstant(helper, *(type.getImpl()), kind, value)); -} - -DFVariable -VarBuilder::buildMuxCopy(const DFVariable &var, GraphHelper &helper) { - if (var.isConstant()) { - return buildConstant(helper, var.getType(), - ((DFConstant *) (var.getImpl()))->getKind(), - ConstantValue{}); - } else if (var.isScalar()) { - return buildScalar("", IODirection::NONE, helper, - var.getType()); - } else /* if (var.isStream()) */ { - return buildStream("", IODirection::NONE, helper, - var.getType()); - } +DFVariableImpl *VarBuilder::buildClone(DFVariableImpl *var) { + return var->clone(); } } // namespace dfcxx diff --git a/src/model/dfcxx/lib/dfcxx/vars/constant.cpp b/src/model/dfcxx/lib/dfcxx/vars/constant.cpp index b3f4deb..239756e 100644 --- a/src/model/dfcxx/lib/dfcxx/vars/constant.cpp +++ b/src/model/dfcxx/lib/dfcxx/vars/constant.cpp @@ -6,240 +6,120 @@ // //===----------------------------------------------------------------------===// -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/types/types.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" #include "dfcxx/vars/constant.h" namespace dfcxx { -DFConstant::DFConstant(GraphHelper &helper, dfcxx::DFTypeImpl &type, - ConstantTypeKind kind, ConstantValue value) : - DFVariableImpl("", IODirection::NONE, helper), - type(type), kind(kind), value(value) {} - -DFTypeImpl &DFConstant::getType() { - return type; -} - -DFVariableImpl &DFConstant::operator+(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ + casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ + casted.value.uint_; - break; - case FLOAT: - val.double_ = value.double_ + casted.value.double_; - break; +DFConstant::DFConstant(KernMeta &meta, DFTypeImpl *type, Value value) : + DFVariableImpl("", IODirection::NONE, meta), + type(*type), value(value) { + if (this->type.isFixed()) { + if (((FixedType &) this->type).isSigned()) { + kind = TypeKind::INT; + } else { + kind = TypeKind::UINT; } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); + kind = TypeKind::FLOAT; } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::ADD, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; } -DFVariableImpl &DFConstant::operator-(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ - casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ - casted.value.uint_; - break; - case FLOAT: - val.double_ = value.double_ - casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SUB, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::clone() const { + return new DFConstant(meta, &type, value); } -DFVariableImpl &DFConstant::operator*(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ * casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ * casted.value.uint_; - break; - case FLOAT: - val.double_ = value.double_ * casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::MUL, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFTypeImpl *DFConstant::getType() { + return &type; } -DFVariableImpl &DFConstant::operator/(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ / casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ / casted.value.uint_; - break; - case FLOAT: - val.double_ = value.double_ / casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::DIV, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +#define GENERIC_CONST_BINARY_OP(OP_TYPE, OP, VAR, RHS) \ +DFVariableImpl *VAR; \ +if (RHS.isConstant()) { \ + Value val {}; \ + DFConstant &casted = (DFConstant &) (RHS); \ + switch (kind) { \ + case INT: \ + val.int_ = value.int_ OP casted.value.int_; \ + break; \ + case UINT: \ + val.uint_ = value.uint_ OP casted.value.uint_; \ + break; \ + case FLOAT: \ + val.double_ = value.double_ OP casted.value.double_; \ + break; \ + } \ + VAR = meta.varBuilder.buildConstant(meta, &type, val); \ + meta.storage.addVariable(VAR); \ + meta.graph.addNode(VAR, OpType::CONST, NodeData {}); \ + return VAR; \ +} \ +VAR = meta.varBuilder.buildStream("", IODirection::NONE, meta, &type); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +meta.graph.addChannel(&RHS, VAR, 1, false); \ +return VAR; + +DFVariableImpl *DFConstant::operator+(DFVariableImpl &rhs) { + GENERIC_CONST_BINARY_OP(OpType::ADD, +, newVar, rhs) } -DFVariableImpl &DFConstant::operator&(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ & casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ & casted.value.uint_; - break; - case FLOAT: - // TODO: For discussion. - // Issue #12 (https://github.com/ispras/utopia-hls/issues/12). - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::AND, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator-(DFVariableImpl &rhs) { + GENERIC_CONST_BINARY_OP(OpType::SUB, -, newVar, rhs) } -DFVariableImpl &DFConstant::operator|(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ | casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ | casted.value.uint_; - break; - case FLOAT: - // TODO: For discussion. - // Issue #12 (https://github.com/ispras/utopia-hls/issues/12). - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::OR, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator*(DFVariableImpl &rhs) { + GENERIC_CONST_BINARY_OP(OpType::MUL, *, newVar, rhs) } -DFVariableImpl &DFConstant::operator^(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.int_ = value.int_ | casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ | casted.value.uint_; - break; - case FLOAT: - // TODO: For discussion. - // Issue #12 (https://github.com/ispras/utopia-hls/issues/12). - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::XOR, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator/(DFVariableImpl &rhs) { + GENERIC_CONST_BINARY_OP(OpType::DIV, /, newVar, rhs) } -DFVariableImpl &DFConstant::operator!() { +#define GENERIC_CONST_BITWISE_OP(OP_TYPE, OP, VAR, RHS) \ +DFVariableImpl *VAR; \ +if (RHS.isConstant()) { \ + Value val {}; \ + DFConstant &casted = (DFConstant &) (RHS); \ + val.uint_ = value.uint_ OP casted.value.uint_; \ + VAR = meta.varBuilder.buildConstant(meta, &type, val); \ + meta.storage.addVariable(VAR); \ + meta.graph.addNode(VAR, OpType::CONST, NodeData {}); \ + return VAR; \ +} \ +VAR = meta.varBuilder.buildStream("", IODirection::NONE, meta, &type); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +meta.graph.addChannel(&RHS, VAR, 1, false); \ +return VAR; + +DFVariableImpl *DFConstant::operator&(DFVariableImpl &rhs) { + GENERIC_CONST_BITWISE_OP(OpType::AND, &, newVar, rhs) +} + +DFVariableImpl *DFConstant::operator|(DFVariableImpl &rhs) { + GENERIC_CONST_BITWISE_OP(OpType::OR, |, newVar, rhs) +} + +DFVariableImpl *DFConstant::operator^(DFVariableImpl &rhs) { + GENERIC_CONST_BITWISE_OP(OpType::XOR, ^, newVar, rhs) +} + +DFVariableImpl *DFConstant::operator!() { DFVariableImpl *newVar; - ConstantValue val{}; - switch (kind) { - case INT: - val.int_ = ~value.int_; - break; - case UINT: - val.uint_ = ~value.uint_; - break; - case FLOAT: - // TODO: For discussion. - // Issue #12 (https://github.com/ispras/utopia-hls/issues/12). - break; - } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NOT, NodeData{}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + Value val {}; + val.uint_ = ~value.uint_; + newVar = meta.varBuilder.buildConstant(meta, &type, val); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::CONST, NodeData {}); + return newVar; } -DFVariableImpl &DFConstant::operator-() { +DFVariableImpl *DFConstant::operator-() { DFVariableImpl *newVar; - ConstantValue val{}; + Value val {}; switch (kind) { case INT: val.int_ = -value.int_; @@ -251,248 +131,90 @@ DFVariableImpl &DFConstant::operator-() { val.double_ = -value.double_; break; } - newVar = helper.varBuilder.buildConstant(helper, type, kind, val); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NEG, NodeData{}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + newVar = meta.varBuilder.buildConstant(meta, &type, val); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::CONST, NodeData {}); + return newVar; } -DFVariableImpl &DFConstant::operator<(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.uint_ = value.int_ < casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ < casted.value.uint_; - break; - case FLOAT: - val.uint_ = value.double_ < casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, - *newType, - kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, - *newType); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::LESS, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +#define GENERIC_CONST_COMP_OP(OP_TYPE, OP, VAR, TYPE_VAR, RHS) \ +DFVariableImpl *VAR; \ +if (RHS.isConstant()) { \ + Value val {}; \ + DFConstant &casted = (DFConstant &) (RHS); \ + switch (kind) { \ + case INT: \ + val.int_ = value.int_ OP casted.value.int_; \ + break; \ + case UINT: \ + val.uint_ = value.uint_ OP casted.value.uint_; \ + break; \ + case FLOAT: \ + val.double_ = value.double_ OP casted.value.double_; \ + break; \ + } \ + VAR = meta.varBuilder.buildConstant(meta, &type, val); \ + meta.storage.addVariable(VAR); \ + meta.graph.addNode(VAR, OpType::CONST, NodeData {}); \ + return VAR; \ +} \ +DFTypeImpl *TYPE_VAR = meta.storage.addType(meta.typeBuilder.buildBool()); \ +VAR = meta.varBuilder.buildStream("", IODirection::NONE, meta, TYPE_VAR); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +meta.graph.addChannel(&RHS, VAR, 1, false); \ +return VAR; + +DFVariableImpl *DFConstant::operator<(DFVariableImpl &rhs) { + GENERIC_CONST_COMP_OP(OpType::LESS, <, newVar, newType, rhs) } -DFVariableImpl &DFConstant::operator<=(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.uint_ = value.int_ <= casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ <= casted.value.uint_; - break; - case FLOAT: - val.uint_ = value.double_ <= casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, - *newType, - kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, - *newType); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::LESSEQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator<=(DFVariableImpl &rhs) { + GENERIC_CONST_COMP_OP(OpType::LESSEQ, <=, newVar, newType, rhs) } -DFVariableImpl &DFConstant::operator>(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.uint_ = value.int_ > casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ > casted.value.uint_; - break; - case FLOAT: - val.uint_ = value.double_ > casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, - *newType, - kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, - *newType); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::GREATER, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator>(DFVariableImpl &rhs) { + GENERIC_CONST_COMP_OP(OpType::GREATER, >, newVar, newType, rhs) } -DFVariableImpl &DFConstant::operator>=(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.uint_ = value.int_ >= casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ >= casted.value.uint_; - break; - case FLOAT: - val.uint_ = value.double_ >= casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, - *newType, - kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, - *newType); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::GREATEREQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator>=(DFVariableImpl &rhs) { + GENERIC_CONST_COMP_OP(OpType::GREATEREQ, >=, newVar, newType, rhs) } -DFVariableImpl &DFConstant::operator==(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.uint_ = value.int_ == casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ == casted.value.uint_; - break; - case FLOAT: - val.uint_ = value.double_ == casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, - *newType, - kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, - *newType); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::EQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator==(DFVariableImpl &rhs) { + GENERIC_CONST_COMP_OP(OpType::EQ, ==, newVar, newType, rhs) } -DFVariableImpl &DFConstant::operator!=(DFVariableImpl &rhs) { - DFVariableImpl *newVar; - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - if (rhs.isConstant()) { - ConstantValue val{}; - DFConstant &casted = (DFConstant &) (rhs); - switch (kind) { - case INT: - val.uint_ = value.int_ != casted.value.int_; - break; - case UINT: - val.uint_ = value.uint_ != casted.value.uint_; - break; - case FLOAT: - val.uint_ = value.double_ != casted.value.double_; - break; - } - newVar = helper.varBuilder.buildConstant(helper, - *newType, - kind, val); - } else { - newVar = helper.varBuilder.buildStream("", IODirection::NONE, helper, - *newType); - } - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NEQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFConstant::operator!=(DFVariableImpl &rhs) { + GENERIC_CONST_COMP_OP(OpType::NEQ, !=, newVar, newType, rhs) } -DFVariableImpl &DFConstant::operator<<(uint8_t bits) { +DFVariableImpl *DFConstant::operator<<(uint8_t bits) { DFVariableImpl *newVar; - ConstantValue val{}; - switch (kind) { - case INT: - val.int_ = value.int_ << bits; - break; - case UINT: - val.uint_ = value.uint_ << bits; - break; - case FLOAT: - // TODO: For discussion. - // Issue #12 (https://github.com/ispras/utopia-hls/issues/12). - break; - } - DFTypeImpl *newType = helper.storage.addType( - helper.typeBuilder.buildShiftedType(type, bits)); - newVar = helper.varBuilder.buildConstant(helper, *newType, kind, val); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SHL, NodeData{.bitShift=bits}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + Value val {}; + val.uint_ = value.uint_ << bits; + DFTypeImpl *newType = meta.storage.addType( + meta.typeBuilder.buildShiftedType(&type, bits)); + newVar = meta.varBuilder.buildConstant(meta, newType, val); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::CONST, NodeData {.bitShift=bits}); + return newVar; } -DFVariableImpl &DFConstant::operator>>(uint8_t bits) { +DFVariableImpl *DFConstant::operator>>(uint8_t bits) { DFVariableImpl *newVar; - ConstantValue val{}; - switch (kind) { - case INT: - val.int_ = value.int_ >> bits; - break; - case UINT: - val.uint_ = value.uint_ >> bits; - break; - case FLOAT: - // TODO: For discussion. - // Issue #12 (https://github.com/ispras/utopia-hls/issues/12). - break; - } - DFTypeImpl *newType = helper.storage.addType( - helper.typeBuilder.buildShiftedType(type, int8_t(bits) * -1)); - newVar = helper.varBuilder.buildConstant(helper, *newType, kind, val); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SHR, NodeData{.bitShift=bits}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + Value val {}; + val.uint_ = value.uint_ >> bits; + DFTypeImpl *newType = meta.storage.addType( + meta.typeBuilder.buildShiftedType(&type, int8_t(bits) * -1)); + newVar = meta.varBuilder.buildConstant(meta, newType, val); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::CONST, NodeData {.bitShift=bits}); + return newVar; } -ConstantTypeKind DFConstant::getKind() const { +DFConstant::TypeKind DFConstant::getKind() const { return kind; } diff --git a/src/model/dfcxx/lib/dfcxx/vars/scalar.cpp b/src/model/dfcxx/lib/dfcxx/vars/scalar.cpp index c2e47a7..cf63718 100644 --- a/src/model/dfcxx/lib/dfcxx/vars/scalar.cpp +++ b/src/model/dfcxx/lib/dfcxx/vars/scalar.cpp @@ -6,195 +6,130 @@ // //===----------------------------------------------------------------------===// -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" #include "dfcxx/vars/scalar.h" namespace dfcxx { DFScalar::DFScalar(const std::string &name, IODirection direction, - GraphHelper &helper, dfcxx::DFTypeImpl &type) : - DFVariableImpl(name, direction, helper), type(type) {} + KernMeta &meta, DFTypeImpl *type) : + DFVariableImpl(name, direction, meta), type(*type) {} -DFTypeImpl &DFScalar::getType() { - return type; +DFVariableImpl *DFScalar::clone() const { + return new DFScalar(name, direction, meta, &type); } -DFVariableImpl &DFScalar::operator+(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::ADD, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFTypeImpl *DFScalar::getType() { + return &type; } -DFVariableImpl &DFScalar::operator-(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SUB, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +#define GENERIC_STREAM_BINARY_OP(OP_TYPE, VAR, RHS) \ +DFVariableImpl *VAR = \ + meta.varBuilder.buildStream("", IODirection::NONE, meta, &type); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +meta.graph.addChannel(&RHS, VAR, 1, false); \ +return VAR; + +DFVariableImpl *DFScalar::operator+(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::ADD, newVar, rhs) } -DFVariableImpl &DFScalar::operator*(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::MUL, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator-(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::SUB, newVar, rhs) } -DFVariableImpl &DFScalar::operator/(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::DIV, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator*(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::MUL, newVar, rhs) } -DFVariableImpl &DFScalar::operator&(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::AND, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator/(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::DIV, newVar, rhs) } -DFVariableImpl &DFScalar::operator|(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::OR, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator&(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::AND, newVar, rhs) } -DFVariableImpl &DFScalar::operator^(DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::XOR, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator|(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::OR, newVar, rhs) } -DFVariableImpl &DFScalar::operator!() { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NOT, NodeData{}); - helper.addChannel(this, newVar, 0, false); - return *newVar; +DFVariableImpl *DFScalar::operator^(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::XOR, newVar, rhs) } -DFVariableImpl &DFScalar::operator-() { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NEG, NodeData{}); - helper.addChannel(this, newVar, 0, false); - return *newVar; +#define GENERIC_STREAM_UNARY_OP(OP_TYPE, VAR) \ +DFVariableImpl *VAR = \ + meta.varBuilder.buildStream("", IODirection::NONE, meta, &type); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +return VAR; + +DFVariableImpl *DFScalar::operator!() { + GENERIC_STREAM_UNARY_OP(OpType::NOT, newVar) } -DFVariableImpl &DFScalar::operator<(DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::LESS, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator-() { + GENERIC_STREAM_UNARY_OP(OpType::NEG, newVar) } -DFVariableImpl &DFScalar::operator<=(DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::LESSEQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +#define GENERIC_STREAM_COMP_OP(OP_TYPE, VAR, TYPE_VAR, RHS) \ +DFTypeImpl *TYPE_VAR = meta.storage.addType(meta.typeBuilder.buildBool()); \ + DFVariableImpl *VAR = \ + meta.varBuilder.buildStream("", IODirection::NONE, meta, TYPE_VAR); \ + meta.storage.addVariable(VAR); \ + meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ + meta.graph.addChannel(this, VAR, 0, false); \ + meta.graph.addChannel(&RHS, VAR, 1, false); \ + return VAR; + +DFVariableImpl *DFScalar::operator<(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::LESS, newVar, newType, rhs) } -DFVariableImpl &DFScalar::operator>(DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::GREATER, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator<=(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::LESSEQ, newVar, newType, rhs) } -DFVariableImpl &DFScalar::operator>=(DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::GREATEREQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator>(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::GREATER, newVar, newType, rhs) } -DFVariableImpl &DFScalar::operator==(DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::EQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator>=(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::GREATEREQ, newVar, newType, rhs) } -DFVariableImpl &DFScalar::operator!=(DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NEQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFScalar::operator==(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::EQ, newVar, newType, rhs) +} + +DFVariableImpl *DFScalar::operator!=(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::NEQ, newVar, newType, rhs) } -DFVariableImpl &DFScalar::operator<<(uint8_t bits) { - DFTypeImpl *newType = helper.storage.addType( - helper.typeBuilder.buildShiftedType(type, bits)); +DFVariableImpl *DFScalar::operator<<(uint8_t bits) { + DFTypeImpl *newType = meta.storage.addType( + meta.typeBuilder.buildShiftedType(&type, bits)); DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SHL, NodeData{.bitShift=bits}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + meta.varBuilder.buildStream("", IODirection::NONE, meta, newType); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::SHL, NodeData {.bitShift=bits}); + meta.graph.addChannel(this, newVar, 0, false); + return newVar; } -DFVariableImpl &DFScalar::operator>>(uint8_t bits) { - DFTypeImpl *newType = helper.storage.addType( - helper.typeBuilder.buildShiftedType(type, int8_t(bits) * -1)); +DFVariableImpl *DFScalar::operator>>(uint8_t bits) { + DFTypeImpl *newType = meta.storage.addType( + meta.typeBuilder.buildShiftedType(&type, int8_t(bits) * -1)); DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SHR, NodeData{.bitShift=bits}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + meta.varBuilder.buildStream("", IODirection::NONE, meta, newType); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::SHR, NodeData {.bitShift=bits}); + meta.graph.addChannel(this, newVar, 0, false); + return newVar; } bool DFScalar::isScalar() const { diff --git a/src/model/dfcxx/lib/dfcxx/vars/stream.cpp b/src/model/dfcxx/lib/dfcxx/vars/stream.cpp index 590a73e..1cd8891 100644 --- a/src/model/dfcxx/lib/dfcxx/vars/stream.cpp +++ b/src/model/dfcxx/lib/dfcxx/vars/stream.cpp @@ -6,195 +6,130 @@ // //===----------------------------------------------------------------------===// -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" #include "dfcxx/vars/stream.h" namespace dfcxx { DFStream::DFStream(const std::string &name, IODirection direction, - GraphHelper &helper, dfcxx::DFTypeImpl &type) : - DFVariableImpl(name, direction, helper), type(type) {} + KernMeta &meta, DFTypeImpl *type) : + DFVariableImpl(name, direction, meta), type(*type) {} -DFTypeImpl &DFStream::getType() { - return type; +DFVariableImpl *DFStream::clone() const { + return new DFStream(name, direction, meta, &type); } -DFVariableImpl &DFStream::operator+(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::ADD, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFTypeImpl *DFStream::getType() { + return &type; } -DFVariableImpl &DFStream::operator-(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SUB, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +#define GENERIC_STREAM_BINARY_OP(OP_TYPE, VAR, RHS) \ +DFVariableImpl *VAR = \ + meta.varBuilder.buildStream("", IODirection::NONE, meta, &type); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +meta.graph.addChannel(&RHS, VAR, 1, false); \ +return VAR; + +DFVariableImpl *DFStream::operator+(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::ADD, newVar, rhs) } -DFVariableImpl &DFStream::operator*(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::MUL, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator-(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::SUB, newVar, rhs) } -DFVariableImpl &DFStream::operator/(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::DIV, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator*(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::MUL, newVar, rhs) } -DFVariableImpl &DFStream::operator&(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::AND, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator/(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::DIV, newVar, rhs) } -DFVariableImpl &DFStream::operator|(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::OR, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator&(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::AND, newVar, rhs) } -DFVariableImpl &DFStream::operator^(dfcxx::DFVariableImpl &rhs) { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::XOR, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator|(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::OR, newVar, rhs) } -DFVariableImpl &DFStream::operator!() { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NOT, NodeData{}); - helper.addChannel(this, newVar, 0, false); - return *newVar; +DFVariableImpl *DFStream::operator^(DFVariableImpl &rhs) { + GENERIC_STREAM_BINARY_OP(OpType::XOR, newVar, rhs) } -DFVariableImpl &DFStream::operator-() { - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, type); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NEG, NodeData{}); - helper.addChannel(this, newVar, 0, false); - return *newVar; +#define GENERIC_STREAM_UNARY_OP(OP_TYPE, VAR) \ +DFVariableImpl *VAR = \ + meta.varBuilder.buildStream("", IODirection::NONE, meta, &type); \ +meta.storage.addVariable(VAR); \ +meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ +meta.graph.addChannel(this, VAR, 0, false); \ +return VAR; + +DFVariableImpl *DFStream::operator!() { + GENERIC_STREAM_UNARY_OP(OpType::NOT, newVar) } -DFVariableImpl &DFStream::operator<(dfcxx::DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::LESS, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator-() { + GENERIC_STREAM_UNARY_OP(OpType::NEG, newVar) } -DFVariableImpl &DFStream::operator<=(dfcxx::DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::LESSEQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +#define GENERIC_STREAM_COMP_OP(OP_TYPE, VAR, TYPE_VAR, RHS) \ +DFTypeImpl *TYPE_VAR = meta.storage.addType(meta.typeBuilder.buildBool()); \ + DFVariableImpl *VAR = \ + meta.varBuilder.buildStream("", IODirection::NONE, meta, TYPE_VAR); \ + meta.storage.addVariable(VAR); \ + meta.graph.addNode(VAR, OP_TYPE, NodeData {}); \ + meta.graph.addChannel(this, VAR, 0, false); \ + meta.graph.addChannel(&RHS, VAR, 1, false); \ + return VAR; + +DFVariableImpl *DFStream::operator<(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::LESS, newVar, newType, rhs) } -DFVariableImpl &DFStream::operator>(dfcxx::DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::GREATER, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator<=(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::LESSEQ, newVar, newType, rhs) } -DFVariableImpl &DFStream::operator>=(dfcxx::DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::GREATEREQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator>(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::GREATER, newVar, newType, rhs) } -DFVariableImpl &DFStream::operator==(dfcxx::DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::EQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator>=(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::GREATEREQ, newVar, newType, rhs) } -DFVariableImpl &DFStream::operator!=(dfcxx::DFVariableImpl &rhs) { - DFTypeImpl *newType = helper.storage.addType(helper.typeBuilder.buildBool()); - DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::NEQ, NodeData{}); - helper.addChannel(this, newVar, 0, false); - helper.addChannel(&rhs, newVar, 1, false); - return *newVar; +DFVariableImpl *DFStream::operator==(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::EQ, newVar, newType, rhs) +} + +DFVariableImpl *DFStream::operator!=(DFVariableImpl &rhs) { + GENERIC_STREAM_COMP_OP(OpType::NEQ, newVar, newType, rhs) } -DFVariableImpl &DFStream::operator<<(uint8_t bits) { - DFTypeImpl *newType = helper.storage.addType( - helper.typeBuilder.buildShiftedType(type, bits)); +DFVariableImpl *DFStream::operator<<(uint8_t bits) { + DFTypeImpl *newType = meta.storage.addType( + meta.typeBuilder.buildShiftedType(&type, bits)); DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SHL, NodeData{.bitShift=bits}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + meta.varBuilder.buildStream("", IODirection::NONE, meta, newType); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::SHL, NodeData {.bitShift=bits}); + meta.graph.addChannel(this, newVar, 0, false); + return newVar; } -DFVariableImpl &DFStream::operator>>(uint8_t bits) { - DFTypeImpl *newType = helper.storage.addType( - helper.typeBuilder.buildShiftedType(type, int8_t(bits) * -1)); +DFVariableImpl *DFStream::operator>>(uint8_t bits) { + DFTypeImpl *newType = meta.storage.addType( + meta.typeBuilder.buildShiftedType(&type, int8_t(bits) * -1)); DFVariableImpl *newVar = - helper.varBuilder.buildStream("", IODirection::NONE, helper, *newType); - helper.storage.addVariable(newVar); - helper.addNode(newVar, OpType::SHR, NodeData{.bitShift=bits}); - helper.addChannel(this, newVar, 0, false); - return *newVar; + meta.varBuilder.buildStream("", IODirection::NONE, meta, newType); + meta.storage.addVariable(newVar); + meta.graph.addNode(newVar, OpType::SHR, NodeData {.bitShift=bits}); + meta.graph.addChannel(this, newVar, 0, false); + return newVar; } bool DFStream::isStream() const { diff --git a/src/model/dfcxx/lib/dfcxx/vars/var.cpp b/src/model/dfcxx/lib/dfcxx/vars/var.cpp index 1fd5feb..e413ef5 100644 --- a/src/model/dfcxx/lib/dfcxx/vars/var.cpp +++ b/src/model/dfcxx/lib/dfcxx/vars/var.cpp @@ -6,26 +6,29 @@ // //===----------------------------------------------------------------------===// -#include "dfcxx/graph.h" -#include "dfcxx/kernstorage.h" -#include "dfcxx/varbuilders/builder.h" +#include "dfcxx/kernmeta.h" #include "dfcxx/vars/var.h" namespace dfcxx { -DFVariableImpl::DFVariableImpl(const std::string &name, IODirection direction, - GraphHelper &helper) : name(name), - direction(direction), - helper(helper) {} +DFVariableImpl::DFVariableImpl(const std::string &name, + IODirection direction, + KernMeta &meta) : name(name), + direction(direction), + meta(meta) {} std::string_view DFVariableImpl::getName() const { return name; } -IODirection DFVariableImpl::getDirection() const { +DFVariableImpl::IODirection DFVariableImpl::getDirection() const { return direction; } +const KernMeta &DFVariableImpl::getMeta() const { + return meta; +} + bool DFVariableImpl::isStream() const { return false; } @@ -38,12 +41,16 @@ bool DFVariableImpl::isConstant() const { return false; } -void DFVariableImpl::connect(dfcxx::DFVariableImpl &connectee) { - helper.addChannel(&connectee, this, 0, true); +void DFVariableImpl::connect(DFVariableImpl *connectee) { + meta.graph.addChannel(connectee, this, 0, true); } DFVariable::DFVariable(DFVariableImpl *impl) : impl(impl) {} +DFVariable::operator DFVariableImpl*() const { + return impl; +} + DFVariableImpl *DFVariable::getImpl() const { return impl; } @@ -52,80 +59,80 @@ std::string_view DFVariable::getName() const { return impl->getName(); } -IODirection DFVariable::getDirection() const { +DFVariableImpl::IODirection DFVariable::getDirection() const { return impl->getDirection(); } DFType DFVariable::getType() const { - return DFType(&(impl->getType())); + return DFType(impl->getType()); } DFVariable DFVariable::operator+(const DFVariable &rhs) { - return DFVariable(&(impl->operator+(*(rhs.impl)))); + return DFVariable(impl->operator+(*(rhs.impl))); } DFVariable DFVariable::operator-(const DFVariable &rhs) { - return DFVariable(&(impl->operator-(*(rhs.impl)))); + return DFVariable(impl->operator-(*(rhs.impl))); } DFVariable DFVariable::operator*(const DFVariable &rhs) { - return DFVariable(&(impl->operator*(*(rhs.impl)))); + return DFVariable(impl->operator*(*(rhs.impl))); } DFVariable DFVariable::operator/(const DFVariable &rhs) { - return DFVariable(&(impl->operator/(*(rhs.impl)))); + return DFVariable(impl->operator/(*(rhs.impl))); } DFVariable DFVariable::operator&(const DFVariable &rhs) { - return DFVariable(&(impl->operator&(*(rhs.impl)))); + return DFVariable(impl->operator&(*(rhs.impl))); } DFVariable DFVariable::operator|(const DFVariable &rhs) { - return DFVariable(&(impl->operator|(*(rhs.impl)))); + return DFVariable(impl->operator|(*(rhs.impl))); } DFVariable DFVariable::operator^(const DFVariable &rhs) { - return DFVariable(&(impl->operator^(*(rhs.impl)))); + return DFVariable(impl->operator^(*(rhs.impl))); } DFVariable DFVariable::operator!() { - return DFVariable(&(impl->operator!())); + return DFVariable(impl->operator!()); } DFVariable DFVariable::operator-() { - return DFVariable(&(impl->operator-())); + return DFVariable(impl->operator-()); } DFVariable DFVariable::operator<(const DFVariable &rhs) { - return DFVariable(&(impl->operator<(*(rhs.impl)))); + return DFVariable(impl->operator<(*(rhs.impl))); } DFVariable DFVariable::operator<=(const DFVariable &rhs) { - return DFVariable(&(impl->operator<=(*(rhs.impl)))); + return DFVariable(impl->operator<=(*(rhs.impl))); } DFVariable DFVariable::operator>(const DFVariable &rhs) { - return DFVariable(&(impl->operator>(*(rhs.impl)))); + return DFVariable(impl->operator>(*(rhs.impl))); } DFVariable DFVariable::operator>=(const DFVariable &rhs) { - return DFVariable(&(impl->operator>=(*(rhs.impl)))); + return DFVariable(impl->operator>=(*(rhs.impl))); } DFVariable DFVariable::operator==(const DFVariable &rhs) { - return DFVariable(&(impl->operator==(*(rhs.impl)))); + return DFVariable(impl->operator==(*(rhs.impl))); } DFVariable DFVariable::operator!=(const DFVariable &rhs) { - return DFVariable(&(impl->operator!=(*(rhs.impl)))); + return DFVariable(impl->operator!=(*(rhs.impl))); } DFVariable DFVariable::operator<<(uint8_t bits) { - return DFVariable(&(impl->operator<<(bits))); + return DFVariable(impl->operator<<(bits)); } DFVariable DFVariable::operator>>(uint8_t bits) { - return DFVariable(&(impl->operator>>(bits))); + return DFVariable(impl->operator>>(bits)); } bool DFVariable::isStream() const { @@ -141,7 +148,7 @@ bool DFVariable::isConstant() const { } void DFVariable::connect(const DFVariable &connectee) { - impl->connect(*(connectee.impl)); + impl->connect(connectee.impl); } DFVariable &DFVariable::operator=(const DFVariable &var) { diff --git a/src/model/dfcxx/templates/vcd.tpl b/src/model/dfcxx/templates/vcd.tpl new file mode 100644 index 0000000..9fe8f3d --- /dev/null +++ b/src/model/dfcxx/templates/vcd.tpl @@ -0,0 +1,17 @@ +$date {{GEN_TIME}} $end +$version Utopia HLS $end +$timescale 1ps $end + +$scope module logic $end +{{#VARS}}$var wire {{WIDTH}} {{NAME}} {{NAME}} $end +{{/VARS}}$upscope $end +$enddefinitions $end + +$dumpvars +{{#INIT_VARS}}b{{INIT_VALUE}} {{NAME}} +{{/INIT_VARS}}$end + +{{#TICKS}}#{{TICK}} +{{#VALUES}}b{{VALUE}} {{NAME}} +{{/VALUES}} +{{/TICKS}}#{{FINAL_TICK}} diff --git a/src/options.h b/src/options.h index e762926..879c972 100644 --- a/src/options.h +++ b/src/options.h @@ -41,10 +41,15 @@ #define OUT_FIRRTL_JSON "out_firrtl" #define OUT_DOT_JSON "out_dot" +#define SIM_ID_JSON "sim" +#define SIM_IN_JSON "in" +#define SIM_OUT_JSON "out" + //===----------------------------------------------------------------------===// // CLI args/flags definitions #define HLS_CMD "hls" +#define SIM_CMD "sim" #define CONFIG_ARG CLI_ARG("config") #define SCHEDULER_GROUP "scheduler" #define ASAP_SCHEDULER_FLAG CLI_FLAG("a") @@ -56,6 +61,9 @@ #define OUT_FIRRTL_ARG CLI_ARG("out-firrtl") #define OUT_DOT_ARG CLI_ARG("out-dot") +#define SIM_IN_ARG CLI_ARG("in") +#define SIM_OUT_ARG CLI_ARG("out") + //===----------------------------------------------------------------------===// using Json = nlohmann::json; @@ -84,6 +92,10 @@ class AppOptions { virtual void fromJson(Json json) { // TODO: Default implementation. } + + operator bool() const { + return options->parsed(); + } virtual Json toJson() const { return toJson(options); @@ -172,7 +184,7 @@ struct HlsOptions final : public AppOptions { // Named options. options->add_option(CONFIG_ARG, - latConfigFile, + latencyCfgFile, "JSON latency configuration path") ->expected(1); @@ -204,7 +216,7 @@ struct HlsOptions final : public AppOptions { } void fromJson(Json json) override { - get(json, CONFIG_JSON, latConfigFile); + get(json, CONFIG_JSON, latencyCfgFile); get(json, ASAP_SCHEDULER_JSON, asapScheduler); get(json, LP_SCHEDULER_JSON, lpScheduler); get(json, OUT_SV_JSON, outNames[OUT_FORMAT_ID_INT(SystemVerilog)]); @@ -213,23 +225,6 @@ struct HlsOptions final : public AppOptions { 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; - DFLatencyConfig latConfig; - std::vector outNames; - bool asapScheduler; - bool lpScheduler; -}; - -struct Options final : public AppOptions { - Options(const std::string &title, - const std::string &version): - AppOptions(title, version), hls(*this) { - - // Top-level options. - options->set_help_all_flag("-H,--help-all", "Print the extended help message and exit"); - options->set_version_flag("-v,--version", version, "Print the tool version"); - } dfcxx::Ops convertFieldToEnum(const std::string field) { if (field == "ADD_INT") { return dfcxx::ADD_INT; } else @@ -265,22 +260,67 @@ struct Options final : public AppOptions { return dfcxx::ADD_INT; } - void parseLatencyConfig(const std::string config) { - std::ifstream in(config); + void parseLatencyConfig() { + std::ifstream in(latencyCfgFile); if (!in.good()) { return; } auto json = Json::parse(in); for (auto &[key, val] : json.items()) { - hls.latConfig[convertFieldToEnum(key)] = val; + latencyCfg[convertFieldToEnum(key)] = val; } } + std::string latencyCfgFile; + DFLatencyConfig latencyCfg; + std::vector outNames; + bool asapScheduler; + bool lpScheduler; +}; + +struct SimOptions final : public AppOptions { + + SimOptions(AppOptions &parent): + AppOptions(parent, SIM_CMD, "DFCxx simulation") { + + // Named options. + options->add_option(SIM_IN_ARG, + inFilePath, + "Simulation input data path")->capture_default_str(); + options->add_option(SIM_OUT_ARG, + outFilePath, + "Simulation results output path")->capture_default_str(); + } + + void fromJson(Json json) override { + get(json, SIM_IN_JSON, inFilePath); + get(json, SIM_OUT_JSON, outFilePath); + } + + std::string inFilePath; + std::string outFilePath; + std::vector files; +}; + +struct Options final : public AppOptions { + Options(const std::string &title, + const std::string &version): + AppOptions(title, version), hls(*this), sim(*this) { + + // Top-level options. + options->set_help_all_flag("-H,--help-all", "Print the extended help message and exit"); + options->set_version_flag("-v,--version", version, "Print the tool version"); + } + void initialize(const std::string &config, int argc, char **argv) { // Read the JSON configuration. read(config); // Command line is of higher priority. parse(argc, argv); - // Parse latency configuration. - parseLatencyConfig(hls.latConfigFile); + + // Subcommand-specific initialization actions. + if (hls) { + // Parse latency configuration. + hls.parseLatencyConfig(); + } } int exit(const CLI::Error &e) const { @@ -289,7 +329,9 @@ struct Options final : public AppOptions { void fromJson(Json json) override { hls.fromJson(json[HLS_ID_JSON]); + sim.fromJson(json[SIM_ID_JSON]); } HlsOptions hls; + SimOptions sim; };