From 135321c90cb019c29949c5bfd92563b614cdb347 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 4 Sep 2021 22:48:23 +0900 Subject: [PATCH 01/54] wip --- src/iyokan_nt.hpp | 222 ++++++++++++++++++++++++++++++++++++++++++++++ src/test0.cpp | 45 ++++++++++ 2 files changed, 267 insertions(+) create mode 100644 src/iyokan_nt.hpp diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp new file mode 100644 index 0000000..c5901d7 --- /dev/null +++ b/src/iyokan_nt.hpp @@ -0,0 +1,222 @@ +#ifndef VIRTUALSECUREPLATFORM_IYOKAN_NT_HPP +#define VIRTUALSECUREPLATFORM_IYOKAN_NT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nt { + +class Allocator { +private: + std::unordered_map> subs_; + std::vector data_; + +public: + Allocator(/* optional snapshot file */) + { + } + + Allocator& subAllocator(const std::string& key) + { + assert(data_.size() == 0); + auto it = subs_.find(key); + if (it == subs_.end()) { + auto sub = std::make_unique(); + std::tie(it, std::ignore) = subs_.emplace(key, std::move(sub)); + } + return *it->second; + } + + template + T* make(size_t index) + { + assert(subs_.size() == 0); + if (data_.size() <= index) + data_.resize(index + 1); + std::any& v = data_.at(index); + assert(!v.has_value()); + return &v.emplace(); + } + + template + T* get(size_t index) + { + assert(subs_.size() == 0); + assert(index < data_.size()); + T* ret = std::any_cast(&data_.at(index)); + assert(ret != nullptr); + return ret; + } +}; + +enum class TAG_KEY { + KIND, + PORT_NAME, + PORT_BIT, +}; +using Tag = std::pair; + +using UID = uint64_t; +struct Label { + UID uid; + std::unordered_map tags; +}; + +class Task { +private: + Label label_; + std::vector dependents_; + +public: + Task(Label label); + virtual ~Task(); + + const Label& label() const + { + return label_; + } + + const std::vector& dependents() const + { + return dependents_; + } + + void addDependent(Task* task) + { + dependents_.push_back(task); + } + + virtual void notifyOneInputReady() = 0; + virtual bool areAllInputsReady() const = 0; + virtual void startAsynchronously() = 0; + virtual bool hasFinished() const = 0; + virtual void tick() = 0; // Reset for next cycle +}; + +// TaskCommon can be used as base class of many "common" tasks. +// "Common" here means: +// 1. # of outputs is 1. (#inputs can be >1.) +// 2. All the inputs and output have the same type. +// 3. (and so on) +// TaskCommon guarantees: +// 1. Its output exists in alc.get(0). +template +class TaskCommon : public Task { +private: + size_t numReadyInputs_; + std::vector inputs_; + T* output_; + +protected: + T input(size_t i) const + { + return *inputs_.at(i); + } + + T& output() + { + return *output_; + } + +public: + TaskCommon(Label label, Allocator& alc, size_t numExpectedInputs) + : Task(std::move(label)), + numReadyInputs_(0), + inputs_(numExpectedInputs, nullptr), + output_(alc.make(0)) + { + } + + virtual ~TaskCommon() + { + } + + void notifyOneInputReady() override + { + numReadyInputs_++; + assert(numReadyInputs_ <= inputs_.size()); + } + + bool areAllInputsReady() const override + { + return numReadyInputs_ == inputs_.size(); + } + + void tick() override + { + numReadyInputs_ = 0; + } + + void addInput(T* newIn) + { + for (size_t i = 0; i < inputs_.size(); i++) { + if (inputs_.at(i) != nullptr) + continue; + inputs_.at(i) = newIn; + return; + } + assert(false && "Too many calls of addInput"); + } + + const T* const getOutput() const + { + return output_; + } +}; + +class NetworkBuilder { +private: + Allocator& alc_; + std::unordered_map uid2task_; + +public: + NetworkBuilder(Allocator& alc); +}; + +class Network { +public: + Network(NetworkBuilder&& builder); + + Task* findByUID(UID uid) const; + Task* findByTags(const std::vector& tags) const; + + void eachTask(std::function handler) const; +}; + +} // namespace nt + +#include "packet.hpp" + +namespace nt { +namespace plain { + +class TaskNand : public TaskCommon { +public: + TaskNand(Label label, Allocator& alc) : TaskCommon(label, alc, 2) + { + } + + ~TaskNand() + { + } + + void startAsynchronously() override + { + output() = ~(input(0) & input(1)); + } + + bool hasFinished() const override + { + return true; + } +}; +} // namespace plain +} // namespace nt + +#endif diff --git a/src/test0.cpp b/src/test0.cpp index 5f990c3..4151cf0 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -851,6 +851,49 @@ void testBlueprint() } } +#include "iyokan_nt.hpp" + +namespace nt { +void testAllocator() +{ + { + Allocator alc; + { + TLWELvl0* zero = alc.make(0); + TLWELvl0* one = alc.make(1); + *zero = TFHEppTestHelper::instance().zero(); + *one = TFHEppTestHelper::instance().one(); + } + { + TLWELvl0* zero = alc.get(0); + TLWELvl0* one = alc.get(1); + assert(*zero == TFHEppTestHelper::instance().zero()); + assert(*one == TFHEppTestHelper::instance().one()); + } + } + { + Allocator root; + { + Allocator &sub0 = root.subAllocator("0"), + &sub1 = root.subAllocator("1"); + TLWELvl0* zero = sub0.make(0); + TLWELvl0* one = sub1.make(0); + *zero = TFHEppTestHelper::instance().zero(); + *one = TFHEppTestHelper::instance().one(); + } + { + Allocator &sub0 = root.subAllocator("0"), + &sub1 = root.subAllocator("1"); + TLWELvl0* zero = sub0.get(0); + TLWELvl0* one = sub1.get(0); + assert(*zero == TFHEppTestHelper::instance().zero()); + assert(*one == TFHEppTestHelper::instance().one()); + } + } +} + +} // namespace nt + int main() { AsyncThread::setNumThreads(std::thread::hardware_concurrency()); @@ -905,4 +948,6 @@ int main() testProgressGraphMaker(); testBlueprint(); + + nt::testAllocator(); } From 151f4e03f49aab400a7e97e53aa6bcd2b3e70529 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 12 Sep 2021 22:40:57 +0900 Subject: [PATCH 02/54] wip --- src/CMakeLists.txt | 1 + src/iyokan_nt.hpp | 124 ++-- src/test0.cpp | 1767 +++++++++++++++++++++++--------------------- 3 files changed, 985 insertions(+), 907 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e7f0d4f..1131220 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ endif(IYOKAN_ENABLE_CUDA) ##### test0 add_executable(test0 test0.cpp iyokan.cpp iyokan_plain.cpp iyokan_tfhepp.cpp error.cpp + iyokan_nt.cpp iyokan_nt_plain.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index c5901d7..0722f7d 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -18,20 +18,9 @@ class Allocator { std::vector data_; public: - Allocator(/* optional snapshot file */) - { - } + Allocator(/* optional snapshot file */); - Allocator& subAllocator(const std::string& key) - { - assert(data_.size() == 0); - auto it = subs_.find(key); - if (it == subs_.end()) { - auto sub = std::make_unique(); - std::tie(it, std::ignore) = subs_.emplace(key, std::move(sub)); - } - return *it->second; - } + Allocator& subAllocator(const std::string& key); template T* make(size_t index) @@ -71,25 +60,42 @@ struct Label { class Task { private: Label label_; - std::vector dependents_; + std::vector parents_, children_; public: - Task(Label label); - virtual ~Task(); + Task(Label label) : label_(label) + { + } + + virtual ~Task() + { + } const Label& label() const { return label_; } - const std::vector& dependents() const + const std::vector& parents() const { - return dependents_; + return parents_; } - void addDependent(Task* task) + const std::vector& children() const { - dependents_.push_back(task); + return children_; + } + + void addChild(Task* task) + { + assert(task != nullptr); + children_.push_back(task); + } + + void addParent(Task* task) + { + assert(task != nullptr); + parents_.push_back(task); } virtual void notifyOneInputReady() = 0; @@ -114,13 +120,15 @@ class TaskCommon : public Task { T* output_; protected: - T input(size_t i) const + const T& input(size_t i) const { + assert(inputs_.at(i) != nullptr); return *inputs_.at(i); } T& output() { + assert(output_ != nullptr); return *output_; } @@ -153,8 +161,19 @@ class TaskCommon : public Task { numReadyInputs_ = 0; } - void addInput(T* newIn) + void addInput(TaskCommon* newIn) { + assert(newIn != nullptr); + addInput(newIn, newIn->output_); + } + + void addInput(Task* newParent, T* newIn) + { + assert(newParent != nullptr && newIn != nullptr); + + addParent(newParent); + newParent->addChild(this); + for (size_t i = 0; i < inputs_.size(); i++) { if (inputs_.at(i) != nullptr) continue; @@ -164,24 +183,18 @@ class TaskCommon : public Task { assert(false && "Too many calls of addInput"); } - const T* const getOutput() const + const T& getOutput() const { - return output_; + return *output_; } }; -class NetworkBuilder { +class Network { private: - Allocator& alc_; - std::unordered_map uid2task_; - -public: - NetworkBuilder(Allocator& alc); -}; + std::unordered_map> uid2task_; -class Network { public: - Network(NetworkBuilder&& builder); + Network(std::unordered_map> uid2task); Task* findByUID(UID uid) const; Task* findByTags(const std::vector& tags) const; @@ -189,34 +202,41 @@ class Network { void eachTask(std::function handler) const; }; -} // namespace nt - -#include "packet.hpp" - -namespace nt { -namespace plain { - -class TaskNand : public TaskCommon { +class NetworkBuilder { public: - TaskNand(Label label, Allocator& alc) : TaskCommon(label, alc, 2) + NetworkBuilder() { } - ~TaskNand() + virtual ~NetworkBuilder() { } - void startAsynchronously() override - { - output() = ~(input(0) & input(1)); - } + virtual Network createNetwork() = 0; - bool hasFinished() const override - { - return true; - } + virtual void connect(UID from, UID to) = 0; + + // not/and/or are C++ keywords, so member functions here are in capitals. + // virtual UID INPUT(const std::string& alcKey, const std::string& portName, + // int portBit) = 0; + // virtual UID OUTPUT(const std::string& alcKey, const std::string& + // portName, + // int portBit) = 0; + virtual UID CONSTONE(const std::string& alcKey) = 0; + virtual UID CONSTZERO(const std::string& alcKey) = 0; + // virtual UID AND(const std::string& alcKey) = 0; + // virtual UID ANDNOT(const std::string& alcKey) = 0; + // virtual UID MUX(const std::string& alcKey) = 0; + virtual UID NAND(const std::string& alcKey) = 0; + // virtual UID NMUX(const std::string& alcKey) = 0; + // virtual UID NOR(const std::string& alcKey) = 0; + // virtual UID NOT(const std::string& alcKey) = 0; + // virtual UID OR(const std::string& alcKey) = 0; + // virtual UID ORNOT(const std::string& alcKey) = 0; + // virtual UID XNOR(const std::string& alcKey) = 0; + // virtual UID XOR(const std::string& alcKey) = 0; }; -} // namespace plain + } // namespace nt #endif diff --git a/src/test0.cpp b/src/test0.cpp index 4151cf0..955a94e 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -1,529 +1,864 @@ -#include "iyokan.hpp" -#include "tfhepp_cufhe_wrapper.hpp" - -#include -#include - -template -void assertNetValid(Network&& net) -{ - error::Stack err; - net.checkValid(err); - if (err.empty()) - return; - - std::cerr << err.str() << std::endl; - assert(0); -} - -template -auto get(TaskNetwork& net, const std::string& kind, const std::string& portName, - int portBit) -{ - return net.template get( - kind, portName, portBit); -} - -template -typename NetworkBuilder::NetworkType readNetworkFromJSON(std::istream& is) -{ - // FIXME: Assume the stream `is` emits Iyokan-L1 JSON - NetworkBuilder builder; - IyokanL1JSONReader{}.read(builder, is); - return typename NetworkBuilder::NetworkType{std::move(builder)}; -} - -// Assume variable names 'NetworkBuilder' and 'net' -#define ASSERT_OUTPUT_EQ(portName, portBit, expected) \ - assert(getOutput(get(net, "output", portName, portBit)) == \ - (expected)) -#define SET_INPUT(portName, portBit, val) \ - setInput(get(net, "input", portName, portBit), val) - -template -void testNOT() -{ - NetworkBuilder builder; - int id0 = builder.INPUT("A", 0); - int id1 = builder.NOT(); - int id2 = builder.OUTPUT("out", 0); - builder.connect(id0, id1); - builder.connect(id1, id2); - - TaskNetwork net = std::move(builder); - auto out = get(net, "output", "out", 0); - - std::array, 8> invals{{{0, 1}, {0, 1}}}; - for (int i = 0; i < 2; i++) { - // Set inputs. - SET_INPUT("A", 0, std::get<0>(invals[i])); - - processAllGates(net); - - // Check if results are okay. - assert(getOutput(out) == std::get<1>(invals[i])); - - net.tick(); - } -} - -template -void testMUX() -{ - NetworkBuilder builder; - int id0 = builder.INPUT("A", 0); - int id1 = builder.INPUT("B", 0); - int id2 = builder.INPUT("S", 0); - int id3 = builder.MUX(); - int id4 = builder.OUTPUT("out", 0); - builder.connect(id0, id3); - builder.connect(id1, id3); - builder.connect(id2, id3); - builder.connect(id3, id4); - - TaskNetwork net = std::move(builder); - - std::array, 8> invals{{/*A,B, S, O*/ - {0, 0, 0, 0}, - {0, 0, 1, 0}, - {0, 1, 0, 0}, - {0, 1, 1, 1}, - {1, 0, 0, 1}, - {1, 0, 1, 0}, - {1, 1, 0, 1}, - {1, 1, 1, 1}}}; - for (int i = 0; i < 8; i++) { - // Set inputs. - SET_INPUT("A", 0, std::get<0>(invals[i])); - SET_INPUT("B", 0, std::get<1>(invals[i])); - SET_INPUT("S", 0, std::get<2>(invals[i])); - - processAllGates(net); - - // Check if results are okay. - ASSERT_OUTPUT_EQ("out", 0, std::get<3>(invals[i])); - - net.tick(); - } -} - -template -void testBinopGates() -{ - NetworkBuilder builder; - int id0 = builder.INPUT("in0", 0); - int id1 = builder.INPUT("in1", 0); - - std::unordered_map> - id2res; - -#define DEFINE_BINOP_GATE_TEST(name, e00, e01, e10, e11) \ - do { \ - int gateId = builder.name(); \ - int outputId = builder.OUTPUT("out_" #name, 0); \ - builder.connect(id0, gateId); \ - builder.connect(id1, gateId); \ - builder.connect(gateId, outputId); \ - id2res["out_" #name] = {e00, e01, e10, e11}; \ - } while (false); - DEFINE_BINOP_GATE_TEST(AND, 0, 0, 0, 1); - DEFINE_BINOP_GATE_TEST(NAND, 1, 1, 1, 0); - DEFINE_BINOP_GATE_TEST(ANDNOT, 0, 0, 1, 0); - DEFINE_BINOP_GATE_TEST(OR, 0, 1, 1, 1); - DEFINE_BINOP_GATE_TEST(ORNOT, 1, 0, 1, 1); - DEFINE_BINOP_GATE_TEST(XOR, 0, 1, 1, 0); - DEFINE_BINOP_GATE_TEST(XNOR, 1, 0, 0, 1); -#undef DEFINE_BINOP_GATE_TEST - - TaskNetwork net = std::move(builder); - - std::array, 4> invals{{{0, 0}, {0, 1}, {1, 0}, {1, 1}}}; - for (int i = 0; i < 4; i++) { - // Set inputs. - SET_INPUT("in0", 0, invals[i].first ? 1 : 0); - SET_INPUT("in1", 0, invals[i].second ? 1 : 0); - - processAllGates(net); - - // Check if results are okay. - for (auto&& [portName, res] : id2res) - ASSERT_OUTPUT_EQ(portName, 0, res[i]); - - net.tick(); - } -} - -template -void testFromJSONtest_pass_4bit() -{ - const std::string fileName = "test/iyokanl1-json/pass-4bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - SET_INPUT("io_in", 0, 0); - SET_INPUT("io_in", 1, 1); - SET_INPUT("io_in", 2, 1); - SET_INPUT("io_in", 3, 0); - - processAllGates(net); - - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 1); - ASSERT_OUTPUT_EQ("io_out", 2, 1); - ASSERT_OUTPUT_EQ("io_out", 3, 0); -} - -template -void testFromJSONtest_and_4bit() -{ - const std::string fileName = "test/iyokanl1-json/and-4bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - SET_INPUT("io_inA", 0, 0); - SET_INPUT("io_inA", 1, 0); - SET_INPUT("io_inA", 2, 1); - SET_INPUT("io_inA", 3, 1); - SET_INPUT("io_inB", 0, 0); - SET_INPUT("io_inB", 1, 1); - SET_INPUT("io_inB", 2, 0); - SET_INPUT("io_inB", 3, 1); - - processAllGates(net); - - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 0); - ASSERT_OUTPUT_EQ("io_out", 2, 0); - ASSERT_OUTPUT_EQ("io_out", 3, 1); -} - -template -void testFromJSONtest_and_4_2bit() -{ - const std::string fileName = "test/iyokanl1-json/and-4_2bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - SET_INPUT("io_inA", 0, 1); - SET_INPUT("io_inA", 1, 0); - SET_INPUT("io_inA", 2, 1); - SET_INPUT("io_inA", 3, 1); - SET_INPUT("io_inB", 0, 1); - SET_INPUT("io_inB", 1, 1); - SET_INPUT("io_inB", 2, 1); - SET_INPUT("io_inB", 3, 1); - - processAllGates(net); - - ASSERT_OUTPUT_EQ("io_out", 0, 1); - ASSERT_OUTPUT_EQ("io_out", 1, 0); -} - -template -void testFromJSONtest_mux_4bit() -{ - const std::string fileName = "test/iyokanl1-json/mux-4bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - SET_INPUT("io_inA", 0, 0); - SET_INPUT("io_inA", 1, 0); - SET_INPUT("io_inA", 2, 1); - SET_INPUT("io_inA", 3, 1); - SET_INPUT("io_inB", 0, 0); - SET_INPUT("io_inB", 1, 1); - SET_INPUT("io_inB", 2, 0); - SET_INPUT("io_inB", 3, 1); - - SET_INPUT("io_sel", 0, 0); - processAllGates(net); - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 0); - ASSERT_OUTPUT_EQ("io_out", 2, 1); - ASSERT_OUTPUT_EQ("io_out", 3, 1); - net.tick(); - - SET_INPUT("io_sel", 0, 1); - processAllGates(net); - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 1); - ASSERT_OUTPUT_EQ("io_out", 2, 0); - ASSERT_OUTPUT_EQ("io_out", 3, 1); -} - -template -void testFromJSONtest_addr_4bit() -{ - const std::string fileName = "test/iyokanl1-json/addr-4bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - SET_INPUT("io_inA", 0, 0); - SET_INPUT("io_inA", 1, 0); - SET_INPUT("io_inA", 2, 1); - SET_INPUT("io_inA", 3, 1); - SET_INPUT("io_inB", 0, 0); - SET_INPUT("io_inB", 1, 1); - SET_INPUT("io_inB", 2, 0); - SET_INPUT("io_inB", 3, 1); - - processAllGates(net); - - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 1); - ASSERT_OUTPUT_EQ("io_out", 2, 1); - ASSERT_OUTPUT_EQ("io_out", 3, 0); -} - -template -void testFromJSONtest_register_4bit() -{ - const std::string fileName = - "test/iyokanl1-json/register-4bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - SET_INPUT("io_in", 0, 0); - SET_INPUT("io_in", 1, 0); - SET_INPUT("io_in", 2, 1); - SET_INPUT("io_in", 3, 1); - - // 1: Reset all DFFs. - SET_INPUT("reset", 0, 1); - processAllGates(net); - net.tick(); - - // 2: Store values into DFFs. - SET_INPUT("reset", 0, 0); - processAllGates(net); - net.tick(); - - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 0); - ASSERT_OUTPUT_EQ("io_out", 2, 0); - ASSERT_OUTPUT_EQ("io_out", 3, 0); - - // 3: Get outputs. - SET_INPUT("reset", 0, 0); - processAllGates(net); - net.tick(); - - ASSERT_OUTPUT_EQ("io_out", 0, 0); - ASSERT_OUTPUT_EQ("io_out", 1, 0); - ASSERT_OUTPUT_EQ("io_out", 2, 1); - ASSERT_OUTPUT_EQ("io_out", 3, 1); -} - -template -void testSequentialCircuit() -{ - /* - B D - reset(0) >---> ANDNOT(4) >---> DFF(2) - ^ A v Q - | | - *--< NOT(3) <--*-----> OUTPUT(1) - A - */ - - NetworkBuilder builder; - int id0 = builder.INPUT("reset", 0); - int id1 = builder.OUTPUT("out", 0); - int id2 = builder.DFF(); - int id3 = builder.NOT(); - int id4 = builder.ANDNOT(); - builder.connect(id2, id1); - builder.connect(id4, id2); - builder.connect(id2, id3); - builder.connect(id3, id4); - builder.connect(id0, id4); - - TaskNetwork net = std::move(builder); - assertNetValid(net); - - auto dff = - std::dynamic_pointer_cast( - net.node(id2)->task()); - auto out = get(net, "output", "out", 0); - - // 1: - SET_INPUT("reset", 0, 1); - processAllGates(net); - - // 2: - net.tick(); - assert(getOutput(dff) == 0); - SET_INPUT("reset", 0, 0); - processAllGates(net); - ASSERT_OUTPUT_EQ("out", 0, 0); - - // 3: - net.tick(); - assert(getOutput(dff) == 1); - processAllGates(net); - ASSERT_OUTPUT_EQ("out", 0, 1); - - // 4: - net.tick(); - assert(getOutput(dff) == 0); - processAllGates(net); - ASSERT_OUTPUT_EQ("out", 0, 0); -} - -template -void testFromJSONtest_counter_4bit() -{ - const std::string fileName = - "test/iyokanl1-json/counter-4bit-iyokanl1.json"; - std::ifstream ifs{fileName}; - assert(ifs); - - auto net = readNetworkFromJSON(ifs); - assertNetValid(net); - - std::vector> outvals{{{0, 0, 0, 0}, - {1, 0, 0, 0}, - {0, 1, 0, 0}, - {1, 1, 0, 0}, - {0, 0, 1, 0}, - {1, 0, 1, 0}, - {0, 1, 1, 0}, - {1, 1, 1, 0}, - {0, 0, 0, 1}, - {1, 0, 0, 1}, - {0, 1, 0, 1}, - {1, 1, 0, 1}, - {0, 0, 1, 1}, - {1, 0, 1, 1}, - {0, 1, 1, 1}, - {1, 1, 1, 1}}}; - - SET_INPUT("reset", 0, 1); - processAllGates(net); - - SET_INPUT("reset", 0, 0); - for (size_t i = 0; i < outvals.size(); i++) { - net.tick(); - processAllGates(net); - ASSERT_OUTPUT_EQ("io_out", 0, outvals[i][0]); - ASSERT_OUTPUT_EQ("io_out", 1, outvals[i][1]); - ASSERT_OUTPUT_EQ("io_out", 2, outvals[i][2]); - ASSERT_OUTPUT_EQ("io_out", 3, outvals[i][3]); - } -} - -template -void testPrioritySetVisitor() -{ - NetworkBuilder builder; - int id0 = builder.INPUT("A", 0); - int id1 = builder.NOT(); - int id2 = builder.OUTPUT("out", 0); - builder.connect(id0, id1); - builder.connect(id1, id2); - - TaskNetwork net = std::move(builder); - auto depnode = get(net, "output", "out", 0)->depnode(); - assert(depnode->priority() == -1); - - // Set priority to each DepNode - GraphVisitor grvis; - net.visit(grvis); - PrioritySetVisitor privis{graph::doTopologicalSort(grvis.getMap())}; - net.visit(privis); - - assert(depnode->priority() == 2); -} - +//#include "iyokan.hpp" +//#include "tfhepp_cufhe_wrapper.hpp" // -#include "iyokan_plain.hpp" - -void processAllGates(PlainNetwork& net, - std::shared_ptr graph = nullptr) -{ - processAllGates(net, std::thread::hardware_concurrency(), graph); -} - -void setInput(std::shared_ptr task, int val) -{ - task->set(val != 0 ? 1_b : 0_b); -} - -int getOutput(std::shared_ptr task) -{ - return task->get() == 1_b ? 1 : 0; -} - -void testProgressGraphMaker() -{ - /* - B D - reset(0) >---> ANDNOT(4) >---> DFF(2) - ^ A v Q - | | - *--< NOT(3) <--*-----> OUTPUT(1) - A - */ - - PlainNetworkBuilder builder; - int id0 = builder.INPUT("reset", 0); - int id1 = builder.OUTPUT("out", 0); - int id2 = builder.DFF(); - int id3 = builder.NOT(); - int id4 = builder.ANDNOT(); - builder.connect(id2, id1); - builder.connect(id4, id2); - builder.connect(id2, id3); - builder.connect(id3, id4); - builder.connect(id0, id4); - - PlainNetwork net = std::move(builder); - assertNetValid(net); - - auto graph = std::make_shared(); - - processAllGates(net, graph); - - std::stringstream ss; - graph->dumpDOT(ss); - std::string dot = ss.str(); - assert(dot.find(fmt::sprintf("n%d [label = \"{INPUT|reset[0]}\"]", id0)) != - std::string::npos); - assert(dot.find(fmt::sprintf("n%d [label = \"{OUTPUT|out[0]}\"]", id1)) != - std::string::npos); - assert(dot.find(fmt::sprintf("n%d [label = \"{DFF}\"]", id2)) != - std::string::npos); - assert(dot.find(fmt::sprintf("n%d [label = \"{NOT}\"]", id3)) != - std::string::npos); - assert(dot.find(fmt::sprintf("n%d [label = \"{ANDNOT}\"]", id4)) != - std::string::npos); - assert(dot.find(fmt::sprintf("n%d -> n%d", id2, id1)) != std::string::npos); - assert(dot.find(fmt::sprintf("n%d -> n%d", id4, id2)) != std::string::npos); - assert(dot.find(fmt::sprintf("n%d -> n%d", id2, id3)) != std::string::npos); - assert(dot.find(fmt::sprintf("n%d -> n%d", id0, id4)) != std::string::npos); - assert(dot.find(fmt::sprintf("n%d -> n%d", id3, id4)) != std::string::npos); -} +//#include +//#include +// +// template +// void assertNetValid(Network&& net) +//{ +// error::Stack err; +// net.checkValid(err); +// if (err.empty()) +// return; +// +// std::cerr << err.str() << std::endl; +// assert(0); +//} +// +// template +// auto get(TaskNetwork& net, const std::string& kind, const std::string& +// portName, +// int portBit) +//{ +// return net.template get( +// kind, portName, portBit); +//} +// +// template +// typename NetworkBuilder::NetworkType readNetworkFromJSON(std::istream& is) +//{ +// // FIXME: Assume the stream `is` emits Iyokan-L1 JSON +// NetworkBuilder builder; +// IyokanL1JSONReader{}.read(builder, is); +// return typename NetworkBuilder::NetworkType{std::move(builder)}; +//} +// +//// Assume variable names 'NetworkBuilder' and 'net' +//#define ASSERT_OUTPUT_EQ(portName, portBit, expected) \ +// assert(getOutput(get(net, "output", portName, portBit)) == +// \ +// (expected)) +//#define SET_INPUT(portName, portBit, val) \ +// setInput(get(net, "input", portName, portBit), val) +// +// template +// void testNOT() +//{ +// NetworkBuilder builder; +// int id0 = builder.INPUT("A", 0); +// int id1 = builder.NOT(); +// int id2 = builder.OUTPUT("out", 0); +// builder.connect(id0, id1); +// builder.connect(id1, id2); +// +// TaskNetwork net = std::move(builder); +// auto out = get(net, "output", "out", 0); +// +// std::array, 8> invals{{{0, 1}, {0, 1}}}; +// for (int i = 0; i < 2; i++) { +// // Set inputs. +// SET_INPUT("A", 0, std::get<0>(invals[i])); +// +// processAllGates(net); +// +// // Check if results are okay. +// assert(getOutput(out) == std::get<1>(invals[i])); +// +// net.tick(); +// } +//} +// +// template +// void testMUX() +//{ +// NetworkBuilder builder; +// int id0 = builder.INPUT("A", 0); +// int id1 = builder.INPUT("B", 0); +// int id2 = builder.INPUT("S", 0); +// int id3 = builder.MUX(); +// int id4 = builder.OUTPUT("out", 0); +// builder.connect(id0, id3); +// builder.connect(id1, id3); +// builder.connect(id2, id3); +// builder.connect(id3, id4); +// +// TaskNetwork net = std::move(builder); +// +// std::array, 8> invals{{/*A,B, S, O*/ +// {0, 0, 0, 0}, +// {0, 0, 1, 0}, +// {0, 1, 0, 0}, +// {0, 1, 1, 1}, +// {1, 0, 0, 1}, +// {1, 0, 1, 0}, +// {1, 1, 0, 1}, +// {1, 1, 1, 1}}}; +// for (int i = 0; i < 8; i++) { +// // Set inputs. +// SET_INPUT("A", 0, std::get<0>(invals[i])); +// SET_INPUT("B", 0, std::get<1>(invals[i])); +// SET_INPUT("S", 0, std::get<2>(invals[i])); +// +// processAllGates(net); +// +// // Check if results are okay. +// ASSERT_OUTPUT_EQ("out", 0, std::get<3>(invals[i])); +// +// net.tick(); +// } +//} +// +// template +// void testBinopGates() +//{ +// NetworkBuilder builder; +// int id0 = builder.INPUT("in0", 0); +// int id1 = builder.INPUT("in1", 0); +// +// std::unordered_map> +// id2res; +// +//#define DEFINE_BINOP_GATE_TEST(name, e00, e01, e10, e11) \ +// do { \ +// int gateId = builder.name(); \ +// int outputId = builder.OUTPUT("out_" #name, 0); \ +// builder.connect(id0, gateId); \ +// builder.connect(id1, gateId); \ +// builder.connect(gateId, outputId); \ +// id2res["out_" #name] = {e00, e01, e10, e11}; \ +// } while (false); +// DEFINE_BINOP_GATE_TEST(AND, 0, 0, 0, 1); +// DEFINE_BINOP_GATE_TEST(NAND, 1, 1, 1, 0); +// DEFINE_BINOP_GATE_TEST(ANDNOT, 0, 0, 1, 0); +// DEFINE_BINOP_GATE_TEST(OR, 0, 1, 1, 1); +// DEFINE_BINOP_GATE_TEST(ORNOT, 1, 0, 1, 1); +// DEFINE_BINOP_GATE_TEST(XOR, 0, 1, 1, 0); +// DEFINE_BINOP_GATE_TEST(XNOR, 1, 0, 0, 1); +//#undef DEFINE_BINOP_GATE_TEST +// +// TaskNetwork net = std::move(builder); +// +// std::array, 4> invals{{{0, 0}, {0, 1}, {1, 0}, {1, +// 1}}}; for (int i = 0; i < 4; i++) { +// // Set inputs. +// SET_INPUT("in0", 0, invals[i].first ? 1 : 0); +// SET_INPUT("in1", 0, invals[i].second ? 1 : 0); +// +// processAllGates(net); +// +// // Check if results are okay. +// for (auto&& [portName, res] : id2res) +// ASSERT_OUTPUT_EQ(portName, 0, res[i]); +// +// net.tick(); +// } +//} +// +// template +// void testFromJSONtest_pass_4bit() +//{ +// const std::string fileName = "test/iyokanl1-json/pass-4bit-iyokanl1.json"; +// std::ifstream ifs{fileName}; +// assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// SET_INPUT("io_in", 0, 0); +// SET_INPUT("io_in", 1, 1); +// SET_INPUT("io_in", 2, 1); +// SET_INPUT("io_in", 3, 0); +// +// processAllGates(net); +// +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 1); +// ASSERT_OUTPUT_EQ("io_out", 2, 1); +// ASSERT_OUTPUT_EQ("io_out", 3, 0); +//} +// +// template +// void testFromJSONtest_and_4bit() +//{ +// const std::string fileName = "test/iyokanl1-json/and-4bit-iyokanl1.json"; +// std::ifstream ifs{fileName}; +// assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// SET_INPUT("io_inA", 0, 0); +// SET_INPUT("io_inA", 1, 0); +// SET_INPUT("io_inA", 2, 1); +// SET_INPUT("io_inA", 3, 1); +// SET_INPUT("io_inB", 0, 0); +// SET_INPUT("io_inB", 1, 1); +// SET_INPUT("io_inB", 2, 0); +// SET_INPUT("io_inB", 3, 1); +// +// processAllGates(net); +// +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 0); +// ASSERT_OUTPUT_EQ("io_out", 2, 0); +// ASSERT_OUTPUT_EQ("io_out", 3, 1); +//} +// +// template +// void testFromJSONtest_and_4_2bit() +//{ +// const std::string fileName = +// "test/iyokanl1-json/and-4_2bit-iyokanl1.json"; std::ifstream +// ifs{fileName}; assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// SET_INPUT("io_inA", 0, 1); +// SET_INPUT("io_inA", 1, 0); +// SET_INPUT("io_inA", 2, 1); +// SET_INPUT("io_inA", 3, 1); +// SET_INPUT("io_inB", 0, 1); +// SET_INPUT("io_inB", 1, 1); +// SET_INPUT("io_inB", 2, 1); +// SET_INPUT("io_inB", 3, 1); +// +// processAllGates(net); +// +// ASSERT_OUTPUT_EQ("io_out", 0, 1); +// ASSERT_OUTPUT_EQ("io_out", 1, 0); +//} +// +// template +// void testFromJSONtest_mux_4bit() +//{ +// const std::string fileName = "test/iyokanl1-json/mux-4bit-iyokanl1.json"; +// std::ifstream ifs{fileName}; +// assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// SET_INPUT("io_inA", 0, 0); +// SET_INPUT("io_inA", 1, 0); +// SET_INPUT("io_inA", 2, 1); +// SET_INPUT("io_inA", 3, 1); +// SET_INPUT("io_inB", 0, 0); +// SET_INPUT("io_inB", 1, 1); +// SET_INPUT("io_inB", 2, 0); +// SET_INPUT("io_inB", 3, 1); +// +// SET_INPUT("io_sel", 0, 0); +// processAllGates(net); +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 0); +// ASSERT_OUTPUT_EQ("io_out", 2, 1); +// ASSERT_OUTPUT_EQ("io_out", 3, 1); +// net.tick(); +// +// SET_INPUT("io_sel", 0, 1); +// processAllGates(net); +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 1); +// ASSERT_OUTPUT_EQ("io_out", 2, 0); +// ASSERT_OUTPUT_EQ("io_out", 3, 1); +//} +// +// template +// void testFromJSONtest_addr_4bit() +//{ +// const std::string fileName = "test/iyokanl1-json/addr-4bit-iyokanl1.json"; +// std::ifstream ifs{fileName}; +// assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// SET_INPUT("io_inA", 0, 0); +// SET_INPUT("io_inA", 1, 0); +// SET_INPUT("io_inA", 2, 1); +// SET_INPUT("io_inA", 3, 1); +// SET_INPUT("io_inB", 0, 0); +// SET_INPUT("io_inB", 1, 1); +// SET_INPUT("io_inB", 2, 0); +// SET_INPUT("io_inB", 3, 1); +// +// processAllGates(net); +// +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 1); +// ASSERT_OUTPUT_EQ("io_out", 2, 1); +// ASSERT_OUTPUT_EQ("io_out", 3, 0); +//} +// +// template +// void testFromJSONtest_register_4bit() +//{ +// const std::string fileName = +// "test/iyokanl1-json/register-4bit-iyokanl1.json"; +// std::ifstream ifs{fileName}; +// assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// SET_INPUT("io_in", 0, 0); +// SET_INPUT("io_in", 1, 0); +// SET_INPUT("io_in", 2, 1); +// SET_INPUT("io_in", 3, 1); +// +// // 1: Reset all DFFs. +// SET_INPUT("reset", 0, 1); +// processAllGates(net); +// net.tick(); +// +// // 2: Store values into DFFs. +// SET_INPUT("reset", 0, 0); +// processAllGates(net); +// net.tick(); +// +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 0); +// ASSERT_OUTPUT_EQ("io_out", 2, 0); +// ASSERT_OUTPUT_EQ("io_out", 3, 0); +// +// // 3: Get outputs. +// SET_INPUT("reset", 0, 0); +// processAllGates(net); +// net.tick(); +// +// ASSERT_OUTPUT_EQ("io_out", 0, 0); +// ASSERT_OUTPUT_EQ("io_out", 1, 0); +// ASSERT_OUTPUT_EQ("io_out", 2, 1); +// ASSERT_OUTPUT_EQ("io_out", 3, 1); +//} +// +// template +// void testSequentialCircuit() +//{ +// /* +// B D +// reset(0) >---> ANDNOT(4) >---> DFF(2) +// ^ A v Q +// | | +// *--< NOT(3) <--*-----> OUTPUT(1) +// A +// */ +// +// NetworkBuilder builder; +// int id0 = builder.INPUT("reset", 0); +// int id1 = builder.OUTPUT("out", 0); +// int id2 = builder.DFF(); +// int id3 = builder.NOT(); +// int id4 = builder.ANDNOT(); +// builder.connect(id2, id1); +// builder.connect(id4, id2); +// builder.connect(id2, id3); +// builder.connect(id3, id4); +// builder.connect(id0, id4); +// +// TaskNetwork net = std::move(builder); +// assertNetValid(net); +// +// auto dff = +// std::dynamic_pointer_cast( +// net.node(id2)->task()); +// auto out = get(net, "output", "out", 0); +// +// // 1: +// SET_INPUT("reset", 0, 1); +// processAllGates(net); +// +// // 2: +// net.tick(); +// assert(getOutput(dff) == 0); +// SET_INPUT("reset", 0, 0); +// processAllGates(net); +// ASSERT_OUTPUT_EQ("out", 0, 0); +// +// // 3: +// net.tick(); +// assert(getOutput(dff) == 1); +// processAllGates(net); +// ASSERT_OUTPUT_EQ("out", 0, 1); +// +// // 4: +// net.tick(); +// assert(getOutput(dff) == 0); +// processAllGates(net); +// ASSERT_OUTPUT_EQ("out", 0, 0); +//} +// +// template +// void testFromJSONtest_counter_4bit() +//{ +// const std::string fileName = +// "test/iyokanl1-json/counter-4bit-iyokanl1.json"; +// std::ifstream ifs{fileName}; +// assert(ifs); +// +// auto net = readNetworkFromJSON(ifs); +// assertNetValid(net); +// +// std::vector> outvals{{{0, 0, 0, 0}, +// {1, 0, 0, 0}, +// {0, 1, 0, 0}, +// {1, 1, 0, 0}, +// {0, 0, 1, 0}, +// {1, 0, 1, 0}, +// {0, 1, 1, 0}, +// {1, 1, 1, 0}, +// {0, 0, 0, 1}, +// {1, 0, 0, 1}, +// {0, 1, 0, 1}, +// {1, 1, 0, 1}, +// {0, 0, 1, 1}, +// {1, 0, 1, 1}, +// {0, 1, 1, 1}, +// {1, 1, 1, 1}}}; +// +// SET_INPUT("reset", 0, 1); +// processAllGates(net); +// +// SET_INPUT("reset", 0, 0); +// for (size_t i = 0; i < outvals.size(); i++) { +// net.tick(); +// processAllGates(net); +// ASSERT_OUTPUT_EQ("io_out", 0, outvals[i][0]); +// ASSERT_OUTPUT_EQ("io_out", 1, outvals[i][1]); +// ASSERT_OUTPUT_EQ("io_out", 2, outvals[i][2]); +// ASSERT_OUTPUT_EQ("io_out", 3, outvals[i][3]); +// } +//} +// +// template +// void testPrioritySetVisitor() +//{ +// NetworkBuilder builder; +// int id0 = builder.INPUT("A", 0); +// int id1 = builder.NOT(); +// int id2 = builder.OUTPUT("out", 0); +// builder.connect(id0, id1); +// builder.connect(id1, id2); +// +// TaskNetwork net = std::move(builder); +// auto depnode = get(net, "output", "out", 0)->depnode(); +// assert(depnode->priority() == -1); +// +// // Set priority to each DepNode +// GraphVisitor grvis; +// net.visit(grvis); +// PrioritySetVisitor privis{graph::doTopologicalSort(grvis.getMap())}; +// net.visit(privis); +// +// assert(depnode->priority() == 2); +//} +// +//// +//#include "iyokan_plain.hpp" +// +// void processAllGates(PlainNetwork& net, +// std::shared_ptr graph = nullptr) +//{ +// processAllGates(net, std::thread::hardware_concurrency(), graph); +//} +// +// void setInput(std::shared_ptr task, int val) +//{ +// task->set(val != 0 ? 1_b : 0_b); +//} +// +// int getOutput(std::shared_ptr task) +//{ +// return task->get() == 1_b ? 1 : 0; +//} +// +// void testProgressGraphMaker() +//{ +// /* +// B D +// reset(0) >---> ANDNOT(4) >---> DFF(2) +// ^ A v Q +// | | +// *--< NOT(3) <--*-----> OUTPUT(1) +// A +// */ +// +// PlainNetworkBuilder builder; +// int id0 = builder.INPUT("reset", 0); +// int id1 = builder.OUTPUT("out", 0); +// int id2 = builder.DFF(); +// int id3 = builder.NOT(); +// int id4 = builder.ANDNOT(); +// builder.connect(id2, id1); +// builder.connect(id4, id2); +// builder.connect(id2, id3); +// builder.connect(id3, id4); +// builder.connect(id0, id4); +// +// PlainNetwork net = std::move(builder); +// assertNetValid(net); +// +// auto graph = std::make_shared(); +// +// processAllGates(net, graph); +// +// std::stringstream ss; +// graph->dumpDOT(ss); +// std::string dot = ss.str(); +// assert(dot.find(fmt::sprintf("n%d [label = \"{INPUT|reset[0]}\"]", id0)) +// != +// std::string::npos); +// assert(dot.find(fmt::sprintf("n%d [label = \"{OUTPUT|out[0]}\"]", id1)) != +// std::string::npos); +// assert(dot.find(fmt::sprintf("n%d [label = \"{DFF}\"]", id2)) != +// std::string::npos); +// assert(dot.find(fmt::sprintf("n%d [label = \"{NOT}\"]", id3)) != +// std::string::npos); +// assert(dot.find(fmt::sprintf("n%d [label = \"{ANDNOT}\"]", id4)) != +// std::string::npos); +// assert(dot.find(fmt::sprintf("n%d -> n%d", id2, id1)) != +// std::string::npos); assert(dot.find(fmt::sprintf("n%d -> n%d", id4, id2)) +// != std::string::npos); assert(dot.find(fmt::sprintf("n%d -> n%d", id2, +// id3)) != std::string::npos); assert(dot.find(fmt::sprintf("n%d -> n%d", +// id0, id4)) != std::string::npos); assert(dot.find(fmt::sprintf("n%d -> +// n%d", id3, id4)) != std::string::npos); +//} +// +//#include "iyokan_tfhepp.hpp" +// +// class TFHEppTestHelper { +// private: +// std::shared_ptr sk_; +// std::shared_ptr gk_; +// std::shared_ptr ck_; +// TLWELvl0 zero_, one_; +// +// private: +// TFHEppTestHelper() +// { +// sk_ = std::make_shared(); +// gk_ = std::make_shared(*sk_); +// zero_ = TFHEpp::bootsSymEncrypt({0}, *sk_).at(0); +// one_ = TFHEpp::bootsSymEncrypt({1}, *sk_).at(0); +// } +// +// public: +// static TFHEppTestHelper& instance() +// { +// static TFHEppTestHelper inst; +// return inst; +// } +// +// void prepareCircuitKey() +// { +// ck_ = std::make_shared(*sk_); +// } +// +// TFHEppWorkerInfo wi() const +// { +// return TFHEppWorkerInfo{gk_, ck_}; +// } +// +// const std::shared_ptr& sk() const +// { +// return sk_; +// } +// +// const std::shared_ptr& gk() const +// { +// return gk_; +// } +// +// const TLWELvl0& zero() const +// { +// return zero_; +// } +// +// const TLWELvl0& one() const +// { +// return one_; +// } +//}; +// +// void processAllGates(TFHEppNetwork& net, +// std::shared_ptr graph = nullptr) +//{ +// processAllGates(net, std::thread::hardware_concurrency(), +// TFHEppTestHelper::instance().wi(), graph); +//} +// +// void setInput(std::shared_ptr task, int val) +//{ +// auto& h = TFHEppTestHelper::instance(); +// task->set(val ? h.one() : h.zero()); +//} +// +// int getOutput(std::shared_ptr task) +//{ +// return TFHEpp::bootsSymDecrypt({task->get()}, +// *TFHEppTestHelper::instance().sk())[0]; +//} +// +// void testTFHEppSerialization() +//{ +// auto& h = TFHEppTestHelper::instance(); +// const std::shared_ptr& sk = h.sk(); +// const std::shared_ptr& gk = h.wi().gateKey; +// +// // Test for secret key +// { +// // Dump +// writeToArchive("_test_sk", *sk); +// // Load +// auto sk2 = std::make_shared(); +// readFromArchive(*sk2, "_test_sk"); +// +// auto zero = TFHEpp::bootsSymEncrypt({0}, *sk2).at(0); +// auto one = TFHEpp::bootsSymEncrypt({1}, *sk2).at(0); +// TLWELvl0 res; +// TFHEpp::HomANDNY(res, zero, one, *gk); +// assert(TFHEpp::bootsSymDecrypt({res}, *sk2).at(0) == 1); +// } +// +// // Test for gate key +// { +// std::stringstream ss{std::ios::binary | std::ios::out | std::ios::in}; +// +// // Dump +// writeToArchive(ss, *gk); +// // Load +// auto gk2 = std::make_shared(); +// readFromArchive(*gk2, ss); +// +// auto zero = TFHEpp::bootsSymEncrypt({0}, *sk).at(0); +// auto one = TFHEpp::bootsSymEncrypt({1}, *sk).at(0); +// TLWELvl0 res; +// TFHEpp::HomANDNY(res, zero, one, *gk2); +// assert(TFHEpp::bootsSymDecrypt({res}, *sk).at(0) == 1); +// } +// +// // Test for TLWE level 0 +// { +// std::stringstream ss{std::ios::binary | std::ios::out | std::ios::in}; +// +// { +// auto zero = TFHEpp::bootsSymEncrypt({0}, *sk).at(0); +// auto one = TFHEpp::bootsSymEncrypt({1}, *sk).at(0); +// writeToArchive(ss, zero); +// writeToArchive(ss, one); +// ss.seekg(0); +// } +// +// { +// TLWELvl0 res, zero, one; +// readFromArchive(zero, ss); +// readFromArchive(one, ss); +// TFHEpp::HomANDNY(res, zero, one, *gk); +// assert(TFHEpp::bootsSymDecrypt({res}, *sk).at(0) == 1); +// } +// } +//} +// +//#ifdef IYOKAN_CUDA_ENABLED +//#include "iyokan_cufhe.hpp" +// +// class CUFHETestHelper { +// private: +// std::shared_ptr gk_; +// cufhe::Ctxt zero_, one_; +// +// private: +// CUFHETestHelper() +// { +// gk_ = std::make_shared(); +// ifftGateKey(*gk_, *TFHEppTestHelper::instance().gk()); +// setCtxtZero(zero_); +// setCtxtOne(one_); +// } +// +// public: +// class CUFHEManager { +// public: +// CUFHEManager() +// { +// cufhe::Initialize(*CUFHETestHelper::instance().gk_); +// } +// +// ~CUFHEManager() +// { +// cufhe::CleanUp(); +// } +// }; +// +// public: +// static CUFHETestHelper& instance() +// { +// static CUFHETestHelper inst; +// return inst; +// } +//}; +// +// void processAllGates(CUFHENetwork& net, +// std::shared_ptr graph = nullptr) +//{ +// processAllGates(net, 240, graph); +//} +// +// void setInput(std::shared_ptr task, int val) +//{ +// TLWELvl0 c; +// if (val) +// setTLWELvl0Trivial1(c); +// else +// setTLWELvl0Trivial0(c); +// task->set(c); +//} +// +// int getOutput(std::shared_ptr task) +//{ +// return decryptTLWELvl0(task->get(), *TFHEppTestHelper::instance().sk()); +//} +// +// void testBridgeBetweenCUFHEAndTFHEpp() +//{ +// auto& ht = TFHEppTestHelper::instance(); +// +// // FIXME: The network constructed here does not have any meanings anymore, +// // but it is enough to check if bridges work correctly. +// // We may need another network here. +// NetworkBuilderBase b0; +// NetworkBuilderBase b1; +// auto t0 = b0.addINPUT("in", 0, false); +// auto t1 = std::make_shared(); +// b1.addTask(NodeLabel{"tfhepp2cufhe", ""}, t1); +// auto t2 = std::make_shared(); +// b1.addTask(NodeLabel{"cufhe2tfhepp", ""}, t2); +// auto t3 = b0.addOUTPUT("out", 0, true); +// connectTasks(t1, t2); +// +// auto net0 = std::make_shared>(std::move(b0)); +// auto net1 = +// std::make_shared>(std::move(b1)); auto +// bridge0 = connectWithBridge(t0, t1); auto bridge1 = connectWithBridge(t2, +// t3); +// +// CUFHENetworkRunner runner{1, 1, ht.wi()}; +// runner.addNetwork(net0); +// runner.addNetwork(net1); +// runner.addBridge(bridge0); +// runner.addBridge(bridge1); +// +// t0->set(ht.one()); +// runner.run(false); +// assert(t3->get() == ht.one()); +// +// net0->tick(); +// net1->tick(); +// bridge0->tick(); +// bridge1->tick(); +// +// t0->set(ht.zero()); +// runner.run(false); +// assert(t3->get() == ht.zero()); +//} +//#endif +// +// void testBlueprint() +//{ +// using namespace blueprint; +// +// NetworkBlueprint blueprint{"test/config-toml/cahp-diamond.toml"}; +// +// { +// const auto& files = blueprint.files(); +// assert(files.size() == 1); +// assert(files[0].type == File::TYPE::YOSYS_JSON); +// assert(std::filesystem::canonical(files[0].path) == +// std::filesystem::canonical( +// "test/yosys-json/cahp-diamond-core-yosys.json")); +// assert(files[0].name == "core"); +// } +// +// { +// const auto& roms = blueprint.builtinROMs(); +// assert(roms.size() == 1); +// assert(roms[0].name == "rom"); +// assert(roms[0].inAddrWidth == 7); +// assert(roms[0].outRdataWidth == 32); +// } +// +// { +// const auto& rams = blueprint.builtinRAMs(); +// assert(rams.size() == 2); +// assert((rams[0].name == "ramA" && rams[1].name == "ramB") || +// (rams[1].name == "ramA" && rams[0].name == "ramB")); +// assert(rams[0].inAddrWidth == 8); +// assert(rams[0].inWdataWidth == 8); +// assert(rams[0].outRdataWidth == 8); +// assert(rams[1].inAddrWidth == 8); +// assert(rams[1].inWdataWidth == 8); +// assert(rams[1].outRdataWidth == 8); +// } +// +// { +// const auto& edges = blueprint.edges(); +// auto assertIn = [&edges](std::string fNodeName, std::string fPortName, +// std::string tNodeName, std::string tPortName, +// int size) { +// for (int i = 0; i < size; i++) { +// auto v = +// std::make_pair(Port{fNodeName, {"output", fPortName, i}}, +// Port{tNodeName, {"input", tPortName, i}}); +// auto it = std::find(edges.begin(), edges.end(), v); +// assert(it != edges.end()); +// } +// }; +// assertIn("core", "io_romAddr", "rom", "addr", 7); +// assertIn("rom", "rdata", "core", "io_romData", 32); +// assertIn("core", "io_memA_writeEnable", "ramA", "wren", 1); +// assertIn("core", "io_memA_address", "ramA", "addr", 8); +// assertIn("core", "io_memA_in", "ramA", "wdata", 8); +// assertIn("ramA", "rdata", "core", "io_memA_out", 8); +// assertIn("core", "io_memB_writeEnable", "ramB", "wren", 1); +// assertIn("core", "io_memB_address", "ramB", "addr", 8); +// assertIn("core", "io_memB_in", "ramB", "wdata", 8); +// assertIn("ramB", "rdata", "core", "io_memB_out", 8); +// } +// +// { +// const Port port = blueprint.at("reset").value(); +// assert(port.nodeName == "core"); +// assert(port.portLabel.kind == "input"); +// assert(port.portLabel.portName == "reset"); +// assert(port.portLabel.portBit == 0); +// } +// +// { +// const Port port = blueprint.at("finflag").value(); +// assert(port.nodeName == "core"); +// assert(port.portLabel.kind == "output"); +// assert(port.portLabel.portName == "io_finishFlag"); +// assert(port.portLabel.portBit == 0); +// } +// +// for (int ireg = 0; ireg < 16; ireg++) { +// for (int ibit = 0; ibit < 16; ibit++) { +// const Port port = +// blueprint.at(utility::fok("reg_x", ireg), ibit).value(); +// assert(port.nodeName == "core"); +// assert(port.portLabel.portName == +// utility::fok("io_regOut_x", ireg)); +// assert(port.portLabel.portBit == ibit); +// } +// } +//} -#include "iyokan_tfhepp.hpp" +#include "iyokan_nt.hpp" +#include "iyokan_nt_plain.hpp" +#include "tfhepp_cufhe_wrapper.hpp" class TFHEppTestHelper { private: @@ -553,11 +888,6 @@ class TFHEppTestHelper { ck_ = std::make_shared(*sk_); } - TFHEppWorkerInfo wi() const - { - return TFHEppWorkerInfo{gk_, ck_}; - } - const std::shared_ptr& sk() const { return sk_; @@ -579,280 +909,6 @@ class TFHEppTestHelper { } }; -void processAllGates(TFHEppNetwork& net, - std::shared_ptr graph = nullptr) -{ - processAllGates(net, std::thread::hardware_concurrency(), - TFHEppTestHelper::instance().wi(), graph); -} - -void setInput(std::shared_ptr task, int val) -{ - auto& h = TFHEppTestHelper::instance(); - task->set(val ? h.one() : h.zero()); -} - -int getOutput(std::shared_ptr task) -{ - return TFHEpp::bootsSymDecrypt({task->get()}, - *TFHEppTestHelper::instance().sk())[0]; -} - -void testTFHEppSerialization() -{ - auto& h = TFHEppTestHelper::instance(); - const std::shared_ptr& sk = h.sk(); - const std::shared_ptr& gk = h.wi().gateKey; - - // Test for secret key - { - // Dump - writeToArchive("_test_sk", *sk); - // Load - auto sk2 = std::make_shared(); - readFromArchive(*sk2, "_test_sk"); - - auto zero = TFHEpp::bootsSymEncrypt({0}, *sk2).at(0); - auto one = TFHEpp::bootsSymEncrypt({1}, *sk2).at(0); - TLWELvl0 res; - TFHEpp::HomANDNY(res, zero, one, *gk); - assert(TFHEpp::bootsSymDecrypt({res}, *sk2).at(0) == 1); - } - - // Test for gate key - { - std::stringstream ss{std::ios::binary | std::ios::out | std::ios::in}; - - // Dump - writeToArchive(ss, *gk); - // Load - auto gk2 = std::make_shared(); - readFromArchive(*gk2, ss); - - auto zero = TFHEpp::bootsSymEncrypt({0}, *sk).at(0); - auto one = TFHEpp::bootsSymEncrypt({1}, *sk).at(0); - TLWELvl0 res; - TFHEpp::HomANDNY(res, zero, one, *gk2); - assert(TFHEpp::bootsSymDecrypt({res}, *sk).at(0) == 1); - } - - // Test for TLWE level 0 - { - std::stringstream ss{std::ios::binary | std::ios::out | std::ios::in}; - - { - auto zero = TFHEpp::bootsSymEncrypt({0}, *sk).at(0); - auto one = TFHEpp::bootsSymEncrypt({1}, *sk).at(0); - writeToArchive(ss, zero); - writeToArchive(ss, one); - ss.seekg(0); - } - - { - TLWELvl0 res, zero, one; - readFromArchive(zero, ss); - readFromArchive(one, ss); - TFHEpp::HomANDNY(res, zero, one, *gk); - assert(TFHEpp::bootsSymDecrypt({res}, *sk).at(0) == 1); - } - } -} - -#ifdef IYOKAN_CUDA_ENABLED -#include "iyokan_cufhe.hpp" - -class CUFHETestHelper { -private: - std::shared_ptr gk_; - cufhe::Ctxt zero_, one_; - -private: - CUFHETestHelper() - { - gk_ = std::make_shared(); - ifftGateKey(*gk_, *TFHEppTestHelper::instance().gk()); - setCtxtZero(zero_); - setCtxtOne(one_); - } - -public: - class CUFHEManager { - public: - CUFHEManager() - { - cufhe::Initialize(*CUFHETestHelper::instance().gk_); - } - - ~CUFHEManager() - { - cufhe::CleanUp(); - } - }; - -public: - static CUFHETestHelper& instance() - { - static CUFHETestHelper inst; - return inst; - } -}; - -void processAllGates(CUFHENetwork& net, - std::shared_ptr graph = nullptr) -{ - processAllGates(net, 240, graph); -} - -void setInput(std::shared_ptr task, int val) -{ - TLWELvl0 c; - if (val) - setTLWELvl0Trivial1(c); - else - setTLWELvl0Trivial0(c); - task->set(c); -} - -int getOutput(std::shared_ptr task) -{ - return decryptTLWELvl0(task->get(), *TFHEppTestHelper::instance().sk()); -} - -void testBridgeBetweenCUFHEAndTFHEpp() -{ - auto& ht = TFHEppTestHelper::instance(); - - // FIXME: The network constructed here does not have any meanings anymore, - // but it is enough to check if bridges work correctly. - // We may need another network here. - NetworkBuilderBase b0; - NetworkBuilderBase b1; - auto t0 = b0.addINPUT("in", 0, false); - auto t1 = std::make_shared(); - b1.addTask(NodeLabel{"tfhepp2cufhe", ""}, t1); - auto t2 = std::make_shared(); - b1.addTask(NodeLabel{"cufhe2tfhepp", ""}, t2); - auto t3 = b0.addOUTPUT("out", 0, true); - connectTasks(t1, t2); - - auto net0 = std::make_shared>(std::move(b0)); - auto net1 = std::make_shared>(std::move(b1)); - auto bridge0 = connectWithBridge(t0, t1); - auto bridge1 = connectWithBridge(t2, t3); - - CUFHENetworkRunner runner{1, 1, ht.wi()}; - runner.addNetwork(net0); - runner.addNetwork(net1); - runner.addBridge(bridge0); - runner.addBridge(bridge1); - - t0->set(ht.one()); - runner.run(false); - assert(t3->get() == ht.one()); - - net0->tick(); - net1->tick(); - bridge0->tick(); - bridge1->tick(); - - t0->set(ht.zero()); - runner.run(false); - assert(t3->get() == ht.zero()); -} -#endif - -void testBlueprint() -{ - using namespace blueprint; - - NetworkBlueprint blueprint{"test/config-toml/cahp-diamond.toml"}; - - { - const auto& files = blueprint.files(); - assert(files.size() == 1); - assert(files[0].type == File::TYPE::YOSYS_JSON); - assert(std::filesystem::canonical(files[0].path) == - std::filesystem::canonical( - "test/yosys-json/cahp-diamond-core-yosys.json")); - assert(files[0].name == "core"); - } - - { - const auto& roms = blueprint.builtinROMs(); - assert(roms.size() == 1); - assert(roms[0].name == "rom"); - assert(roms[0].inAddrWidth == 7); - assert(roms[0].outRdataWidth == 32); - } - - { - const auto& rams = blueprint.builtinRAMs(); - assert(rams.size() == 2); - assert((rams[0].name == "ramA" && rams[1].name == "ramB") || - (rams[1].name == "ramA" && rams[0].name == "ramB")); - assert(rams[0].inAddrWidth == 8); - assert(rams[0].inWdataWidth == 8); - assert(rams[0].outRdataWidth == 8); - assert(rams[1].inAddrWidth == 8); - assert(rams[1].inWdataWidth == 8); - assert(rams[1].outRdataWidth == 8); - } - - { - const auto& edges = blueprint.edges(); - auto assertIn = [&edges](std::string fNodeName, std::string fPortName, - std::string tNodeName, std::string tPortName, - int size) { - for (int i = 0; i < size; i++) { - auto v = - std::make_pair(Port{fNodeName, {"output", fPortName, i}}, - Port{tNodeName, {"input", tPortName, i}}); - auto it = std::find(edges.begin(), edges.end(), v); - assert(it != edges.end()); - } - }; - assertIn("core", "io_romAddr", "rom", "addr", 7); - assertIn("rom", "rdata", "core", "io_romData", 32); - assertIn("core", "io_memA_writeEnable", "ramA", "wren", 1); - assertIn("core", "io_memA_address", "ramA", "addr", 8); - assertIn("core", "io_memA_in", "ramA", "wdata", 8); - assertIn("ramA", "rdata", "core", "io_memA_out", 8); - assertIn("core", "io_memB_writeEnable", "ramB", "wren", 1); - assertIn("core", "io_memB_address", "ramB", "addr", 8); - assertIn("core", "io_memB_in", "ramB", "wdata", 8); - assertIn("ramB", "rdata", "core", "io_memB_out", 8); - } - - { - const Port port = blueprint.at("reset").value(); - assert(port.nodeName == "core"); - assert(port.portLabel.kind == "input"); - assert(port.portLabel.portName == "reset"); - assert(port.portLabel.portBit == 0); - } - - { - const Port port = blueprint.at("finflag").value(); - assert(port.nodeName == "core"); - assert(port.portLabel.kind == "output"); - assert(port.portLabel.portName == "io_finishFlag"); - assert(port.portLabel.portBit == 0); - } - - for (int ireg = 0; ireg < 16; ireg++) { - for (int ibit = 0; ibit < 16; ibit++) { - const Port port = - blueprint.at(utility::fok("reg_x", ireg), ibit).value(); - assert(port.nodeName == "core"); - assert(port.portLabel.portName == - utility::fok("io_regOut_x", ireg)); - assert(port.portLabel.portBit == ibit); - } - } -} - -#include "iyokan_nt.hpp" - namespace nt { void testAllocator() { @@ -896,58 +952,59 @@ void testAllocator() int main() { - AsyncThread::setNumThreads(std::thread::hardware_concurrency()); - - testNOT(); - testMUX(); - testBinopGates(); - testFromJSONtest_pass_4bit(); - testFromJSONtest_and_4bit(); - testFromJSONtest_and_4_2bit(); - testFromJSONtest_mux_4bit(); - testFromJSONtest_addr_4bit(); - testFromJSONtest_register_4bit(); - testSequentialCircuit(); - testFromJSONtest_counter_4bit(); - testPrioritySetVisitor(); - - testNOT(); - testMUX(); - testBinopGates(); - testFromJSONtest_pass_4bit(); - testFromJSONtest_pass_4bit(); - testFromJSONtest_and_4bit(); - testFromJSONtest_and_4_2bit(); - testFromJSONtest_mux_4bit(); - testFromJSONtest_addr_4bit(); - testFromJSONtest_register_4bit(); - testSequentialCircuit(); - testFromJSONtest_counter_4bit(); - testPrioritySetVisitor(); - testTFHEppSerialization(); - -#ifdef IYOKAN_CUDA_ENABLED - { - CUFHETestHelper::CUFHEManager man; - - testNOT(); - testMUX(); - testBinopGates(); - testFromJSONtest_pass_4bit(); - testFromJSONtest_and_4bit(); - testFromJSONtest_and_4_2bit(); - testFromJSONtest_mux_4bit(); - testFromJSONtest_addr_4bit(); - testFromJSONtest_register_4bit(); - testSequentialCircuit(); - testFromJSONtest_counter_4bit(); - testPrioritySetVisitor(); - testBridgeBetweenCUFHEAndTFHEpp(); - } -#endif - - testProgressGraphMaker(); - testBlueprint(); + // AsyncThread::setNumThreads(std::thread::hardware_concurrency()); + + // testNOT(); + // testMUX(); + // testBinopGates(); + // testFromJSONtest_pass_4bit(); + // testFromJSONtest_and_4bit(); + // testFromJSONtest_and_4_2bit(); + // testFromJSONtest_mux_4bit(); + // testFromJSONtest_addr_4bit(); + // testFromJSONtest_register_4bit(); + // testSequentialCircuit(); + // testFromJSONtest_counter_4bit(); + // testPrioritySetVisitor(); + // + // testNOT(); + // testMUX(); + // testBinopGates(); + // testFromJSONtest_pass_4bit(); + // testFromJSONtest_pass_4bit(); + // testFromJSONtest_and_4bit(); + // testFromJSONtest_and_4_2bit(); + // testFromJSONtest_mux_4bit(); + // testFromJSONtest_addr_4bit(); + // testFromJSONtest_register_4bit(); + // testSequentialCircuit(); + // testFromJSONtest_counter_4bit(); + // testPrioritySetVisitor(); + // testTFHEppSerialization(); + // + //#ifdef IYOKAN_CUDA_ENABLED + // { + // CUFHETestHelper::CUFHEManager man; + // + // testNOT(); + // testMUX(); + // testBinopGates(); + // testFromJSONtest_pass_4bit(); + // testFromJSONtest_and_4bit(); + // testFromJSONtest_and_4_2bit(); + // testFromJSONtest_mux_4bit(); + // testFromJSONtest_addr_4bit(); + // testFromJSONtest_register_4bit(); + // testSequentialCircuit(); + // testFromJSONtest_counter_4bit(); + // testPrioritySetVisitor(); + // testBridgeBetweenCUFHEAndTFHEpp(); + // } + //#endif + + // testProgressGraphMaker(); + // testBlueprint(); nt::testAllocator(); + nt::plain::test0(); } From 9a79a3a5c37cacc90e6dce095f5130deb19c0c41 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 14 Sep 2021 19:50:58 +0900 Subject: [PATCH 03/54] wip --- src/iyokan_nt.cpp | 78 +++++++++++++++++ src/iyokan_nt_plain.cpp | 185 ++++++++++++++++++++++++++++++++++++++++ src/iyokan_nt_plain.hpp | 8 ++ 3 files changed, 271 insertions(+) create mode 100644 src/iyokan_nt.cpp create mode 100644 src/iyokan_nt_plain.cpp create mode 100644 src/iyokan_nt_plain.hpp diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp new file mode 100644 index 0000000..fface6c --- /dev/null +++ b/src/iyokan_nt.cpp @@ -0,0 +1,78 @@ +#include "iyokan_nt.hpp" +#include + +namespace nt { + +/* class Allocator */ + +Allocator::Allocator() +{ +} + +Allocator& Allocator::subAllocator(const std::string& key) +{ + assert(data_.size() == 0); + auto it = subs_.find(key); + if (it == subs_.end()) { + auto sub = std::make_unique(); + std::tie(it, std::ignore) = subs_.emplace(key, std::move(sub)); + } + return *it->second; +} + +/* class Network */ + +Network::Network(std::unordered_map> uid2task) + : uid2task_(std::move(uid2task)) +{ +} + +Task* Network::findByUID(UID uid) const +{ + Task* ret = uid2task_.at(uid).get(); + assert(ret != nullptr); + return ret; +} + +Task* Network::findByTags(const std::vector& tags) const +{ + // FIXME: More efficient way? + for (auto&& [uid, task] : uid2task_) { + const auto& taskTags = task->label().tags; + bool found = std::all_of(tags.begin(), tags.end(), [&](const Tag& t) { + auto it = taskTags.find(t.first); + return it != taskTags.end() && it->second == t.second; + }); + if (found) + return task.get(); + } + throw std::runtime_error("Network::findByTags: not found"); +} + +/**************************************************/ +/***** TEST ***************************************/ +/**************************************************/ + +/* +void testBuildAdder(NetworkBuilder& nb) +{ + UID a = nb.INPUT("0", "A", 0), b = nb.INPUT("1", "B", 0), + ci = nb.INPUT("2", "Ci", 0), s = nb.OUTPUT("3", "S", 0), + c0 = nb.OUTPUT("4", "C0", 0), and0 = nb.AND("5"), and1 = nb.AND("6"), + or0 = nb.OR("7"), xor0 = nb.XOR("8"), xor1 = nb.XOR("9"); + nb.connect(a, xor0); + nb.connect(b, xor0); + nb.connect(ci, xor1); + nb.connect(xor0, xor1); + nb.connect(a, and0); + nb.connect(b, and0); + nb.connect(ci, and1); + nb.connect(xor0, and1); + nb.connect(and1, or0); + nb.connect(and0, or0); + nb.connect(xor1, s); + nb.connect(or0, c0); +} +*/ + +} // namespace nt diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp new file mode 100644 index 0000000..f84628d --- /dev/null +++ b/src/iyokan_nt_plain.cpp @@ -0,0 +1,185 @@ +#include "iyokan_nt_plain.hpp" +#include "iyokan_nt.hpp" +#include "packet.hpp" + +namespace nt { +namespace plain { + +class TaskConstZero : public TaskCommon { +public: + TaskConstZero(Label label, Allocator& alc) : TaskCommon(label, alc, 0) + { + } + + ~TaskConstZero() + { + } + + void startAsynchronously() override + { + output() = 0_b; + } + + bool hasFinished() const override + { + return true; + } +}; + +class TaskConstOne : public TaskCommon { +public: + TaskConstOne(Label label, Allocator& alc) : TaskCommon(label, alc, 0) + { + } + + ~TaskConstOne() + { + } + + void startAsynchronously() override + { + output() = 1_b; + } + + bool hasFinished() const override + { + return true; + } +}; + +class TaskNand : public TaskCommon { +public: + TaskNand(Label label, Allocator& alc) : TaskCommon(label, alc, 2) + { + } + + ~TaskNand() + { + } + + void startAsynchronously() override + { + output() = ~(input(0) & input(1)); + } + + bool hasFinished() const override + { + return true; + } +}; + +class PlainNetworkBuilder : public NetworkBuilder { +private: + Allocator& alc_; + std::unordered_map>> uid2common_; + UID nextUID_; + +private: + UID genUID() + { + return nextUID_++; + } + +public: + PlainNetworkBuilder(Allocator& alc) : alc_(alc), uid2common_(), nextUID_(0) + { + } + + ~PlainNetworkBuilder() + { + } + + // createNetwork destructs the builder + Network createNetwork() override + { + std::unordered_map> uid2task; + for (auto&& [uid, task] : uid2common_) + uid2task.emplace(uid, std::move(task)); + uid2common_.clear(); + return Network{std::move(uid2task)}; + } + + void connect(UID fromUID, UID toUID) override + { + auto &from = uid2common_.at(fromUID), &to = uid2common_.at(toUID); + to->addInput(from.get()); + } + +#define DEF_COMMON_TASK(TaskType, capName, camelName) \ + UID capName(const std::string& alcKey) override \ + { \ + Allocator& subalc = alc_.subAllocator(alcKey); \ + UID uid = genUID(); \ + auto task = std::make_unique( \ + Label{uid, {{TAG_KEY::KIND, #camelName}}}, subalc); \ + uid2common_.emplace(uid, std::move(task)); \ + return uid; \ + } + DEF_COMMON_TASK(TaskConstOne, CONSTONE, ConstOne); + DEF_COMMON_TASK(TaskConstZero, CONSTZERO, ConstZero); + DEF_COMMON_TASK(TaskNand, NAND, Nand); +#undef DEF_COMMON_TASK +}; + +/**************************************************/ +/***** TEST ***************************************/ +/**************************************************/ + +void test0() +{ + { + Allocator root; + TaskConstOne t{Label{1, {}}, root}; + t.startAsynchronously(); + assert(t.hasFinished()); + assert(t.getOutput() == 1_b); + } + + { + Allocator root; + TaskConstZero t{Label{1, {}}, root}; + t.startAsynchronously(); + assert(t.hasFinished()); + assert(t.getOutput() == 0_b); + } + + { + Allocator root; + Allocator &sub0 = root.subAllocator("0"), + &sub1 = root.subAllocator("1"), + &sub2 = root.subAllocator("2"); + TaskConstZero t0{Label{0, {}}, sub0}; + TaskConstOne t1{Label{1, {}}, sub1}; + TaskNand t2{Label{2, {}}, sub2}; + t2.addInput(&t0); + t2.addInput(&t1); + t0.startAsynchronously(); + t1.startAsynchronously(); + t2.startAsynchronously(); + assert(t0.hasFinished() && t1.hasFinished() && t2.hasFinished()); + assert(t2.getOutput() == 1_b); + } + + { + Allocator root; + PlainNetworkBuilder nb{root}; + UID id0 = nb.CONSTZERO("0"), id1 = nb.CONSTONE("1"), id2 = nb.NAND("2"); + nb.connect(id0, id2); + nb.connect(id1, id2); + Network nw{nb.createNetwork()}; + Task* t0 = nw.findByUID(id0); + Task* t1 = nw.findByUID(id1); + Task* t2 = nw.findByUID(id2); + assert(t0 != nullptr && t1 != nullptr && t2 != nullptr); + t0->startAsynchronously(); + t1->startAsynchronously(); + t2->startAsynchronously(); + assert(t0->hasFinished() && t1->hasFinished() && t2->hasFinished()); + assert(dynamic_cast*>(t2)->getOutput() == 1_b); + } + + // FIXME NetworkRunner +} + +} // namespace plain +} // namespace nt diff --git a/src/iyokan_nt_plain.hpp b/src/iyokan_nt_plain.hpp new file mode 100644 index 0000000..1ebe0a1 --- /dev/null +++ b/src/iyokan_nt_plain.hpp @@ -0,0 +1,8 @@ +#ifndef VIRTUALSECUREPLATFORM_IYOKAN_NT_PLAIN_HPP +#define VIRTUALSECUREPLATFORM_IYOKAN_NT_PLAIN_HPP + +namespace nt::plain { +void test0(); +} + +#endif From 48cede0524bddd76339717c37dd1a164435ea6fa Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 15 Sep 2021 00:20:03 +0900 Subject: [PATCH 04/54] wip --- src/iyokan_nt.cpp | 131 ++++++++++++++++++++++++++++++++++++++++ src/iyokan_nt.hpp | 89 ++++++++++++++++++++++++++- src/iyokan_nt_plain.cpp | 91 +++++++++++++++++++++++----- 3 files changed, 294 insertions(+), 17 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index fface6c..11136bd 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -20,6 +20,30 @@ Allocator& Allocator::subAllocator(const std::string& key) return *it->second; } +/* class ReadyQueue */ + +bool ReadyQueue::empty() const +{ + return queue_.empty(); +} + +void ReadyQueue::pop() +{ + queue_.pop(); +} + +Task* ReadyQueue::peek() const +{ + auto [pri, task] = queue_.top(); + return task; +} + +void ReadyQueue::push(Task* task) +{ + queue_.emplace(task->priority(), task); + task->setQueued(); +} + /* class Network */ Network::Network(std::unordered_map> uid2task) @@ -27,6 +51,11 @@ Network::Network(std::unordered_map> uid2task) { } +size_t Network::size() const +{ + return uid2task_.size(); +} + Task* Network::findByUID(UID uid) const { Task* ret = uid2task_.at(uid).get(); @@ -49,6 +78,108 @@ Task* Network::findByTags(const std::vector& tags) const throw std::runtime_error("Network::findByTags: not found"); } +void Network::pushReadyTasks(ReadyQueue& readyQueue) +{ + for (auto&& [uid, task] : uid2task_) + if (task->areAllInputsReady()) + readyQueue.push(task.get()); +} + +void Network::tick() +{ + for (auto&& [uid, task] : uid2task_) + task->tick(); +} + +/* class Worker */ + +Worker::Worker() : target_(nullptr) +{ +} + +Worker::~Worker() +{ +} + +void Worker::update(ReadyQueue& readyQueue, size_t& numFinishedTargets) +{ + if (target_ == nullptr && !readyQueue.empty()) { + // Try to find the task to tackle next + Task* cand = readyQueue.peek(); + assert(cand != nullptr); + if (canExecute(cand)) { + target_ = cand; + readyQueue.pop(); + startTask(target_); + } + } + + if (target_ != nullptr && target_->hasFinished()) { + for (Task* child : target_->children()) { + child->notifyOneInputReady(); + if (!child->hasQueued() && child->areAllInputsReady()) + readyQueue.push(child); + } + target_ = nullptr; + numFinishedTargets++; + } +} + +bool Worker::isWorking() const +{ + return target_ != nullptr; +} + +/* class NetworkRunner */ + +NetworkRunner::NetworkRunner(Network network, + std::vector> workers) + : network_(std::move(network)), + workers_(std::move(workers)), + readyQueue_(), + numFinishedTargets_(0) +{ + assert(workers_.size() != 0); + for (auto&& w : workers) + assert(w != nullptr); +} + +const Network& NetworkRunner::network() const +{ + return network_; +} + +size_t NetworkRunner::numFinishedTargets() const +{ + return numFinishedTargets_; +} + +void NetworkRunner::prepareToRun() +{ + assert(readyQueue_.empty()); + + numFinishedTargets_ = 0; + network_.pushReadyTasks(readyQueue_); +} + +bool NetworkRunner::isRunning() const +{ + return std::any_of(workers_.begin(), workers_.end(), + [](auto&& w) { return w->isWorking(); }) || + !readyQueue_.empty(); +} + +void NetworkRunner::update() +{ + for (auto&& w : workers_) + w->update(readyQueue_, numFinishedTargets_); +} + +void NetworkRunner::tick() +{ + network_.tick(); +} + /**************************************************/ /***** TEST ***************************************/ /**************************************************/ diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 0722f7d..0ace4d0 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -57,10 +58,16 @@ struct Label { std::unordered_map tags; }; +namespace plain { +class WorkerInfo; +} + class Task { private: Label label_; std::vector parents_, children_; + int priority_; + bool hasQueued_; public: Task(Label label) : label_(label) @@ -86,6 +93,16 @@ class Task { return children_; } + int priority() const + { + return priority_; + } + + bool hasQueued() const + { + return hasQueued_; + } + void addChild(Task* task) { assert(task != nullptr); @@ -98,11 +115,31 @@ class Task { parents_.push_back(task); } + void setPriority(int newPri) + { + priority_ = newPri; + } + + void setQueued() + { + assert(!hasQueued_); + hasQueued_ = true; + } + virtual void notifyOneInputReady() = 0; virtual bool areAllInputsReady() const = 0; - virtual void startAsynchronously() = 0; virtual bool hasFinished() const = 0; virtual void tick() = 0; // Reset for next cycle + + virtual bool canRunPlain() const + { + return false; + } + + virtual void startAsynchronously(plain::WorkerInfo&) + { + assert(0 && "Internal error: not implemented task for plain mode"); + } }; // TaskCommon can be used as base class of many "common" tasks. @@ -189,6 +226,17 @@ class TaskCommon : public Task { } }; +class ReadyQueue { +private: + std::priority_queue> queue_; + +public: + bool empty() const; + void pop(); + Task* peek() const; + void push(Task* task); +}; + class Network { private: std::unordered_map> uid2task_; @@ -196,9 +244,13 @@ class Network { public: Network(std::unordered_map> uid2task); + size_t size() const; + Task* findByUID(UID uid) const; Task* findByTags(const std::vector& tags) const; + void pushReadyTasks(ReadyQueue& readyQueue); + void tick(); void eachTask(std::function handler) const; }; @@ -237,6 +289,41 @@ class NetworkBuilder { // virtual UID XOR(const std::string& alcKey) = 0; }; +class Worker { +private: + Task* target_; + +public: + Worker(); + virtual ~Worker(); + + void update(ReadyQueue& readyQueue, size_t& numFinishedTargets); + bool isWorking() const; + +protected: + virtual void startTask(Task* task) = 0; + virtual bool canExecute(Task* task) = 0; +}; + +class NetworkRunner { +private: + Network network_; + std::vector> workers_; + ReadyQueue readyQueue_; + size_t numFinishedTargets_; + +public: + NetworkRunner(Network network, + std::vector> workers); + + const Network& network() const; + size_t numFinishedTargets() const; + void prepareToRun(); + bool isRunning() const; + void update(); + void tick(); +}; + } // namespace nt #endif diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index f84628d..b399850 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -5,6 +5,25 @@ namespace nt { namespace plain { +class WorkerInfo { +}; + +class Worker : public nt::Worker { +private: + WorkerInfo wi_; + +protected: + bool canExecute(Task* task) override + { + return task->canRunPlain(); + } + + void startTask(Task* task) override + { + task->startAsynchronously(wi_); + } +}; + class TaskConstZero : public TaskCommon { public: TaskConstZero(Label label, Allocator& alc) : TaskCommon(label, alc, 0) @@ -15,7 +34,7 @@ class TaskConstZero : public TaskCommon { { } - void startAsynchronously() override + void startAsynchronously(WorkerInfo&) override { output() = 0_b; } @@ -24,6 +43,11 @@ class TaskConstZero : public TaskCommon { { return true; } + + bool canRunPlain() const override + { + return true; + } }; class TaskConstOne : public TaskCommon { @@ -36,7 +60,7 @@ class TaskConstOne : public TaskCommon { { } - void startAsynchronously() override + void startAsynchronously(WorkerInfo&) override { output() = 1_b; } @@ -45,6 +69,11 @@ class TaskConstOne : public TaskCommon { { return true; } + + bool canRunPlain() const override + { + return true; + } }; class TaskNand : public TaskCommon { @@ -57,7 +86,7 @@ class TaskNand : public TaskCommon { { } - void startAsynchronously() override + void startAsynchronously(WorkerInfo&) override { output() = ~(input(0) & input(1)); } @@ -66,9 +95,14 @@ class TaskNand : public TaskCommon { { return true; } + + bool canRunPlain() const override + { + return true; + } }; -class PlainNetworkBuilder : public NetworkBuilder { +class NetworkBuilder : public nt::NetworkBuilder { private: Allocator& alc_; std::unordered_map>> uid2common_; @@ -81,11 +115,11 @@ class PlainNetworkBuilder : public NetworkBuilder { } public: - PlainNetworkBuilder(Allocator& alc) : alc_(alc), uid2common_(), nextUID_(0) + NetworkBuilder(Allocator& alc) : alc_(alc), uid2common_(), nextUID_(0) { } - ~PlainNetworkBuilder() + ~NetworkBuilder() { } @@ -127,10 +161,12 @@ class PlainNetworkBuilder : public NetworkBuilder { void test0() { + WorkerInfo wi; + { Allocator root; TaskConstOne t{Label{1, {}}, root}; - t.startAsynchronously(); + t.startAsynchronously(wi); assert(t.hasFinished()); assert(t.getOutput() == 1_b); } @@ -138,7 +174,7 @@ void test0() { Allocator root; TaskConstZero t{Label{1, {}}, root}; - t.startAsynchronously(); + t.startAsynchronously(wi); assert(t.hasFinished()); assert(t.getOutput() == 0_b); } @@ -153,16 +189,16 @@ void test0() TaskNand t2{Label{2, {}}, sub2}; t2.addInput(&t0); t2.addInput(&t1); - t0.startAsynchronously(); - t1.startAsynchronously(); - t2.startAsynchronously(); + t0.startAsynchronously(wi); + t1.startAsynchronously(wi); + t2.startAsynchronously(wi); assert(t0.hasFinished() && t1.hasFinished() && t2.hasFinished()); assert(t2.getOutput() == 1_b); } { Allocator root; - PlainNetworkBuilder nb{root}; + NetworkBuilder nb{root}; UID id0 = nb.CONSTZERO("0"), id1 = nb.CONSTONE("1"), id2 = nb.NAND("2"); nb.connect(id0, id2); nb.connect(id1, id2); @@ -171,14 +207,37 @@ void test0() Task* t1 = nw.findByUID(id1); Task* t2 = nw.findByUID(id2); assert(t0 != nullptr && t1 != nullptr && t2 != nullptr); - t0->startAsynchronously(); - t1->startAsynchronously(); - t2->startAsynchronously(); + t0->startAsynchronously(wi); + t1->startAsynchronously(wi); + t2->startAsynchronously(wi); assert(t0->hasFinished() && t1->hasFinished() && t2->hasFinished()); assert(dynamic_cast*>(t2)->getOutput() == 1_b); } - // FIXME NetworkRunner + { + Allocator root; + NetworkBuilder nb{root}; + UID id0 = nb.CONSTZERO("0"), id1 = nb.CONSTONE("1"), id2 = nb.NAND("2"); + nb.connect(id0, id2); + nb.connect(id1, id2); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + runner.prepareToRun(); + + while (runner.numFinishedTargets() < runner.network().size()) { + assert(runner.isRunning()); + runner.update(); + } + + Task* t0 = runner.network().findByUID(id0); + Task* t1 = runner.network().findByUID(id1); + Task* t2 = runner.network().findByUID(id2); + assert(t0->hasFinished() && t1->hasFinished() && t2->hasFinished()); + assert(dynamic_cast*>(t2)->getOutput() == 1_b); + } } } // namespace plain From 85e2c32768c860237b9f0f164c32a9faa3d92073 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 19 Sep 2021 23:02:37 +0900 Subject: [PATCH 05/54] wip --- src/error.hpp | 25 +++ src/iyokan_nt.cpp | 371 +++++++++++++++++++++++++++++++++++--- src/iyokan_nt.hpp | 175 +++++++++++++----- src/iyokan_nt_plain.cpp | 386 ++++++++++++++++++++++++++++++++-------- 4 files changed, 815 insertions(+), 142 deletions(-) diff --git a/src/error.hpp b/src/error.hpp index ddc4578..494963d 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -18,6 +18,31 @@ inline void initialize(const std::string& tag) spdlog::set_default_logger(spdlog::stderr_color_mt(tag)); } +template +[[noreturn]] void diefmt(Args&&... args) +{ + using namespace backward; + + // Print error message + spdlog::error(std::forward(args)...); + +#ifndef NDEBUG + { + // Print backtrace + spdlog::error("Preparing backtrace..."); + std::stringstream ss; + StackTrace st; + st.load_here(32); + Printer p; + p.print(st, ss); + spdlog::error(ss.str()); + } +#endif + + // Abort + std::exit(EXIT_FAILURE); +} + template [[noreturn]] void die(Args... args) { diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 11136bd..8581170 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,4 +1,9 @@ #include "iyokan_nt.hpp" +#include "error.hpp" +#include "utility.hpp" + +#include + #include namespace nt { @@ -20,6 +25,32 @@ Allocator& Allocator::subAllocator(const std::string& key) return *it->second; } +/* class TaskFinder */ + +void TaskFinder::add(Task* task) +{ + const Label& label = task->label(); + byUID_.emplace(label.uid, task); + + if (label.cname) { + const ConfigName& cname = task->label().cname.value(); + byConfigName_.emplace( + std::make_tuple(cname.nodeName, cname.portName, cname.portBit), + task); + } +} + +Task* TaskFinder::findByUID(UID uid) const +{ + return byUID_.at(uid); +} + +Task* TaskFinder::findByConfigName(const ConfigName& cname) const +{ + return byConfigName_.at( + std::make_tuple(cname.nodeName, cname.portName, cname.portBit)); +} + /* class ReadyQueue */ bool ReadyQueue::empty() const @@ -46,51 +77,72 @@ void ReadyQueue::push(Task* task) /* class Network */ -Network::Network(std::unordered_map> uid2task) - : uid2task_(std::move(uid2task)) +Network::Network(TaskFinder finder, std::vector> tasks) + : finder_(std::move(finder)), tasks_(std::move(tasks)) { } size_t Network::size() const { - return uid2task_.size(); -} - -Task* Network::findByUID(UID uid) const -{ - Task* ret = uid2task_.at(uid).get(); - assert(ret != nullptr); - return ret; + return tasks_.size(); } -Task* Network::findByTags(const std::vector& tags) const +const TaskFinder& Network::finder() const { - // FIXME: More efficient way? - for (auto&& [uid, task] : uid2task_) { - const auto& taskTags = task->label().tags; - bool found = std::all_of(tags.begin(), tags.end(), [&](const Tag& t) { - auto it = taskTags.find(t.first); - return it != taskTags.end() && it->second == t.second; - }); - if (found) - return task.get(); - } - throw std::runtime_error("Network::findByTags: not found"); + return finder_; } void Network::pushReadyTasks(ReadyQueue& readyQueue) { - for (auto&& [uid, task] : uid2task_) + for (auto&& task : tasks_) if (task->areAllInputsReady()) readyQueue.push(task.get()); } void Network::tick() { - for (auto&& [uid, task] : uid2task_) + for (auto&& task : tasks_) task->tick(); } +/* class NetworkBuilder */ + +NetworkBuilder::NetworkBuilder(Allocator& alcRoot) + : finder_(), tasks_(), consumed_(false), currentAlc_(&alcRoot) +{ +} + +NetworkBuilder::~NetworkBuilder() +{ +} + +Allocator& NetworkBuilder::currentAllocator() +{ + return *currentAlc_; +} + +const TaskFinder& NetworkBuilder::finder() const +{ + assert(!consumed_); + return finder_; +} + +Network NetworkBuilder::createNetwork() +{ + assert(!consumed_); + consumed_ = true; + return Network{std::move(finder_), std::move(tasks_)}; +} + +void NetworkBuilder::withSubAllocator(const std::string& alcKey, + std::function f) +{ + Allocator* original = currentAlc_; + currentAlc_ = &original->subAllocator(alcKey); + f(*this); + currentAlc_ = original; +} + /* class Worker */ Worker::Worker() : target_(nullptr) @@ -180,6 +232,277 @@ void NetworkRunner::tick() network_.tick(); } +/* class Blueprint */ + +Blueprint::Blueprint(const std::string& fileName) +{ + namespace fs = std::filesystem; + + // Read the file + std::stringstream inputStream; + { + std::ifstream ifs{fileName}; + if (!ifs) + error::die("File not found: ", fileName); + inputStream << ifs.rdbuf(); + source_ = inputStream.str(); + inputStream.seekg(std::ios::beg); + } + + // Parse config file + const auto src = toml::parse(inputStream, fileName); + + // Find working directory of config + fs::path wd = fs::absolute(fileName); + wd.remove_filename(); + + // [[file]] + { + const auto srcFiles = + toml::find_or>(src, "file", {}); + for (const auto& srcFile : srcFiles) { + std::string typeStr = toml::find(srcFile, "type"); + fs::path path = toml::find(srcFile, "path"); + std::string name = toml::find(srcFile, "name"); + + blueprint::File::TYPE type; + if (typeStr == "iyokanl1-json") + type = blueprint::File::TYPE::IYOKANL1_JSON; + else if (typeStr == "yosys-json") + type = blueprint::File::TYPE::YOSYS_JSON; + else + error::die("Invalid file type: ", typeStr); + + if (path.is_relative()) + path = wd / path; // Make path absolute + + files_.push_back(blueprint::File{type, path.string(), name}); + } + } + + // [[builtin]] + { + const auto srcBuiltins = + toml::find_or>(src, "builtin", {}); + for (const auto& srcBuiltin : srcBuiltins) { + const auto type = toml::find(srcBuiltin, "type"); + const auto name = toml::find(srcBuiltin, "name"); + + if (type == "rom" || type == "mux-rom") { + auto romType = type == "rom" + ? blueprint::BuiltinROM::TYPE::CMUX_MEMORY + : blueprint::BuiltinROM::TYPE::MUX; + const auto inAddrWidth = + toml::find(srcBuiltin, "in_addr_width"); + const auto outRdataWidth = + toml::find(srcBuiltin, "out_rdata_width"); + + builtinROMs_.push_back(blueprint::BuiltinROM{ + romType, name, inAddrWidth, outRdataWidth}); + } + else if (type == "ram" || type == "mux-ram") { + auto ramType = type == "ram" + ? blueprint::BuiltinRAM::TYPE::CMUX_MEMORY + : blueprint::BuiltinRAM::TYPE::MUX; + const auto inAddrWidth = + toml::find(srcBuiltin, "in_addr_width"); + const auto inWdataWidth = + toml::find(srcBuiltin, "in_wdata_width"); + const auto outRdataWidth = + toml::find(srcBuiltin, "out_rdata_width"); + + builtinRAMs_.push_back(blueprint::BuiltinRAM{ + ramType, name, inAddrWidth, inWdataWidth, outRdataWidth}); + } + } + } + + // [connect] + { + const auto srcConnect = toml::find_or(src, "connect", {}); + for (const auto& [srcKey, srcValue] : srcConnect) { + if (srcKey == "TOGND") { // TOGND = [@...[n:m], @...[n:m], ...] + auto ary = toml::get>(srcValue); + for (const auto& portStr : ary) { // @...[n:m] + if (portStr.empty() || portStr.at(0) != '@') + error::die("Invalid port name for TOGND: ", portStr); + auto ports = parsePortString(portStr, "output"); + for (auto&& port : ports) { // @...[n] + const std::string& name = port.portName; + int bit = port.portBit; + auto [it, inserted] = atPortWidths_.emplace(name, 0); + it->second = std::max(it->second, bit + 1); + } + } + continue; + } + + std::string srcTo = srcKey, + srcFrom = toml::get(srcValue), + errMsg = utility::fok("Invalid connect: ", srcTo, " = ", + srcFrom); + + // Check if input is correct. + if (srcTo.empty() || srcFrom.empty() || + (srcTo[0] == '@' && srcFrom[0] == '@')) + error::die(errMsg); + + // Others. + std::vector portsTo = + parsePortString(srcTo, "input"), + portsFrom = + parsePortString(srcFrom, "output"); + if (portsTo.size() != portsFrom.size()) + error::die(errMsg); + + for (size_t i = 0; i < portsTo.size(); i++) { + const blueprint::Port& to = portsTo[i]; + const blueprint::Port& from = portsFrom[i]; + + if (srcTo[0] == '@') { // @... = ... + if (!to.nodeName.empty() || from.nodeName.empty()) + error::die(errMsg); + + const std::string& name = to.portName; + int bit = to.portBit; + + { + auto [it, inserted] = + atPorts_.emplace(std::make_tuple(name, bit), from); + if (!inserted) + spdlog::warn( + "{} is used multiple times. Only the first " + "one is effective.", + srcTo); + } + + auto [it, inserted] = atPortWidths_.emplace(name, 0); + it->second = std::max(it->second, bit + 1); + } + else if (srcFrom[0] == '@') { // ... = @... + if (!from.nodeName.empty() || to.nodeName.empty()) + error::die(errMsg); + + const std::string& name = from.portName; + int bit = from.portBit; + + { + auto [it, inserted] = + atPorts_.emplace(std::make_tuple(name, bit), to); + if (!inserted) + spdlog::warn( + "{} is used multiple times. Only the first " + "one is effective. (FIXME)", + srcFrom); + } + + auto [it, inserted] = atPortWidths_.emplace(name, 0); + it->second = std::max(it->second, bit + 1); + } + else { // ... = ... + edges_.emplace_back(from, to); + } + } + } + } +} + +std::vector Blueprint::parsePortString(const std::string& src, + const std::string& kind) +{ + std::string nodeName, portName; + int portBitFrom, portBitTo; + + auto match = utility::regexMatch( + src, + std::regex(R"(^@?(?:([^/]+)/)?([^[]+)(?:\[([0-9]+):([0-9]+)\])?$)")); + if (match.empty()) + error::die("Invalid port string: ", src); + + assert(match.size() == 1 + 4); + + nodeName = match[1]; + portName = match[2]; + + if (match[3].empty()) { // hoge/piyo + assert(match[4].empty()); + portBitFrom = 0; + portBitTo = 0; + } + else { // hoge/piyo[foo:bar] + assert(!match[4].empty()); + portBitFrom = std::stoi(match[3]); + portBitTo = std::stoi(match[4]); + } + + std::vector ret; + for (int i = portBitFrom; i < portBitTo + 1; i++) + ret.push_back(blueprint::Port{nodeName, kind, portName, i}); + return ret; +} + +bool Blueprint::needsCircuitKey() const +{ + for (const auto& bprom : builtinROMs_) + if (bprom.type == blueprint::BuiltinROM::TYPE::CMUX_MEMORY) + return true; + for (const auto& bpram : builtinRAMs_) + if (bpram.type == blueprint::BuiltinRAM::TYPE::CMUX_MEMORY) + return true; + return false; +} + +const std::string& Blueprint::sourceFile() const +{ + return sourceFile_; +} + +const std::string& Blueprint::source() const +{ + return source_; +} + +const std::vector& Blueprint::files() const +{ + return files_; +} + +const std::vector& Blueprint::builtinROMs() const +{ + return builtinROMs_; +} + +const std::vector& Blueprint::builtinRAMs() const +{ + return builtinRAMs_; +} + +const std::vector>& +Blueprint::edges() const +{ + return edges_; +} + +const std::map, blueprint::Port>& +Blueprint::atPorts() const +{ + return atPorts_; +} + +std::optional Blueprint::at(const std::string& portName, + int portBit) const +{ + auto it = atPorts_.find(std::make_tuple(portName, portBit)); + if (it == atPorts_.end()) + return std::nullopt; + return it->second; +} + +const std::unordered_map& Blueprint::atPortWidths() const +{ + return atPortWidths_; +} + /**************************************************/ /***** TEST ***************************************/ /**************************************************/ diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 0ace4d0..e903f4b 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ class Allocator { template T* make(size_t index) { - assert(subs_.size() == 0); if (data_.size() <= index) data_.resize(index + 1); std::any& v = data_.at(index); @@ -37,7 +37,6 @@ class Allocator { template T* get(size_t index) { - assert(subs_.size() == 0); assert(index < data_.size()); T* ret = std::any_cast(&data_.at(index)); assert(ret != nullptr); @@ -45,17 +44,15 @@ class Allocator { } }; -enum class TAG_KEY { - KIND, - PORT_NAME, - PORT_BIT, -}; -using Tag = std::pair; - using UID = uint64_t; +struct ConfigName { + std::string nodeName, portName; + int portBit; +}; struct Label { UID uid; - std::unordered_map tags; + std::string kind; + std::optional cname; }; namespace plain { @@ -142,6 +139,17 @@ class Task { } }; +class TaskFinder { +private: + std::unordered_map byUID_; + std::map, Task*> byConfigName_; + +public: + void add(Task* task); + Task* findByUID(UID uid) const; + Task* findByConfigName(const ConfigName& cname) const; +}; + // TaskCommon can be used as base class of many "common" tasks. // "Common" here means: // 1. # of outputs is 1. (#inputs can be >1.) @@ -153,13 +161,19 @@ template class TaskCommon : public Task { private: size_t numReadyInputs_; + const size_t numMinExpectedInputs_, numMaxExpectedInputs_; std::vector inputs_; T* output_; protected: + size_t getInputSize() const + { + return inputs_.size(); + } + const T& input(size_t i) const { - assert(inputs_.at(i) != nullptr); + assert(i < inputs_.size()); return *inputs_.at(i); } @@ -170,10 +184,14 @@ class TaskCommon : public Task { } public: - TaskCommon(Label label, Allocator& alc, size_t numExpectedInputs) + TaskCommon(Label label, Allocator& alc, size_t numMinExpectedInputs, + std::optional numMaxExpectedInputs = std::nullopt) : Task(std::move(label)), numReadyInputs_(0), - inputs_(numExpectedInputs, nullptr), + numMinExpectedInputs_(numMinExpectedInputs), + numMaxExpectedInputs_( + numMaxExpectedInputs.value_or(numMinExpectedInputs)), + inputs_(), output_(alc.make(0)) { } @@ -207,22 +225,12 @@ class TaskCommon : public Task { void addInput(Task* newParent, T* newIn) { assert(newParent != nullptr && newIn != nullptr); + assert(inputs_.size() < numMaxExpectedInputs_); addParent(newParent); newParent->addChild(this); - for (size_t i = 0; i < inputs_.size(); i++) { - if (inputs_.at(i) != nullptr) - continue; - inputs_.at(i) = newIn; - return; - } - assert(false && "Too many calls of addInput"); - } - - const T& getOutput() const - { - return *output_; + inputs_.push_back(newIn); } }; @@ -239,15 +247,14 @@ class ReadyQueue { class Network { private: - std::unordered_map> uid2task_; + TaskFinder finder_; + std::vector> tasks_; public: - Network(std::unordered_map> uid2task); + Network(TaskFinder finder, std::vector> tasks); size_t size() const; - - Task* findByUID(UID uid) const; - Task* findByTags(const std::vector& tags) const; + const TaskFinder& finder() const; void pushReadyTasks(ReadyQueue& readyQueue); void tick(); @@ -255,25 +262,44 @@ class Network { }; class NetworkBuilder { -public: - NetworkBuilder() - { - } +private: + TaskFinder finder_; + std::vector> tasks_; + bool consumed_; + Allocator* currentAlc_; - virtual ~NetworkBuilder() +protected: + // Create a new task. T must be derived from class Task. + template + T* emplaceTask(Args&&... args) { + assert(!consumed_); + T* task = new T(std::forward(args)...); + tasks_.emplace_back(task); + finder_.add(task); + return task; } - virtual Network createNetwork() = 0; + Allocator& currentAllocator(); + +public: + NetworkBuilder(Allocator& alcRoot); + virtual ~NetworkBuilder(); + + const TaskFinder& finder() const; + + Network createNetwork(); + + void withSubAllocator(const std::string& alcKey, + std::function f); virtual void connect(UID from, UID to) = 0; // not/and/or are C++ keywords, so member functions here are in capitals. - // virtual UID INPUT(const std::string& alcKey, const std::string& portName, - // int portBit) = 0; - // virtual UID OUTPUT(const std::string& alcKey, const std::string& - // portName, - // int portBit) = 0; + virtual UID INPUT(const std::string& alcKey, const std::string& nodeName, + const std::string& portName, int portBit) = 0; + virtual UID OUTPUT(const std::string& alcKey, const std::string& nodeName, + const std::string& portName, int portBit) = 0; virtual UID CONSTONE(const std::string& alcKey) = 0; virtual UID CONSTZERO(const std::string& alcKey) = 0; // virtual UID AND(const std::string& alcKey) = 0; @@ -324,6 +350,73 @@ class NetworkRunner { void tick(); }; +namespace blueprint { // blueprint components +struct File { + enum class TYPE { + IYOKANL1_JSON, + YOSYS_JSON, + } type; + std::string path, name; +}; + +struct BuiltinROM { + enum class TYPE { + CMUX_MEMORY, + MUX, + } type; + std::string name; + size_t inAddrWidth, outRdataWidth; +}; + +struct BuiltinRAM { + enum class TYPE { + CMUX_MEMORY, + MUX, + } type; + std::string name; + size_t inAddrWidth, inWdataWidth, outRdataWidth; +}; + +struct Port { + std::string nodeName, kind, portName; + int portBit; +}; +} // namespace blueprint + +class Blueprint { +private: + std::string sourceFile_, source_; + + std::vector files_; + std::vector builtinROMs_; + std::vector builtinRAMs_; + std::vector> edges_; + + std::map, blueprint::Port> atPorts_; + std::unordered_map atPortWidths_; + +private: + std::vector parsePortString(const std::string& src, + const std::string& kind); + +public: + Blueprint(const std::string& fileName); + + bool needsCircuitKey() const; + const std::string& sourceFile() const; + const std::string& source() const; + const std::vector& files() const; + const std::vector& builtinROMs() const; + const std::vector& builtinRAMs() const; + const std::vector>& edges() + const; + const std::map, blueprint::Port>& atPorts() + const; + std::optional at(const std::string& portName, + int portBit = 0) const; + const std::unordered_map& atPortWidths() const; +}; + } // namespace nt #endif diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index b399850..377ee39 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -24,19 +24,44 @@ class Worker : public nt::Worker { } }; -class TaskConstZero : public TaskCommon { +class TaskInput : public TaskCommon { public: - TaskConstZero(Label label, Allocator& alc) : TaskCommon(label, alc, 0) + TaskInput(Label label, Allocator& alc) : TaskCommon(label, alc, 0, 1) + { + } + + void startAsynchronously(WorkerInfo&) override + { + if (getInputSize() == 1) + output() = input(0); + } + + bool hasFinished() const override + { + return true; + } + + bool canRunPlain() const override + { + return true; + } + + void setInput(Bit val) { + // Set the input i.e., set the output value of this gate + output() = val; } +}; - ~TaskConstZero() +class TaskOutput : public TaskCommon { +public: + TaskOutput(Label label, Allocator& alc) : TaskCommon(label, alc, 1) { } void startAsynchronously(WorkerInfo&) override { - output() = 0_b; + output() = input(0); } bool hasFinished() const override @@ -48,15 +73,38 @@ class TaskConstZero : public TaskCommon { { return true; } + + const Bit& getOutput() + { + return output(); + } }; -class TaskConstOne : public TaskCommon { +class TaskConstZero : public TaskCommon { public: - TaskConstOne(Label label, Allocator& alc) : TaskCommon(label, alc, 0) + TaskConstZero(Label label, Allocator& alc) : TaskCommon(label, alc, 0) { } - ~TaskConstOne() + void startAsynchronously(WorkerInfo&) override + { + output() = 0_b; + } + + bool hasFinished() const override + { + return true; + } + + bool canRunPlain() const override + { + return true; + } +}; + +class TaskConstOne : public TaskCommon { +public: + TaskConstOne(Label label, Allocator& alc) : TaskCommon(label, alc, 0) { } @@ -82,10 +130,6 @@ class TaskNand : public TaskCommon { { } - ~TaskNand() - { - } - void startAsynchronously(WorkerInfo&) override { output() = ~(input(0) & input(1)); @@ -104,8 +148,7 @@ class TaskNand : public TaskCommon { class NetworkBuilder : public nt::NetworkBuilder { private: - Allocator& alc_; - std::unordered_map>> uid2common_; + std::unordered_map*> uid2common_; UID nextUID_; private: @@ -115,7 +158,8 @@ class NetworkBuilder : public nt::NetworkBuilder { } public: - NetworkBuilder(Allocator& alc) : alc_(alc), uid2common_(), nextUID_(0) + NetworkBuilder(Allocator& alc) + : nt::NetworkBuilder(alc), uid2common_(), nextUID_(0) { } @@ -123,38 +167,236 @@ class NetworkBuilder : public nt::NetworkBuilder { { } - // createNetwork destructs the builder - Network createNetwork() override - { - std::unordered_map> uid2task; - for (auto&& [uid, task] : uid2common_) - uid2task.emplace(uid, std::move(task)); - uid2common_.clear(); - return Network{std::move(uid2task)}; - } - void connect(UID fromUID, UID toUID) override { auto &from = uid2common_.at(fromUID), &to = uid2common_.at(toUID); - to->addInput(from.get()); + to->addInput(from); } -#define DEF_COMMON_TASK(TaskType, capName, camelName) \ - UID capName(const std::string& alcKey) override \ - { \ - Allocator& subalc = alc_.subAllocator(alcKey); \ - UID uid = genUID(); \ - auto task = std::make_unique( \ - Label{uid, {{TAG_KEY::KIND, #camelName}}}, subalc); \ - uid2common_.emplace(uid, std::move(task)); \ - return uid; \ +#define DEF_COMMON_TASK(TaskType, capName, camelName) \ + UID capName(const std::string& alcKey) override \ + { \ + UID uid = genUID(); \ + TaskType* task = nullptr; \ + this->withSubAllocator(alcKey, [&](auto&&) { \ + task = emplaceTask(Label{uid, #camelName, std::nullopt}, \ + currentAllocator()); \ + }); \ + uid2common_.emplace(uid, task); \ + return uid; \ } DEF_COMMON_TASK(TaskConstOne, CONSTONE, ConstOne); DEF_COMMON_TASK(TaskConstZero, CONSTZERO, ConstZero); DEF_COMMON_TASK(TaskNand, NAND, Nand); #undef DEF_COMMON_TASK + + UID INPUT(const std::string& alcKey, const std::string& nodeName, + const std::string& portName, int portBit) override + { + UID uid = genUID(); + TaskInput* task = nullptr; + withSubAllocator(alcKey, [&](auto&&) { + task = emplaceTask( + Label{uid, "Input", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + }); + uid2common_.emplace(uid, task); + return uid; + } + + UID OUTPUT(const std::string& alcKey, const std::string& nodeName, + const std::string& portName, int portBit) override + { + UID uid = genUID(); + TaskOutput* task = nullptr; + withSubAllocator(alcKey, [&](auto&&) { + task = emplaceTask( + Label{uid, "Output", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + }); + uid2common_.emplace(uid, task); + return uid; + } }; +enum class SCHED { + TOPO, + RANKU, +}; + +struct RunParameter { + std::string blueprintFile, inputFile, outputFile; + int numCPUWorkers, numCycles; + SCHED sched; + + void print() const + { + spdlog::info("Run Parameters"); + spdlog::info("\tMode: Plain"); + spdlog::info("\tBlueprint: {}", blueprintFile); + spdlog::info("\t# of CPU workers: {}", numCPUWorkers); + spdlog::info("\t# of cycles: {}", numCycles); + spdlog::info("\tInput file (request packet): {}", inputFile); + spdlog::info("\tOutput file (result packet): {}", outputFile); + spdlog::info("\tSchedule: {}", sched == SCHED::TOPO ? "topo" : "ranku"); + } +}; + +class Frontend { +private: + RunParameter pr_; + std::unique_ptr runner_; + PlainPacket reqPacket_; + int currentCycle_; + nt::Blueprint bp_; + +private: + static void readNetworkFromFile(const blueprint::File& file, + nt::NetworkBuilder& nb); + static void makeMUXRAM(const blueprint::BuiltinRAM& ram, + nt::NetworkBuilder& nb); + static void makeMUXROM(const blueprint::BuiltinROM& rom, + nt::NetworkBuilder& nb); + +protected: + void makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb); + void makeROM(const blueprint::BuiltinROM& rom, nt::NetworkBuilder& nb); + void make1bitROMWithMUX(const blueprint::BuiltinROM& rom, + const std::vector& addrInputs, + nt::NetworkBuilder& nb); + +public: + Frontend(const RunParameter& pr, Allocator& alc); +}; + +Frontend::Frontend(const RunParameter& pr, Allocator& alc) + : pr_(pr), + runner_(nullptr), + reqPacket_(readFromArchive(pr_.inputFile)), + currentCycle_(0), + bp_(pr_.blueprintFile) +{ + NetworkBuilder nb{alc}; + + // [[file]] + for (auto&& file : bp_.files()) + nb.withSubAllocator(file.name, + [&](auto&& nb) { readNetworkFromFile(file, nb); }); + + // [[builtin]] type = ram | type = mux-ram + for (auto&& ram : bp_.builtinRAMs()) { + nb.withSubAllocator(ram.name, [&](auto&& nb) { + switch (ram.type) { + case blueprint::BuiltinRAM::TYPE::CMUX_MEMORY: + makeRAM(ram, nb); + break; + case blueprint::BuiltinRAM::TYPE::MUX: + makeMUXRAM(ram, nb); + break; + } + }); + } + + // [[builtin]] type = rom | type = mux-rom + for (auto&& rom : bp_.builtinROMs()) { + nb.withSubAllocator(rom.name, [&](auto&& nb) { + switch (rom.type) { + case blueprint::BuiltinROM::TYPE::CMUX_MEMORY: + makeROM(rom, nb); + break; + case blueprint::BuiltinROM::TYPE::MUX: + makeMUXROM(rom, nb); + break; + } + }); + + auto it = reqPacket_.rom.find(rom.name); + if (it != reqPacket_.rom.end()) { + // FIXME: rom init + assert(false); + } + } + + auto get = [&](const blueprint::Port& port) -> Task* { + Task* task = nb.finder().findByConfigName( + {port.nodeName, port.portName, port.portBit}); + if (task->label().kind != port.kind) + error::diefmt("Invalid port: {}/{}[{}] is not {}"); + return task; + }; + + // [connect] + // We need to treat "... = @..." and "@... = ..." differently from + // "..." = ...". + // First, check if ports that are connected to or from "@..." exist. + for (auto&& [key, port] : bp_.atPorts()) { + get(port); // Only checks if port exists + } + // Then, connect other ports. `get` checks if they also exist. + for (auto&& [src, dst] : bp_.edges()) { + assert(src.kind == "output"); + assert(dst.kind == "input"); + nb.connect(get(src)->label().uid, get(dst)->label().uid); + } + + // Set priority to each DepNode + // FIXME +} + +void Frontend::readNetworkFromFile(const blueprint::File& file, + nt::NetworkBuilder& nb) +{ + // FIXME +} + +void Frontend::makeMUXRAM(const blueprint::BuiltinRAM& ram, + nt::NetworkBuilder& nb) +{ + // FIXME +} + +void Frontend::makeMUXROM(const blueprint::BuiltinROM& rom, + nt::NetworkBuilder& nb) +{ + // FIXME +} + +void Frontend::makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb) +{ + // FIXME: relax this constraint + if (ram.inWdataWidth != ram.outRdataWidth) + error::die( + "Invalid RAM size; RAM that has different sizes of " + "wdata and rdata is not implemented."); + + // FIXME +} + +void Frontend::makeROM(const blueprint::BuiltinROM& rom, nt::NetworkBuilder& nb) +{ + // Create inputs + std::vector addrInputs; + for (size_t i = 0; i < rom.inAddrWidth; i++) { + UID id = nb.INPUT(fmt::format("addr{}", i), rom.name, "addr", i); + addrInputs.push_back(id); + } + + // Create 1bit ROMs + for (size_t i = 0; i < rom.outRdataWidth; i++) { + nb.withSubAllocator(fmt::format("rom1bit{}", i), + [&](nt::NetworkBuilder& nb) { + make1bitROMWithMUX(rom, addrInputs, nb); + }); + } +} + +void Frontend::make1bitROMWithMUX(const blueprint::BuiltinROM& rom, + const std::vector& addrInputs, + nt::NetworkBuilder& nb) +{ + // FIXME +} + /**************************************************/ /***** TEST ***************************************/ /**************************************************/ @@ -165,78 +407,68 @@ void test0() { Allocator root; - TaskConstOne t{Label{1, {}}, root}; - t.startAsynchronously(wi); - assert(t.hasFinished()); - assert(t.getOutput() == 1_b); - } - - { - Allocator root; - TaskConstZero t{Label{1, {}}, root}; - t.startAsynchronously(wi); - assert(t.hasFinished()); - assert(t.getOutput() == 0_b); + TaskConstOne t0{Label{1, "", std::nullopt}, + root.subAllocator("constone")}; + TaskOutput t1{Label{2, "", std::nullopt}, root.subAllocator("out")}; + t1.addInput(&t0); + t0.startAsynchronously(wi); + t1.startAsynchronously(wi); + assert(t0.hasFinished()); + assert(t1.getOutput() == 1_b); } { Allocator root; Allocator &sub0 = root.subAllocator("0"), &sub1 = root.subAllocator("1"), - &sub2 = root.subAllocator("2"); - TaskConstZero t0{Label{0, {}}, sub0}; - TaskConstOne t1{Label{1, {}}, sub1}; - TaskNand t2{Label{2, {}}, sub2}; + &sub2 = root.subAllocator("2"), + &sub3 = root.subAllocator("3"); + TaskConstZero t0{Label{0, "", std::nullopt}, sub0}; + TaskConstOne t1{Label{1, "", std::nullopt}, sub1}; + TaskNand t2{Label{2, "", std::nullopt}, sub2}; + TaskOutput t3{Label{3, "", std::nullopt}, sub3}; t2.addInput(&t0); t2.addInput(&t1); + t3.addInput(&t2); t0.startAsynchronously(wi); t1.startAsynchronously(wi); t2.startAsynchronously(wi); - assert(t0.hasFinished() && t1.hasFinished() && t2.hasFinished()); - assert(t2.getOutput() == 1_b); + t3.startAsynchronously(wi); + assert(t0.hasFinished() && t1.hasFinished() && t2.hasFinished() && + t3.hasFinished()); + assert(t3.getOutput() == 1_b); } { Allocator root; NetworkBuilder nb{root}; - UID id0 = nb.CONSTZERO("0"), id1 = nb.CONSTONE("1"), id2 = nb.NAND("2"); - nb.connect(id0, id2); - nb.connect(id1, id2); - Network nw{nb.createNetwork()}; - Task* t0 = nw.findByUID(id0); - Task* t1 = nw.findByUID(id1); - Task* t2 = nw.findByUID(id2); - assert(t0 != nullptr && t1 != nullptr && t2 != nullptr); - t0->startAsynchronously(wi); - t1->startAsynchronously(wi); - t2->startAsynchronously(wi); - assert(t0->hasFinished() && t1->hasFinished() && t2->hasFinished()); - assert(dynamic_cast*>(t2)->getOutput() == 1_b); - } - - { - Allocator root; - NetworkBuilder nb{root}; - UID id0 = nb.CONSTZERO("0"), id1 = nb.CONSTONE("1"), id2 = nb.NAND("2"); + UID id0 = nb.INPUT("0", "", "A", 0), id1 = nb.INPUT("1", "", "B", 0), + id2 = nb.NAND("2"), id3 = nb.OUTPUT("3", "", "C", 0); nb.connect(id0, id2); nb.connect(id1, id2); + nb.connect(id2, id3); std::vector> workers; workers.emplace_back(std::make_unique()); NetworkRunner runner{nb.createNetwork(), std::move(workers)}; - runner.prepareToRun(); + Task* t0 = runner.network().finder().findByUID(id0); + Task* t1 = runner.network().finder().findByUID(id1); + Task* t3 = runner.network().finder().findByUID(id3); + TaskInput *inA = dynamic_cast(t0), + *inB = dynamic_cast(t1); + TaskOutput* out = dynamic_cast(t3); + + inA->setInput(1_b); + inB->setInput(1_b); + runner.prepareToRun(); while (runner.numFinishedTargets() < runner.network().size()) { assert(runner.isRunning()); runner.update(); } - Task* t0 = runner.network().findByUID(id0); - Task* t1 = runner.network().findByUID(id1); - Task* t2 = runner.network().findByUID(id2); - assert(t0->hasFinished() && t1->hasFinished() && t2->hasFinished()); - assert(dynamic_cast*>(t2)->getOutput() == 1_b); + assert(out->getOutput() == 0_b); } } From e9e50dfc173886c69c94d8f8c7c14556948c1289 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 19 Sep 2021 23:57:07 +0900 Subject: [PATCH 06/54] wip --- src/iyokan-packet.cpp | 3 ++- src/iyokan_nt.cpp | 29 ++++++++++++++++------------- src/iyokan_nt.hpp | 15 ++++++++++----- src/packet.hpp | 2 -- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/iyokan-packet.cpp b/src/iyokan-packet.cpp index 2109faf..21e775a 100644 --- a/src/iyokan-packet.cpp +++ b/src/iyokan-packet.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace { enum class TYPE { @@ -336,7 +338,6 @@ int main(int argc, char** argv) toml2packet --in packet.toml --out packet.plain */ - using namespace utility; using namespace std::chrono; error::initialize("iyokan-packet"); diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 8581170..ae17977 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,13 +1,25 @@ #include "iyokan_nt.hpp" #include "error.hpp" -#include "utility.hpp" #include +#include #include namespace nt { +std::vector regexMatch(const std::string& text, + const std::regex& re) +{ + std::vector ret; + std::smatch m; + if (!std::regex_match(text, m, re)) + return ret; + for (auto&& elm : m) + ret.push_back(elm.str()); + return ret; +} + /* class Allocator */ Allocator::Allocator() @@ -134,15 +146,6 @@ Network NetworkBuilder::createNetwork() return Network{std::move(finder_), std::move(tasks_)}; } -void NetworkBuilder::withSubAllocator(const std::string& alcKey, - std::function f) -{ - Allocator* original = currentAlc_; - currentAlc_ = &original->subAllocator(alcKey); - f(*this); - currentAlc_ = original; -} - /* class Worker */ Worker::Worker() : target_(nullptr) @@ -339,8 +342,8 @@ Blueprint::Blueprint(const std::string& fileName) std::string srcTo = srcKey, srcFrom = toml::get(srcValue), - errMsg = utility::fok("Invalid connect: ", srcTo, " = ", - srcFrom); + errMsg = fmt::format("Invalid connect: {} = {}", srcTo, + srcFrom); // Check if input is correct. if (srcTo.empty() || srcFrom.empty() || @@ -413,7 +416,7 @@ std::vector Blueprint::parsePortString(const std::string& src, std::string nodeName, portName; int portBitFrom, portBitTo; - auto match = utility::regexMatch( + auto match = regexMatch( src, std::regex(R"(^@?(?:([^/]+)/)?([^[]+)(?:\[([0-9]+):([0-9]+)\])?$)")); if (match.empty()) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index e903f4b..db9ec72 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -1,10 +1,10 @@ #ifndef VIRTUALSECUREPLATFORM_IYOKAN_NT_HPP #define VIRTUALSECUREPLATFORM_IYOKAN_NT_HPP -#include #include #include -#include + +#include #include #include #include @@ -258,7 +258,6 @@ class Network { void pushReadyTasks(ReadyQueue& readyQueue); void tick(); - void eachTask(std::function handler) const; }; class NetworkBuilder { @@ -290,8 +289,14 @@ class NetworkBuilder { Network createNetwork(); - void withSubAllocator(const std::string& alcKey, - std::function f); + template + void withSubAllocator(const std::string& alcKey, F f) + { + Allocator* original = currentAlc_; + currentAlc_ = &original->subAllocator(alcKey); + f(*this); + currentAlc_ = original; + } virtual void connect(UID from, UID to) = 0; diff --git a/src/packet.hpp b/src/packet.hpp index c9c9847..f828349 100644 --- a/src/packet.hpp +++ b/src/packet.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -17,7 +16,6 @@ #include "tfhepp_cufhe_wrapper.hpp" #include "error.hpp" -#include "utility.hpp" enum class Bit : bool {}; inline constexpr Bit operator~(Bit l) noexcept From 7895376a0291abdfbf1c7f323dbc892aa27890eb Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 21 Sep 2021 18:28:01 +0900 Subject: [PATCH 07/54] Add loguru as a submodule --- .gitmodules | 3 +++ thirdparty/loguru | 1 + 2 files changed, 4 insertions(+) create mode 160000 thirdparty/loguru diff --git a/.gitmodules b/.gitmodules index d11ae3e..c186e0c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "fmt"] path = thirdparty/fmt url = https://github.com/fmtlib/fmt.git +[submodule "thirdparty/loguru"] + path = thirdparty/loguru + url = https://github.com/emilk/loguru.git diff --git a/thirdparty/loguru b/thirdparty/loguru new file mode 160000 index 0000000..323d0eb --- /dev/null +++ b/thirdparty/loguru @@ -0,0 +1 @@ +Subproject commit 323d0eb1b7ba0bda39d9b8494aca456639bfd2d5 From 03fdb5af56aface77a3c8f34c166829f4e40dfcb Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 21 Sep 2021 23:11:48 +0900 Subject: [PATCH 08/54] wip --- CMakeLists.txt | 6 +- src/CMakeLists.txt | 2 +- src/error_nt.cpp | 31 ++++ src/error_nt.hpp | 26 ++++ src/iyokan_nt.cpp | 36 ++--- src/iyokan_nt_plain.cpp | 46 +++--- src/packet_nt.cpp | 317 ++++++++++++++++++++++++++++++++++++++++ src/packet_nt.hpp | 132 +++++++++++++++++ 8 files changed, 559 insertions(+), 37 deletions(-) create mode 100644 src/error_nt.cpp create mode 100644 src/error_nt.hpp create mode 100644 src/packet_nt.cpp create mode 100644 src/packet_nt.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2389283..1e944fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ if (IYOKAN_ENABLE_CUDA) else() add_subdirectory(thirdparty/cuFHE/thirdparties/TFHEpp) endif(IYOKAN_ENABLE_CUDA) + +add_subdirectory(thirdparty/fmt) add_subdirectory(thirdparty/spdlog) set(IYOKAN_CXXFLAGS -Wall -Wextra -Wno-sign-compare) @@ -47,13 +49,13 @@ set(IYOKAN_INCLUDE_DIRS $ $ $ - $ $ + $ ) if (IYOKAN_ENABLE_CUDA) list(APPEND IYOKAN_INCLUDE_DIRS ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) endif(IYOKAN_ENABLE_CUDA) -set(IYOKAN_LIBS tfhe++ Threads::Threads OpenMP::OpenMP_CXX Backward::Backward stdc++fs) +set(IYOKAN_LIBS tfhe++ Threads::Threads OpenMP::OpenMP_CXX Backward::Backward stdc++fs fmt::fmt) if (IYOKAN_80BIT_SECURITY) # For TFHEpp headers list(APPEND IYOKAN_COMPILE_DEFINITIONS USE_80BIT_SECURITY) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1131220..9eda21c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,7 @@ endif(IYOKAN_ENABLE_CUDA) ##### test0 add_executable(test0 test0.cpp iyokan.cpp iyokan_plain.cpp iyokan_tfhepp.cpp error.cpp - iyokan_nt.cpp iyokan_nt_plain.cpp + iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/error_nt.cpp b/src/error_nt.cpp new file mode 100644 index 0000000..607fcdd --- /dev/null +++ b/src/error_nt.cpp @@ -0,0 +1,31 @@ +#include "error_nt.hpp" + +#include + +namespace nt::error { +void initialize() +{ + loguru::g_stderr_verbosity = loguru::Verbosity_INFO; +} + +void abortWithBacktrace() +{ + using namespace backward; + +#ifndef NDEBUG + { + // Print backtrace + LOG_F(ERROR, "Preparing backtrace..."); + StackTrace st; + st.load_here(32); + Printer p; + p.print(st, stderr); + } +#endif + + // Abort + std::exit(EXIT_FAILURE); +} +} // namespace nt::error + +#include diff --git a/src/error_nt.hpp b/src/error_nt.hpp new file mode 100644 index 0000000..02d869a --- /dev/null +++ b/src/error_nt.hpp @@ -0,0 +1,26 @@ +#ifndef VIRTUALSECUREPLATFORM_ERROR_NT_HPP +#define VIRTUALSECUREPLATFORM_ERROR_NT_HPP + +#define LOGURU_WITH_STREAMS 1 +#include + +namespace nt::error { +void initialize(); +[[noreturn]] void abortWithBacktrace(); +} // namespace nt::error + +#define DBG 1 // verbosity debug for loguru + +#define ERROR_DIE(...) \ + { \ + LOG_F(ERROR, __VA_ARGS__); \ + ::nt::error::abortWithBacktrace(); \ + } + +#define ERRDIE(cont) \ + do { \ + LOG_S(ERROR) << cont; \ + nt::error::abortWithBacktrace(); \ + } while (false); + +#endif diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index ae17977..0937b19 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,6 +1,7 @@ #include "iyokan_nt.hpp" -#include "error.hpp" +#include "error_nt.hpp" +#include #include #include @@ -246,7 +247,7 @@ Blueprint::Blueprint(const std::string& fileName) { std::ifstream ifs{fileName}; if (!ifs) - error::die("File not found: ", fileName); + ERROR_DIE("File not found: %s", fileName.c_str()); inputStream << ifs.rdbuf(); source_ = inputStream.str(); inputStream.seekg(std::ios::beg); @@ -274,7 +275,7 @@ Blueprint::Blueprint(const std::string& fileName) else if (typeStr == "yosys-json") type = blueprint::File::TYPE::YOSYS_JSON; else - error::die("Invalid file type: ", typeStr); + ERROR_DIE("Invalid file type: %s", typeStr.c_str()); if (path.is_relative()) path = wd / path; // Make path absolute @@ -328,7 +329,8 @@ Blueprint::Blueprint(const std::string& fileName) auto ary = toml::get>(srcValue); for (const auto& portStr : ary) { // @...[n:m] if (portStr.empty() || portStr.at(0) != '@') - error::die("Invalid port name for TOGND: ", portStr); + ERROR_DIE("Invalid port name for TOGND: %s", + portStr.c_str()); auto ports = parsePortString(portStr, "output"); for (auto&& port : ports) { // @...[n] const std::string& name = port.portName; @@ -348,7 +350,7 @@ Blueprint::Blueprint(const std::string& fileName) // Check if input is correct. if (srcTo.empty() || srcFrom.empty() || (srcTo[0] == '@' && srcFrom[0] == '@')) - error::die(errMsg); + ERROR_DIE("%s", errMsg.c_str()); // Others. std::vector portsTo = @@ -356,7 +358,7 @@ Blueprint::Blueprint(const std::string& fileName) portsFrom = parsePortString(srcFrom, "output"); if (portsTo.size() != portsFrom.size()) - error::die(errMsg); + ERROR_DIE("%s", errMsg.c_str()); for (size_t i = 0; i < portsTo.size(); i++) { const blueprint::Port& to = portsTo[i]; @@ -364,7 +366,7 @@ Blueprint::Blueprint(const std::string& fileName) if (srcTo[0] == '@') { // @... = ... if (!to.nodeName.empty() || from.nodeName.empty()) - error::die(errMsg); + ERROR_DIE("%s", errMsg.c_str()); const std::string& name = to.portName; int bit = to.portBit; @@ -373,10 +375,10 @@ Blueprint::Blueprint(const std::string& fileName) auto [it, inserted] = atPorts_.emplace(std::make_tuple(name, bit), from); if (!inserted) - spdlog::warn( - "{} is used multiple times. Only the first " - "one is effective.", - srcTo); + LOG_S(WARNING) + << srcTo + << " is used multiple times. Only the first " + "one is effective."; } auto [it, inserted] = atPortWidths_.emplace(name, 0); @@ -384,7 +386,7 @@ Blueprint::Blueprint(const std::string& fileName) } else if (srcFrom[0] == '@') { // ... = @... if (!from.nodeName.empty() || to.nodeName.empty()) - error::die(errMsg); + ERROR_DIE("%s", errMsg.c_str()); const std::string& name = from.portName; int bit = from.portBit; @@ -393,10 +395,10 @@ Blueprint::Blueprint(const std::string& fileName) auto [it, inserted] = atPorts_.emplace(std::make_tuple(name, bit), to); if (!inserted) - spdlog::warn( - "{} is used multiple times. Only the first " - "one is effective. (FIXME)", - srcFrom); + LOG_S(WARNING) + << srcFrom + << " is used multiple times. Only the first " + "one is effective. (FIXME)"; } auto [it, inserted] = atPortWidths_.emplace(name, 0); @@ -420,7 +422,7 @@ std::vector Blueprint::parsePortString(const std::string& src, src, std::regex(R"(^@?(?:([^/]+)/)?([^[]+)(?:\[([0-9]+):([0-9]+)\])?$)")); if (match.empty()) - error::die("Invalid port string: ", src); + ERROR_DIE("Invalid port string: %s", src.c_str()); assert(match.size() == 1 + 4); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 377ee39..9875055 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -1,6 +1,15 @@ #include "iyokan_nt_plain.hpp" +#include "error_nt.hpp" #include "iyokan_nt.hpp" -#include "packet.hpp" +#include "packet_nt.hpp" + +template +std::string fok2(T1 t1, T2 t2) +{ + std::stringstream ss; + ss << t1 << t2; + return ss.str(); +} namespace nt { namespace plain { @@ -231,14 +240,15 @@ struct RunParameter { void print() const { - spdlog::info("Run Parameters"); - spdlog::info("\tMode: Plain"); - spdlog::info("\tBlueprint: {}", blueprintFile); - spdlog::info("\t# of CPU workers: {}", numCPUWorkers); - spdlog::info("\t# of cycles: {}", numCycles); - spdlog::info("\tInput file (request packet): {}", inputFile); - spdlog::info("\tOutput file (result packet): {}", outputFile); - spdlog::info("\tSchedule: {}", sched == SCHED::TOPO ? "topo" : "ranku"); + LOG_S(INFO) << "Run parameters"; + LOG_S(INFO) << "\tMode: plain"; + LOG_S(INFO) << "\tBlueprint: " << blueprintFile; + LOG_S(INFO) << "\t# of CPU Workers: " << numCPUWorkers; + LOG_S(INFO) << "\t# of cycles: " << numCycles; + LOG_S(INFO) << "\tInput file (request packet): " << inputFile; + LOG_S(INFO) << "\tOutput file (result packet): " << outputFile; + LOG_S(INFO) << "\tSchedule: " + << (sched == SCHED::TOPO ? "topo" : "ranku"); } }; @@ -272,7 +282,7 @@ class Frontend { Frontend::Frontend(const RunParameter& pr, Allocator& alc) : pr_(pr), runner_(nullptr), - reqPacket_(readFromArchive(pr_.inputFile)), + reqPacket_(readPlainPacket(pr_.inputFile)), currentCycle_(0), bp_(pr_.blueprintFile) { @@ -321,7 +331,10 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) Task* task = nb.finder().findByConfigName( {port.nodeName, port.portName, port.portBit}); if (task->label().kind != port.kind) - error::diefmt("Invalid port: {}/{}[{}] is not {}"); + ERROR_DIE("Invalid port: %s/%s[%d] is %s, not %s", + port.nodeName.c_str(), port.portName.c_str(), + port.portBit, task->label().kind.c_str(), + port.kind.c_str()); return task; }; @@ -365,7 +378,7 @@ void Frontend::makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb) { // FIXME: relax this constraint if (ram.inWdataWidth != ram.outRdataWidth) - error::die( + ERROR_DIE( "Invalid RAM size; RAM that has different sizes of " "wdata and rdata is not implemented."); @@ -377,16 +390,15 @@ void Frontend::makeROM(const blueprint::BuiltinROM& rom, nt::NetworkBuilder& nb) // Create inputs std::vector addrInputs; for (size_t i = 0; i < rom.inAddrWidth; i++) { - UID id = nb.INPUT(fmt::format("addr{}", i), rom.name, "addr", i); + UID id = nb.INPUT(fok2("addr", i), rom.name, "addr", i); addrInputs.push_back(id); } // Create 1bit ROMs for (size_t i = 0; i < rom.outRdataWidth; i++) { - nb.withSubAllocator(fmt::format("rom1bit{}", i), - [&](nt::NetworkBuilder& nb) { - make1bitROMWithMUX(rom, addrInputs, nb); - }); + nb.withSubAllocator(fok2("rom1bit{}", i), [&](nt::NetworkBuilder& nb) { + make1bitROMWithMUX(rom, addrInputs, nb); + }); } } diff --git a/src/packet_nt.cpp b/src/packet_nt.cpp new file mode 100644 index 0000000..24427fc --- /dev/null +++ b/src/packet_nt.cpp @@ -0,0 +1,317 @@ +#include "packet_nt.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +template +void readFromArchive(T& res, std::istream& is) +{ + cereal::PortableBinaryInputArchive ar{is}; + ar(res); +} + +template +void readFromArchive(T& res, const std::string& path) +{ + try { + std::ifstream ifs{path, std::ios::binary}; + if (!ifs) + ERRDIE( + "Can't open the file to read from; Maybe not found?: " << path); + readFromArchive(res, ifs); + } + catch (std::exception& ex) { + ERRDIE("Invalid archive: " << path); + } +} + +template +T readFromArchive(std::istream& is) +{ + T ret; + readFromArchive(ret, is); + return ret; +} + +template +T readFromArchive(const std::string& path) +{ + T ret; + readFromArchive(ret, path); + return ret; +} + +template +void writeToArchive(std::ostream& os, const T& src) +{ + cereal::PortableBinaryOutputArchive ar{os}; + ar(src); +} + +template +void writeToArchive(const std::string& path, const T& src) +{ + try { + std::ofstream ofs{path, std::ios::binary}; + if (!ofs) + ERRDIE("Can't open the file to write in; maybe not allowed?: " + << path); + return writeToArchive(ofs, src); + } + catch (std::exception& ex) { + ERRDIE("Unable to write into archive: " << path << ": " << ex.what()); + } +} + +template +bool isCorrectArchive(const std::string& path) +{ + try { + std::ifstream ifs{path, std::ios::binary}; + if (!ifs) + return false; + T cont; + readFromArchive(cont, ifs); + return true; + } + catch (std::exception& ex) { + return false; + } +} + +} // namespace + +namespace nt { +uint64_t bitvec2i(const std::vector& src, int start, int end) +{ + if (end == -1) + end = src.size(); + assert(end - start < 64); + uint64_t ret = 0; + for (size_t i = start; i < end; i++) + ret |= (static_cast(src.at(i)) << (i - start)); + return ret; +} + +std::vector encryptBits(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + std::vector in; + in.reserve(src.size()); + for (auto&& bit : src) + in.push_back(bit == 1_b ? 1 : 0); + return TFHEpp::bootsSymEncrypt(in, key); +} + +std::vector encryptROM(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + using P = TFHEpp::lvl1param; + std::vector ret; + + PolyLvl1 pmu = {}; + for (size_t i = 0; i < src.size(); i++) { + pmu[i % P::n] = src[i] == 1_b ? P::μ : -P::μ; + if (i % P::n == P::n - 1) + ret.push_back( + TFHEpp::trlweSymEncrypt(pmu, P::α, key.key.lvl1)); + } + if (src.size() % P::n != 0) + ret.push_back(TFHEpp::trlweSymEncrypt(pmu, P::α, key.key.lvl1)); + + return ret; +} + +std::vector encryptROMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + return encryptBits(key, src); +} + +std::vector encryptRAM(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + using P = TFHEpp::lvl1param; + std::vector ret; + + for (auto&& bit : src) { + PolyLvl1 pmu = {}; + pmu[0] = bit == 1_b ? P::μ : -P::μ; + ret.push_back(TFHEpp::trlweSymEncrypt(pmu, P::α, key.key.lvl1)); + } + + return ret; +} + +std::vector encryptRAMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + return encryptBits(key, src); +} + +std::vector decrypt(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + std::vector ret; + for (auto it = src.begin(); it != src.end();) { + uint8_t byte = 0; + for (uint32_t i = 0; i < 8; i++, ++it) { + assert(it != src.end()); + uint8_t val = TFHEpp::bootsSymDecrypt(std::vector{*it}, key).at(0); + byte |= (val & 1u) << i; + } + ret.push_back(byte); + } + return ret; +} + +std::vector decryptBits(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + auto bitvals = TFHEpp::bootsSymDecrypt(src, key); + std::vector bits; + for (auto&& bitval : bitvals) + bits.push_back(bitval != 0 ? 1_b : 0_b); + return bits; +} + +std::vector decryptRAM(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + std::vector ret; + for (auto&& encbit : src) { + uint8_t bitval = + TFHEpp::trlweSymDecrypt(encbit, key.key.lvl1).at(0); + ret.push_back(bitval != 0 ? 1_b : 0_b); + } + + return ret; +} + +std::vector decryptRAMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + return decryptBits(key, src); +} + +std::vector decryptROM(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + std::vector ret; + for (auto&& encblk : src) { + auto blk = TFHEpp::trlweSymDecrypt(encblk, key.key.lvl1); + for (uint8_t bitval : blk) + ret.push_back(bitval != 0 ? 1_b : 0_b); + } + + return ret; +} + +std::vector decryptROMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src) +{ + return decryptBits(key, src); +} + +TFHEPacket PlainPacket::encrypt(const TFHEpp::SecretKey& key) const +{ + TFHEPacket tfhe{{}, {}, {}, {}, {}, numCycles}; + + // Encrypt RAM + for (auto&& [name, src] : ram) { + if (auto [it, inserted] = tfhe.ram.emplace(name, encryptRAM(key, src)); + !inserted) + ERRDIE("Invalid PlainPacket. Duplicate ram's key: " << name); + if (auto [it, inserted] = + tfhe.ramInTLWE.emplace(name, encryptRAMInTLWE(key, src)); + !inserted) + ERRDIE("Invalid PlainPacket. Duplicate ram's key: " << name); + } + + // Encrypt ROM + for (auto&& [name, src] : rom) { + if (auto [it, inserted] = tfhe.rom.emplace(name, encryptROM(key, src)); + !inserted) + ERRDIE("Invalid PlainPacket. Duplicate rom's key: " << name); + if (auto [it, inserted] = + tfhe.romInTLWE.emplace(name, encryptROMInTLWE(key, src)); + !inserted) + ERRDIE("Invalid PlainPacket. Duplicate rom's key: " << name); + } + + // Encrypt bits + for (auto&& [name, src] : bits) { + auto [it, inserted] = tfhe.bits.emplace(name, encryptBits(key, src)); + if (!inserted) + ERRDIE("Invalid PlainPacket. Duplicate bits's key: " << name); + } + + return tfhe; +} + +PlainPacket TFHEPacket::decrypt(const TFHEpp::SecretKey& key) const +{ + PlainPacket plain{{}, {}, {}, numCycles}; + + // Decrypt RAM + for (auto&& [name, trlwes] : ram) + plain.ram.emplace(name, decryptRAM(key, trlwes)); + for (auto&& [name, tlwes] : ramInTLWE) + plain.ram.emplace(name, decryptRAMInTLWE(key, tlwes)); + + // Decrypt ROM + for (auto&& [name, trlwes] : rom) + plain.rom.emplace(name, decryptROM(key, trlwes)); + for (auto&& [name, tlwes] : romInTLWE) + plain.rom.emplace(name, decryptROMInTLWE(key, tlwes)); + + // Decrypt bits + for (auto&& [name, tlwes] : bits) { + auto [it, inserted] = plain.bits.emplace(name, decryptBits(key, tlwes)); + if (!inserted) + ERRDIE("Invalid TFHEPacket. Duplicate bits's key: " << name); + } + + return plain; +} + +PlainPacket readPlainPacket(std::istream& is) +{ + return readFromArchive(is); +} + +PlainPacket readPlainPacket(const std::string& path) +{ + return readFromArchive(path); +} + +TFHEPacket readTFHEPacket(std::istream& is) +{ + return readFromArchive(is); +} + +TFHEPacket readTFHEPacket(const std::string& path) +{ + return readFromArchive(path); +} + +void writePlainPacket(std::ostream& os, const PlainPacket& pkt) +{ + writeToArchive(os, pkt); +} + +void writeTFHEPacket(std::ostream& os, const TFHEPacket& pkt) +{ + writeToArchive(os, pkt); +} + +} // namespace nt diff --git a/src/packet_nt.hpp b/src/packet_nt.hpp new file mode 100644 index 0000000..a27c6f8 --- /dev/null +++ b/src/packet_nt.hpp @@ -0,0 +1,132 @@ +#ifndef VIRTUALSECUREPLATFORM_PACKET_NT_HPP +#define VIRTUALSECUREPLATFORM_PACKET_NT_HPP + +#include "error_nt.hpp" +#include "tfhepp_cufhe_wrapper.hpp" + +#include + +namespace nt { + +enum class Bit : bool {}; +inline constexpr Bit operator~(Bit l) noexcept +{ + return Bit(~static_cast(l)); +} +inline constexpr Bit operator|(Bit l, Bit r) noexcept +{ + return Bit(static_cast(l) | static_cast(r)); +} +inline constexpr Bit operator&(Bit l, Bit r) noexcept +{ + return Bit(static_cast(l) & static_cast(r)); +} +inline constexpr Bit operator^(Bit l, Bit r) noexcept +{ + return Bit(static_cast(l) ^ static_cast(r)); +} +inline constexpr Bit operator|=(Bit& l, Bit r) noexcept +{ + return l = l | r; +} +inline constexpr Bit operator&=(Bit& l, Bit r) noexcept +{ + return l = l & r; +} +inline constexpr Bit operator^=(Bit& l, Bit r) noexcept +{ + return l = l ^ r; +} +inline Bit operator"" _b(unsigned long long x) +{ + return Bit(x != 0); +} + +struct TFHEppBKey { + std::shared_ptr gk; + std::shared_ptr ck; + + TFHEppBKey() + { + } + + TFHEppBKey(const TFHEpp::SecretKey& sk) + : gk(std::make_shared(sk)), + ck(std::make_shared(sk)) + { + } + + template + void serialize(Archive& ar) + { + ar(gk, ck); + } +}; + +struct TFHEPacket; + +struct PlainPacket { + std::unordered_map> ram; + std::unordered_map> rom; + std::unordered_map> bits; + std::optional numCycles; + + template + void serialize(Archive& ar) + { + ar(ram, rom, bits, numCycles); + } + + TFHEPacket encrypt(const TFHEpp::SecretKey& key) const; +}; + +struct TFHEPacket { + std::unordered_map> ram; + std::unordered_map> ramInTLWE; + std::unordered_map> rom; + std::unordered_map> romInTLWE; + std::unordered_map> bits; + std::optional numCycles; + + template + void serialize(Archive& ar) + { + ar(ram, ramInTLWE, rom, romInTLWE, bits, numCycles); + } + + PlainPacket decrypt(const TFHEpp::SecretKey& key) const; +}; + +uint64_t bitvec2i(const std::vector& src, int start = 0, int end = -1); +std::vector encryptBits(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector encryptROM(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector encryptROMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector encryptRAM(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector encryptRAMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector decrypt(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector decryptBits(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector decryptRAM(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector decryptRAMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector decryptROM(const TFHEpp::SecretKey& key, + const std::vector& src); +std::vector decryptROMInTLWE(const TFHEpp::SecretKey& key, + const std::vector& src); +PlainPacket readPlainPacket(std::istream& is); +PlainPacket readPlainPacket(const std::string& path); +TFHEPacket readTFHEPacket(std::istream& is); +TFHEPacket readTFHEPacket(const std::string& path); +void writePlainPacket(std::ostream& os, const PlainPacket& pkt); +void writeTFHEPacket(std::ostream& os, const TFHEPacket& pkt); + +} // namespace nt + +#endif From 871a48ed1d1a164b6b3d0d35f4cf771d213adc7a Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 22 Sep 2021 22:30:46 +0900 Subject: [PATCH 09/54] wip --- src/CMakeLists.txt | 2 +- src/dataholder_nt.cpp | 25 +++++++++++++++++++++++++ src/dataholder_nt.hpp | 33 +++++++++++++++++++++++++++++++++ src/error_nt.cpp | 3 +++ src/iyokan_nt.hpp | 20 +++++++++++++++++++- src/iyokan_nt_plain.cpp | 27 +++++++++++++++------------ 6 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 src/dataholder_nt.cpp create mode 100644 src/dataholder_nt.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9eda21c..dd93729 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,7 @@ endif(IYOKAN_ENABLE_CUDA) ##### test0 add_executable(test0 test0.cpp iyokan.cpp iyokan_plain.cpp iyokan_tfhepp.cpp error.cpp - iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp + iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/dataholder_nt.cpp b/src/dataholder_nt.cpp new file mode 100644 index 0000000..d315a1f --- /dev/null +++ b/src/dataholder_nt.cpp @@ -0,0 +1,25 @@ +#include "dataholder_nt.hpp" + +namespace nt { + +DataHolder::DataHolder() : dataBit_(nullptr), type_(TYPE::UND) +{ +} + +DataHolder::DataHolder(Bit *dataBit) : dataBit_(dataBit), type_(TYPE::BIT) +{ +} + +Bit DataHolder::getBit() const +{ + assert(type_ == TYPE::BIT); + return *dataBit_; +} + +void DataHolder::setBit(Bit *dataBit) +{ + dataBit_ = dataBit; + type_ = TYPE::BIT; +} + +} // namespace nt diff --git a/src/dataholder_nt.hpp b/src/dataholder_nt.hpp new file mode 100644 index 0000000..1e36b1f --- /dev/null +++ b/src/dataholder_nt.hpp @@ -0,0 +1,33 @@ +#ifndef VIRTUALSECUREPLATFORM_DATAHOLDER_NT_HPP +#define VIRTUALSECUREPLATFORM_DATAHOLDER_NT_HPP + +#include + +#include + +namespace nt { + +enum class Bit : bool; + +class DataHolder { +private: + union { + Bit *dataBit_; + }; + + enum class TYPE { + UND, + BIT, + } type_; + +public: + DataHolder(); + DataHolder(Bit *dataBit); + + Bit getBit() const; + void setBit(Bit *dataBit); +}; + +} // namespace nt + +#endif diff --git a/src/error_nt.cpp b/src/error_nt.cpp index 607fcdd..eb10a3f 100644 --- a/src/error_nt.cpp +++ b/src/error_nt.cpp @@ -2,6 +2,8 @@ #include +#include + namespace nt::error { void initialize() { @@ -26,6 +28,7 @@ void abortWithBacktrace() // Abort std::exit(EXIT_FAILURE); } + } // namespace nt::error #include diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index db9ec72..105cfad 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -59,6 +59,9 @@ namespace plain { class WorkerInfo; } +// Data holder for getOutput/setInput +class DataHolder; + class Task { private: Label label_; @@ -67,7 +70,12 @@ class Task { bool hasQueued_; public: - Task(Label label) : label_(label) + Task(Label label) + : label_(label), + parents_(), + children_(), + priority_(0), + hasQueued_(false) { } @@ -128,6 +136,16 @@ class Task { virtual bool hasFinished() const = 0; virtual void tick() = 0; // Reset for next cycle + virtual void getOutput(DataHolder&) + { + assert(0 && "Internal error: unreachable here"); + } + + virtual void setInput(const DataHolder&) + { + assert(0 && "Internal error: unreachable here"); + } + virtual bool canRunPlain() const { return false; diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 9875055..b8bf985 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -1,4 +1,5 @@ #include "iyokan_nt_plain.hpp" +#include "dataholder_nt.hpp" #include "error_nt.hpp" #include "iyokan_nt.hpp" #include "packet_nt.hpp" @@ -55,10 +56,10 @@ class TaskInput : public TaskCommon { return true; } - void setInput(Bit val) + void setInput(const DataHolder& h) override { // Set the input i.e., set the output value of this gate - output() = val; + output() = h.getBit(); } }; @@ -83,9 +84,9 @@ class TaskOutput : public TaskCommon { return true; } - const Bit& getOutput() + void getOutput(DataHolder& h) override { - return output(); + h.setBit(&output()); } }; @@ -416,6 +417,8 @@ void Frontend::make1bitROMWithMUX(const blueprint::BuiltinROM& rom, void test0() { WorkerInfo wi; + DataHolder dh; + Bit /*bit0 = 0_b,*/ bit1 = 1_b; { Allocator root; @@ -426,7 +429,8 @@ void test0() t0.startAsynchronously(wi); t1.startAsynchronously(wi); assert(t0.hasFinished()); - assert(t1.getOutput() == 1_b); + t1.getOutput(dh); + assert(dh.getBit() == 1_b); } { @@ -448,7 +452,8 @@ void test0() t3.startAsynchronously(wi); assert(t0.hasFinished() && t1.hasFinished() && t2.hasFinished() && t3.hasFinished()); - assert(t3.getOutput() == 1_b); + t3.getOutput(dh); + assert(dh.getBit() == 1_b); } { @@ -467,12 +472,9 @@ void test0() Task* t0 = runner.network().finder().findByUID(id0); Task* t1 = runner.network().finder().findByUID(id1); Task* t3 = runner.network().finder().findByUID(id3); - TaskInput *inA = dynamic_cast(t0), - *inB = dynamic_cast(t1); - TaskOutput* out = dynamic_cast(t3); - inA->setInput(1_b); - inB->setInput(1_b); + t0->setInput(&bit1); + t1->setInput(&bit1); runner.prepareToRun(); while (runner.numFinishedTargets() < runner.network().size()) { @@ -480,7 +482,8 @@ void test0() runner.update(); } - assert(out->getOutput() == 0_b); + t3->getOutput(dh); + assert(dh.getBit() == 0_b); } } From 2862388b522ab5b0909e8ce9296bf135b6d11020 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 22 Sep 2021 23:38:34 +0900 Subject: [PATCH 10/54] wip --- src/dataholder_nt.hpp | 1 + src/iyokan_nt.cpp | 92 +++++++++++++++++++- src/iyokan_nt.hpp | 184 ++++++++++++++++++---------------------- src/iyokan_nt_plain.cpp | 183 ++++++++++++++++++++++++--------------- 4 files changed, 291 insertions(+), 169 deletions(-) diff --git a/src/dataholder_nt.hpp b/src/dataholder_nt.hpp index 1e36b1f..8874a34 100644 --- a/src/dataholder_nt.hpp +++ b/src/dataholder_nt.hpp @@ -9,6 +9,7 @@ namespace nt { enum class Bit : bool; +// DataHolder holds data using Task::setInput/Task::getOutput. class DataHolder { private: union { diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 0937b19..f9dd1ac 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -38,6 +38,94 @@ Allocator& Allocator::subAllocator(const std::string& key) return *it->second; } +/* class Task */ + +Task::Task(Label label) + : label_(std::move(label)), + parents_(), + children_(), + priority_(0), + hasQueued_(false) +{ +} + +Task::~Task() +{ +} + +const Label& Task::label() const +{ + return label_; +} + +const std::vector& Task::parents() const +{ + return parents_; +} + +const std::vector& Task::children() const +{ + return children_; +} + +int Task::priority() const +{ + return priority_; +} + +bool Task::hasQueued() const +{ + return hasQueued_; +} + +void Task::addChild(Task* task) +{ + assert(task != nullptr); + children_.push_back(task); +} + +void Task::addParent(Task* task) +{ + assert(task != nullptr); + parents_.push_back(task); +} + +void Task::setPriority(int newPri) +{ + priority_ = newPri; +} + +void Task::setQueued() +{ + assert(!hasQueued_); + hasQueued_ = true; +} + +void Task::tick() +{ + hasQueued_ = false; +} + +void Task::getOutput(DataHolder&) +{ + assert(0 && "Internal error: unreachable here"); +} + +void Task::setInput(const DataHolder&) +{ + assert(0 && "Internal error: unreachable here"); +} + +bool Task::canRunPlain() const +{ + return false; +} + +void Task::startAsynchronously(plain::WorkerInfo&) +{ + assert(0 && "Internal error: not implemented task for plain mode"); +} + /* class TaskFinder */ void TaskFinder::add(Task* task) @@ -172,8 +260,10 @@ void Worker::update(ReadyQueue& readyQueue, size_t& numFinishedTargets) if (target_ != nullptr && target_->hasFinished()) { for (Task* child : target_->children()) { + if (child->hasQueued()) + continue; child->notifyOneInputReady(); - if (!child->hasQueued() && child->areAllInputsReady()) + if (child->areAllInputsReady()) readyQueue.push(child); } target_ = nullptr; diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 105cfad..fcb78eb 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -14,6 +14,12 @@ namespace nt { +// Forward declarations +namespace plain { +class WorkerInfo; +} +class DataHolder; + class Allocator { private: std::unordered_map> subs_; @@ -55,13 +61,6 @@ struct Label { std::optional cname; }; -namespace plain { -class WorkerInfo; -} - -// Data holder for getOutput/setInput -class DataHolder; - class Task { private: Label label_; @@ -70,91 +69,29 @@ class Task { bool hasQueued_; public: - Task(Label label) - : label_(label), - parents_(), - children_(), - priority_(0), - hasQueued_(false) - { - } - - virtual ~Task() - { - } - - const Label& label() const - { - return label_; - } - - const std::vector& parents() const - { - return parents_; - } - - const std::vector& children() const - { - return children_; - } - - int priority() const - { - return priority_; - } - - bool hasQueued() const - { - return hasQueued_; - } - - void addChild(Task* task) - { - assert(task != nullptr); - children_.push_back(task); - } - - void addParent(Task* task) - { - assert(task != nullptr); - parents_.push_back(task); - } - - void setPriority(int newPri) - { - priority_ = newPri; - } - - void setQueued() - { - assert(!hasQueued_); - hasQueued_ = true; - } + Task(Label label); + virtual ~Task(); + + const Label& label() const; + const std::vector& parents() const; + const std::vector& children() const; + int priority() const; + bool hasQueued() const; + void addChild(Task* task); + void addParent(Task* task); + void setPriority(int newPri); + void setQueued(); virtual void notifyOneInputReady() = 0; virtual bool areAllInputsReady() const = 0; virtual bool hasFinished() const = 0; - virtual void tick() = 0; // Reset for next cycle - virtual void getOutput(DataHolder&) - { - assert(0 && "Internal error: unreachable here"); - } - - virtual void setInput(const DataHolder&) - { - assert(0 && "Internal error: unreachable here"); - } - - virtual bool canRunPlain() const - { - return false; - } - - virtual void startAsynchronously(plain::WorkerInfo&) - { - assert(0 && "Internal error: not implemented task for plain mode"); - } + // tick() resets the internal state of the task for the next cycle + virtual void tick(); + virtual void getOutput(DataHolder&); + virtual void setInput(const DataHolder&); + virtual bool canRunPlain() const; + virtual void startAsynchronously(plain::WorkerInfo&); }; class TaskFinder { @@ -218,19 +155,20 @@ class TaskCommon : public Task { { } - void notifyOneInputReady() override + virtual void notifyOneInputReady() override { numReadyInputs_++; assert(numReadyInputs_ <= inputs_.size()); } - bool areAllInputsReady() const override + virtual bool areAllInputsReady() const override { return numReadyInputs_ == inputs_.size(); } - void tick() override + virtual void tick() override { + Task::tick(); numReadyInputs_ = 0; } @@ -252,6 +190,47 @@ class TaskCommon : public Task { } }; +// class TaskDFF can be used as base class of DFF tasks. +// TaskDFF inherits TaskCommon, so it has addInput member functions. +// NetworkBuilder can use it to connect common gates with DFFs. +template +class TaskDFF : public TaskCommon { +public: + TaskDFF(Label label, Allocator& alc) + : TaskCommon(std::move(label), alc, 1) + { + } + + virtual ~TaskDFF() + { + } + + void notifyOneInputReady() override + { + assert(0 && "Internal error: unreachable here"); + } + + bool areAllInputsReady() const override + { + // Since areAllInputsReady() is called after calling of tick(), the + // input should already be in output(). + return true; + } + + bool hasFinished() const override + { + // Since hasFinished() is called after calling of tick(), the + // input should already be in output(). + return true; + } + + void tick() override + { + TaskCommon::tick(); + this->output() = this->input(0); + } +}; + class ReadyQueue { private: std::priority_queue> queue_; @@ -318,24 +297,27 @@ class NetworkBuilder { virtual void connect(UID from, UID to) = 0; - // not/and/or are C++ keywords, so member functions here are in capitals. + // not/and/or are C++ keywords, so the member functions here are in + // capitals. virtual UID INPUT(const std::string& alcKey, const std::string& nodeName, const std::string& portName, int portBit) = 0; virtual UID OUTPUT(const std::string& alcKey, const std::string& nodeName, const std::string& portName, int portBit) = 0; + + virtual UID AND(const std::string& alcKey) = 0; + virtual UID ANDNOT(const std::string& alcKey) = 0; virtual UID CONSTONE(const std::string& alcKey) = 0; virtual UID CONSTZERO(const std::string& alcKey) = 0; - // virtual UID AND(const std::string& alcKey) = 0; - // virtual UID ANDNOT(const std::string& alcKey) = 0; - // virtual UID MUX(const std::string& alcKey) = 0; + virtual UID DFF(const std::string& alcKey) = 0; + virtual UID MUX(const std::string& alcKey) = 0; virtual UID NAND(const std::string& alcKey) = 0; - // virtual UID NMUX(const std::string& alcKey) = 0; - // virtual UID NOR(const std::string& alcKey) = 0; - // virtual UID NOT(const std::string& alcKey) = 0; - // virtual UID OR(const std::string& alcKey) = 0; - // virtual UID ORNOT(const std::string& alcKey) = 0; - // virtual UID XNOR(const std::string& alcKey) = 0; - // virtual UID XOR(const std::string& alcKey) = 0; + virtual UID NMUX(const std::string& alcKey) = 0; + virtual UID NOR(const std::string& alcKey) = 0; + virtual UID NOT(const std::string& alcKey) = 0; + virtual UID OR(const std::string& alcKey) = 0; + virtual UID ORNOT(const std::string& alcKey) = 0; + virtual UID XNOR(const std::string& alcKey) = 0; + virtual UID XOR(const std::string& alcKey) = 0; }; class Worker { diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index b8bf985..d7b559c 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -90,71 +90,59 @@ class TaskOutput : public TaskCommon { } }; -class TaskConstZero : public TaskCommon { +class TaskDFF : public nt::TaskDFF { public: - TaskConstZero(Label label, Allocator& alc) : TaskCommon(label, alc, 0) + TaskDFF(Label label, Allocator& alc) + : nt::TaskDFF(std::move(label), alc) { } - void startAsynchronously(WorkerInfo&) override - { - output() = 0_b; - } - - bool hasFinished() const override - { - return true; - } - bool canRunPlain() const override { return true; } -}; - -class TaskConstOne : public TaskCommon { -public: - TaskConstOne(Label label, Allocator& alc) : TaskCommon(label, alc, 0) - { - } void startAsynchronously(WorkerInfo&) override { - output() = 1_b; - } - - bool hasFinished() const override - { - return true; - } - - bool canRunPlain() const override - { - return true; + // Nothing to do, because the main process is done in + // nt::TaskDFF::tick(). } }; -class TaskNand : public TaskCommon { -public: - TaskNand(Label label, Allocator& alc) : TaskCommon(label, alc, 2) - { - } - - void startAsynchronously(WorkerInfo&) override - { - output() = ~(input(0) & input(1)); - } - - bool hasFinished() const override - { - return true; - } - - bool canRunPlain() const override - { - return true; - } -}; +#define DEF_COMMON_TASK_CLASS(CamelName, inputSize, expr) \ + class Task##CamelName : public TaskCommon { \ + public: \ + Task##CamelName(Label label, Allocator& alc) \ + : TaskCommon(std::move(label), alc, inputSize) \ + { \ + } \ + void startAsynchronously(WorkerInfo&) override \ + { \ + output() = (expr); \ + } \ + bool hasFinished() const override \ + { \ + return true; \ + } \ + bool canRunPlain() const override \ + { \ + return true; \ + } \ + }; +DEF_COMMON_TASK_CLASS(And, 2, (input(0) & input(1))); +DEF_COMMON_TASK_CLASS(Andnot, 2, (input(0) & ~input(1))); +DEF_COMMON_TASK_CLASS(ConstOne, 0, 1_b); +DEF_COMMON_TASK_CLASS(ConstZero, 0, 0_b); +DEF_COMMON_TASK_CLASS(Mux, 3, input(2) == 0_b ? input(0) : input(1)); +DEF_COMMON_TASK_CLASS(Nand, 2, ~(input(0) & input(1))); +DEF_COMMON_TASK_CLASS(Nmux, 3, input(2) == 0_b ? ~input(0) : ~input(1)); +DEF_COMMON_TASK_CLASS(Nor, 2, ~(input(0) | input(1))); +DEF_COMMON_TASK_CLASS(Not, 1, ~input(0)); +DEF_COMMON_TASK_CLASS(Or, 2, (input(0) | input(1))); +DEF_COMMON_TASK_CLASS(Ornot, 2, (input(0) | ~input(1))); +DEF_COMMON_TASK_CLASS(Xnor, 2, ~(input(0) ^ input(1))); +DEF_COMMON_TASK_CLASS(Xor, 2, (input(0) ^ input(1))); +#undef DEF_COMMON_TASK_CLASS class NetworkBuilder : public nt::NetworkBuilder { private: @@ -183,21 +171,32 @@ class NetworkBuilder : public nt::NetworkBuilder { to->addInput(from); } -#define DEF_COMMON_TASK(TaskType, capName, camelName) \ - UID capName(const std::string& alcKey) override \ - { \ - UID uid = genUID(); \ - TaskType* task = nullptr; \ - this->withSubAllocator(alcKey, [&](auto&&) { \ - task = emplaceTask(Label{uid, #camelName, std::nullopt}, \ - currentAllocator()); \ - }); \ - uid2common_.emplace(uid, task); \ - return uid; \ - } - DEF_COMMON_TASK(TaskConstOne, CONSTONE, ConstOne); - DEF_COMMON_TASK(TaskConstZero, CONSTZERO, ConstZero); - DEF_COMMON_TASK(TaskNand, NAND, Nand); +#define DEF_COMMON_TASK(CAPName, CamelName) \ + UID CAPName(const std::string& alcKey) override \ + { \ + UID uid = genUID(); \ + Task##CamelName* task = nullptr; \ + this->withSubAllocator(alcKey, [&](auto&&) { \ + task = emplaceTask( \ + Label{uid, #CamelName, std::nullopt}, currentAllocator()); \ + }); \ + uid2common_.emplace(uid, task); \ + return uid; \ + } + DEF_COMMON_TASK(AND, And); + DEF_COMMON_TASK(ANDNOT, Andnot); + DEF_COMMON_TASK(CONSTONE, ConstOne); + DEF_COMMON_TASK(CONSTZERO, ConstZero); + DEF_COMMON_TASK(DFF, DFF); + DEF_COMMON_TASK(MUX, Mux); + DEF_COMMON_TASK(NAND, Nand); + DEF_COMMON_TASK(NMUX, Nmux); + DEF_COMMON_TASK(NOR, Nor); + DEF_COMMON_TASK(NOT, Not); + DEF_COMMON_TASK(OR, Or); + DEF_COMMON_TASK(ORNOT, Ornot); + DEF_COMMON_TASK(XNOR, Xnor); + DEF_COMMON_TASK(XOR, Xor); #undef DEF_COMMON_TASK UID INPUT(const std::string& alcKey, const std::string& nodeName, @@ -418,7 +417,7 @@ void test0() { WorkerInfo wi; DataHolder dh; - Bit /*bit0 = 0_b,*/ bit1 = 1_b; + Bit bit0 = 0_b, bit1 = 1_b; { Allocator root; @@ -485,6 +484,56 @@ void test0() t3->getOutput(dh); assert(dh.getBit() == 0_b); } + + { + /* + B D + reset(0) >---> ANDNOT(4) >---> DFF(2) + ^ A v Q + | | + *--< NOT(3) <--*-----> OUTPUT(1) + A + */ + Allocator root; + NetworkBuilder nb{root}; + UID id0 = nb.INPUT("0", "", "reset", 0), + id1 = nb.OUTPUT("1", "", "out", 0), id2 = nb.DFF("2"), + id3 = nb.NOT("3"), id4 = nb.ANDNOT("4"); + nb.connect(id2, id1); + nb.connect(id4, id2); + nb.connect(id2, id3); + nb.connect(id3, id4); + nb.connect(id0, id4); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + Task* t0 = runner.network().finder().findByUID(id0); + Task* t1 = runner.network().finder().findByUID(id1); + + auto run = [&] { + runner.prepareToRun(); + while (runner.numFinishedTargets() < runner.network().size()) { + assert(runner.isRunning()); + runner.update(); + } + }; + + t0->setInput(&bit1); + run(); + t0->setInput(&bit0); + + runner.tick(); + run(); + t1->getOutput(dh); + assert(dh.getBit() == 0_b); + + runner.tick(); + run(); + t1->getOutput(dh); + assert(dh.getBit() == 1_b); + } } } // namespace plain From 83302a3f295a10c2b746ccb54e37e5a6c99d094e Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Thu, 23 Sep 2021 00:00:35 +0900 Subject: [PATCH 11/54] wip --- src/error_nt.hpp | 10 +++------- src/iyokan_nt.cpp | 25 ++++++++++++------------- src/iyokan_nt.hpp | 4 +++- src/iyokan_nt_plain.cpp | 11 +++++------ src/packet_nt.cpp | 22 +++++++++++----------- 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/error_nt.hpp b/src/error_nt.hpp index 02d869a..5d1fe8c 100644 --- a/src/error_nt.hpp +++ b/src/error_nt.hpp @@ -11,16 +11,12 @@ void initialize(); #define DBG 1 // verbosity debug for loguru -#define ERROR_DIE(...) \ - { \ - LOG_F(ERROR, __VA_ARGS__); \ - ::nt::error::abortWithBacktrace(); \ - } - -#define ERRDIE(cont) \ +#define ERR_DIE(cont) \ do { \ LOG_S(ERROR) << cont; \ nt::error::abortWithBacktrace(); \ } while (false); +#define ERR_UNREACHABLE ERR_DIE("Internal error: unreachable here") + #endif diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index f9dd1ac..6f6ba44 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -2,7 +2,7 @@ #include "error_nt.hpp" #include -#include +#include // FIXME: this makes compilation slow #include #include @@ -108,12 +108,12 @@ void Task::tick() void Task::getOutput(DataHolder&) { - assert(0 && "Internal error: unreachable here"); + ERR_UNREACHABLE; } void Task::setInput(const DataHolder&) { - assert(0 && "Internal error: unreachable here"); + ERR_UNREACHABLE; } bool Task::canRunPlain() const @@ -123,7 +123,7 @@ bool Task::canRunPlain() const void Task::startAsynchronously(plain::WorkerInfo&) { - assert(0 && "Internal error: not implemented task for plain mode"); + ERR_UNREACHABLE; } /* class TaskFinder */ @@ -337,7 +337,7 @@ Blueprint::Blueprint(const std::string& fileName) { std::ifstream ifs{fileName}; if (!ifs) - ERROR_DIE("File not found: %s", fileName.c_str()); + ERR_DIE("File not found: " << fileName); inputStream << ifs.rdbuf(); source_ = inputStream.str(); inputStream.seekg(std::ios::beg); @@ -365,7 +365,7 @@ Blueprint::Blueprint(const std::string& fileName) else if (typeStr == "yosys-json") type = blueprint::File::TYPE::YOSYS_JSON; else - ERROR_DIE("Invalid file type: %s", typeStr.c_str()); + ERR_DIE("Invalid file type: " << typeStr); if (path.is_relative()) path = wd / path; // Make path absolute @@ -419,8 +419,7 @@ Blueprint::Blueprint(const std::string& fileName) auto ary = toml::get>(srcValue); for (const auto& portStr : ary) { // @...[n:m] if (portStr.empty() || portStr.at(0) != '@') - ERROR_DIE("Invalid port name for TOGND: %s", - portStr.c_str()); + ERR_DIE("Invalid port name for TOGND: " << portStr); auto ports = parsePortString(portStr, "output"); for (auto&& port : ports) { // @...[n] const std::string& name = port.portName; @@ -440,7 +439,7 @@ Blueprint::Blueprint(const std::string& fileName) // Check if input is correct. if (srcTo.empty() || srcFrom.empty() || (srcTo[0] == '@' && srcFrom[0] == '@')) - ERROR_DIE("%s", errMsg.c_str()); + ERR_DIE(errMsg); // Others. std::vector portsTo = @@ -448,7 +447,7 @@ Blueprint::Blueprint(const std::string& fileName) portsFrom = parsePortString(srcFrom, "output"); if (portsTo.size() != portsFrom.size()) - ERROR_DIE("%s", errMsg.c_str()); + ERR_DIE(errMsg); for (size_t i = 0; i < portsTo.size(); i++) { const blueprint::Port& to = portsTo[i]; @@ -456,7 +455,7 @@ Blueprint::Blueprint(const std::string& fileName) if (srcTo[0] == '@') { // @... = ... if (!to.nodeName.empty() || from.nodeName.empty()) - ERROR_DIE("%s", errMsg.c_str()); + ERR_DIE(errMsg); const std::string& name = to.portName; int bit = to.portBit; @@ -476,7 +475,7 @@ Blueprint::Blueprint(const std::string& fileName) } else if (srcFrom[0] == '@') { // ... = @... if (!from.nodeName.empty() || to.nodeName.empty()) - ERROR_DIE("%s", errMsg.c_str()); + ERR_DIE(errMsg); const std::string& name = from.portName; int bit = from.portBit; @@ -512,7 +511,7 @@ std::vector Blueprint::parsePortString(const std::string& src, src, std::regex(R"(^@?(?:([^/]+)/)?([^[]+)(?:\[([0-9]+):([0-9]+)\])?$)")); if (match.empty()) - ERROR_DIE("Invalid port string: %s", src.c_str()); + ERR_DIE("Invalid port string: " << src); assert(match.size() == 1 + 4); diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index fcb78eb..2ce14a0 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -12,6 +12,8 @@ #include #include +#include "error_nt.hpp" + namespace nt { // Forward declarations @@ -207,7 +209,7 @@ class TaskDFF : public TaskCommon { void notifyOneInputReady() override { - assert(0 && "Internal error: unreachable here"); + ERR_UNREACHABLE; } bool areAllInputsReady() const override diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index d7b559c..308de04 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -1,6 +1,5 @@ #include "iyokan_nt_plain.hpp" #include "dataholder_nt.hpp" -#include "error_nt.hpp" #include "iyokan_nt.hpp" #include "packet_nt.hpp" @@ -331,10 +330,10 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) Task* task = nb.finder().findByConfigName( {port.nodeName, port.portName, port.portBit}); if (task->label().kind != port.kind) - ERROR_DIE("Invalid port: %s/%s[%d] is %s, not %s", - port.nodeName.c_str(), port.portName.c_str(), - port.portBit, task->label().kind.c_str(), - port.kind.c_str()); + ERR_DIE("Invalid port: " << port.nodeName << "/" << port.portName + << "[" << port.portBit << "] is " + << task->label().kind << ", not " + << port.kind); return task; }; @@ -378,7 +377,7 @@ void Frontend::makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb) { // FIXME: relax this constraint if (ram.inWdataWidth != ram.outRdataWidth) - ERROR_DIE( + ERR_DIE( "Invalid RAM size; RAM that has different sizes of " "wdata and rdata is not implemented."); diff --git a/src/packet_nt.cpp b/src/packet_nt.cpp index 24427fc..2f96b03 100644 --- a/src/packet_nt.cpp +++ b/src/packet_nt.cpp @@ -25,12 +25,12 @@ void readFromArchive(T& res, const std::string& path) try { std::ifstream ifs{path, std::ios::binary}; if (!ifs) - ERRDIE( + ERR_DIE( "Can't open the file to read from; Maybe not found?: " << path); readFromArchive(res, ifs); } catch (std::exception& ex) { - ERRDIE("Invalid archive: " << path); + ERR_DIE("Invalid archive: " << path); } } @@ -63,12 +63,12 @@ void writeToArchive(const std::string& path, const T& src) try { std::ofstream ofs{path, std::ios::binary}; if (!ofs) - ERRDIE("Can't open the file to write in; maybe not allowed?: " - << path); + ERR_DIE("Can't open the file to write in; maybe not allowed?: " + << path); return writeToArchive(ofs, src); } catch (std::exception& ex) { - ERRDIE("Unable to write into archive: " << path << ": " << ex.what()); + ERR_DIE("Unable to write into archive: " << path << ": " << ex.what()); } } @@ -230,29 +230,29 @@ TFHEPacket PlainPacket::encrypt(const TFHEpp::SecretKey& key) const for (auto&& [name, src] : ram) { if (auto [it, inserted] = tfhe.ram.emplace(name, encryptRAM(key, src)); !inserted) - ERRDIE("Invalid PlainPacket. Duplicate ram's key: " << name); + ERR_DIE("Invalid PlainPacket. Duplicate ram's key: " << name); if (auto [it, inserted] = tfhe.ramInTLWE.emplace(name, encryptRAMInTLWE(key, src)); !inserted) - ERRDIE("Invalid PlainPacket. Duplicate ram's key: " << name); + ERR_DIE("Invalid PlainPacket. Duplicate ram's key: " << name); } // Encrypt ROM for (auto&& [name, src] : rom) { if (auto [it, inserted] = tfhe.rom.emplace(name, encryptROM(key, src)); !inserted) - ERRDIE("Invalid PlainPacket. Duplicate rom's key: " << name); + ERR_DIE("Invalid PlainPacket. Duplicate rom's key: " << name); if (auto [it, inserted] = tfhe.romInTLWE.emplace(name, encryptROMInTLWE(key, src)); !inserted) - ERRDIE("Invalid PlainPacket. Duplicate rom's key: " << name); + ERR_DIE("Invalid PlainPacket. Duplicate rom's key: " << name); } // Encrypt bits for (auto&& [name, src] : bits) { auto [it, inserted] = tfhe.bits.emplace(name, encryptBits(key, src)); if (!inserted) - ERRDIE("Invalid PlainPacket. Duplicate bits's key: " << name); + ERR_DIE("Invalid PlainPacket. Duplicate bits's key: " << name); } return tfhe; @@ -278,7 +278,7 @@ PlainPacket TFHEPacket::decrypt(const TFHEpp::SecretKey& key) const for (auto&& [name, tlwes] : bits) { auto [it, inserted] = plain.bits.emplace(name, decryptBits(key, tlwes)); if (!inserted) - ERRDIE("Invalid TFHEPacket. Duplicate bits's key: " << name); + ERR_DIE("Invalid TFHEPacket. Duplicate bits's key: " << name); } return plain; From 83fb71eb312a3dafac7417ff8f16564d195fa6a6 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Thu, 23 Sep 2021 00:05:09 +0900 Subject: [PATCH 12/54] wip --- src/iyokan_nt.cpp | 31 ++++++++++++++++++++----------- src/iyokan_nt.hpp | 7 +++++-- src/iyokan_nt_plain.cpp | 22 ++++------------------ 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 6f6ba44..8a6b70b 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -290,22 +290,28 @@ NetworkRunner::NetworkRunner(Network network, assert(w != nullptr); } -const Network& NetworkRunner::network() const +void NetworkRunner::prepareToRun() { - return network_; + assert(readyQueue_.empty()); + + numFinishedTargets_ = 0; + network_.pushReadyTasks(readyQueue_); } -size_t NetworkRunner::numFinishedTargets() const +void NetworkRunner::update() { - return numFinishedTargets_; + for (auto&& w : workers_) + w->update(readyQueue_, numFinishedTargets_); } -void NetworkRunner::prepareToRun() +const Network& NetworkRunner::network() const { - assert(readyQueue_.empty()); + return network_; +} - numFinishedTargets_ = 0; - network_.pushReadyTasks(readyQueue_); +size_t NetworkRunner::numFinishedTargets() const +{ + return numFinishedTargets_; } bool NetworkRunner::isRunning() const @@ -315,10 +321,13 @@ bool NetworkRunner::isRunning() const !readyQueue_.empty(); } -void NetworkRunner::update() +void NetworkRunner::run() { - for (auto&& w : workers_) - w->update(readyQueue_, numFinishedTargets_); + prepareToRun(); + while (numFinishedTargets() < network().size()) { + assert(isRunning() && "Invalid network: maybe some unreachable tasks?"); + update(); + } } void NetworkRunner::tick() diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 2ce14a0..aa22917 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -345,15 +345,18 @@ class NetworkRunner { ReadyQueue readyQueue_; size_t numFinishedTargets_; +private: + void prepareToRun(); + void update(); + public: NetworkRunner(Network network, std::vector> workers); const Network& network() const; size_t numFinishedTargets() const; - void prepareToRun(); bool isRunning() const; - void update(); + void run(); void tick(); }; diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 308de04..b6a563c 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -473,13 +473,7 @@ void test0() t0->setInput(&bit1); t1->setInput(&bit1); - - runner.prepareToRun(); - while (runner.numFinishedTargets() < runner.network().size()) { - assert(runner.isRunning()); - runner.update(); - } - + runner.run(); t3->getOutput(dh); assert(dh.getBit() == 0_b); } @@ -511,25 +505,17 @@ void test0() Task* t0 = runner.network().finder().findByUID(id0); Task* t1 = runner.network().finder().findByUID(id1); - auto run = [&] { - runner.prepareToRun(); - while (runner.numFinishedTargets() < runner.network().size()) { - assert(runner.isRunning()); - runner.update(); - } - }; - t0->setInput(&bit1); - run(); + runner.run(); t0->setInput(&bit0); runner.tick(); - run(); + runner.run(); t1->getOutput(dh); assert(dh.getBit() == 0_b); runner.tick(); - run(); + runner.run(); t1->getOutput(dh); assert(dh.getBit() == 1_b); } From a51aa2901e73dee43ac599c81d3230a94d582f0c Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Thu, 23 Sep 2021 00:44:26 +0900 Subject: [PATCH 13/54] Bump thirdparty/cuFHE from afbd970f37cb2f3d0b658b9dad50462dbfcb8114 to 491b90eb8e9e3063622878e9e61fcfd5ea87b5b5 --- thirdparty/cuFHE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/cuFHE b/thirdparty/cuFHE index afbd970..491b90e 160000 --- a/thirdparty/cuFHE +++ b/thirdparty/cuFHE @@ -1 +1 @@ -Subproject commit afbd970f37cb2f3d0b658b9dad50462dbfcb8114 +Subproject commit 491b90eb8e9e3063622878e9e61fcfd5ea87b5b5 From cbcbefecc049762393ba76a2fbb48cbd30c6c0a3 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Fri, 29 Oct 2021 21:05:22 +0900 Subject: [PATCH 14/54] wip --- src/iyokan_nt.cpp | 436 ++++++++++++++++++++++++++++++++++++++++ src/iyokan_nt.hpp | 3 + src/iyokan_nt_plain.cpp | 22 +- thirdparty/cuFHE | 2 +- 4 files changed, 461 insertions(+), 2 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 8a6b70b..b521c0a 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -2,6 +2,7 @@ #include "error_nt.hpp" #include +#include #include // FIXME: this makes compilation slow #include @@ -606,6 +607,441 @@ const std::unordered_map& Blueprint::atPortWidths() const return atPortWidths_; } +///* class YosysJSONReader */ +// +// namespace { +// +// class YosysJSONReader { +// private: +// enum class PORT { +// IN, +// OUT, +// }; +// +// struct Port { +// PORT type; +// int id, bit; +// +// Port(PORT type, int id, int bit) : type(type), id(id), bit(bit) +// { +// } +// }; +// +// enum class CELL { +// NOT, +// AND, +// ANDNOT, +// NAND, +// OR, +// XOR, +// XNOR, +// NOR, +// ORNOT, +// DFFP, +// SDFFPP0, +// SDFFPP1, +// MUX, +// }; +// +// struct Cell { +// CELL type; +// int id, bit0, bit1, bit2; +// +// Cell(CELL type, int id, int bit0) +// : type(type), id(id), bit0(bit0), bit1(-1), bit2(-1) +// { +// } +// Cell(CELL type, int id, int bit0, int bit1) +// : type(type), id(id), bit0(bit0), bit1(bit1), bit2(-1) +// { +// } +// Cell(CELL type, int id, int bit0, int bit1, int bit2) +// : type(type), id(id), bit0(bit0), bit1(bit1), bit2(bit2) +// { +// } +// }; +// +// private: +// static int getConnBit(const picojson::object& conn, const std::string& +// key) +// { +// using namespace picojson; +// const auto& bits = conn.at(key).get(); +// if (bits.size() != 1) +// ERR_DIE("Invalid JSON: wrong conn size: expected 1, got " +// << bits.size()); +// if (!bits.at(0).is()) +// ERR_DIE( +// "Connection of cells to a constant driver is not +// implemented."); +// return bits.at(0).get(); +// } +// +// public: +// template +// static void read(NetworkBuilder& builder, std::istream& is) +// { +// // Convert Yosys JSON to gates. Thanks to: +// // +// https://github.com/virtualsecureplatform/Iyokan-L1/blob/ef7c9a993ddbfd54ef58e66b116b681e59d90a3c/Converter/YosysConverter.cs +// using namespace picojson; +// +// value v; +// const std::string err = parse(v, is); +// if (!err.empty()) +// ERR_DIE("Invalid JSON of network: " << err); +// +// object& root = v.get(); +// object& modules = root.at("modules").get(); +// if (modules.size() != 1) +// ERR_DIE(".modules should be an object of size 1"); +// object& modul = modules.begin()->second.get(); +// object& ports = modul.at("ports").get(); +// object& cells = modul.at("cells").get(); +// +// std::unordered_map bit2id; +// +// // Create INPUT/OUTPUT and extract port connection info +// std::vector portvec; +// for (auto&& [key, valAny] : ports) { +// object& val = valAny.template get(); +// std::string& direction = val["direction"].get(); +// array& bits = val["bits"].get(); +// +// if (key == "clock") +// continue; +// if (key == "reset" && bits.size() == 0) +// continue; +// if (direction != "input" && direction != "output") +// ERR_DIE("Invalid direction token: " << direction); +// +// const bool isDirInput = direction == "input"; +// const std::string& portName = key; +// for (size_t i = 0; i < bits.size(); i++) { +// const int portBit = i; +// +// if (bits.at(i).is()) { +// // Yosys document +// // (https://yosyshq.net/yosys/cmd_write_json.html) says: +// // +// // Signal bits that are connected to a constant driver +// // are denoted as string "0" or "1" instead of a +// number. +// // +// // We handle this case here. +// +// if (isDirInput) +// ERR_DIE( +// "Invalid bits: INPUT that is connected to a " +// "constant driver is not implemented"); +// +// std::string cnstStr = bits.at(i).get(); +// bool cnst = cnstStr == "1"; +// if (!cnst && cnstStr != "0") +// LOG_S(WARNING) +// << "Constant bit of '{}' is regarded as '0'." +// << cnstStr; +// +// int id1 = builder.OUTPUT(portName, portBit), +// id0 = cnst ? builder.CONSTONE() : builder.CONSTZERO(); +// builder.connect(id0, id1); +// } +// else { +// const int bit = bits.at(i).get(); +// +// int id = isDirInput ? builder.INPUT(portName, portBit) +// : builder.OUTPUT(portName, portBit); +// portvec.emplace_back(isDirInput ? PORT::IN : PORT::OUT, +// id, +// bit); +// if (isDirInput) +// bit2id.emplace(bit, id); +// } +// } +// } +// +// // Create gates and extract gate connection info +// const std::unordered_map mapCell = { +// {"$_NOT_", CELL::NOT}, +// {"$_AND_", CELL::AND}, +// {"$_ANDNOT_", CELL::ANDNOT}, +// {"$_NAND_", CELL::NAND}, +// {"$_OR_", CELL::OR}, +// {"$_XOR_", CELL::XOR}, +// {"$_XNOR_", CELL::XNOR}, +// {"$_NOR_", CELL::NOR}, +// {"$_ORNOT_", CELL::ORNOT}, +// {"$_DFF_P_", CELL::DFFP}, +// {"$_SDFF_PP0_", CELL::SDFFPP0}, +// {"$_SDFF_PP1_", CELL::SDFFPP1}, +// {"$_MUX_", CELL::MUX}, +// }; +// std::vector cellvec; +// for (auto&& [_key, valAny] : cells) { +// object& val = valAny.template get(); +// const std::string& type = val.at("type").get(); +// object& conn = val.at("connections").get(); +// auto get = [&](const char* key) -> int { +// return getConnBit(conn, key); +// }; +// +// int bit = -1, id = -1; +// switch (mapCell.at(type)) { +// case CELL::AND: +// id = builder.AND(); +// cellvec.emplace_back(CELL::AND, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::NAND: +// id = builder.NAND(); +// cellvec.emplace_back(CELL::NAND, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::XOR: +// id = builder.XOR(); +// cellvec.emplace_back(CELL::XOR, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::XNOR: +// id = builder.XNOR(); +// cellvec.emplace_back(CELL::XNOR, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::NOR: +// id = builder.NOR(); +// cellvec.emplace_back(CELL::NOR, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::ANDNOT: +// id = builder.ANDNOT(); +// cellvec.emplace_back(CELL::ANDNOT, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::OR: +// id = builder.OR(); +// cellvec.emplace_back(CELL::OR, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::ORNOT: +// id = builder.ORNOT(); +// cellvec.emplace_back(CELL::ORNOT, id, get("A"), get("B")); +// bit = get("Y"); +// break; +// case CELL::DFFP: +// id = builder.DFF(); +// cellvec.emplace_back(CELL::DFFP, id, get("D")); +// bit = get("Q"); +// break; +// case CELL::SDFFPP0: +// id = builder.SDFF(Bit(false)); +// cellvec.emplace_back(CELL::DFFP, id, get("D")); +// bit = get("Q"); +// break; +// case CELL::SDFFPP1: +// id = builder.SDFF(Bit(true)); +// cellvec.emplace_back(CELL::DFFP, id, get("D")); +// bit = get("Q"); +// break; +// case CELL::NOT: +// id = builder.NOT(); +// cellvec.emplace_back(CELL::NOT, id, get("A")); +// bit = get("Y"); +// break; +// case CELL::MUX: +// id = builder.MUX(); +// cellvec.emplace_back(CELL::MUX, id, get("A"), get("B"), +// get("S")); +// bit = get("Y"); +// break; +// } +// bit2id.emplace(bit, id); +// } +// +// for (auto&& port : portvec) { +// if (port.type == PORT::IN) +// // Actually nothing to do! +// continue; +// builder.connect(bit2id.at(port.bit), port.id); +// } +// +// for (auto&& cell : cellvec) { +// switch (cell.type) { +// case CELL::AND: +// case CELL::NAND: +// case CELL::XOR: +// case CELL::XNOR: +// case CELL::NOR: +// case CELL::ANDNOT: +// case CELL::OR: +// case CELL::ORNOT: +// builder.connect(bit2id.at(cell.bit0), cell.id); +// builder.connect(bit2id.at(cell.bit1), cell.id); +// break; +// case CELL::DFFP: +// case CELL::SDFFPP0: +// case CELL::SDFFPP1: +// case CELL::NOT: +// builder.connect(bit2id.at(cell.bit0), cell.id); +// break; +// case CELL::MUX: +// builder.connect(bit2id.at(cell.bit0), cell.id); +// builder.connect(bit2id.at(cell.bit1), cell.id); +// builder.connect(bit2id.at(cell.bit2), cell.id); +// break; +// } +// } +// } +//}; +// +//} // namespace + +void readYosysJSONNetwork(std::istream& is, NetworkBuilder& nb) +{ + // YosysJSONReader::read(nb, is); +} + +///* class IyokanL1JSONReader */ +// +// namespace { +// +// class IyokanL1JSONReader { +// public: +// template +// static void read(NetworkBuilder& builder, std::istream& is) +// { +// std::unordered_map id2taskId; +// auto addId = [&](int id, int taskId) { id2taskId.emplace(id, taskId); +// }; auto findTaskId = [&](int id) { +// auto it = id2taskId.find(id); +// if (it == id2taskId.end()) +// error::die("Invalid JSON"); +// return it->second; +// }; +// auto connectIds = [&](int from, int to) { +// builder.connect(findTaskId(from), findTaskId(to)); +// }; +// +// picojson::value v; +// const std::string err = picojson::parse(v, is); +// if (!err.empty()) +// error::die("Invalid JSON of network: ", err); +// +// picojson::object& obj = v.get(); +// picojson::array& cells = obj["cells"].get(); +// picojson::array& ports = obj["ports"].get(); +// for (const auto& e : ports) { +// picojson::object port = e.get(); +// std::string type = port.at("type").get(); +// int id = static_cast(port.at("id").get()); +// std::string portName = port.at("portName").get(); +// int portBit = static_cast(port.at("portBit").get()); +// if (type == "input") +// addId(id, builder.INPUT(portName, portBit)); +// else if (type == "output") +// addId(id, builder.OUTPUT(portName, portBit)); +// } +// for (const auto& e : cells) { +// picojson::object cell = e.get(); +// std::string type = cell.at("type").get(); +// int id = static_cast(cell.at("id").get()); +// if (type == "AND") +// addId(id, builder.AND()); +// else if (type == "NAND") +// addId(id, builder.NAND()); +// else if (type == "ANDNOT") +// addId(id, builder.ANDNOT()); +// else if (type == "XOR") +// addId(id, builder.XOR()); +// else if (type == "XNOR") +// addId(id, builder.XNOR()); +// else if (type == "DFFP") +// addId(id, builder.DFF()); +// else if (type == "NOT") +// addId(id, builder.NOT()); +// else if (type == "NOR") +// addId(id, builder.NOR()); +// else if (type == "OR") +// addId(id, builder.OR()); +// else if (type == "ORNOT") +// addId(id, builder.ORNOT()); +// else if (type == "MUX") +// addId(id, builder.MUX()); +// else { +// bool valid = false; +// // If builder.RAM() exists +// if constexpr (detail::hasMethodFuncRAM) { +// if (type == "RAM") { +// int addr = cell.at("ramAddress").get(), +// bit = cell.at("ramBit").get(); +// addId(id, builder.RAM(addr, bit)); +// valid = true; +// } +// } +// +// if (!valid) { +// error::die("Invalid JSON of network. Invalid type: ", +// type); +// } +// } +// } +// for (const auto& e : ports) { +// picojson::object port = e.get(); +// std::string type = port.at("type").get(); +// int id = static_cast(port.at("id").get()); +// picojson::array& bits = port.at("bits").get(); +// if (type == "input") { +// // nothing to do! +// } +// else if (type == "output") { +// for (const auto& b : bits) { +// int logic = static_cast(b.get()); +// connectIds(logic, id); +// } +// } +// } +// for (const auto& e : cells) { +// picojson::object cell = e.get(); +// std::string type = cell.at("type").get(); +// int id = static_cast(cell.at("id").get()); +// picojson::object input = cell.at("input").get(); +// if (type == "AND" || type == "NAND" || type == "XOR" || +// type == "XNOR" || type == "NOR" || type == "ANDNOT" || +// type == "OR" || type == "ORNOT") { +// int A = static_cast(input.at("A").get()); +// int B = static_cast(input.at("B").get()); +// connectIds(A, id); +// connectIds(B, id); +// } +// else if (type == "DFFP" || type == "RAM") { +// int D = static_cast(input.at("D").get()); +// connectIds(D, id); +// } +// else if (type == "NOT") { +// int A = static_cast(input.at("A").get()); +// connectIds(A, id); +// } +// else if (type == "MUX") { +// int A = static_cast(input.at("A").get()); +// int B = static_cast(input.at("B").get()); +// int S = static_cast(input.at("S").get()); +// connectIds(A, id); +// connectIds(B, id); +// connectIds(S, id); +// } +// else { +// error::die("Invalid JSON of network. Invalid type: ", type); +// } +// } +// } +//}; +// +//} // namespace + +void readIyokanL1JSONNetwork(std::istream& is, NetworkBuilder& nb) +{ + // IyokanL1JSONReader::read(nb, is); +} + /**************************************************/ /***** TEST ***************************************/ /**************************************************/ diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index aa22917..4365caa 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -427,6 +427,9 @@ class Blueprint { const std::unordered_map& atPortWidths() const; }; +void readYosysJSONNetwork(std::istream& is, NetworkBuilder& nb); +void readIyokanL1JSONNetwork(std::istream& is, NetworkBuilder& nb); + } // namespace nt #endif diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index b6a563c..a611972 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -3,6 +3,8 @@ #include "iyokan_nt.hpp" #include "packet_nt.hpp" +#include + template std::string fok2(T1 t1, T2 t2) { @@ -353,12 +355,30 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // Set priority to each DepNode // FIXME + + // FIXME check if network is valid } void Frontend::readNetworkFromFile(const blueprint::File& file, nt::NetworkBuilder& nb) { - // FIXME + std::ifstream ifs{file.path, std::ios::binary}; + if (!ifs) + ERR_DIE("Invalid [[file]] path: " << file.path); + + switch (file.type) { + case blueprint::File::TYPE::IYOKANL1_JSON: + LOG_S(WARNING) + << "[[file]] of type 'iyokanl1-json' is deprecated. You don't need " + "to use Iyokan-L1. Use Yosys JSON directly by specifying type " + "'yosys-json'."; + readIyokanL1JSONNetwork(ifs, nb); + break; + + case blueprint::File::TYPE::YOSYS_JSON: + readYosysJSONNetwork(ifs, nb); + break; + } } void Frontend::makeMUXRAM(const blueprint::BuiltinRAM& ram, diff --git a/thirdparty/cuFHE b/thirdparty/cuFHE index 491b90e..afbd970 160000 --- a/thirdparty/cuFHE +++ b/thirdparty/cuFHE @@ -1 +1 @@ -Subproject commit 491b90eb8e9e3063622878e9e61fcfd5ea87b5b5 +Subproject commit afbd970f37cb2f3d0b658b9dad50462dbfcb8114 From 106b93e161d72096082f40c1274ba32a70dc5d10 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 30 Oct 2021 22:16:34 +0900 Subject: [PATCH 15/54] wip --- src/iyokan_nt.cpp | 17 +----- src/iyokan_nt.hpp | 75 ++++++++++-------------- src/iyokan_nt_plain.cpp | 127 +++++++++++++++++----------------------- src/test0.cpp | 18 +++--- 4 files changed, 95 insertions(+), 142 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index b521c0a..1ebb632 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -28,17 +28,6 @@ Allocator::Allocator() { } -Allocator& Allocator::subAllocator(const std::string& key) -{ - assert(data_.size() == 0); - auto it = subs_.find(key); - if (it == subs_.end()) { - auto sub = std::make_unique(); - std::tie(it, std::ignore) = subs_.emplace(key, std::move(sub)); - } - return *it->second; -} - /* class Task */ Task::Task(Label label) @@ -209,8 +198,8 @@ void Network::tick() /* class NetworkBuilder */ -NetworkBuilder::NetworkBuilder(Allocator& alcRoot) - : finder_(), tasks_(), consumed_(false), currentAlc_(&alcRoot) +NetworkBuilder::NetworkBuilder(Allocator& alc) + : finder_(), tasks_(), consumed_(false), alc_(&alc) { } @@ -220,7 +209,7 @@ NetworkBuilder::~NetworkBuilder() Allocator& NetworkBuilder::currentAllocator() { - return *currentAlc_; + return *alc_; } const TaskFinder& NetworkBuilder::finder() const diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 4365caa..28a03d7 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -23,30 +23,28 @@ class WorkerInfo; class DataHolder; class Allocator { +public: + using Index = size_t; + private: - std::unordered_map> subs_; - std::vector data_; + std::vector> data_; public: Allocator(/* optional snapshot file */); - Allocator& subAllocator(const std::string& key); - template - T* make(size_t index) + T* make() { - if (data_.size() <= index) - data_.resize(index + 1); - std::any& v = data_.at(index); - assert(!v.has_value()); + data_.emplace_back(std::make_unique()); + std::any& v = *data_.back(); return &v.emplace(); } template - T* get(size_t index) + T* get(Index index) { assert(index < data_.size()); - T* ret = std::any_cast(&data_.at(index)); + T* ret = std::any_cast(data_.at(index).get()); assert(ret != nullptr); return ret; } @@ -112,8 +110,6 @@ class TaskFinder { // 1. # of outputs is 1. (#inputs can be >1.) // 2. All the inputs and output have the same type. // 3. (and so on) -// TaskCommon guarantees: -// 1. Its output exists in alc.get(0). template class TaskCommon : public Task { private: @@ -149,7 +145,7 @@ class TaskCommon : public Task { numMaxExpectedInputs_( numMaxExpectedInputs.value_or(numMinExpectedInputs)), inputs_(), - output_(alc.make(0)) + output_(alc.make()) { } @@ -264,7 +260,7 @@ class NetworkBuilder { TaskFinder finder_; std::vector> tasks_; bool consumed_; - Allocator* currentAlc_; + Allocator* alc_; protected: // Create a new task. T must be derived from class Task. @@ -281,45 +277,36 @@ class NetworkBuilder { Allocator& currentAllocator(); public: - NetworkBuilder(Allocator& alcRoot); + NetworkBuilder(Allocator& alc); virtual ~NetworkBuilder(); const TaskFinder& finder() const; Network createNetwork(); - template - void withSubAllocator(const std::string& alcKey, F f) - { - Allocator* original = currentAlc_; - currentAlc_ = &original->subAllocator(alcKey); - f(*this); - currentAlc_ = original; - } - virtual void connect(UID from, UID to) = 0; // not/and/or are C++ keywords, so the member functions here are in // capitals. - virtual UID INPUT(const std::string& alcKey, const std::string& nodeName, - const std::string& portName, int portBit) = 0; - virtual UID OUTPUT(const std::string& alcKey, const std::string& nodeName, - const std::string& portName, int portBit) = 0; - - virtual UID AND(const std::string& alcKey) = 0; - virtual UID ANDNOT(const std::string& alcKey) = 0; - virtual UID CONSTONE(const std::string& alcKey) = 0; - virtual UID CONSTZERO(const std::string& alcKey) = 0; - virtual UID DFF(const std::string& alcKey) = 0; - virtual UID MUX(const std::string& alcKey) = 0; - virtual UID NAND(const std::string& alcKey) = 0; - virtual UID NMUX(const std::string& alcKey) = 0; - virtual UID NOR(const std::string& alcKey) = 0; - virtual UID NOT(const std::string& alcKey) = 0; - virtual UID OR(const std::string& alcKey) = 0; - virtual UID ORNOT(const std::string& alcKey) = 0; - virtual UID XNOR(const std::string& alcKey) = 0; - virtual UID XOR(const std::string& alcKey) = 0; + virtual UID INPUT(const std::string& nodeName, const std::string& portName, + int portBit) = 0; + virtual UID OUTPUT(const std::string& nodeName, const std::string& portName, + int portBit) = 0; + + virtual UID AND() = 0; + virtual UID ANDNOT() = 0; + virtual UID CONSTONE() = 0; + virtual UID CONSTZERO() = 0; + virtual UID DFF() = 0; + virtual UID MUX() = 0; + virtual UID NAND() = 0; + virtual UID NMUX() = 0; + virtual UID NOR() = 0; + virtual UID NOT() = 0; + virtual UID OR() = 0; + virtual UID ORNOT() = 0; + virtual UID XNOR() = 0; + virtual UID XOR() = 0; }; class Worker { diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index a611972..761462f 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -172,17 +172,15 @@ class NetworkBuilder : public nt::NetworkBuilder { to->addInput(from); } -#define DEF_COMMON_TASK(CAPName, CamelName) \ - UID CAPName(const std::string& alcKey) override \ - { \ - UID uid = genUID(); \ - Task##CamelName* task = nullptr; \ - this->withSubAllocator(alcKey, [&](auto&&) { \ - task = emplaceTask( \ - Label{uid, #CamelName, std::nullopt}, currentAllocator()); \ - }); \ - uid2common_.emplace(uid, task); \ - return uid; \ +#define DEF_COMMON_TASK(CAPName, CamelName) \ + UID CAPName() override \ + { \ + UID uid = genUID(); \ + Task##CamelName* task = nullptr; \ + task = emplaceTask( \ + Label{uid, #CamelName, std::nullopt}, currentAllocator()); \ + uid2common_.emplace(uid, task); \ + return uid; \ } DEF_COMMON_TASK(AND, And); DEF_COMMON_TASK(ANDNOT, Andnot); @@ -200,30 +198,26 @@ class NetworkBuilder : public nt::NetworkBuilder { DEF_COMMON_TASK(XOR, Xor); #undef DEF_COMMON_TASK - UID INPUT(const std::string& alcKey, const std::string& nodeName, - const std::string& portName, int portBit) override + UID INPUT(const std::string& nodeName, const std::string& portName, + int portBit) override { UID uid = genUID(); TaskInput* task = nullptr; - withSubAllocator(alcKey, [&](auto&&) { - task = emplaceTask( - Label{uid, "Input", ConfigName{nodeName, portName, portBit}}, - currentAllocator()); - }); + task = emplaceTask( + Label{uid, "Input", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); uid2common_.emplace(uid, task); return uid; } - UID OUTPUT(const std::string& alcKey, const std::string& nodeName, - const std::string& portName, int portBit) override + UID OUTPUT(const std::string& nodeName, const std::string& portName, + int portBit) override { UID uid = genUID(); TaskOutput* task = nullptr; - withSubAllocator(alcKey, [&](auto&&) { - task = emplaceTask( - Label{uid, "Output", ConfigName{nodeName, portName, portBit}}, - currentAllocator()); - }); + task = emplaceTask( + Label{uid, "Output", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); uid2common_.emplace(uid, task); return uid; } @@ -291,35 +285,30 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // [[file]] for (auto&& file : bp_.files()) - nb.withSubAllocator(file.name, - [&](auto&& nb) { readNetworkFromFile(file, nb); }); + readNetworkFromFile(file, nb); // [[builtin]] type = ram | type = mux-ram for (auto&& ram : bp_.builtinRAMs()) { - nb.withSubAllocator(ram.name, [&](auto&& nb) { - switch (ram.type) { - case blueprint::BuiltinRAM::TYPE::CMUX_MEMORY: - makeRAM(ram, nb); - break; - case blueprint::BuiltinRAM::TYPE::MUX: - makeMUXRAM(ram, nb); - break; - } - }); + switch (ram.type) { + case blueprint::BuiltinRAM::TYPE::CMUX_MEMORY: + makeRAM(ram, nb); + break; + case blueprint::BuiltinRAM::TYPE::MUX: + makeMUXRAM(ram, nb); + break; + } } // [[builtin]] type = rom | type = mux-rom for (auto&& rom : bp_.builtinROMs()) { - nb.withSubAllocator(rom.name, [&](auto&& nb) { - switch (rom.type) { - case blueprint::BuiltinROM::TYPE::CMUX_MEMORY: - makeROM(rom, nb); - break; - case blueprint::BuiltinROM::TYPE::MUX: - makeMUXROM(rom, nb); - break; - } - }); + switch (rom.type) { + case blueprint::BuiltinROM::TYPE::CMUX_MEMORY: + makeROM(rom, nb); + break; + case blueprint::BuiltinROM::TYPE::MUX: + makeMUXROM(rom, nb); + break; + } auto it = reqPacket_.rom.find(rom.name); if (it != reqPacket_.rom.end()) { @@ -409,15 +398,13 @@ void Frontend::makeROM(const blueprint::BuiltinROM& rom, nt::NetworkBuilder& nb) // Create inputs std::vector addrInputs; for (size_t i = 0; i < rom.inAddrWidth; i++) { - UID id = nb.INPUT(fok2("addr", i), rom.name, "addr", i); + UID id = nb.INPUT(rom.name, "addr", i); addrInputs.push_back(id); } // Create 1bit ROMs for (size_t i = 0; i < rom.outRdataWidth; i++) { - nb.withSubAllocator(fok2("rom1bit{}", i), [&](nt::NetworkBuilder& nb) { - make1bitROMWithMUX(rom, addrInputs, nb); - }); + make1bitROMWithMUX(rom, addrInputs, nb); } } @@ -439,10 +426,9 @@ void test0() Bit bit0 = 0_b, bit1 = 1_b; { - Allocator root; - TaskConstOne t0{Label{1, "", std::nullopt}, - root.subAllocator("constone")}; - TaskOutput t1{Label{2, "", std::nullopt}, root.subAllocator("out")}; + Allocator alc; + TaskConstOne t0{Label{1, "", std::nullopt}, alc}; + TaskOutput t1{Label{2, "", std::nullopt}, alc}; t1.addInput(&t0); t0.startAsynchronously(wi); t1.startAsynchronously(wi); @@ -452,15 +438,11 @@ void test0() } { - Allocator root; - Allocator &sub0 = root.subAllocator("0"), - &sub1 = root.subAllocator("1"), - &sub2 = root.subAllocator("2"), - &sub3 = root.subAllocator("3"); - TaskConstZero t0{Label{0, "", std::nullopt}, sub0}; - TaskConstOne t1{Label{1, "", std::nullopt}, sub1}; - TaskNand t2{Label{2, "", std::nullopt}, sub2}; - TaskOutput t3{Label{3, "", std::nullopt}, sub3}; + Allocator alc; + TaskConstZero t0{Label{0, "", std::nullopt}, alc}; + TaskConstOne t1{Label{1, "", std::nullopt}, alc}; + TaskNand t2{Label{2, "", std::nullopt}, alc}; + TaskOutput t3{Label{3, "", std::nullopt}, alc}; t2.addInput(&t0); t2.addInput(&t1); t3.addInput(&t2); @@ -475,10 +457,10 @@ void test0() } { - Allocator root; - NetworkBuilder nb{root}; - UID id0 = nb.INPUT("0", "", "A", 0), id1 = nb.INPUT("1", "", "B", 0), - id2 = nb.NAND("2"), id3 = nb.OUTPUT("3", "", "C", 0); + Allocator alc; + NetworkBuilder nb{alc}; + UID id0 = nb.INPUT("", "A", 0), id1 = nb.INPUT("", "B", 0), + id2 = nb.NAND(), id3 = nb.OUTPUT("", "C", 0); nb.connect(id0, id2); nb.connect(id1, id2); nb.connect(id2, id3); @@ -507,11 +489,10 @@ void test0() *--< NOT(3) <--*-----> OUTPUT(1) A */ - Allocator root; - NetworkBuilder nb{root}; - UID id0 = nb.INPUT("0", "", "reset", 0), - id1 = nb.OUTPUT("1", "", "out", 0), id2 = nb.DFF("2"), - id3 = nb.NOT("3"), id4 = nb.ANDNOT("4"); + Allocator alc; + NetworkBuilder nb{alc}; + UID id0 = nb.INPUT("", "reset", 0), id1 = nb.OUTPUT("", "out", 0), + id2 = nb.DFF(), id3 = nb.NOT(), id4 = nb.ANDNOT(); nb.connect(id2, id1); nb.connect(id4, id2); nb.connect(id2, id3); diff --git a/src/test0.cpp b/src/test0.cpp index 955a94e..78acce8 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -915,8 +915,8 @@ void testAllocator() { Allocator alc; { - TLWELvl0* zero = alc.make(0); - TLWELvl0* one = alc.make(1); + TLWELvl0* zero = alc.make(); + TLWELvl0* one = alc.make(); *zero = TFHEppTestHelper::instance().zero(); *one = TFHEppTestHelper::instance().one(); } @@ -928,20 +928,16 @@ void testAllocator() } } { - Allocator root; + Allocator alc; { - Allocator &sub0 = root.subAllocator("0"), - &sub1 = root.subAllocator("1"); - TLWELvl0* zero = sub0.make(0); - TLWELvl0* one = sub1.make(0); + TLWELvl0* zero = alc.make(); + TLWELvl0* one = alc.make(); *zero = TFHEppTestHelper::instance().zero(); *one = TFHEppTestHelper::instance().one(); } { - Allocator &sub0 = root.subAllocator("0"), - &sub1 = root.subAllocator("1"); - TLWELvl0* zero = sub0.get(0); - TLWELvl0* one = sub1.get(0); + TLWELvl0* zero = alc.get(0); + TLWELvl0* one = alc.get(1); assert(*zero == TFHEppTestHelper::instance().zero()); assert(*one == TFHEppTestHelper::instance().one()); } From 6b6ac586db0c66f2e45b32460b2c0139eca3db8b Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 30 Oct 2021 23:14:30 +0900 Subject: [PATCH 16/54] wip --- src/error_nt.hpp | 2 +- src/iyokan_nt_plain.cpp | 14 +++++++------- src/packet_nt.hpp | 9 +++++++-- src/test0.cpp | 2 ++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/error_nt.hpp b/src/error_nt.hpp index 5d1fe8c..108833c 100644 --- a/src/error_nt.hpp +++ b/src/error_nt.hpp @@ -9,7 +9,7 @@ void initialize(); [[noreturn]] void abortWithBacktrace(); } // namespace nt::error -#define DBG 1 // verbosity debug for loguru +#define LOG_DBG LOG_S(1) #define ERR_DIE(cont) \ do { \ diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 761462f..a0ef85b 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -131,17 +131,17 @@ class TaskDFF : public nt::TaskDFF { } \ }; DEF_COMMON_TASK_CLASS(And, 2, (input(0) & input(1))); -DEF_COMMON_TASK_CLASS(Andnot, 2, (input(0) & ~input(1))); +DEF_COMMON_TASK_CLASS(Andnot, 2, (input(0) & !input(1))); DEF_COMMON_TASK_CLASS(ConstOne, 0, 1_b); DEF_COMMON_TASK_CLASS(ConstZero, 0, 0_b); DEF_COMMON_TASK_CLASS(Mux, 3, input(2) == 0_b ? input(0) : input(1)); -DEF_COMMON_TASK_CLASS(Nand, 2, ~(input(0) & input(1))); -DEF_COMMON_TASK_CLASS(Nmux, 3, input(2) == 0_b ? ~input(0) : ~input(1)); -DEF_COMMON_TASK_CLASS(Nor, 2, ~(input(0) | input(1))); -DEF_COMMON_TASK_CLASS(Not, 1, ~input(0)); +DEF_COMMON_TASK_CLASS(Nand, 2, !(input(0) & input(1))); +DEF_COMMON_TASK_CLASS(Nmux, 3, input(2) == 0_b ? !input(0) : !input(1)); +DEF_COMMON_TASK_CLASS(Nor, 2, !(input(0) | input(1))); +DEF_COMMON_TASK_CLASS(Not, 1, !input(0)); DEF_COMMON_TASK_CLASS(Or, 2, (input(0) | input(1))); -DEF_COMMON_TASK_CLASS(Ornot, 2, (input(0) | ~input(1))); -DEF_COMMON_TASK_CLASS(Xnor, 2, ~(input(0) ^ input(1))); +DEF_COMMON_TASK_CLASS(Ornot, 2, (input(0) | !input(1))); +DEF_COMMON_TASK_CLASS(Xnor, 2, !(input(0) ^ input(1))); DEF_COMMON_TASK_CLASS(Xor, 2, (input(0) ^ input(1))); #undef DEF_COMMON_TASK_CLASS diff --git a/src/packet_nt.hpp b/src/packet_nt.hpp index a27c6f8..c54f2cf 100644 --- a/src/packet_nt.hpp +++ b/src/packet_nt.hpp @@ -9,9 +9,9 @@ namespace nt { enum class Bit : bool {}; -inline constexpr Bit operator~(Bit l) noexcept +inline constexpr Bit operator!(Bit l) noexcept { - return Bit(~static_cast(l)); + return Bit(!static_cast(l)); } inline constexpr Bit operator|(Bit l, Bit r) noexcept { @@ -41,6 +41,11 @@ inline Bit operator"" _b(unsigned long long x) { return Bit(x != 0); } +inline std::ostream& operator<<(std::ostream& os, const Bit& bit) +{ + os << (bit == 1_b ? 1 : 0); + return os; +} struct TFHEppBKey { std::shared_ptr gk; diff --git a/src/test0.cpp b/src/test0.cpp index 78acce8..653dda3 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -1001,6 +1001,8 @@ int main() // testProgressGraphMaker(); // testBlueprint(); + loguru::g_stderr_verbosity = loguru::Verbosity_1; + nt::testAllocator(); nt::plain::test0(); } From 7a756bea24c818953f9737547dd07bc134cb372a Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 31 Oct 2021 20:53:37 +0900 Subject: [PATCH 17/54] wip --- src/CMakeLists.txt | 1 + src/blueprint.cpp | 296 ++++++++++++++++ src/iyokan_nt.cpp | 751 +--------------------------------------- src/iyokan_nt.hpp | 6 +- src/iyokan_nt_plain.cpp | 98 +++++- src/network_reader.cpp | 444 ++++++++++++++++++++++++ 6 files changed, 842 insertions(+), 754 deletions(-) create mode 100644 src/blueprint.cpp create mode 100644 src/network_reader.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd93729..f60d9e3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ endif(IYOKAN_ENABLE_CUDA) add_executable(test0 test0.cpp iyokan.cpp iyokan_plain.cpp iyokan_tfhepp.cpp error.cpp iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp + network_reader.cpp blueprint.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/blueprint.cpp b/src/blueprint.cpp new file mode 100644 index 0000000..9e79c9a --- /dev/null +++ b/src/blueprint.cpp @@ -0,0 +1,296 @@ +#include "iyokan_nt.hpp" + +#include +#include + +#include + +namespace { +std::vector regexMatch(const std::string& text, + const std::regex& re) +{ + std::vector ret; + std::smatch m; + if (!std::regex_match(text, m, re)) + return ret; + for (auto&& elm : m) + ret.push_back(elm.str()); + return ret; +} + +} // namespace + +namespace nt { + +/* class Blueprint */ + +Blueprint::Blueprint(const std::string& fileName) +{ + namespace fs = std::filesystem; + + // Read the file + std::stringstream inputStream; + { + std::ifstream ifs{fileName}; + if (!ifs) + ERR_DIE("File not found: " << fileName); + inputStream << ifs.rdbuf(); + source_ = inputStream.str(); + inputStream.seekg(std::ios::beg); + } + + // Parse config file + const auto src = toml::parse(inputStream, fileName); + + // Find working directory of config + fs::path wd = fs::absolute(fileName); + wd.remove_filename(); + + // [[file]] + { + const auto srcFiles = + toml::find_or>(src, "file", {}); + for (const auto& srcFile : srcFiles) { + std::string typeStr = toml::find(srcFile, "type"); + fs::path path = toml::find(srcFile, "path"); + std::string name = toml::find(srcFile, "name"); + + blueprint::File::TYPE type; + if (typeStr == "iyokanl1-json") + type = blueprint::File::TYPE::IYOKANL1_JSON; + else if (typeStr == "yosys-json") + type = blueprint::File::TYPE::YOSYS_JSON; + else + ERR_DIE("Invalid file type: " << typeStr); + + if (path.is_relative()) + path = wd / path; // Make path absolute + + files_.push_back(blueprint::File{type, path.string(), name}); + } + } + + // [[builtin]] + { + const auto srcBuiltins = + toml::find_or>(src, "builtin", {}); + for (const auto& srcBuiltin : srcBuiltins) { + const auto type = toml::find(srcBuiltin, "type"); + const auto name = toml::find(srcBuiltin, "name"); + + if (type == "rom" || type == "mux-rom") { + auto romType = type == "rom" + ? blueprint::BuiltinROM::TYPE::CMUX_MEMORY + : blueprint::BuiltinROM::TYPE::MUX; + const auto inAddrWidth = + toml::find(srcBuiltin, "in_addr_width"); + const auto outRdataWidth = + toml::find(srcBuiltin, "out_rdata_width"); + + builtinROMs_.push_back(blueprint::BuiltinROM{ + romType, name, inAddrWidth, outRdataWidth}); + } + else if (type == "ram" || type == "mux-ram") { + auto ramType = type == "ram" + ? blueprint::BuiltinRAM::TYPE::CMUX_MEMORY + : blueprint::BuiltinRAM::TYPE::MUX; + const auto inAddrWidth = + toml::find(srcBuiltin, "in_addr_width"); + const auto inWdataWidth = + toml::find(srcBuiltin, "in_wdata_width"); + const auto outRdataWidth = + toml::find(srcBuiltin, "out_rdata_width"); + + builtinRAMs_.push_back(blueprint::BuiltinRAM{ + ramType, name, inAddrWidth, inWdataWidth, outRdataWidth}); + } + } + } + + // [connect] + { + const auto srcConnect = toml::find_or(src, "connect", {}); + for (const auto& [srcKey, srcValue] : srcConnect) { + if (srcKey == "TOGND") { // TOGND = [@...[n:m], @...[n:m], ...] + auto ary = toml::get>(srcValue); + for (const auto& portStr : ary) { // @...[n:m] + if (portStr.empty() || portStr.at(0) != '@') + ERR_DIE("Invalid port name for TOGND: " << portStr); + auto ports = parsePortString(portStr, "output"); + for (auto&& port : ports) { // @...[n] + const std::string& name = port.portName; + int bit = port.portBit; + auto [it, inserted] = atPortWidths_.emplace(name, 0); + it->second = std::max(it->second, bit + 1); + } + } + continue; + } + + std::string srcTo = srcKey, + srcFrom = toml::get(srcValue), + errMsg = fmt::format("Invalid connect: {} = {}", srcTo, + srcFrom); + + // Check if input is correct. + if (srcTo.empty() || srcFrom.empty() || + (srcTo[0] == '@' && srcFrom[0] == '@')) + ERR_DIE(errMsg); + + // Others. + std::vector portsTo = + parsePortString(srcTo, "input"), + portsFrom = + parsePortString(srcFrom, "output"); + if (portsTo.size() != portsFrom.size()) + ERR_DIE(errMsg); + + for (size_t i = 0; i < portsTo.size(); i++) { + const blueprint::Port& to = portsTo[i]; + const blueprint::Port& from = portsFrom[i]; + + if (srcTo[0] == '@') { // @... = ... + if (!to.nodeName.empty() || from.nodeName.empty()) + ERR_DIE(errMsg); + + const std::string& name = to.portName; + int bit = to.portBit; + + { + auto [it, inserted] = + atPorts_.emplace(std::make_tuple(name, bit), from); + if (!inserted) + LOG_S(WARNING) + << srcTo + << " is used multiple times. Only the first " + "one is effective."; + } + + auto [it, inserted] = atPortWidths_.emplace(name, 0); + it->second = std::max(it->second, bit + 1); + } + else if (srcFrom[0] == '@') { // ... = @... + if (!from.nodeName.empty() || to.nodeName.empty()) + ERR_DIE(errMsg); + + const std::string& name = from.portName; + int bit = from.portBit; + + { + auto [it, inserted] = + atPorts_.emplace(std::make_tuple(name, bit), to); + if (!inserted) + LOG_S(WARNING) + << srcFrom + << " is used multiple times. Only the first " + "one is effective. (FIXME)"; + } + + auto [it, inserted] = atPortWidths_.emplace(name, 0); + it->second = std::max(it->second, bit + 1); + } + else { // ... = ... + edges_.emplace_back(from, to); + } + } + } + } +} + +std::vector Blueprint::parsePortString(const std::string& src, + const std::string& kind) +{ + std::string nodeName, portName; + int portBitFrom, portBitTo; + + auto match = regexMatch( + src, + std::regex(R"(^@?(?:([^/]+)/)?([^[]+)(?:\[([0-9]+):([0-9]+)\])?$)")); + if (match.empty()) + ERR_DIE("Invalid port string: " << src); + + assert(match.size() == 1 + 4); + + nodeName = match[1]; + portName = match[2]; + + if (match[3].empty()) { // hoge/piyo + assert(match[4].empty()); + portBitFrom = 0; + portBitTo = 0; + } + else { // hoge/piyo[foo:bar] + assert(!match[4].empty()); + portBitFrom = std::stoi(match[3]); + portBitTo = std::stoi(match[4]); + } + + std::vector ret; + for (int i = portBitFrom; i < portBitTo + 1; i++) + ret.push_back(blueprint::Port{nodeName, kind, portName, i}); + return ret; +} + +bool Blueprint::needsCircuitKey() const +{ + for (const auto& bprom : builtinROMs_) + if (bprom.type == blueprint::BuiltinROM::TYPE::CMUX_MEMORY) + return true; + for (const auto& bpram : builtinRAMs_) + if (bpram.type == blueprint::BuiltinRAM::TYPE::CMUX_MEMORY) + return true; + return false; +} + +const std::string& Blueprint::sourceFile() const +{ + return sourceFile_; +} + +const std::string& Blueprint::source() const +{ + return source_; +} + +const std::vector& Blueprint::files() const +{ + return files_; +} + +const std::vector& Blueprint::builtinROMs() const +{ + return builtinROMs_; +} + +const std::vector& Blueprint::builtinRAMs() const +{ + return builtinRAMs_; +} + +const std::vector>& +Blueprint::edges() const +{ + return edges_; +} + +const std::map, blueprint::Port>& +Blueprint::atPorts() const +{ + return atPorts_; +} + +std::optional Blueprint::at(const std::string& portName, + int portBit) const +{ + auto it = atPorts_.find(std::make_tuple(portName, portBit)); + if (it == atPorts_.end()) + return std::nullopt; + return it->second; +} + +const std::unordered_map& Blueprint::atPortWidths() const +{ + return atPortWidths_; +} + +} // namespace nt diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 1ebb632..56e9810 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,27 +1,10 @@ #include "iyokan_nt.hpp" #include "error_nt.hpp" -#include -#include -#include // FIXME: this makes compilation slow - -#include -#include +#include namespace nt { -std::vector regexMatch(const std::string& text, - const std::regex& re) -{ - std::vector ret; - std::smatch m; - if (!std::regex_match(text, m, re)) - return ret; - for (auto&& elm : m) - ret.push_back(elm.str()); - return ret; -} - /* class Allocator */ Allocator::Allocator() @@ -325,736 +308,4 @@ void NetworkRunner::tick() network_.tick(); } -/* class Blueprint */ - -Blueprint::Blueprint(const std::string& fileName) -{ - namespace fs = std::filesystem; - - // Read the file - std::stringstream inputStream; - { - std::ifstream ifs{fileName}; - if (!ifs) - ERR_DIE("File not found: " << fileName); - inputStream << ifs.rdbuf(); - source_ = inputStream.str(); - inputStream.seekg(std::ios::beg); - } - - // Parse config file - const auto src = toml::parse(inputStream, fileName); - - // Find working directory of config - fs::path wd = fs::absolute(fileName); - wd.remove_filename(); - - // [[file]] - { - const auto srcFiles = - toml::find_or>(src, "file", {}); - for (const auto& srcFile : srcFiles) { - std::string typeStr = toml::find(srcFile, "type"); - fs::path path = toml::find(srcFile, "path"); - std::string name = toml::find(srcFile, "name"); - - blueprint::File::TYPE type; - if (typeStr == "iyokanl1-json") - type = blueprint::File::TYPE::IYOKANL1_JSON; - else if (typeStr == "yosys-json") - type = blueprint::File::TYPE::YOSYS_JSON; - else - ERR_DIE("Invalid file type: " << typeStr); - - if (path.is_relative()) - path = wd / path; // Make path absolute - - files_.push_back(blueprint::File{type, path.string(), name}); - } - } - - // [[builtin]] - { - const auto srcBuiltins = - toml::find_or>(src, "builtin", {}); - for (const auto& srcBuiltin : srcBuiltins) { - const auto type = toml::find(srcBuiltin, "type"); - const auto name = toml::find(srcBuiltin, "name"); - - if (type == "rom" || type == "mux-rom") { - auto romType = type == "rom" - ? blueprint::BuiltinROM::TYPE::CMUX_MEMORY - : blueprint::BuiltinROM::TYPE::MUX; - const auto inAddrWidth = - toml::find(srcBuiltin, "in_addr_width"); - const auto outRdataWidth = - toml::find(srcBuiltin, "out_rdata_width"); - - builtinROMs_.push_back(blueprint::BuiltinROM{ - romType, name, inAddrWidth, outRdataWidth}); - } - else if (type == "ram" || type == "mux-ram") { - auto ramType = type == "ram" - ? blueprint::BuiltinRAM::TYPE::CMUX_MEMORY - : blueprint::BuiltinRAM::TYPE::MUX; - const auto inAddrWidth = - toml::find(srcBuiltin, "in_addr_width"); - const auto inWdataWidth = - toml::find(srcBuiltin, "in_wdata_width"); - const auto outRdataWidth = - toml::find(srcBuiltin, "out_rdata_width"); - - builtinRAMs_.push_back(blueprint::BuiltinRAM{ - ramType, name, inAddrWidth, inWdataWidth, outRdataWidth}); - } - } - } - - // [connect] - { - const auto srcConnect = toml::find_or(src, "connect", {}); - for (const auto& [srcKey, srcValue] : srcConnect) { - if (srcKey == "TOGND") { // TOGND = [@...[n:m], @...[n:m], ...] - auto ary = toml::get>(srcValue); - for (const auto& portStr : ary) { // @...[n:m] - if (portStr.empty() || portStr.at(0) != '@') - ERR_DIE("Invalid port name for TOGND: " << portStr); - auto ports = parsePortString(portStr, "output"); - for (auto&& port : ports) { // @...[n] - const std::string& name = port.portName; - int bit = port.portBit; - auto [it, inserted] = atPortWidths_.emplace(name, 0); - it->second = std::max(it->second, bit + 1); - } - } - continue; - } - - std::string srcTo = srcKey, - srcFrom = toml::get(srcValue), - errMsg = fmt::format("Invalid connect: {} = {}", srcTo, - srcFrom); - - // Check if input is correct. - if (srcTo.empty() || srcFrom.empty() || - (srcTo[0] == '@' && srcFrom[0] == '@')) - ERR_DIE(errMsg); - - // Others. - std::vector portsTo = - parsePortString(srcTo, "input"), - portsFrom = - parsePortString(srcFrom, "output"); - if (portsTo.size() != portsFrom.size()) - ERR_DIE(errMsg); - - for (size_t i = 0; i < portsTo.size(); i++) { - const blueprint::Port& to = portsTo[i]; - const blueprint::Port& from = portsFrom[i]; - - if (srcTo[0] == '@') { // @... = ... - if (!to.nodeName.empty() || from.nodeName.empty()) - ERR_DIE(errMsg); - - const std::string& name = to.portName; - int bit = to.portBit; - - { - auto [it, inserted] = - atPorts_.emplace(std::make_tuple(name, bit), from); - if (!inserted) - LOG_S(WARNING) - << srcTo - << " is used multiple times. Only the first " - "one is effective."; - } - - auto [it, inserted] = atPortWidths_.emplace(name, 0); - it->second = std::max(it->second, bit + 1); - } - else if (srcFrom[0] == '@') { // ... = @... - if (!from.nodeName.empty() || to.nodeName.empty()) - ERR_DIE(errMsg); - - const std::string& name = from.portName; - int bit = from.portBit; - - { - auto [it, inserted] = - atPorts_.emplace(std::make_tuple(name, bit), to); - if (!inserted) - LOG_S(WARNING) - << srcFrom - << " is used multiple times. Only the first " - "one is effective. (FIXME)"; - } - - auto [it, inserted] = atPortWidths_.emplace(name, 0); - it->second = std::max(it->second, bit + 1); - } - else { // ... = ... - edges_.emplace_back(from, to); - } - } - } - } -} - -std::vector Blueprint::parsePortString(const std::string& src, - const std::string& kind) -{ - std::string nodeName, portName; - int portBitFrom, portBitTo; - - auto match = regexMatch( - src, - std::regex(R"(^@?(?:([^/]+)/)?([^[]+)(?:\[([0-9]+):([0-9]+)\])?$)")); - if (match.empty()) - ERR_DIE("Invalid port string: " << src); - - assert(match.size() == 1 + 4); - - nodeName = match[1]; - portName = match[2]; - - if (match[3].empty()) { // hoge/piyo - assert(match[4].empty()); - portBitFrom = 0; - portBitTo = 0; - } - else { // hoge/piyo[foo:bar] - assert(!match[4].empty()); - portBitFrom = std::stoi(match[3]); - portBitTo = std::stoi(match[4]); - } - - std::vector ret; - for (int i = portBitFrom; i < portBitTo + 1; i++) - ret.push_back(blueprint::Port{nodeName, kind, portName, i}); - return ret; -} - -bool Blueprint::needsCircuitKey() const -{ - for (const auto& bprom : builtinROMs_) - if (bprom.type == blueprint::BuiltinROM::TYPE::CMUX_MEMORY) - return true; - for (const auto& bpram : builtinRAMs_) - if (bpram.type == blueprint::BuiltinRAM::TYPE::CMUX_MEMORY) - return true; - return false; -} - -const std::string& Blueprint::sourceFile() const -{ - return sourceFile_; -} - -const std::string& Blueprint::source() const -{ - return source_; -} - -const std::vector& Blueprint::files() const -{ - return files_; -} - -const std::vector& Blueprint::builtinROMs() const -{ - return builtinROMs_; -} - -const std::vector& Blueprint::builtinRAMs() const -{ - return builtinRAMs_; -} - -const std::vector>& -Blueprint::edges() const -{ - return edges_; -} - -const std::map, blueprint::Port>& -Blueprint::atPorts() const -{ - return atPorts_; -} - -std::optional Blueprint::at(const std::string& portName, - int portBit) const -{ - auto it = atPorts_.find(std::make_tuple(portName, portBit)); - if (it == atPorts_.end()) - return std::nullopt; - return it->second; -} - -const std::unordered_map& Blueprint::atPortWidths() const -{ - return atPortWidths_; -} - -///* class YosysJSONReader */ -// -// namespace { -// -// class YosysJSONReader { -// private: -// enum class PORT { -// IN, -// OUT, -// }; -// -// struct Port { -// PORT type; -// int id, bit; -// -// Port(PORT type, int id, int bit) : type(type), id(id), bit(bit) -// { -// } -// }; -// -// enum class CELL { -// NOT, -// AND, -// ANDNOT, -// NAND, -// OR, -// XOR, -// XNOR, -// NOR, -// ORNOT, -// DFFP, -// SDFFPP0, -// SDFFPP1, -// MUX, -// }; -// -// struct Cell { -// CELL type; -// int id, bit0, bit1, bit2; -// -// Cell(CELL type, int id, int bit0) -// : type(type), id(id), bit0(bit0), bit1(-1), bit2(-1) -// { -// } -// Cell(CELL type, int id, int bit0, int bit1) -// : type(type), id(id), bit0(bit0), bit1(bit1), bit2(-1) -// { -// } -// Cell(CELL type, int id, int bit0, int bit1, int bit2) -// : type(type), id(id), bit0(bit0), bit1(bit1), bit2(bit2) -// { -// } -// }; -// -// private: -// static int getConnBit(const picojson::object& conn, const std::string& -// key) -// { -// using namespace picojson; -// const auto& bits = conn.at(key).get(); -// if (bits.size() != 1) -// ERR_DIE("Invalid JSON: wrong conn size: expected 1, got " -// << bits.size()); -// if (!bits.at(0).is()) -// ERR_DIE( -// "Connection of cells to a constant driver is not -// implemented."); -// return bits.at(0).get(); -// } -// -// public: -// template -// static void read(NetworkBuilder& builder, std::istream& is) -// { -// // Convert Yosys JSON to gates. Thanks to: -// // -// https://github.com/virtualsecureplatform/Iyokan-L1/blob/ef7c9a993ddbfd54ef58e66b116b681e59d90a3c/Converter/YosysConverter.cs -// using namespace picojson; -// -// value v; -// const std::string err = parse(v, is); -// if (!err.empty()) -// ERR_DIE("Invalid JSON of network: " << err); -// -// object& root = v.get(); -// object& modules = root.at("modules").get(); -// if (modules.size() != 1) -// ERR_DIE(".modules should be an object of size 1"); -// object& modul = modules.begin()->second.get(); -// object& ports = modul.at("ports").get(); -// object& cells = modul.at("cells").get(); -// -// std::unordered_map bit2id; -// -// // Create INPUT/OUTPUT and extract port connection info -// std::vector portvec; -// for (auto&& [key, valAny] : ports) { -// object& val = valAny.template get(); -// std::string& direction = val["direction"].get(); -// array& bits = val["bits"].get(); -// -// if (key == "clock") -// continue; -// if (key == "reset" && bits.size() == 0) -// continue; -// if (direction != "input" && direction != "output") -// ERR_DIE("Invalid direction token: " << direction); -// -// const bool isDirInput = direction == "input"; -// const std::string& portName = key; -// for (size_t i = 0; i < bits.size(); i++) { -// const int portBit = i; -// -// if (bits.at(i).is()) { -// // Yosys document -// // (https://yosyshq.net/yosys/cmd_write_json.html) says: -// // -// // Signal bits that are connected to a constant driver -// // are denoted as string "0" or "1" instead of a -// number. -// // -// // We handle this case here. -// -// if (isDirInput) -// ERR_DIE( -// "Invalid bits: INPUT that is connected to a " -// "constant driver is not implemented"); -// -// std::string cnstStr = bits.at(i).get(); -// bool cnst = cnstStr == "1"; -// if (!cnst && cnstStr != "0") -// LOG_S(WARNING) -// << "Constant bit of '{}' is regarded as '0'." -// << cnstStr; -// -// int id1 = builder.OUTPUT(portName, portBit), -// id0 = cnst ? builder.CONSTONE() : builder.CONSTZERO(); -// builder.connect(id0, id1); -// } -// else { -// const int bit = bits.at(i).get(); -// -// int id = isDirInput ? builder.INPUT(portName, portBit) -// : builder.OUTPUT(portName, portBit); -// portvec.emplace_back(isDirInput ? PORT::IN : PORT::OUT, -// id, -// bit); -// if (isDirInput) -// bit2id.emplace(bit, id); -// } -// } -// } -// -// // Create gates and extract gate connection info -// const std::unordered_map mapCell = { -// {"$_NOT_", CELL::NOT}, -// {"$_AND_", CELL::AND}, -// {"$_ANDNOT_", CELL::ANDNOT}, -// {"$_NAND_", CELL::NAND}, -// {"$_OR_", CELL::OR}, -// {"$_XOR_", CELL::XOR}, -// {"$_XNOR_", CELL::XNOR}, -// {"$_NOR_", CELL::NOR}, -// {"$_ORNOT_", CELL::ORNOT}, -// {"$_DFF_P_", CELL::DFFP}, -// {"$_SDFF_PP0_", CELL::SDFFPP0}, -// {"$_SDFF_PP1_", CELL::SDFFPP1}, -// {"$_MUX_", CELL::MUX}, -// }; -// std::vector cellvec; -// for (auto&& [_key, valAny] : cells) { -// object& val = valAny.template get(); -// const std::string& type = val.at("type").get(); -// object& conn = val.at("connections").get(); -// auto get = [&](const char* key) -> int { -// return getConnBit(conn, key); -// }; -// -// int bit = -1, id = -1; -// switch (mapCell.at(type)) { -// case CELL::AND: -// id = builder.AND(); -// cellvec.emplace_back(CELL::AND, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::NAND: -// id = builder.NAND(); -// cellvec.emplace_back(CELL::NAND, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::XOR: -// id = builder.XOR(); -// cellvec.emplace_back(CELL::XOR, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::XNOR: -// id = builder.XNOR(); -// cellvec.emplace_back(CELL::XNOR, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::NOR: -// id = builder.NOR(); -// cellvec.emplace_back(CELL::NOR, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::ANDNOT: -// id = builder.ANDNOT(); -// cellvec.emplace_back(CELL::ANDNOT, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::OR: -// id = builder.OR(); -// cellvec.emplace_back(CELL::OR, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::ORNOT: -// id = builder.ORNOT(); -// cellvec.emplace_back(CELL::ORNOT, id, get("A"), get("B")); -// bit = get("Y"); -// break; -// case CELL::DFFP: -// id = builder.DFF(); -// cellvec.emplace_back(CELL::DFFP, id, get("D")); -// bit = get("Q"); -// break; -// case CELL::SDFFPP0: -// id = builder.SDFF(Bit(false)); -// cellvec.emplace_back(CELL::DFFP, id, get("D")); -// bit = get("Q"); -// break; -// case CELL::SDFFPP1: -// id = builder.SDFF(Bit(true)); -// cellvec.emplace_back(CELL::DFFP, id, get("D")); -// bit = get("Q"); -// break; -// case CELL::NOT: -// id = builder.NOT(); -// cellvec.emplace_back(CELL::NOT, id, get("A")); -// bit = get("Y"); -// break; -// case CELL::MUX: -// id = builder.MUX(); -// cellvec.emplace_back(CELL::MUX, id, get("A"), get("B"), -// get("S")); -// bit = get("Y"); -// break; -// } -// bit2id.emplace(bit, id); -// } -// -// for (auto&& port : portvec) { -// if (port.type == PORT::IN) -// // Actually nothing to do! -// continue; -// builder.connect(bit2id.at(port.bit), port.id); -// } -// -// for (auto&& cell : cellvec) { -// switch (cell.type) { -// case CELL::AND: -// case CELL::NAND: -// case CELL::XOR: -// case CELL::XNOR: -// case CELL::NOR: -// case CELL::ANDNOT: -// case CELL::OR: -// case CELL::ORNOT: -// builder.connect(bit2id.at(cell.bit0), cell.id); -// builder.connect(bit2id.at(cell.bit1), cell.id); -// break; -// case CELL::DFFP: -// case CELL::SDFFPP0: -// case CELL::SDFFPP1: -// case CELL::NOT: -// builder.connect(bit2id.at(cell.bit0), cell.id); -// break; -// case CELL::MUX: -// builder.connect(bit2id.at(cell.bit0), cell.id); -// builder.connect(bit2id.at(cell.bit1), cell.id); -// builder.connect(bit2id.at(cell.bit2), cell.id); -// break; -// } -// } -// } -//}; -// -//} // namespace - -void readYosysJSONNetwork(std::istream& is, NetworkBuilder& nb) -{ - // YosysJSONReader::read(nb, is); -} - -///* class IyokanL1JSONReader */ -// -// namespace { -// -// class IyokanL1JSONReader { -// public: -// template -// static void read(NetworkBuilder& builder, std::istream& is) -// { -// std::unordered_map id2taskId; -// auto addId = [&](int id, int taskId) { id2taskId.emplace(id, taskId); -// }; auto findTaskId = [&](int id) { -// auto it = id2taskId.find(id); -// if (it == id2taskId.end()) -// error::die("Invalid JSON"); -// return it->second; -// }; -// auto connectIds = [&](int from, int to) { -// builder.connect(findTaskId(from), findTaskId(to)); -// }; -// -// picojson::value v; -// const std::string err = picojson::parse(v, is); -// if (!err.empty()) -// error::die("Invalid JSON of network: ", err); -// -// picojson::object& obj = v.get(); -// picojson::array& cells = obj["cells"].get(); -// picojson::array& ports = obj["ports"].get(); -// for (const auto& e : ports) { -// picojson::object port = e.get(); -// std::string type = port.at("type").get(); -// int id = static_cast(port.at("id").get()); -// std::string portName = port.at("portName").get(); -// int portBit = static_cast(port.at("portBit").get()); -// if (type == "input") -// addId(id, builder.INPUT(portName, portBit)); -// else if (type == "output") -// addId(id, builder.OUTPUT(portName, portBit)); -// } -// for (const auto& e : cells) { -// picojson::object cell = e.get(); -// std::string type = cell.at("type").get(); -// int id = static_cast(cell.at("id").get()); -// if (type == "AND") -// addId(id, builder.AND()); -// else if (type == "NAND") -// addId(id, builder.NAND()); -// else if (type == "ANDNOT") -// addId(id, builder.ANDNOT()); -// else if (type == "XOR") -// addId(id, builder.XOR()); -// else if (type == "XNOR") -// addId(id, builder.XNOR()); -// else if (type == "DFFP") -// addId(id, builder.DFF()); -// else if (type == "NOT") -// addId(id, builder.NOT()); -// else if (type == "NOR") -// addId(id, builder.NOR()); -// else if (type == "OR") -// addId(id, builder.OR()); -// else if (type == "ORNOT") -// addId(id, builder.ORNOT()); -// else if (type == "MUX") -// addId(id, builder.MUX()); -// else { -// bool valid = false; -// // If builder.RAM() exists -// if constexpr (detail::hasMethodFuncRAM) { -// if (type == "RAM") { -// int addr = cell.at("ramAddress").get(), -// bit = cell.at("ramBit").get(); -// addId(id, builder.RAM(addr, bit)); -// valid = true; -// } -// } -// -// if (!valid) { -// error::die("Invalid JSON of network. Invalid type: ", -// type); -// } -// } -// } -// for (const auto& e : ports) { -// picojson::object port = e.get(); -// std::string type = port.at("type").get(); -// int id = static_cast(port.at("id").get()); -// picojson::array& bits = port.at("bits").get(); -// if (type == "input") { -// // nothing to do! -// } -// else if (type == "output") { -// for (const auto& b : bits) { -// int logic = static_cast(b.get()); -// connectIds(logic, id); -// } -// } -// } -// for (const auto& e : cells) { -// picojson::object cell = e.get(); -// std::string type = cell.at("type").get(); -// int id = static_cast(cell.at("id").get()); -// picojson::object input = cell.at("input").get(); -// if (type == "AND" || type == "NAND" || type == "XOR" || -// type == "XNOR" || type == "NOR" || type == "ANDNOT" || -// type == "OR" || type == "ORNOT") { -// int A = static_cast(input.at("A").get()); -// int B = static_cast(input.at("B").get()); -// connectIds(A, id); -// connectIds(B, id); -// } -// else if (type == "DFFP" || type == "RAM") { -// int D = static_cast(input.at("D").get()); -// connectIds(D, id); -// } -// else if (type == "NOT") { -// int A = static_cast(input.at("A").get()); -// connectIds(A, id); -// } -// else if (type == "MUX") { -// int A = static_cast(input.at("A").get()); -// int B = static_cast(input.at("B").get()); -// int S = static_cast(input.at("S").get()); -// connectIds(A, id); -// connectIds(B, id); -// connectIds(S, id); -// } -// else { -// error::die("Invalid JSON of network. Invalid type: ", type); -// } -// } -// } -//}; -// -//} // namespace - -void readIyokanL1JSONNetwork(std::istream& is, NetworkBuilder& nb) -{ - // IyokanL1JSONReader::read(nb, is); -} - -/**************************************************/ -/***** TEST ***************************************/ -/**************************************************/ - -/* -void testBuildAdder(NetworkBuilder& nb) -{ - UID a = nb.INPUT("0", "A", 0), b = nb.INPUT("1", "B", 0), - ci = nb.INPUT("2", "Ci", 0), s = nb.OUTPUT("3", "S", 0), - c0 = nb.OUTPUT("4", "C0", 0), and0 = nb.AND("5"), and1 = nb.AND("6"), - or0 = nb.OR("7"), xor0 = nb.XOR("8"), xor1 = nb.XOR("9"); - nb.connect(a, xor0); - nb.connect(b, xor0); - nb.connect(ci, xor1); - nb.connect(xor0, xor1); - nb.connect(a, and0); - nb.connect(b, and0); - nb.connect(ci, and1); - nb.connect(xor0, and1); - nb.connect(and1, or0); - nb.connect(and0, or0); - nb.connect(xor1, s); - nb.connect(or0, c0); -} -*/ - } // namespace nt diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 28a03d7..ac94c1d 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -414,8 +414,10 @@ class Blueprint { const std::unordered_map& atPortWidths() const; }; -void readYosysJSONNetwork(std::istream& is, NetworkBuilder& nb); -void readIyokanL1JSONNetwork(std::istream& is, NetworkBuilder& nb); +void readYosysJSONNetwork(const std::string& nodeName, std::istream& is, + NetworkBuilder& nb); +void readIyokanL1JSONNetwork(const std::string& nodeName, std::istream& is, + NetworkBuilder& nb); } // namespace nt diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index a0ef85b..b35bd56 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -361,11 +361,11 @@ void Frontend::readNetworkFromFile(const blueprint::File& file, << "[[file]] of type 'iyokanl1-json' is deprecated. You don't need " "to use Iyokan-L1. Use Yosys JSON directly by specifying type " "'yosys-json'."; - readIyokanL1JSONNetwork(ifs, nb); + readIyokanL1JSONNetwork(file.name, ifs, nb); break; case blueprint::File::TYPE::YOSYS_JSON: - readYosysJSONNetwork(ifs, nb); + readYosysJSONNetwork(file.name, ifs, nb); break; } } @@ -520,6 +520,100 @@ void test0() t1->getOutput(dh); assert(dh.getBit() == 1_b); } + + { + Allocator alc; + NetworkBuilder nb{alc}; + + std::ifstream ifs{"test/yosys-json/addr-4bit-yosys.json"}; + assert(ifs); + readYosysJSONNetwork("addr", ifs, nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tA0 = finder.findByConfigName({"addr", "io_inA", 0}), + *tA1 = finder.findByConfigName({"addr", "io_inA", 1}), + *tA2 = finder.findByConfigName({"addr", "io_inA", 2}), + *tA3 = finder.findByConfigName({"addr", "io_inA", 3}); + Task *tB0 = finder.findByConfigName({"addr", "io_inB", 0}), + *tB1 = finder.findByConfigName({"addr", "io_inB", 1}), + *tB2 = finder.findByConfigName({"addr", "io_inB", 2}), + *tB3 = finder.findByConfigName({"addr", "io_inB", 3}); + Task *tO0 = finder.findByConfigName({"addr", "io_out", 0}), + *tO1 = finder.findByConfigName({"addr", "io_out", 1}), + *tO2 = finder.findByConfigName({"addr", "io_out", 2}), + *tO3 = finder.findByConfigName({"addr", "io_out", 3}); + + tA0->setInput(&bit1); + tA1->setInput(&bit0); + tA2->setInput(&bit1); + tA3->setInput(&bit0); + tB0->setInput(&bit0); + tB1->setInput(&bit1); + tB2->setInput(&bit0); + tB3->setInput(&bit1); + + runner.run(); + + tO0->getOutput(dh); + assert(dh.getBit() == 1_b); + tO1->getOutput(dh); + assert(dh.getBit() == 1_b); + tO2->getOutput(dh); + assert(dh.getBit() == 1_b); + tO3->getOutput(dh); + assert(dh.getBit() == 1_b); + } + + { + Allocator alc; + NetworkBuilder nb{alc}; + + std::ifstream ifs{"test/iyokanl1-json/addr-4bit-iyokanl1.json"}; + assert(ifs); + readIyokanL1JSONNetwork("addr", ifs, nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tA0 = finder.findByConfigName({"addr", "io_inA", 0}), + *tA1 = finder.findByConfigName({"addr", "io_inA", 1}), + *tA2 = finder.findByConfigName({"addr", "io_inA", 2}), + *tA3 = finder.findByConfigName({"addr", "io_inA", 3}); + Task *tB0 = finder.findByConfigName({"addr", "io_inB", 0}), + *tB1 = finder.findByConfigName({"addr", "io_inB", 1}), + *tB2 = finder.findByConfigName({"addr", "io_inB", 2}), + *tB3 = finder.findByConfigName({"addr", "io_inB", 3}); + Task *tO0 = finder.findByConfigName({"addr", "io_out", 0}), + *tO1 = finder.findByConfigName({"addr", "io_out", 1}), + *tO2 = finder.findByConfigName({"addr", "io_out", 2}), + *tO3 = finder.findByConfigName({"addr", "io_out", 3}); + + tA0->setInput(&bit1); + tA1->setInput(&bit0); + tA2->setInput(&bit0); + tA3->setInput(&bit0); + tB0->setInput(&bit0); + tB1->setInput(&bit1); + tB2->setInput(&bit0); + tB3->setInput(&bit1); + + runner.run(); + + tO0->getOutput(dh); + assert(dh.getBit() == 1_b); + tO1->getOutput(dh); + assert(dh.getBit() == 1_b); + tO2->getOutput(dh); + assert(dh.getBit() == 0_b); + tO3->getOutput(dh); + assert(dh.getBit() == 1_b); + } } } // namespace plain diff --git a/src/network_reader.cpp b/src/network_reader.cpp new file mode 100644 index 0000000..0558e19 --- /dev/null +++ b/src/network_reader.cpp @@ -0,0 +1,444 @@ +#include "iyokan_nt.hpp" + +#include + +namespace nt { + +/* class YosysJSONReader */ + +namespace { + +class YosysJSONReader { +private: + enum class PORT { + IN, + OUT, + }; + + struct Port { + PORT type; + int id, bit; + + Port(PORT type, int id, int bit) : type(type), id(id), bit(bit) + { + } + }; + + enum class CELL { + NOT, + AND, + ANDNOT, + NAND, + OR, + XOR, + XNOR, + NOR, + ORNOT, + DFFP, + // SDFFPP0, + // SDFFPP1, + MUX, + }; + + struct Cell { + CELL type; + int id, bit0, bit1, bit2; + + Cell(CELL type, int id, int bit0) + : type(type), id(id), bit0(bit0), bit1(-1), bit2(-1) + { + } + Cell(CELL type, int id, int bit0, int bit1) + : type(type), id(id), bit0(bit0), bit1(bit1), bit2(-1) + { + } + Cell(CELL type, int id, int bit0, int bit1, int bit2) + : type(type), id(id), bit0(bit0), bit1(bit1), bit2(bit2) + { + } + }; + +private: + static int getConnBit(const picojson::object& conn, const std::string& key) + { + using namespace picojson; + const auto& bits = conn.at(key).get(); + if (bits.size() != 1) + ERR_DIE("Invalid JSON: wrong conn size: expected 1, got " + << bits.size()); + if (!bits.at(0).is()) + ERR_DIE( + "Connection of cells to a constant driver is not implemented."); + return bits.at(0).get(); + } + +public: + template + static void read(const std::string& nodeName, std::istream& is, + NetworkBuilder& builder) + { + // Convert Yosys JSON to gates. Thanks to: + // https://github.com/virtualsecureplatform/Iyokan-L1/blob/ef7c9a993ddbfd54ef58e66b116b681e59d90a3c/Converter/YosysConverter.cs + using namespace picojson; + + value v; + const std::string err = parse(v, is); + if (!err.empty()) + ERR_DIE("Invalid JSON of network: " << err); + + object& root = v.get(); + object& modules = root.at("modules").get(); + if (modules.size() != 1) + ERR_DIE(".modules should be an object of size 1"); + object& modul = modules.begin()->second.get(); + object& ports = modul.at("ports").get(); + object& cells = modul.at("cells").get(); + + std::unordered_map bit2id; + + // Create INPUT/OUTPUT and extract port connection info + std::vector portvec; + for (auto&& [key, valAny] : ports) { + object& val = valAny.template get(); + std::string& direction = val["direction"].get(); + array& bits = val["bits"].get(); + + if (key == "clock") + continue; + if (key == "reset" && bits.size() == 0) + continue; + if (direction != "input" && direction != "output") + ERR_DIE("Invalid direction token: " << direction); + + const bool isDirInput = direction == "input"; + const std::string& portName = key; + for (size_t i = 0; i < bits.size(); i++) { + const int portBit = i; + + if (bits.at(i).is()) { + // Yosys document + // (https://yosyshq.net/yosys/cmd_write_json.html) says: + // + // Signal bits that are connected to a constant driver + // are denoted as string "0" or "1" instead of a number. + // + // We handle this case here. + + if (isDirInput) + ERR_DIE( + "Invalid bits: INPUT that is connected to a " + "constant driver is not implemented"); + + std::string cnstStr = bits.at(i).get(); + bool cnst = cnstStr == "1"; + if (!cnst && cnstStr != "0") + LOG_S(WARNING) + << "Constant bit of '{}' is regarded as '0'." + << cnstStr; + + int id1 = builder.OUTPUT(nodeName, portName, portBit), + id0 = cnst ? builder.CONSTONE() : builder.CONSTZERO(); + builder.connect(id0, id1); + } + else { + const int bit = bits.at(i).get(); + + int id = isDirInput + ? builder.INPUT(nodeName, portName, portBit) + : builder.OUTPUT(nodeName, portName, portBit); + portvec.emplace_back(isDirInput ? PORT::IN : PORT::OUT, id, + bit); + if (isDirInput) + bit2id.emplace(bit, id); + } + } + } + + // Create gates and extract gate connection info + const std::unordered_map mapCell = { + {"$_NOT_", CELL::NOT}, + {"$_AND_", CELL::AND}, + {"$_ANDNOT_", CELL::ANDNOT}, + {"$_NAND_", CELL::NAND}, + {"$_OR_", CELL::OR}, + {"$_XOR_", CELL::XOR}, + {"$_XNOR_", CELL::XNOR}, + {"$_NOR_", CELL::NOR}, + {"$_ORNOT_", CELL::ORNOT}, + {"$_DFF_P_", CELL::DFFP}, + //{"$_SDFF_PP0_", CELL::SDFFPP0}, + //{"$_SDFF_PP1_", CELL::SDFFPP1}, + {"$_MUX_", CELL::MUX}, + }; + std::vector cellvec; + for (auto&& [_key, valAny] : cells) { + object& val = valAny.template get(); + const std::string& type = val.at("type").get(); + object& conn = val.at("connections").get(); + auto get = [&](const char* key) -> int { + return getConnBit(conn, key); + }; + + int bit = -1, id = -1; + switch (mapCell.at(type)) { + case CELL::AND: + id = builder.AND(); + cellvec.emplace_back(CELL::AND, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::NAND: + id = builder.NAND(); + cellvec.emplace_back(CELL::NAND, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::XOR: + id = builder.XOR(); + cellvec.emplace_back(CELL::XOR, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::XNOR: + id = builder.XNOR(); + cellvec.emplace_back(CELL::XNOR, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::NOR: + id = builder.NOR(); + cellvec.emplace_back(CELL::NOR, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::ANDNOT: + id = builder.ANDNOT(); + cellvec.emplace_back(CELL::ANDNOT, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::OR: + id = builder.OR(); + cellvec.emplace_back(CELL::OR, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::ORNOT: + id = builder.ORNOT(); + cellvec.emplace_back(CELL::ORNOT, id, get("A"), get("B")); + bit = get("Y"); + break; + case CELL::DFFP: + id = builder.DFF(); + cellvec.emplace_back(CELL::DFFP, id, get("D")); + bit = get("Q"); + break; + /* + case CELL::SDFFPP0: + id = builder.SDFF(Bit(false)); + cellvec.emplace_back(CELL::DFFP, id, get("D")); + bit = get("Q"); + break; + case CELL::SDFFPP1: + id = builder.SDFF(Bit(true)); + cellvec.emplace_back(CELL::DFFP, id, get("D")); + bit = get("Q"); + break; + */ + case CELL::NOT: + id = builder.NOT(); + cellvec.emplace_back(CELL::NOT, id, get("A")); + bit = get("Y"); + break; + case CELL::MUX: + id = builder.MUX(); + cellvec.emplace_back(CELL::MUX, id, get("A"), get("B"), + get("S")); + bit = get("Y"); + break; + } + bit2id.emplace(bit, id); + } + + for (auto&& port : portvec) { + if (port.type == PORT::IN) + // Actually nothing to do! + continue; + builder.connect(bit2id.at(port.bit), port.id); + } + + for (auto&& cell : cellvec) { + switch (cell.type) { + case CELL::AND: + case CELL::NAND: + case CELL::XOR: + case CELL::XNOR: + case CELL::NOR: + case CELL::ANDNOT: + case CELL::OR: + case CELL::ORNOT: + builder.connect(bit2id.at(cell.bit0), cell.id); + builder.connect(bit2id.at(cell.bit1), cell.id); + break; + case CELL::DFFP: + // case CELL::SDFFPP0: + // case CELL::SDFFPP1: + case CELL::NOT: + builder.connect(bit2id.at(cell.bit0), cell.id); + break; + case CELL::MUX: + builder.connect(bit2id.at(cell.bit0), cell.id); + builder.connect(bit2id.at(cell.bit1), cell.id); + builder.connect(bit2id.at(cell.bit2), cell.id); + break; + } + } + } +}; + +} // namespace + +void readYosysJSONNetwork(const std::string& nodeName, std::istream& is, + NetworkBuilder& nb) +{ + YosysJSONReader::read(nodeName, is, nb); +} + +/* class IyokanL1JSONReader */ + +namespace { + +class IyokanL1JSONReader { +public: + template + static void read(const std::string& nodeName, std::istream& is, + NetworkBuilder& builder) + { + std::unordered_map id2taskId; + auto addId = [&](int id, int taskId) { id2taskId.emplace(id, taskId); }; + auto findTaskId = [&](int id) { + auto it = id2taskId.find(id); + if (it == id2taskId.end()) + ERR_DIE("Invalid JSON"); + return it->second; + }; + auto connectIds = [&](int from, int to) { + builder.connect(findTaskId(from), findTaskId(to)); + }; + + picojson::value v; + const std::string err = picojson::parse(v, is); + if (!err.empty()) + ERR_DIE("Invalid JSON of network: " << err); + + picojson::object& obj = v.get(); + picojson::array& cells = obj["cells"].get(); + picojson::array& ports = obj["ports"].get(); + for (const auto& e : ports) { + picojson::object port = e.get(); + std::string type = port.at("type").get(); + int id = static_cast(port.at("id").get()); + std::string portName = port.at("portName").get(); + int portBit = static_cast(port.at("portBit").get()); + if (type == "input") + addId(id, builder.INPUT(nodeName, portName, portBit)); + else if (type == "output") + addId(id, builder.OUTPUT(nodeName, portName, portBit)); + } + for (const auto& e : cells) { + picojson::object cell = e.get(); + std::string type = cell.at("type").get(); + int id = static_cast(cell.at("id").get()); + if (type == "AND") + addId(id, builder.AND()); + else if (type == "NAND") + addId(id, builder.NAND()); + else if (type == "ANDNOT") + addId(id, builder.ANDNOT()); + else if (type == "XOR") + addId(id, builder.XOR()); + else if (type == "XNOR") + addId(id, builder.XNOR()); + else if (type == "DFFP") + addId(id, builder.DFF()); + else if (type == "NOT") + addId(id, builder.NOT()); + else if (type == "NOR") + addId(id, builder.NOR()); + else if (type == "OR") + addId(id, builder.OR()); + else if (type == "ORNOT") + addId(id, builder.ORNOT()); + else if (type == "MUX") + addId(id, builder.MUX()); + else { + bool valid = false; + /* FIXME + // If builder.RAM() exists + if constexpr (detail::hasMethodFuncRAM) { + if (type == "RAM") { + int addr = cell.at("ramAddress").get(), + bit = cell.at("ramBit").get(); + addId(id, builder.RAM(addr, bit)); + valid = true; + } + } + */ + + if (!valid) + ERR_DIE("Invalid JSON of network. Invalid type: " << type); + } + } + for (const auto& e : ports) { + picojson::object port = e.get(); + std::string type = port.at("type").get(); + int id = static_cast(port.at("id").get()); + picojson::array& bits = port.at("bits").get(); + if (type == "input") { + // nothing to do! + } + else if (type == "output") { + for (const auto& b : bits) { + int logic = static_cast(b.get()); + connectIds(logic, id); + } + } + } + for (const auto& e : cells) { + picojson::object cell = e.get(); + std::string type = cell.at("type").get(); + int id = static_cast(cell.at("id").get()); + picojson::object input = cell.at("input").get(); + if (type == "AND" || type == "NAND" || type == "XOR" || + type == "XNOR" || type == "NOR" || type == "ANDNOT" || + type == "OR" || type == "ORNOT") { + int A = static_cast(input.at("A").get()); + int B = static_cast(input.at("B").get()); + connectIds(A, id); + connectIds(B, id); + } + else if (type == "DFFP" || type == "RAM") { + int D = static_cast(input.at("D").get()); + connectIds(D, id); + } + else if (type == "NOT") { + int A = static_cast(input.at("A").get()); + connectIds(A, id); + } + else if (type == "MUX") { + int A = static_cast(input.at("A").get()); + int B = static_cast(input.at("B").get()); + int S = static_cast(input.at("S").get()); + connectIds(A, id); + connectIds(B, id); + connectIds(S, id); + } + else { + ERR_DIE("Invalid JSON of network. Invalid type: " << type); + } + } + } +}; + +} // namespace + +void readIyokanL1JSONNetwork(const std::string& nodeName, std::istream& is, + NetworkBuilder& nb) +{ + IyokanL1JSONReader::read(nodeName, is, nb); +} + +} // namespace nt From de90856a1c519ad05061e11368a3d47ab9e2c9e5 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 31 Oct 2021 21:06:52 +0900 Subject: [PATCH 18/54] wip --- src/iyokan_nt_plain.cpp | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index b35bd56..d75f634 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -614,6 +614,50 @@ void test0() tO3->getOutput(dh); assert(dh.getBit() == 1_b); } + + { + Allocator alc; + NetworkBuilder nb{alc}; + + std::ifstream ifs{"test/yosys-json/counter-4bit-yosys.json"}; + assert(ifs); + readYosysJSONNetwork("counter", ifs, nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tRst = finder.findByConfigName({"counter", "reset", 0}), + *tOut0 = finder.findByConfigName({"counter", "io_out", 0}), + *tOut1 = finder.findByConfigName({"counter", "io_out", 1}), + *tOut2 = finder.findByConfigName({"counter", "io_out", 2}), + *tOut3 = finder.findByConfigName({"counter", "io_out", 3}); + + tRst->setInput(&bit1); + runner.run(); + tRst->setInput(&bit0); + + // Cycle #1 + runner.tick(); + runner.run(); + // Cycle #2 + runner.tick(); + runner.run(); + // Cycle #3 + runner.tick(); + runner.run(); + + // The output is 2, that is, '0b0010' + tOut0->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut1->getOutput(dh); + assert(dh.getBit() == 1_b); + tOut2->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut3->getOutput(dh); + assert(dh.getBit() == 0_b); + } } } // namespace plain From 529737d64fe989f66cb2f9ca76a28085087a52e7 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 31 Oct 2021 22:13:36 +0900 Subject: [PATCH 19/54] wip --- src/iyokan_nt.cpp | 32 +++++++------- src/iyokan_nt.hpp | 30 ++++++++++++- src/iyokan_nt_plain.cpp | 93 ++++++++++++++++++++++++++++++++++++++++- src/network_reader.cpp | 18 ++++---- 4 files changed, 145 insertions(+), 28 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 56e9810..cab8575 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -94,6 +94,11 @@ bool Task::canRunPlain() const return false; } +void Task::onAfterFirstTick() +{ + // Do nothing by default. +} + void Task::startAsynchronously(plain::WorkerInfo&) { ERR_UNREACHABLE; @@ -166,19 +171,6 @@ const TaskFinder& Network::finder() const return finder_; } -void Network::pushReadyTasks(ReadyQueue& readyQueue) -{ - for (auto&& task : tasks_) - if (task->areAllInputsReady()) - readyQueue.push(task.get()); -} - -void Network::tick() -{ - for (auto&& task : tasks_) - task->tick(); -} - /* class NetworkBuilder */ NetworkBuilder::NetworkBuilder(Allocator& alc) @@ -268,7 +260,12 @@ void NetworkRunner::prepareToRun() assert(readyQueue_.empty()); numFinishedTargets_ = 0; - network_.pushReadyTasks(readyQueue_); + + // Push ready tasks to the ready queue. + network_.eachTask([&](Task* task) { + if (task->areAllInputsReady()) + readyQueue_.push(task); + }); } void NetworkRunner::update() @@ -305,7 +302,12 @@ void NetworkRunner::run() void NetworkRunner::tick() { - network_.tick(); + network_.eachTask([&](Task* task) { task->tick(); }); +} + +void NetworkRunner::onAfterFirstTick() +{ + network_.eachTask([&](Task* task) { task->onAfterFirstTick(); }); } } // namespace nt diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index ac94c1d..5e2352b 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -88,9 +88,21 @@ class Task { // tick() resets the internal state of the task for the next cycle virtual void tick(); + + // Get output value. Only available for output gates. virtual void getOutput(DataHolder&); + + // Set input value. Only available for input gates. virtual void setInput(const DataHolder&); + + // onAfterFirstTick() will be called after the first tick. + virtual void onAfterFirstTick(); + + // Return true iff this task can be run in plaintext mode. virtual bool canRunPlain() const; + + // Start this task asynchronously in plaintext mode. + // Only available when canRunPlain() returns true. virtual void startAsynchronously(plain::WorkerInfo&); }; @@ -251,8 +263,19 @@ class Network { size_t size() const; const TaskFinder& finder() const; - void pushReadyTasks(ReadyQueue& readyQueue); - void tick(); + template + void eachTask(F f) const + { + for (auto&& task : tasks_) + f(task.get()); + } + + template + void eachTask(F f) + { + for (auto&& task : tasks_) + f(task.get()); + } }; class NetworkBuilder { @@ -305,6 +328,8 @@ class NetworkBuilder { virtual UID NOT() = 0; virtual UID OR() = 0; virtual UID ORNOT() = 0; + virtual UID SDFF0() = 0; + virtual UID SDFF1() = 0; virtual UID XNOR() = 0; virtual UID XOR() = 0; }; @@ -345,6 +370,7 @@ class NetworkRunner { bool isRunning() const; void run(); void tick(); + void onAfterFirstTick(); }; namespace blueprint { // blueprint components diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index d75f634..f03b996 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -92,10 +92,23 @@ class TaskOutput : public TaskCommon { }; class TaskDFF : public nt::TaskDFF { +private: + std::optional initialValue_; + public: TaskDFF(Label label, Allocator& alc) - : nt::TaskDFF(std::move(label), alc) + : nt::TaskDFF(std::move(label), alc), initialValue_(std::nullopt) + { + } + + TaskDFF(Bit initialValue, Label label, Allocator& alc) + : nt::TaskDFF(std::move(label), alc), initialValue_(initialValue) + { + } + + void onAfterFirstTick() override { + output() = initialValue_.value_or(output()); } bool canRunPlain() const override @@ -198,6 +211,24 @@ class NetworkBuilder : public nt::NetworkBuilder { DEF_COMMON_TASK(XOR, Xor); #undef DEF_COMMON_TASK + UID SDFF0() override + { + UID uid = genUID(); + TaskDFF* task = emplaceTask( + 0_b, Label{uid, "SDFF0", std::nullopt}, currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } + + UID SDFF1() override + { + UID uid = genUID(); + TaskDFF* task = emplaceTask( + 1_b, Label{uid, "SDFF1", std::nullopt}, currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } + UID INPUT(const std::string& nodeName, const std::string& portName, int portBit) override { @@ -658,6 +689,66 @@ void test0() tOut3->getOutput(dh); assert(dh.getBit() == 0_b); } + + { + Allocator alc; + NetworkBuilder nb{alc}; + + std::ifstream ifs{"test/yosys-json/register-init-4bit-yosys.json"}; + assert(ifs); + readYosysJSONNetwork("register_init", ifs, nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tIn0 = finder.findByConfigName({"register_init", "io_in", 0}), + *tIn1 = finder.findByConfigName({"register_init", "io_in", 1}), + *tIn2 = finder.findByConfigName({"register_init", "io_in", 2}), + *tIn3 = finder.findByConfigName({"register_init", "io_in", 3}), + *tOut0 = finder.findByConfigName({"register_init", "io_out", 0}), + *tOut1 = finder.findByConfigName({"register_init", "io_out", 1}), + *tOut2 = finder.findByConfigName({"register_init", "io_out", 2}), + *tOut3 = finder.findByConfigName({"register_init", "io_out", 3}); + + // Set 0xc to input + tIn0->setInput(&bit0); + tIn1->setInput(&bit0); + tIn2->setInput(&bit1); + tIn3->setInput(&bit1); + + // Skip the reset cycle (assume --skip-reset flag). + + // Cycle #1 + runner.tick(); + runner.onAfterFirstTick(); + runner.run(); + + // The output is 9, that is, '0b1001' + tOut0->getOutput(dh); + assert(dh.getBit() == 1_b); + tOut1->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut2->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut3->getOutput(dh); + assert(dh.getBit() == 1_b); + + // Cycle #2 + runner.tick(); + runner.run(); + + // The output is 12, that is, '0b1100' + tOut0->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut1->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut2->getOutput(dh); + assert(dh.getBit() == 1_b); + tOut3->getOutput(dh); + assert(dh.getBit() == 1_b); + } } } // namespace plain diff --git a/src/network_reader.cpp b/src/network_reader.cpp index 0558e19..e3ce4e4 100644 --- a/src/network_reader.cpp +++ b/src/network_reader.cpp @@ -35,8 +35,8 @@ class YosysJSONReader { NOR, ORNOT, DFFP, - // SDFFPP0, - // SDFFPP1, + SDFFPP0, + SDFFPP1, MUX, }; @@ -166,8 +166,8 @@ class YosysJSONReader { {"$_NOR_", CELL::NOR}, {"$_ORNOT_", CELL::ORNOT}, {"$_DFF_P_", CELL::DFFP}, - //{"$_SDFF_PP0_", CELL::SDFFPP0}, - //{"$_SDFF_PP1_", CELL::SDFFPP1}, + {"$_SDFF_PP0_", CELL::SDFFPP0}, + {"$_SDFF_PP1_", CELL::SDFFPP1}, {"$_MUX_", CELL::MUX}, }; std::vector cellvec; @@ -226,18 +226,16 @@ class YosysJSONReader { cellvec.emplace_back(CELL::DFFP, id, get("D")); bit = get("Q"); break; - /* case CELL::SDFFPP0: - id = builder.SDFF(Bit(false)); + id = builder.SDFF0(); cellvec.emplace_back(CELL::DFFP, id, get("D")); bit = get("Q"); break; case CELL::SDFFPP1: - id = builder.SDFF(Bit(true)); + id = builder.SDFF1(); cellvec.emplace_back(CELL::DFFP, id, get("D")); bit = get("Q"); break; - */ case CELL::NOT: id = builder.NOT(); cellvec.emplace_back(CELL::NOT, id, get("A")); @@ -274,8 +272,8 @@ class YosysJSONReader { builder.connect(bit2id.at(cell.bit1), cell.id); break; case CELL::DFFP: - // case CELL::SDFFPP0: - // case CELL::SDFFPP1: + case CELL::SDFFPP0: + case CELL::SDFFPP1: case CELL::NOT: builder.connect(bit2id.at(cell.bit0), cell.id); break; From d66e39067458c16a8dbdb499163b4bafbea8b105 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 31 Oct 2021 23:25:24 +0900 Subject: [PATCH 20/54] wip --- src/blueprint.cpp | 77 +++++++++++++++++++++ src/iyokan_nt.hpp | 3 + src/iyokan_nt_plain.cpp | 149 +++++++++++++++++++++++++--------------- 3 files changed, 173 insertions(+), 56 deletions(-) diff --git a/src/blueprint.cpp b/src/blueprint.cpp index 9e79c9a..b8c11b4 100644 --- a/src/blueprint.cpp +++ b/src/blueprint.cpp @@ -293,4 +293,81 @@ const std::unordered_map& Blueprint::atPortWidths() const return atPortWidths_; } +/* makeMUXROM */ + +namespace { +void make1bitROMWithMUX(const std::string& nodeName, + const std::vector& addrInputs, + size_t outRdataWidth, size_t indexOutRdata, + NetworkBuilder& nb) +{ + /* + INPUT + addr[1] ------------------------------+ + INPUT | + addr[0] --+-----------------+ | + | | | + | ROM | | + | romdata[0] -- |\ | + | ROM | | --+ | + | romdata[1] -- |/ +-- |\ OUTPUT + | | | -- ... -- rdata[indexOutRdata] + +-----------------+ +-- |/ + | | + ROM | | + romdata[2] -- +\ | + ROM | | --+ + romdata[3] -- |/ + + ... + + ROM + addr[2^inAddrWidth-1] -- ... + */ + + const int inAddrWidth = addrInputs.size(); + + // Create ROMs + std::vector workingIds; + for (size_t i = 0; i < (1 << inAddrWidth); i++) { + UID id = nb.ROM(nodeName, "romdata", indexOutRdata + i * outRdataWidth); + workingIds.push_back(id); + } + + // Create MUXs + for (size_t i = 0; i < inAddrWidth; i++) { + assert(workingIds.size() > 0 && workingIds.size() % 2 == 0); + std::vector newWorkingIds; + for (int j = 0; j < workingIds.size(); j += 2) { + int id = nb.MUX(); + nb.connect(workingIds.at(j), id); + nb.connect(workingIds.at(j + 1), id); + nb.connect(addrInputs.at(i), id); + newWorkingIds.push_back(id); + } + workingIds.swap(newWorkingIds); + } + assert(workingIds.size() == 1); + + // Create output + int id = nb.OUTPUT(nodeName, "rdata", indexOutRdata); + nb.connect(workingIds.at(0), id); +} +} // namespace + +void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb) +{ + // Create inputs + std::vector addrInputs; + for (size_t i = 0; i < rom.inAddrWidth; i++) { + UID id = nb.INPUT(rom.name, "addr", i); + addrInputs.push_back(id); + } + + // Create 1bit ROMs + for (size_t i = 0; i < rom.outRdataWidth; i++) { + make1bitROMWithMUX(rom.name, addrInputs, rom.outRdataWidth, i, nb); + } +} + } // namespace nt diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 5e2352b..87b8d85 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -315,6 +315,8 @@ class NetworkBuilder { int portBit) = 0; virtual UID OUTPUT(const std::string& nodeName, const std::string& portName, int portBit) = 0; + virtual UID ROM(const std::string& nodeName, const std::string& portName, + int portBit) = 0; virtual UID AND() = 0; virtual UID ANDNOT() = 0; @@ -444,6 +446,7 @@ void readYosysJSONNetwork(const std::string& nodeName, std::istream& is, NetworkBuilder& nb); void readIyokanL1JSONNetwork(const std::string& nodeName, std::istream& is, NetworkBuilder& nb); +void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb); } // namespace nt diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index f03b996..180ed43 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -123,6 +123,29 @@ class TaskDFF : public nt::TaskDFF { } }; +class TaskROM : public TaskCommon { +public: + TaskROM(Bit value, Label label, Allocator& alc) + : TaskCommon(label, alc, 0) + { + output() = value; + } + + void startAsynchronously(WorkerInfo&) override + { + } + + bool hasFinished() const override + { + return true; + } + + bool canRunPlain() const override + { + return true; + } +}; + #define DEF_COMMON_TASK_CLASS(CamelName, inputSize, expr) \ class Task##CamelName : public TaskCommon { \ public: \ @@ -162,6 +185,7 @@ class NetworkBuilder : public nt::NetworkBuilder { private: std::unordered_map*> uid2common_; UID nextUID_; + const PlainPacket* const reqPacket_; private: UID genUID() @@ -171,7 +195,18 @@ class NetworkBuilder : public nt::NetworkBuilder { public: NetworkBuilder(Allocator& alc) - : nt::NetworkBuilder(alc), uid2common_(), nextUID_(0) + : nt::NetworkBuilder(alc), + uid2common_(), + nextUID_(0), + reqPacket_(nullptr) + { + } + + NetworkBuilder(const PlainPacket& reqPacket, Allocator& alc) + : nt::NetworkBuilder(alc), + uid2common_(), + nextUID_(0), + reqPacket_(&reqPacket) { } @@ -252,6 +287,21 @@ class NetworkBuilder : public nt::NetworkBuilder { uid2common_.emplace(uid, task); return uid; } + + UID ROM(const std::string& nodeName, const std::string& portName, + int portBit) override + { + assert(reqPacket_ != nullptr); + assert(portName == "romdata"); + + UID uid = genUID(); + TaskROM* task = emplaceTask( + reqPacket_->rom.at(nodeName).at(portBit), + Label{uid, "ROM", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } }; enum class SCHED { @@ -289,17 +339,6 @@ class Frontend { private: static void readNetworkFromFile(const blueprint::File& file, nt::NetworkBuilder& nb); - static void makeMUXRAM(const blueprint::BuiltinRAM& ram, - nt::NetworkBuilder& nb); - static void makeMUXROM(const blueprint::BuiltinROM& rom, - nt::NetworkBuilder& nb); - -protected: - void makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb); - void makeROM(const blueprint::BuiltinROM& rom, nt::NetworkBuilder& nb); - void make1bitROMWithMUX(const blueprint::BuiltinROM& rom, - const std::vector& addrInputs, - nt::NetworkBuilder& nb); public: Frontend(const RunParameter& pr, Allocator& alc); @@ -312,7 +351,7 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) currentCycle_(0), bp_(pr_.blueprintFile) { - NetworkBuilder nb{alc}; + NetworkBuilder nb{reqPacket_, alc}; // [[file]] for (auto&& file : bp_.files()) @@ -322,10 +361,14 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) for (auto&& ram : bp_.builtinRAMs()) { switch (ram.type) { case blueprint::BuiltinRAM::TYPE::CMUX_MEMORY: - makeRAM(ram, nb); + // FIXME + // makeRAM(ram, nb); + ERR_UNREACHABLE; break; case blueprint::BuiltinRAM::TYPE::MUX: - makeMUXRAM(ram, nb); + // FIXME + // makeMUXRAM(ram, nb); + ERR_UNREACHABLE; break; } } @@ -334,18 +377,14 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) for (auto&& rom : bp_.builtinROMs()) { switch (rom.type) { case blueprint::BuiltinROM::TYPE::CMUX_MEMORY: - makeROM(rom, nb); + // FIXME + // makeROM(rom, nb); + ERR_UNREACHABLE; break; case blueprint::BuiltinROM::TYPE::MUX: makeMUXROM(rom, nb); break; } - - auto it = reqPacket_.rom.find(rom.name); - if (it != reqPacket_.rom.end()) { - // FIXME: rom init - assert(false); - } } auto get = [&](const blueprint::Port& port) -> Task* { @@ -401,18 +440,7 @@ void Frontend::readNetworkFromFile(const blueprint::File& file, } } -void Frontend::makeMUXRAM(const blueprint::BuiltinRAM& ram, - nt::NetworkBuilder& nb) -{ - // FIXME -} - -void Frontend::makeMUXROM(const blueprint::BuiltinROM& rom, - nt::NetworkBuilder& nb) -{ - // FIXME -} - +/* void Frontend::makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb) { // FIXME: relax this constraint @@ -423,28 +451,7 @@ void Frontend::makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb) // FIXME } - -void Frontend::makeROM(const blueprint::BuiltinROM& rom, nt::NetworkBuilder& nb) -{ - // Create inputs - std::vector addrInputs; - for (size_t i = 0; i < rom.inAddrWidth; i++) { - UID id = nb.INPUT(rom.name, "addr", i); - addrInputs.push_back(id); - } - - // Create 1bit ROMs - for (size_t i = 0; i < rom.outRdataWidth; i++) { - make1bitROMWithMUX(rom, addrInputs, nb); - } -} - -void Frontend::make1bitROMWithMUX(const blueprint::BuiltinROM& rom, - const std::vector& addrInputs, - nt::NetworkBuilder& nb) -{ - // FIXME -} +*/ /**************************************************/ /***** TEST ***************************************/ @@ -749,6 +756,36 @@ void test0() tOut3->getOutput(dh); assert(dh.getBit() == 1_b); } + + { + PlainPacket pkt; + pkt.rom["rom"] = {0_b, 1_b, 0_b, 0_b, 1_b, 0_b, 1_b, 1_b}; + + Allocator alc; + NetworkBuilder nb{pkt, alc}; + makeMUXROM(blueprint::BuiltinROM{blueprint::BuiltinROM::TYPE::MUX, + "rom", 2, 2}, + nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tAddr0 = finder.findByConfigName({"rom", "addr", 0}), + *tAddr1 = finder.findByConfigName({"rom", "addr", 1}), + *tRdata0 = finder.findByConfigName({"rom", "rdata", 0}), + *tRdata1 = finder.findByConfigName({"rom", "rdata", 1}); + + tAddr0->setInput(&bit0); + tAddr1->setInput(&bit1); + runner.run(); + + tRdata0->getOutput(dh); + assert(dh.getBit() == 1_b); + tRdata1->getOutput(dh); + assert(dh.getBit() == 0_b); + } } } // namespace plain From 6fce13c344d9a08b29cad07f2a3b205c100b470a Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Mon, 1 Nov 2021 00:47:59 +0900 Subject: [PATCH 21/54] wip --- src/blueprint.cpp | 77 ------------ src/iyokan_nt.cpp | 262 +++++++++++++++++++++++++++++++++++++++- src/iyokan_nt.hpp | 3 + src/iyokan_nt_plain.cpp | 74 ++++++++++++ 4 files changed, 337 insertions(+), 79 deletions(-) diff --git a/src/blueprint.cpp b/src/blueprint.cpp index b8c11b4..9e79c9a 100644 --- a/src/blueprint.cpp +++ b/src/blueprint.cpp @@ -293,81 +293,4 @@ const std::unordered_map& Blueprint::atPortWidths() const return atPortWidths_; } -/* makeMUXROM */ - -namespace { -void make1bitROMWithMUX(const std::string& nodeName, - const std::vector& addrInputs, - size_t outRdataWidth, size_t indexOutRdata, - NetworkBuilder& nb) -{ - /* - INPUT - addr[1] ------------------------------+ - INPUT | - addr[0] --+-----------------+ | - | | | - | ROM | | - | romdata[0] -- |\ | - | ROM | | --+ | - | romdata[1] -- |/ +-- |\ OUTPUT - | | | -- ... -- rdata[indexOutRdata] - +-----------------+ +-- |/ - | | - ROM | | - romdata[2] -- +\ | - ROM | | --+ - romdata[3] -- |/ - - ... - - ROM - addr[2^inAddrWidth-1] -- ... - */ - - const int inAddrWidth = addrInputs.size(); - - // Create ROMs - std::vector workingIds; - for (size_t i = 0; i < (1 << inAddrWidth); i++) { - UID id = nb.ROM(nodeName, "romdata", indexOutRdata + i * outRdataWidth); - workingIds.push_back(id); - } - - // Create MUXs - for (size_t i = 0; i < inAddrWidth; i++) { - assert(workingIds.size() > 0 && workingIds.size() % 2 == 0); - std::vector newWorkingIds; - for (int j = 0; j < workingIds.size(); j += 2) { - int id = nb.MUX(); - nb.connect(workingIds.at(j), id); - nb.connect(workingIds.at(j + 1), id); - nb.connect(addrInputs.at(i), id); - newWorkingIds.push_back(id); - } - workingIds.swap(newWorkingIds); - } - assert(workingIds.size() == 1); - - // Create output - int id = nb.OUTPUT(nodeName, "rdata", indexOutRdata); - nb.connect(workingIds.at(0), id); -} -} // namespace - -void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb) -{ - // Create inputs - std::vector addrInputs; - for (size_t i = 0; i < rom.inAddrWidth; i++) { - UID id = nb.INPUT(rom.name, "addr", i); - addrInputs.push_back(id); - } - - // Create 1bit ROMs - for (size_t i = 0; i < rom.outRdataWidth; i++) { - make1bitROMWithMUX(rom.name, addrInputs, rom.outRdataWidth, i, nb); - } -} - } // namespace nt diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index cab8575..3c97f69 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -112,10 +112,14 @@ void TaskFinder::add(Task* task) byUID_.emplace(label.uid, task); if (label.cname) { - const ConfigName& cname = task->label().cname.value(); - byConfigName_.emplace( + const ConfigName& cname = label.cname.value(); + auto [it, inserted] = byConfigName_.emplace( std::make_tuple(cname.nodeName, cname.portName, cname.portBit), task); + if (!inserted) + ERR_DIE("Same config name already exists: " + << cname.nodeName << "/" << cname.portName << "[" + << cname.portBit << "]"); } } @@ -310,4 +314,258 @@ void NetworkRunner::onAfterFirstTick() network_.eachTask([&](Task* task) { task->onAfterFirstTick(); }); } +/* makeMUXROM */ + +namespace { +void make1bitROMWithMUX(const std::string& nodeName, + const std::vector& addrInputs, + size_t outRdataWidth, size_t indexOutRdata, + NetworkBuilder& nb) +{ + /* + INPUT + addr[1] ------------------------------+ + INPUT | + addr[0] --+-----------------+ | + | | | + | ROM | | + | romdata[0] -- |\ | + | ROM | | --+ | + | romdata[1] -- |/ +-- |\ OUTPUT + | | | -- ... -- rdata[indexOutRdata] + +-----------------+ +-- |/ + | | + ROM | | + romdata[2] -- +\ | + ROM | | --+ + romdata[3] -- |/ + + ... + + ROM + addr[2^inAddrWidth-1] -- ... + */ + + const size_t inAddrWidth = addrInputs.size(); + + // Create ROMs + std::vector workingIds; + for (size_t i = 0; i < (1 << inAddrWidth); i++) { + UID id = nb.ROM(nodeName, "romdata", indexOutRdata + i * outRdataWidth); + workingIds.push_back(id); + } + + // Create MUXs + for (size_t i = 0; i < inAddrWidth; i++) { + assert(workingIds.size() > 0 && workingIds.size() % 2 == 0); + std::vector newWorkingIds; + for (size_t j = 0; j < workingIds.size(); j += 2) { + UID id = nb.MUX(); + nb.connect(workingIds.at(j), id); + nb.connect(workingIds.at(j + 1), id); + nb.connect(addrInputs.at(i), id); + newWorkingIds.push_back(id); + } + workingIds.swap(newWorkingIds); + } + assert(workingIds.size() == 1); + + // Create output + UID id = nb.OUTPUT(nodeName, "rdata", indexOutRdata); + nb.connect(workingIds.at(0), id); +} +} // namespace + +void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb) +{ + // Create inputs + std::vector addrInputs; + for (size_t i = 0; i < rom.inAddrWidth; i++) { + UID id = nb.INPUT(rom.name, "addr", i); + addrInputs.push_back(id); + } + + // Create 1bit ROMs + for (size_t i = 0; i < rom.outRdataWidth; i++) { + make1bitROMWithMUX(rom.name, addrInputs, rom.outRdataWidth, i, nb); + } +} + +/* makeMUXRAM */ + +namespace { + +void make1bitRAMWithMUX(const std::string& nodeName, + const std::vector& addrInputs, UID wrenInput, + size_t indexWRdata, NetworkBuilder& nb) +{ + /* + wdata[indexWRdata] + | + | +---------------------+ + | | | + | +--|\ | + | | |-- ramdata[.] --+------------+-|\ + +------|/ | |-- rdata[indexWRdata] + | | +---------------------+ +-|/ + | a +--|\ |---+ + | | |-- ramdata[.] --+ + +---------------|/ + | | + | b + + ... + + | + +---- ... -- ramdata[2^inAddrWidth-1] -- + + + a b + | | + ----- + addr[0] --- /0 1\ DMUX + ------- ... + | | + +-------+-------+ + | + ... ... + | | + | | + ----- + addr[inAddrWidth-1] --- /0 1\ DMUX + ------- + | + wren + + + DMUX: (in, sel) -> (out0, out1) + out0 = andnot(in, sel) + out1 = and(in, sel) + */ + + const size_t inAddrWidth = addrInputs.size(); + + // Create input "wdata[indexWRdata]" + UID wdataInput = nb.INPUT(nodeName, "wdata", indexWRdata); + + // Create DMUXs + std::vector workingIds = {wrenInput}, newWorkingIds; + for (auto it = addrInputs.rbegin(); it != addrInputs.rend(); ++it) { + UID addr = *it; + for (UID src : workingIds) { + // Create DMUX + // dst0 = andnot(src, addr) + // dst1 = and(src, addr) + UID dst0 = nb.ANDNOT(); + UID dst1 = nb.AND(); + nb.connect(src, dst0); + nb.connect(addr, dst0); + nb.connect(src, dst1); + nb.connect(addr, dst1); + + newWorkingIds.push_back(dst0); + newWorkingIds.push_back(dst1); + } + workingIds.swap(newWorkingIds); + newWorkingIds.clear(); + } + assert(workingIds.size() == (1 << inAddrWidth)); + + // Create RAMs + for (size_t addr = 0; addr < (1 << inAddrWidth); addr++) { + /* + +-------------------------+ + | | + +--|\ RAM |-- + INPUT | |-- ramdata[ ... ] --+ + wdata[indexRWdata] ----|/ + | + sel + */ + UID sel = workingIds.at(addr), mux = nb.MUX(), + ram = nb.RAM(nodeName, "ramdata", addr * inAddrWidth + indexWRdata); + nb.connect(ram, mux); + nb.connect(wdataInput, mux); + nb.connect(sel, mux); + nb.connect(mux, ram); + newWorkingIds.push_back(ram); + } + workingIds.swap(newWorkingIds); + newWorkingIds.clear(); + + // Create MUXs + for (size_t i = 0; i < inAddrWidth; i++) { + assert(workingIds.size() > 0 && workingIds.size() % 2 == 0); + for (size_t j = 0; j < workingIds.size(); j += 2) { + UID id = nb.MUX(); + nb.connect(workingIds.at(j), id); + nb.connect(workingIds.at(j + 1), id); + nb.connect(addrInputs.at(i), id); + newWorkingIds.push_back(id); + } + workingIds.swap(newWorkingIds); + newWorkingIds.clear(); + } + assert(workingIds.size() == 1); + + // Create output "rdata[indexWRdata]" + UID rdataOutput = nb.OUTPUT(nodeName, "rdata", indexWRdata); + nb.connect(workingIds.at(0), rdataOutput); +} + +} // namespace + +/* + // Iyokan-L1 JSON of MUX RAM pre-compiled (and optimized) by Yosys +extern char _binary_mux_ram_8_8_8_min_json_start[]; +extern char _binary_mux_ram_8_8_8_min_json_end[]; +extern char _binary_mux_ram_8_8_8_min_json_size[]; +extern char _binary_mux_ram_8_16_16_min_json_start[]; +extern char _binary_mux_ram_8_16_16_min_json_end[]; +extern char _binary_mux_ram_8_16_16_min_json_size[]; +extern char _binary_mux_ram_9_16_16_min_json_start[]; +extern char _binary_mux_ram_9_16_16_min_json_end[]; +extern char _binary_mux_ram_9_16_16_min_json_size[]; +*/ + +void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb) +{ + assert(ram.inWdataWidth == ram.outRdataWidth); + + /* +#define USE_PRECOMPILED_BINARY(addrW, dataW) \ + if (inAddrWidth == addrW && dataWidth == dataW) { \ + std::stringstream ss{std::string{ \ + _binary_mux_ram_##addrW##_##dataW##_##dataW##_min_json_start, \ + _binary_mux_ram_##addrW##_##dataW##_##dataW##_min_json_end}}; \ + IyokanL1JSONReader::read(b, ss); \ + auto net = std::make_shared( \ + std::move(b)); \ + \ + error::Stack err; \ + net->checkValid(err); \ + assert(err.empty()); \ + \ + return net; \ + } + USE_PRECOMPILED_BINARY(8, 8); + USE_PRECOMPILED_BINARY(8, 16); + USE_PRECOMPILED_BINARY(9, 16); +#undef USE_PRECOMPILED_BINARY +*/ + + // Create inputs + std::vector addrInputs; + for (size_t i = 0; i < ram.inAddrWidth; i++) { + UID id = nb.INPUT(ram.name, "addr", i); + addrInputs.push_back(id); + } + UID wrenInput = nb.INPUT(ram.name, "wren", 0); + + // Create 1bitRAMs + for (size_t i = 0; i < ram.outRdataWidth; i++) { + make1bitRAMWithMUX(ram.name, addrInputs, wrenInput, i, nb); + } +} + } // namespace nt diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 87b8d85..6db474b 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -317,6 +317,8 @@ class NetworkBuilder { int portBit) = 0; virtual UID ROM(const std::string& nodeName, const std::string& portName, int portBit) = 0; + virtual UID RAM(const std::string& nodeName, const std::string& portName, + int portBit) = 0; virtual UID AND() = 0; virtual UID ANDNOT() = 0; @@ -447,6 +449,7 @@ void readYosysJSONNetwork(const std::string& nodeName, std::istream& is, void readIyokanL1JSONNetwork(const std::string& nodeName, std::istream& is, NetworkBuilder& nb); void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb); +void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb); } // namespace nt diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 180ed43..eb5a851 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -302,6 +302,24 @@ class NetworkBuilder : public nt::NetworkBuilder { uid2common_.emplace(uid, task); return uid; } + + UID RAM(const std::string& nodeName, const std::string& portName, + int portBit) override + { + assert(reqPacket_ != nullptr); + assert(portName == "ramdata"); + + UID uid = genUID(); + TaskDFF* task = emplaceTask( + reqPacket_->ram.at(nodeName).at(portBit), + Label{uid, "RAM", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + uid2common_.emplace(uid, task); + + // FIXME: We need to memorize this task to make a response packet. + + return uid; + } }; enum class SCHED { @@ -786,6 +804,62 @@ void test0() tRdata1->getOutput(dh); assert(dh.getBit() == 0_b); } + + { + PlainPacket pkt; + pkt.ram["ram"] = {0_b, 1_b, 0_b, 0_b, 1_b, 0_b, 1_b, 1_b}; + + Allocator alc; + NetworkBuilder nb{pkt, alc}; + makeMUXRAM(blueprint::BuiltinRAM{blueprint::BuiltinRAM::TYPE::MUX, + "ram", 2, 2, 2}, + nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tAddr0 = finder.findByConfigName({"ram", "addr", 0}), + *tAddr1 = finder.findByConfigName({"ram", "addr", 1}), + *tWren = finder.findByConfigName({"ram", "wren", 0}), + *tRdata0 = finder.findByConfigName({"ram", "rdata", 0}), + *tRdata1 = finder.findByConfigName({"ram", "rdata", 1}), + *tWdata0 = finder.findByConfigName({"ram", "wdata", 0}), + *tWdata1 = finder.findByConfigName({"ram", "wdata", 1}); + + // Reset cycle + runner.run(); + + // Cycle #1 + runner.tick(); + runner.onAfterFirstTick(); + tAddr0->setInput(&bit1); + tAddr1->setInput(&bit0); + tWren->setInput(&bit0); + tWdata0->setInput(&bit1); + tWdata1->setInput(&bit1); + runner.run(); + + tRdata0->getOutput(dh); + assert(dh.getBit() == 0_b); + tRdata1->getOutput(dh); + assert(dh.getBit() == 0_b); + + // Cycle #2 + runner.tick(); + tWren->setInput(&bit1); + runner.run(); + + // Cycle #3 + runner.tick(); + runner.run(); + + tRdata0->getOutput(dh); + assert(dh.getBit() == 1_b); + tRdata1->getOutput(dh); + assert(dh.getBit() == 1_b); + } } } // namespace plain From 9bd89210f87a2d2b94f9e9880e7a8f5c96bcbf19 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Mon, 1 Nov 2021 17:30:29 +0900 Subject: [PATCH 22/54] wip --- src/iyokan_nt_plain.cpp | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index eb5a851..9339530 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -377,37 +377,21 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // [[builtin]] type = ram | type = mux-ram for (auto&& ram : bp_.builtinRAMs()) { - switch (ram.type) { - case blueprint::BuiltinRAM::TYPE::CMUX_MEMORY: - // FIXME - // makeRAM(ram, nb); - ERR_UNREACHABLE; - break; - case blueprint::BuiltinRAM::TYPE::MUX: - // FIXME - // makeMUXRAM(ram, nb); - ERR_UNREACHABLE; - break; - } + // We ignore ram.type and always use mux-ram in plaintext mode. + makeMUXRAM(ram, nb); } // [[builtin]] type = rom | type = mux-rom for (auto&& rom : bp_.builtinROMs()) { - switch (rom.type) { - case blueprint::BuiltinROM::TYPE::CMUX_MEMORY: - // FIXME - // makeROM(rom, nb); - ERR_UNREACHABLE; - break; - case blueprint::BuiltinROM::TYPE::MUX: - makeMUXROM(rom, nb); - break; - } + // We ignore rom.type and always use mux-rom in plaintext mode. + makeMUXROM(rom, nb); } auto get = [&](const blueprint::Port& port) -> Task* { Task* task = nb.finder().findByConfigName( {port.nodeName, port.portName, port.portBit}); + // FIXME: + // ここで大文字小文字の不一致が発生する。そもそも文字列一致で行うべきではない処理。 if (task->label().kind != port.kind) ERR_DIE("Invalid port: " << port.nodeName << "/" << port.portName << "[" << port.portBit << "] is " From 6e5c371becea4bd99ac198afcc9a3373f7c84dbd Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Mon, 1 Nov 2021 17:43:15 +0900 Subject: [PATCH 23/54] wip --- src/blueprint.cpp | 14 +++++++------- src/iyokan_nt.hpp | 15 ++++++++++++--- src/iyokan_nt_plain.cpp | 6 ++---- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/blueprint.cpp b/src/blueprint.cpp index 9e79c9a..8c0c443 100644 --- a/src/blueprint.cpp +++ b/src/blueprint.cpp @@ -116,7 +116,7 @@ Blueprint::Blueprint(const std::string& fileName) for (const auto& portStr : ary) { // @...[n:m] if (portStr.empty() || portStr.at(0) != '@') ERR_DIE("Invalid port name for TOGND: " << portStr); - auto ports = parsePortString(portStr, "output"); + auto ports = parsePortString(portStr, Label::OUTPUT); for (auto&& port : ports) { // @...[n] const std::string& name = port.portName; int bit = port.portBit; @@ -138,10 +138,10 @@ Blueprint::Blueprint(const std::string& fileName) ERR_DIE(errMsg); // Others. - std::vector portsTo = - parsePortString(srcTo, "input"), - portsFrom = - parsePortString(srcFrom, "output"); + std::vector portsTo = parsePortString( + srcTo, Label::INPUT), + portsFrom = parsePortString( + srcFrom, Label::OUTPUT); if (portsTo.size() != portsFrom.size()) ERR_DIE(errMsg); @@ -198,7 +198,7 @@ Blueprint::Blueprint(const std::string& fileName) } std::vector Blueprint::parsePortString(const std::string& src, - const std::string& kind) + const char* const kind) { std::string nodeName, portName; int portBitFrom, portBitTo; @@ -227,7 +227,7 @@ std::vector Blueprint::parsePortString(const std::string& src, std::vector ret; for (int i = portBitFrom; i < portBitTo + 1; i++) - ret.push_back(blueprint::Port{nodeName, kind, portName, i}); + ret.push_back(blueprint::Port{kind, nodeName, portName, i}); return ret; } diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 6db474b..964630b 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -56,9 +56,17 @@ struct ConfigName { int portBit; }; struct Label { + static inline const char* const INPUT = "Input"; + static inline const char* const OUTPUT = "Output"; + UID uid; - std::string kind; + const char* const kind; // Stores a string literal std::optional cname; + + Label(UID uid, const char* const kind, std::optional cname) + : uid(uid), kind(kind), cname(std::move(cname)) + { + } }; class Task { @@ -405,7 +413,8 @@ struct BuiltinRAM { }; struct Port { - std::string nodeName, kind, portName; + const char* kind; // Store a string literal + std::string nodeName, portName; int portBit; }; } // namespace blueprint @@ -424,7 +433,7 @@ class Blueprint { private: std::vector parsePortString(const std::string& src, - const std::string& kind); + const char* const kind); public: Blueprint(const std::string& fileName); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 9339530..04a3526 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -390,8 +390,6 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) auto get = [&](const blueprint::Port& port) -> Task* { Task* task = nb.finder().findByConfigName( {port.nodeName, port.portName, port.portBit}); - // FIXME: - // ここで大文字小文字の不一致が発生する。そもそも文字列一致で行うべきではない処理。 if (task->label().kind != port.kind) ERR_DIE("Invalid port: " << port.nodeName << "/" << port.portName << "[" << port.portBit << "] is " @@ -409,8 +407,8 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) } // Then, connect other ports. `get` checks if they also exist. for (auto&& [src, dst] : bp_.edges()) { - assert(src.kind == "output"); - assert(dst.kind == "input"); + assert(src.kind == Label::OUTPUT); + assert(dst.kind == Label::INPUT); nb.connect(get(src)->label().uid, get(dst)->label().uid); } From 161444456756b91de544f8b1bb328f75903d402c Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Mon, 1 Nov 2021 18:08:10 +0900 Subject: [PATCH 24/54] wip --- src/iyokan_nt.hpp | 8 ++--- src/iyokan_nt_plain.cpp | 70 +++++++++++------------------------------ src/network_reader.cpp | 41 ++++++++++++++---------- 3 files changed, 48 insertions(+), 71 deletions(-) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 964630b..ae51301 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -56,6 +56,9 @@ struct ConfigName { int portBit; }; struct Label { + // String literals for member variable `kind`. + // If label is for inputs or outputs, these member variable must be used, + // that is, kind == INPUT or kind == OUTPUT. static inline const char* const INPUT = "Input"; static inline const char* const OUTPUT = "Output"; @@ -453,10 +456,7 @@ class Blueprint { const std::unordered_map& atPortWidths() const; }; -void readYosysJSONNetwork(const std::string& nodeName, std::istream& is, - NetworkBuilder& nb); -void readIyokanL1JSONNetwork(const std::string& nodeName, std::istream& is, - NetworkBuilder& nb); +void readNetworkFromFile(const blueprint::File& file, NetworkBuilder& nb); void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb); void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 04a3526..d2336c0 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -354,10 +354,6 @@ class Frontend { int currentCycle_; nt::Blueprint bp_; -private: - static void readNetworkFromFile(const blueprint::File& file, - nt::NetworkBuilder& nb); - public: Frontend(const RunParameter& pr, Allocator& alc); }; @@ -418,41 +414,6 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // FIXME check if network is valid } -void Frontend::readNetworkFromFile(const blueprint::File& file, - nt::NetworkBuilder& nb) -{ - std::ifstream ifs{file.path, std::ios::binary}; - if (!ifs) - ERR_DIE("Invalid [[file]] path: " << file.path); - - switch (file.type) { - case blueprint::File::TYPE::IYOKANL1_JSON: - LOG_S(WARNING) - << "[[file]] of type 'iyokanl1-json' is deprecated. You don't need " - "to use Iyokan-L1. Use Yosys JSON directly by specifying type " - "'yosys-json'."; - readIyokanL1JSONNetwork(file.name, ifs, nb); - break; - - case blueprint::File::TYPE::YOSYS_JSON: - readYosysJSONNetwork(file.name, ifs, nb); - break; - } -} - -/* -void Frontend::makeRAM(const blueprint::BuiltinRAM& ram, nt::NetworkBuilder& nb) -{ - // FIXME: relax this constraint - if (ram.inWdataWidth != ram.outRdataWidth) - ERR_DIE( - "Invalid RAM size; RAM that has different sizes of " - "wdata and rdata is not implemented."); - - // FIXME -} -*/ - /**************************************************/ /***** TEST ***************************************/ /**************************************************/ @@ -563,9 +524,10 @@ void test0() Allocator alc; NetworkBuilder nb{alc}; - std::ifstream ifs{"test/yosys-json/addr-4bit-yosys.json"}; - assert(ifs); - readYosysJSONNetwork("addr", ifs, nb); + readNetworkFromFile( + blueprint::File{blueprint::File::TYPE::YOSYS_JSON, + "test/yosys-json/addr-4bit-yosys.json", "addr"}, + nb); std::vector> workers; workers.emplace_back(std::make_unique()); @@ -610,9 +572,11 @@ void test0() Allocator alc; NetworkBuilder nb{alc}; - std::ifstream ifs{"test/iyokanl1-json/addr-4bit-iyokanl1.json"}; - assert(ifs); - readIyokanL1JSONNetwork("addr", ifs, nb); + readNetworkFromFile( + blueprint::File{blueprint::File::TYPE::IYOKANL1_JSON, + "test/iyokanl1-json/addr-4bit-iyokanl1.json", + "addr"}, + nb); std::vector> workers; workers.emplace_back(std::make_unique()); @@ -657,9 +621,11 @@ void test0() Allocator alc; NetworkBuilder nb{alc}; - std::ifstream ifs{"test/yosys-json/counter-4bit-yosys.json"}; - assert(ifs); - readYosysJSONNetwork("counter", ifs, nb); + readNetworkFromFile( + blueprint::File{blueprint::File::TYPE::YOSYS_JSON, + "test/yosys-json/counter-4bit-yosys.json", + "counter"}, + nb); std::vector> workers; workers.emplace_back(std::make_unique()); @@ -701,9 +667,11 @@ void test0() Allocator alc; NetworkBuilder nb{alc}; - std::ifstream ifs{"test/yosys-json/register-init-4bit-yosys.json"}; - assert(ifs); - readYosysJSONNetwork("register_init", ifs, nb); + readNetworkFromFile( + blueprint::File{blueprint::File::TYPE::YOSYS_JSON, + "test/yosys-json/register-init-4bit-yosys.json", + "register_init"}, + nb); std::vector> workers; workers.emplace_back(std::make_unique()); diff --git a/src/network_reader.cpp b/src/network_reader.cpp index e3ce4e4..9cef02f 100644 --- a/src/network_reader.cpp +++ b/src/network_reader.cpp @@ -2,12 +2,12 @@ #include -namespace nt { - -/* class YosysJSONReader */ +#include namespace { +/* class YosysJSONReader */ + class YosysJSONReader { private: enum class PORT { @@ -287,18 +287,8 @@ class YosysJSONReader { } }; -} // namespace - -void readYosysJSONNetwork(const std::string& nodeName, std::istream& is, - NetworkBuilder& nb) -{ - YosysJSONReader::read(nodeName, is, nb); -} - /* class IyokanL1JSONReader */ -namespace { - class IyokanL1JSONReader { public: template @@ -433,10 +423,29 @@ class IyokanL1JSONReader { } // namespace -void readIyokanL1JSONNetwork(const std::string& nodeName, std::istream& is, - NetworkBuilder& nb) +namespace nt { + +/* readNetworkFromFile */ + +void readNetworkFromFile(const blueprint::File& file, nt::NetworkBuilder& nb) { - IyokanL1JSONReader::read(nodeName, is, nb); + std::ifstream ifs{file.path, std::ios::binary}; + if (!ifs) + ERR_DIE("Invalid [[file]] path: " << file.path); + + switch (file.type) { + case blueprint::File::TYPE::IYOKANL1_JSON: + LOG_S(WARNING) + << "[[file]] of type 'iyokanl1-json' is deprecated. You don't need " + "to use Iyokan-L1. Use Yosys JSON directly by specifying type " + "'yosys-json'."; + IyokanL1JSONReader::read(file.name, ifs, nb); + break; + + case blueprint::File::TYPE::YOSYS_JSON: + YosysJSONReader::read(file.name, ifs, nb); + break; + } } } // namespace nt From baedb12111524a2e468dc1be3ebd0c3e689c7b09 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Mon, 1 Nov 2021 20:59:44 +0900 Subject: [PATCH 25/54] wip --- src/blueprint.cpp | 20 +++++----- src/dataholder_nt.cpp | 5 ++- src/dataholder_nt.hpp | 10 +++-- src/iyokan_nt.cpp | 12 ++++++ src/iyokan_nt.hpp | 19 ++++++--- src/iyokan_nt_plain.cpp | 88 +++++++++++++++++++++++++++++++++++++---- src/packet_nt.cpp | 14 +++++++ src/packet_nt.hpp | 2 + 8 files changed, 143 insertions(+), 27 deletions(-) diff --git a/src/blueprint.cpp b/src/blueprint.cpp index 8c0c443..965a844 100644 --- a/src/blueprint.cpp +++ b/src/blueprint.cpp @@ -118,8 +118,8 @@ Blueprint::Blueprint(const std::string& fileName) ERR_DIE("Invalid port name for TOGND: " << portStr); auto ports = parsePortString(portStr, Label::OUTPUT); for (auto&& port : ports) { // @...[n] - const std::string& name = port.portName; - int bit = port.portBit; + const std::string& name = port.cname.portName; + int bit = port.cname.portBit; auto [it, inserted] = atPortWidths_.emplace(name, 0); it->second = std::max(it->second, bit + 1); } @@ -150,11 +150,12 @@ Blueprint::Blueprint(const std::string& fileName) const blueprint::Port& from = portsFrom[i]; if (srcTo[0] == '@') { // @... = ... - if (!to.nodeName.empty() || from.nodeName.empty()) + if (!to.cname.nodeName.empty() || + from.cname.nodeName.empty()) ERR_DIE(errMsg); - const std::string& name = to.portName; - int bit = to.portBit; + const std::string& name = to.cname.portName; + int bit = to.cname.portBit; { auto [it, inserted] = @@ -170,11 +171,12 @@ Blueprint::Blueprint(const std::string& fileName) it->second = std::max(it->second, bit + 1); } else if (srcFrom[0] == '@') { // ... = @... - if (!from.nodeName.empty() || to.nodeName.empty()) + if (!from.cname.nodeName.empty() || + to.cname.nodeName.empty()) ERR_DIE(errMsg); - const std::string& name = from.portName; - int bit = from.portBit; + const std::string& name = from.cname.portName; + int bit = from.cname.portBit; { auto [it, inserted] = @@ -227,7 +229,7 @@ std::vector Blueprint::parsePortString(const std::string& src, std::vector ret; for (int i = portBitFrom; i < portBitTo + 1; i++) - ret.push_back(blueprint::Port{kind, nodeName, portName, i}); + ret.push_back(blueprint::Port{kind, {nodeName, portName, i}}); return ret; } diff --git a/src/dataholder_nt.cpp b/src/dataholder_nt.cpp index d315a1f..73b726f 100644 --- a/src/dataholder_nt.cpp +++ b/src/dataholder_nt.cpp @@ -6,7 +6,8 @@ DataHolder::DataHolder() : dataBit_(nullptr), type_(TYPE::UND) { } -DataHolder::DataHolder(Bit *dataBit) : dataBit_(dataBit), type_(TYPE::BIT) +DataHolder::DataHolder(const Bit *const dataBit) + : dataBit_(dataBit), type_(TYPE::BIT) { } @@ -16,7 +17,7 @@ Bit DataHolder::getBit() const return *dataBit_; } -void DataHolder::setBit(Bit *dataBit) +void DataHolder::setBit(const Bit *const dataBit) { dataBit_ = dataBit; type_ = TYPE::BIT; diff --git a/src/dataholder_nt.hpp b/src/dataholder_nt.hpp index 8874a34..9f0abd8 100644 --- a/src/dataholder_nt.hpp +++ b/src/dataholder_nt.hpp @@ -10,10 +10,14 @@ namespace nt { enum class Bit : bool; // DataHolder holds data using Task::setInput/Task::getOutput. +// FIXME: The name "DataHolder" is misleading. Maybe "IODataPointer" or +// something is more suitable, because we actually do not "hold" the data but +// "point" them. DataHolder::setBit() does not set the bit itself but set the +// pointer to a bit. class DataHolder { private: union { - Bit *dataBit_; + const Bit* dataBit_; }; enum class TYPE { @@ -23,10 +27,10 @@ class DataHolder { public: DataHolder(); - DataHolder(Bit *dataBit); + DataHolder(const Bit* const dataBit); Bit getBit() const; - void setBit(Bit *dataBit); + void setBit(const Bit* const dataBit); }; } // namespace nt diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 3c97f69..c4ee19a 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -5,6 +5,18 @@ namespace nt { +/* struct ConfigName */ +std::ostream& operator<<(std::ostream& os, const ConfigName& c) +{ + os << c.nodeName << "/" << c.portName << "[" << c.portBit << "]"; + return os; +} + +/* struct Label */ +// Initialization of static variables. +const char* const Label::INPUT = "Input"; +const char* const Label::OUTPUT = "Output"; + /* class Allocator */ Allocator::Allocator() diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index ae51301..1dfb2fc 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -54,13 +54,23 @@ using UID = uint64_t; struct ConfigName { std::string nodeName, portName; int portBit; + + // Although all member variables of ConfigName are public, we make + // operator<< its friend function for clarity. + friend std::ostream& operator<<(std::ostream& os, const ConfigName& c); }; + struct Label { // String literals for member variable `kind`. // If label is for inputs or outputs, these member variable must be used, - // that is, kind == INPUT or kind == OUTPUT. - static inline const char* const INPUT = "Input"; - static inline const char* const OUTPUT = "Output"; + // that is, kind == Label::INPUT or kind == Label::OUTPUT. + // The instances of these variables exist in iyokan_nt.cpp. + // We CANNOT use (C++17) `inline` here, because `inline` does NOT guarantee + // that these variables have the same value in different compilation units + // (FIXME: This behaviour is confirmed only on g++-10. We need to check the + // C++ standard). + static const char* const INPUT; + static const char* const OUTPUT; UID uid; const char* const kind; // Stores a string literal @@ -417,8 +427,7 @@ struct BuiltinRAM { struct Port { const char* kind; // Store a string literal - std::string nodeName, portName; - int portBit; + ConfigName cname; }; } // namespace blueprint diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index d2336c0..eb33112 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -270,7 +270,7 @@ class NetworkBuilder : public nt::NetworkBuilder { UID uid = genUID(); TaskInput* task = nullptr; task = emplaceTask( - Label{uid, "Input", ConfigName{nodeName, portName, portBit}}, + Label{uid, Label::INPUT, ConfigName{nodeName, portName, portBit}}, currentAllocator()); uid2common_.emplace(uid, task); return uid; @@ -282,7 +282,7 @@ class NetworkBuilder : public nt::NetworkBuilder { UID uid = genUID(); TaskOutput* task = nullptr; task = emplaceTask( - Label{uid, "Output", ConfigName{nodeName, portName, portBit}}, + Label{uid, Label::OUTPUT, ConfigName{nodeName, portName, portBit}}, currentAllocator()); uid2common_.emplace(uid, task); return uid; @@ -349,18 +349,19 @@ struct RunParameter { class Frontend { private: RunParameter pr_; - std::unique_ptr runner_; + std::optional network_; PlainPacket reqPacket_; int currentCycle_; nt::Blueprint bp_; public: Frontend(const RunParameter& pr, Allocator& alc); + void run(); }; Frontend::Frontend(const RunParameter& pr, Allocator& alc) : pr_(pr), - runner_(nullptr), + network_(std::nullopt), reqPacket_(readPlainPacket(pr_.inputFile)), currentCycle_(0), bp_(pr_.blueprintFile) @@ -384,11 +385,9 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) } auto get = [&](const blueprint::Port& port) -> Task* { - Task* task = nb.finder().findByConfigName( - {port.nodeName, port.portName, port.portBit}); + Task* task = nb.finder().findByConfigName(port.cname); if (task->label().kind != port.kind) - ERR_DIE("Invalid port: " << port.nodeName << "/" << port.portName - << "[" << port.portBit << "] is " + ERR_DIE("Invalid port: " << port.cname << " is " << task->label().kind << ", not " << port.kind); return task; @@ -411,9 +410,46 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // Set priority to each DepNode // FIXME + network_.emplace(nb.createNetwork()); // FIXME check if network is valid } +void Frontend::run() +{ + const Bit bit0 = 0_b, bit1 = 1_b; + + // Create workers + std::vector> workers; + for (size_t i = 0; i < pr_.numCPUWorkers; i++) + workers.emplace_back(std::make_unique()); + + // Create runner and finder for the network + NetworkRunner runner{std::move(network_.value()), std::move(workers)}; + network_ = std::nullopt; + const TaskFinder& finder = runner.network().finder(); + + // Process reset cycle if @reset is used + // FIXME: Add support for --skip-reset flag + if (auto reset = bp_.at("reset"); reset && reset->kind == Label::INPUT) { + Task* t = finder.findByConfigName(reset->cname); + t->setInput(&bit1); // Set reset on + runner.run(); + t->setInput(&bit0); // Set reset off + } + + // Process normal cycles + for (size_t i = 0; i < pr_.numCycles; i++) { + runner.tick(); + if (i == 0) + // Set initial values of SDFF and RAM + runner.onAfterFirstTick(); + // FIXME set (circular) inputs + runner.run(); + } + + // FIXME output result packet +} + /**************************************************/ /***** TEST ***************************************/ /**************************************************/ @@ -810,6 +846,42 @@ void test0() tRdata1->getOutput(dh); assert(dh.getBit() == 1_b); } + + { + // Prepare the input packet + PlainPacket inPkt{ + {}, // ram + {}, // rom + { // bits + {"A", /* 0xc */ {0_b, 0_b, 1_b, 1_b}}, + {"B", /* 0xa */ {0_b, 1_b, 0_b, 1_b}}}, + std::nullopt, // numCycles + }; + writePlainPacket("_test_in", inPkt); + + // Prepare the expected output packet + PlainPacket expectedOutPkt{ + {}, // ram + {}, // rom + {{"out", /* 6 */ {0_b, 1_b, 1_b, 0_b}}}, // bits + 1, // numCycles + }; + + Allocator alc; + Frontend frontend{ + RunParameter{ + "test/config-toml/addr-4bit.toml", // blueprintFile + "_test_in", // inputFile + "_test_out", // outputFile + 2, // numCPUWorkers + 1, // numCycles + SCHED::RANKU, // sched + }, + alc}; + frontend.run(); + PlainPacket got = readPlainPacket("_test_out"); + assert(got == expectedOutPkt); + } } } // namespace plain diff --git a/src/packet_nt.cpp b/src/packet_nt.cpp index 2f96b03..5db788c 100644 --- a/src/packet_nt.cpp +++ b/src/packet_nt.cpp @@ -222,6 +222,15 @@ std::vector decryptROMInTLWE(const TFHEpp::SecretKey& key, return decryptBits(key, src); } +bool PlainPacket::operator==(const PlainPacket& rhs) const +{ + // Check if member variables of *this and rhs are equal. + // If we used C++20, we could make C++ compilers derive this code by using + // '= default'! + return ram == rhs.ram && rom == rhs.rom && bits == rhs.bits && + numCycles == rhs.numCycles; +} + TFHEPacket PlainPacket::encrypt(const TFHEpp::SecretKey& key) const { TFHEPacket tfhe{{}, {}, {}, {}, {}, numCycles}; @@ -309,6 +318,11 @@ void writePlainPacket(std::ostream& os, const PlainPacket& pkt) writeToArchive(os, pkt); } +void writePlainPacket(const std::string& path, const PlainPacket& pkt) +{ + writeToArchive(path, pkt); +} + void writeTFHEPacket(std::ostream& os, const TFHEPacket& pkt) { writeToArchive(os, pkt); diff --git a/src/packet_nt.hpp b/src/packet_nt.hpp index c54f2cf..f1fb48a 100644 --- a/src/packet_nt.hpp +++ b/src/packet_nt.hpp @@ -82,6 +82,7 @@ struct PlainPacket { ar(ram, rom, bits, numCycles); } + bool operator==(const PlainPacket& rhs) const; TFHEPacket encrypt(const TFHEpp::SecretKey& key) const; }; @@ -130,6 +131,7 @@ PlainPacket readPlainPacket(const std::string& path); TFHEPacket readTFHEPacket(std::istream& is); TFHEPacket readTFHEPacket(const std::string& path); void writePlainPacket(std::ostream& os, const PlainPacket& pkt); +void writePlainPacket(const std::string& path, const PlainPacket& pkt); void writeTFHEPacket(std::ostream& os, const TFHEPacket& pkt); } // namespace nt From 2eeee08c78cb5102a004f599b86752cc14ec256f Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 00:58:37 +0900 Subject: [PATCH 26/54] wip --- src/iyokan_nt.cpp | 33 +++++++- src/iyokan_nt.hpp | 11 ++- src/iyokan_nt_plain.cpp | 171 +++++++++++++++++++++++++--------------- src/test0.cpp | 1 + 4 files changed, 144 insertions(+), 72 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index c4ee19a..a165b4f 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,6 +1,8 @@ #include "iyokan_nt.hpp" #include "error_nt.hpp" +#include + #include namespace nt { @@ -12,6 +14,15 @@ std::ostream& operator<<(std::ostream& os, const ConfigName& c) return os; } +bool operator<(const ConfigName& lhs, const ConfigName& rhs) +{ + if (int res = lhs.nodeName.compare(rhs.nodeName); res != 0) + return res < 0; + if (int res = lhs.portName.compare(rhs.portName); res != 0) + return res < 0; + return lhs.portBit < rhs.portBit; +} + /* struct Label */ // Initialization of static variables. const char* const Label::INPUT = "Input"; @@ -106,7 +117,7 @@ bool Task::canRunPlain() const return false; } -void Task::onAfterFirstTick() +void Task::onAfterTick(size_t) { // Do nothing by default. } @@ -321,9 +332,9 @@ void NetworkRunner::tick() network_.eachTask([&](Task* task) { task->tick(); }); } -void NetworkRunner::onAfterFirstTick() +void NetworkRunner::onAfterTick(size_t currentCycle) { - network_.eachTask([&](Task* task) { task->onAfterFirstTick(); }); + network_.eachTask([&](Task* task) { task->onAfterTick(currentCycle); }); } /* makeMUXROM */ @@ -580,4 +591,20 @@ void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb) } } +void test0() +{ + // operator< for ConfigName + { + bool res = false; + res = ConfigName{"abc", "def", 0} < ConfigName{"abc", "dfe", 0}; + assert(res); + res = ConfigName{"acc", "def", 0} < ConfigName{"abc", "dfe", 0}; + assert(!res); + res = ConfigName{"abc", "def", 0} < ConfigName{"abc", "def", 0}; + assert(!res); + res = ConfigName{"abc", "def", 0} < ConfigName{"abc", "def", 1}; + assert(res); + } +} + } // namespace nt diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 1dfb2fc..d26d569 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -56,8 +56,9 @@ struct ConfigName { int portBit; // Although all member variables of ConfigName are public, we make - // operator<< its friend function for clarity. + // operator<< and operator< its friend function for clarity. friend std::ostream& operator<<(std::ostream& os, const ConfigName& c); + friend bool operator<(const ConfigName& lhs, const ConfigName& rhs); }; struct Label { @@ -116,8 +117,8 @@ class Task { // Set input value. Only available for input gates. virtual void setInput(const DataHolder&); - // onAfterFirstTick() will be called after the first tick. - virtual void onAfterFirstTick(); + // onAfterTick() will be called after each tick. + virtual void onAfterTick(size_t currentCycle); // Return true iff this task can be run in plaintext mode. virtual bool canRunPlain() const; @@ -395,7 +396,7 @@ class NetworkRunner { bool isRunning() const; void run(); void tick(); - void onAfterFirstTick(); + void onAfterTick(size_t currentCycle); }; namespace blueprint { // blueprint components @@ -469,6 +470,8 @@ void readNetworkFromFile(const blueprint::File& file, NetworkBuilder& nb); void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb); void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb); +void test0(); + } // namespace nt #endif diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index eb33112..9fe2061 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -35,11 +35,37 @@ class Worker : public nt::Worker { } }; +// struct InputSource is used by class TaskInput to set correct input value +// every cycle. +struct InputSource { + int atPortWidth, atPortBit; + std::vector* bits; +}; + class TaskInput : public TaskCommon { +private: + std::optional source_; + public: TaskInput(Label label, Allocator& alc) : TaskCommon(label, alc, 0, 1) { } + TaskInput(InputSource source, Label label, Allocator& alc) + : TaskCommon(label, alc, 0, 1), source_(source) + { + } + + void onAfterTick(size_t currentCycle) override + { + if (source_) { + // Set the output value from the source + assert(getInputSize() == 0); + InputSource& s = source_.value(); + size_t index = + (s.atPortWidth * currentCycle + s.atPortBit) % s.bits->size(); + output() = s.bits->at(index); + } + } void startAsynchronously(WorkerInfo&) override { @@ -106,9 +132,10 @@ class TaskDFF : public nt::TaskDFF { { } - void onAfterFirstTick() override + void onAfterTick(size_t currentCycle) override { - output() = initialValue_.value_or(output()); + if (currentCycle == 0) + output() = initialValue_.value_or(output()); } bool canRunPlain() const override @@ -186,6 +213,7 @@ class NetworkBuilder : public nt::NetworkBuilder { std::unordered_map*> uid2common_; UID nextUID_; const PlainPacket* const reqPacket_; + const std::map* const cname2source_; private: UID genUID() @@ -194,19 +222,13 @@ class NetworkBuilder : public nt::NetworkBuilder { } public: - NetworkBuilder(Allocator& alc) - : nt::NetworkBuilder(alc), - uid2common_(), - nextUID_(0), - reqPacket_(nullptr) - { - } - - NetworkBuilder(const PlainPacket& reqPacket, Allocator& alc) + NetworkBuilder(const std::map& cname2source, + const PlainPacket& reqPacket, Allocator& alc) : nt::NetworkBuilder(alc), uid2common_(), nextUID_(0), - reqPacket_(&reqPacket) + reqPacket_(&reqPacket), + cname2source_(&cname2source) { } @@ -267,11 +289,15 @@ class NetworkBuilder : public nt::NetworkBuilder { UID INPUT(const std::string& nodeName, const std::string& portName, int portBit) override { + Allocator& alc = currentAllocator(); UID uid = genUID(); + ConfigName cname = ConfigName{nodeName, portName, portBit}; + Label label{uid, Label::INPUT, cname}; TaskInput* task = nullptr; - task = emplaceTask( - Label{uid, Label::INPUT, ConfigName{nodeName, portName, portBit}}, - currentAllocator()); + if (auto it = cname2source_->find(cname); it != cname2source_->end()) + task = emplaceTask(it->second, label, alc); + else + task = emplaceTask(label, alc); uid2common_.emplace(uid, task); return uid; } @@ -366,7 +392,23 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) currentCycle_(0), bp_(pr_.blueprintFile) { - NetworkBuilder nb{reqPacket_, alc}; + // Create map from ConfigName to InputSource + std::map cname2source; + for (auto&& [key, port] : bp_.atPorts()) { + if (port.kind != Label::INPUT) + continue; + auto& [atPortName, atPortBit] = key; + auto it = reqPacket_.bits.find(atPortName); + if (it == reqPacket_.bits.end()) + continue; + if (atPortName == "reset") + ERR_DIE("@reset cannot be set by user's input"); + cname2source.emplace(port.cname, + InputSource{bp_.atPortWidths().at(atPortName), + atPortBit, &it->second}); + } + + NetworkBuilder nb{cname2source, reqPacket_, alc}; // [[file]] for (auto&& file : bp_.files()) @@ -440,10 +482,7 @@ void Frontend::run() // Process normal cycles for (size_t i = 0; i < pr_.numCycles; i++) { runner.tick(); - if (i == 0) - // Set initial values of SDFF and RAM - runner.onAfterFirstTick(); - // FIXME set (circular) inputs + runner.onAfterTick(i); runner.run(); } @@ -459,6 +498,8 @@ void test0() WorkerInfo wi; DataHolder dh; Bit bit0 = 0_b, bit1 = 1_b; + PlainPacket pkt; + std::map c2s; { Allocator alc; @@ -493,7 +534,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{alc}; + NetworkBuilder nb{c2s, pkt, alc}; UID id0 = nb.INPUT("", "A", 0), id1 = nb.INPUT("", "B", 0), id2 = nb.NAND(), id3 = nb.OUTPUT("", "C", 0); nb.connect(id0, id2); @@ -525,7 +566,7 @@ void test0() A */ Allocator alc; - NetworkBuilder nb{alc}; + NetworkBuilder nb{c2s, pkt, alc}; UID id0 = nb.INPUT("", "reset", 0), id1 = nb.OUTPUT("", "out", 0), id2 = nb.DFF(), id3 = nb.NOT(), id4 = nb.ANDNOT(); nb.connect(id2, id1); @@ -558,7 +599,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{alc}; + NetworkBuilder nb{c2s, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::YOSYS_JSON, @@ -606,7 +647,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{alc}; + NetworkBuilder nb{c2s, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::IYOKANL1_JSON, @@ -655,7 +696,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{alc}; + NetworkBuilder nb{c2s, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::YOSYS_JSON, @@ -701,7 +742,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{alc}; + NetworkBuilder nb{c2s, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::YOSYS_JSON, @@ -733,7 +774,7 @@ void test0() // Cycle #1 runner.tick(); - runner.onAfterFirstTick(); + runner.onAfterTick(0); runner.run(); // The output is 9, that is, '0b1001' @@ -766,7 +807,7 @@ void test0() pkt.rom["rom"] = {0_b, 1_b, 0_b, 0_b, 1_b, 0_b, 1_b, 1_b}; Allocator alc; - NetworkBuilder nb{pkt, alc}; + NetworkBuilder nb{c2s, pkt, alc}; makeMUXROM(blueprint::BuiltinROM{blueprint::BuiltinROM::TYPE::MUX, "rom", 2, 2}, nb); @@ -796,7 +837,7 @@ void test0() pkt.ram["ram"] = {0_b, 1_b, 0_b, 0_b, 1_b, 0_b, 1_b, 1_b}; Allocator alc; - NetworkBuilder nb{pkt, alc}; + NetworkBuilder nb{c2s, pkt, alc}; makeMUXRAM(blueprint::BuiltinRAM{blueprint::BuiltinRAM::TYPE::MUX, "ram", 2, 2, 2}, nb); @@ -819,7 +860,7 @@ void test0() // Cycle #1 runner.tick(); - runner.onAfterFirstTick(); + runner.onAfterTick(0); tAddr0->setInput(&bit1); tAddr1->setInput(&bit0); tWren->setInput(&bit0); @@ -847,41 +888,41 @@ void test0() assert(dh.getBit() == 1_b); } - { - // Prepare the input packet - PlainPacket inPkt{ - {}, // ram - {}, // rom - { // bits - {"A", /* 0xc */ {0_b, 0_b, 1_b, 1_b}}, - {"B", /* 0xa */ {0_b, 1_b, 0_b, 1_b}}}, - std::nullopt, // numCycles - }; - writePlainPacket("_test_in", inPkt); - - // Prepare the expected output packet - PlainPacket expectedOutPkt{ - {}, // ram - {}, // rom - {{"out", /* 6 */ {0_b, 1_b, 1_b, 0_b}}}, // bits - 1, // numCycles - }; - - Allocator alc; - Frontend frontend{ - RunParameter{ - "test/config-toml/addr-4bit.toml", // blueprintFile - "_test_in", // inputFile - "_test_out", // outputFile - 2, // numCPUWorkers - 1, // numCycles - SCHED::RANKU, // sched - }, - alc}; - frontend.run(); - PlainPacket got = readPlainPacket("_test_out"); - assert(got == expectedOutPkt); - } + //{ + // // Prepare the input packet + // PlainPacket inPkt{ + // {}, // ram + // {}, // rom + // { // bits + // {"A", /* 0xc */ {0_b, 0_b, 1_b, 1_b}}, + // {"B", /* 0xa */ {0_b, 1_b, 0_b, 1_b}}}, + // std::nullopt, // numCycles + // }; + // writePlainPacket("_test_in", inPkt); + + // // Prepare the expected output packet + // PlainPacket expectedOutPkt{ + // {}, // ram + // {}, // rom + // {{"out", /* 6 */ {0_b, 1_b, 1_b, 0_b}}}, // bits + // 1, // numCycles + // }; + + // Allocator alc; + // Frontend frontend{ + // RunParameter{ + // "test/config-toml/addr-4bit.toml", // blueprintFile + // "_test_in", // inputFile + // "_test_out", // outputFile + // 2, // numCPUWorkers + // 1, // numCycles + // SCHED::RANKU, // sched + // }, + // alc}; + // frontend.run(); + // PlainPacket got = readPlainPacket("_test_out"); + // assert(got == expectedOutPkt); + //} } } // namespace plain diff --git a/src/test0.cpp b/src/test0.cpp index 653dda3..263dc34 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -1004,5 +1004,6 @@ int main() loguru::g_stderr_verbosity = loguru::Verbosity_1; nt::testAllocator(); + nt::test0(); nt::plain::test0(); } From c22b3808e45503688af5d093b3c18f0ae931278d Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 01:08:49 +0900 Subject: [PATCH 27/54] wip --- src/iyokan_nt_plain.cpp | 91 +++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 9fe2061..5113bc5 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -47,7 +47,8 @@ class TaskInput : public TaskCommon { std::optional source_; public: - TaskInput(Label label, Allocator& alc) : TaskCommon(label, alc, 0, 1) + TaskInput(Label label, Allocator& alc) + : TaskCommon(label, alc, 0, 1), source_(std::nullopt) { } TaskInput(InputSource source, Label label, Allocator& alc) @@ -312,6 +313,8 @@ class NetworkBuilder : public nt::NetworkBuilder { currentAllocator()); uid2common_.emplace(uid, task); return uid; + + // FIXME: We need to memorize this task to make a response packet. } UID ROM(const std::string& nodeName, const std::string& portName, @@ -395,17 +398,27 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // Create map from ConfigName to InputSource std::map cname2source; for (auto&& [key, port] : bp_.atPorts()) { + // Find only inputs, that is, "[connect] ... = @..." if (port.kind != Label::INPUT) continue; + + // Get "@atPortName[atPortBit]" auto& [atPortName, atPortBit] = key; + + // Check if reqPacket_ contains input data for @atPortName auto it = reqPacket_.bits.find(atPortName); if (it == reqPacket_.bits.end()) continue; + + // Die if users try to set the value of @reset[0] since it is set only + // by system if (atPortName == "reset") ERR_DIE("@reset cannot be set by user's input"); - cname2source.emplace(port.cname, - InputSource{bp_.atPortWidths().at(atPortName), - atPortBit, &it->second}); + + // Add a new entry to cname2source + InputSource s{bp_.atPortWidths().at(atPortName), atPortBit, + &it->second}; + cname2source.emplace(port.cname, s); } NetworkBuilder nb{cname2source, reqPacket_, alc}; @@ -888,41 +901,41 @@ void test0() assert(dh.getBit() == 1_b); } - //{ - // // Prepare the input packet - // PlainPacket inPkt{ - // {}, // ram - // {}, // rom - // { // bits - // {"A", /* 0xc */ {0_b, 0_b, 1_b, 1_b}}, - // {"B", /* 0xa */ {0_b, 1_b, 0_b, 1_b}}}, - // std::nullopt, // numCycles - // }; - // writePlainPacket("_test_in", inPkt); - - // // Prepare the expected output packet - // PlainPacket expectedOutPkt{ - // {}, // ram - // {}, // rom - // {{"out", /* 6 */ {0_b, 1_b, 1_b, 0_b}}}, // bits - // 1, // numCycles - // }; - - // Allocator alc; - // Frontend frontend{ - // RunParameter{ - // "test/config-toml/addr-4bit.toml", // blueprintFile - // "_test_in", // inputFile - // "_test_out", // outputFile - // 2, // numCPUWorkers - // 1, // numCycles - // SCHED::RANKU, // sched - // }, - // alc}; - // frontend.run(); - // PlainPacket got = readPlainPacket("_test_out"); - // assert(got == expectedOutPkt); - //} + { + // Prepare the input packet + PlainPacket inPkt{ + {}, // ram + {}, // rom + { // bits + {"A", /* 0xc */ {0_b, 0_b, 1_b, 1_b}}, + {"B", /* 0xa */ {0_b, 1_b, 0_b, 1_b}}}, + std::nullopt, // numCycles + }; + writePlainPacket("_test_in", inPkt); + + // Prepare the expected output packet + PlainPacket expectedOutPkt{ + {}, // ram + {}, // rom + {{"out", /* 6 */ {0_b, 1_b, 1_b, 0_b}}}, // bits + 1, // numCycles + }; + + Allocator alc; + Frontend frontend{ + RunParameter{ + "test/config-toml/addr-4bit.toml", // blueprintFile + "_test_in", // inputFile + "_test_out", // outputFile + 2, // numCPUWorkers + 1, // numCycles + SCHED::RANKU, // sched + }, + alc}; + frontend.run(); + PlainPacket got = readPlainPacket("_test_out"); + assert(got == expectedOutPkt); + } } } // namespace plain From 9061afe1032f54bce7487f29bf498b0f5c959099 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 01:11:46 +0900 Subject: [PATCH 28/54] wip --- src/iyokan_nt_plain.cpp | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 5113bc5..5093afa 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -214,7 +214,7 @@ class NetworkBuilder : public nt::NetworkBuilder { std::unordered_map*> uid2common_; UID nextUID_; const PlainPacket* const reqPacket_; - const std::map* const cname2source_; + const std::map* const cname2isource_; private: UID genUID() @@ -223,13 +223,13 @@ class NetworkBuilder : public nt::NetworkBuilder { } public: - NetworkBuilder(const std::map& cname2source, + NetworkBuilder(const std::map& cname2isource, const PlainPacket& reqPacket, Allocator& alc) : nt::NetworkBuilder(alc), uid2common_(), nextUID_(0), reqPacket_(&reqPacket), - cname2source_(&cname2source) + cname2isource_(&cname2isource) { } @@ -295,7 +295,7 @@ class NetworkBuilder : public nt::NetworkBuilder { ConfigName cname = ConfigName{nodeName, portName, portBit}; Label label{uid, Label::INPUT, cname}; TaskInput* task = nullptr; - if (auto it = cname2source_->find(cname); it != cname2source_->end()) + if (auto it = cname2isource_->find(cname); it != cname2isource_->end()) task = emplaceTask(it->second, label, alc); else task = emplaceTask(label, alc); @@ -313,8 +313,6 @@ class NetworkBuilder : public nt::NetworkBuilder { currentAllocator()); uid2common_.emplace(uid, task); return uid; - - // FIXME: We need to memorize this task to make a response packet. } UID ROM(const std::string& nodeName, const std::string& portName, @@ -396,7 +394,7 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) bp_(pr_.blueprintFile) { // Create map from ConfigName to InputSource - std::map cname2source; + std::map cname2isource; for (auto&& [key, port] : bp_.atPorts()) { // Find only inputs, that is, "[connect] ... = @..." if (port.kind != Label::INPUT) @@ -415,13 +413,13 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) if (atPortName == "reset") ERR_DIE("@reset cannot be set by user's input"); - // Add a new entry to cname2source + // Add a new entry to cname2isource InputSource s{bp_.atPortWidths().at(atPortName), atPortBit, &it->second}; - cname2source.emplace(port.cname, s); + cname2isource.emplace(port.cname, s); } - NetworkBuilder nb{cname2source, reqPacket_, alc}; + NetworkBuilder nb{cname2isource, reqPacket_, alc}; // [[file]] for (auto&& file : bp_.files()) @@ -512,7 +510,7 @@ void test0() DataHolder dh; Bit bit0 = 0_b, bit1 = 1_b; PlainPacket pkt; - std::map c2s; + std::map c2is; { Allocator alc; @@ -547,7 +545,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; UID id0 = nb.INPUT("", "A", 0), id1 = nb.INPUT("", "B", 0), id2 = nb.NAND(), id3 = nb.OUTPUT("", "C", 0); nb.connect(id0, id2); @@ -579,7 +577,7 @@ void test0() A */ Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; UID id0 = nb.INPUT("", "reset", 0), id1 = nb.OUTPUT("", "out", 0), id2 = nb.DFF(), id3 = nb.NOT(), id4 = nb.ANDNOT(); nb.connect(id2, id1); @@ -612,7 +610,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::YOSYS_JSON, @@ -660,7 +658,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::IYOKANL1_JSON, @@ -709,7 +707,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::YOSYS_JSON, @@ -755,7 +753,7 @@ void test0() { Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; readNetworkFromFile( blueprint::File{blueprint::File::TYPE::YOSYS_JSON, @@ -820,7 +818,7 @@ void test0() pkt.rom["rom"] = {0_b, 1_b, 0_b, 0_b, 1_b, 0_b, 1_b, 1_b}; Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; makeMUXROM(blueprint::BuiltinROM{blueprint::BuiltinROM::TYPE::MUX, "rom", 2, 2}, nb); @@ -850,7 +848,7 @@ void test0() pkt.ram["ram"] = {0_b, 1_b, 0_b, 0_b, 1_b, 0_b, 1_b, 1_b}; Allocator alc; - NetworkBuilder nb{c2s, pkt, alc}; + NetworkBuilder nb{c2is, pkt, alc}; makeMUXRAM(blueprint::BuiltinRAM{blueprint::BuiltinRAM::TYPE::MUX, "ram", 2, 2, 2}, nb); From e75c0ba57a82d97873b1e6599ef901ff7bebdaf3 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 21:31:32 +0900 Subject: [PATCH 29/54] wip --- src/iyokan_nt.hpp | 6 +++++ src/iyokan_nt_plain.cpp | 58 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index d26d569..319f0b7 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -220,6 +220,12 @@ class TaskCommon : public Task { inputs_.push_back(newIn); } + + // public output(). Debug purpose only. + T& DEBUG_output() + { + return output(); + } }; // class TaskDFF can be used as base class of DFF tasks. diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 5093afa..c285dbc 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -381,6 +381,10 @@ class Frontend { int currentCycle_; nt::Blueprint bp_; +private: + void makeResPacket(PlainPacket& out, const TaskFinder& finder, + int numCycles); + public: Frontend(const RunParameter& pr, Allocator& alc); void run(); @@ -467,6 +471,34 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) // FIXME check if network is valid } +void Frontend::makeResPacket(PlainPacket& out, const TaskFinder& finder, + int numCycles) +{ + DataHolder dh; + + // Set the current number of cycles + out.numCycles = numCycles; + + // Get values of output @port + out.bits.clear(); + for (auto&& [key, port] : bp_.atPorts()) { + // Find "[connect] @atPortName[atPortBit] = ..." + if (port.kind != Label::OUTPUT) + continue; + auto& [atPortName, atPortBit] = key; + + // Get the value + Task* t = finder.findByConfigName(port.cname); + t->getOutput(dh); + + // Assign the value to the corresponding bit of the response packet + auto& bits = out.bits[atPortName]; + if (bits.size() < atPortBit + 1) + bits.resize(atPortBit + 1); + bits.at(atPortBit) = dh.getBit(); + } +} + void Frontend::run() { const Bit bit0 = 0_b, bit1 = 1_b; @@ -492,12 +524,36 @@ void Frontend::run() // Process normal cycles for (size_t i = 0; i < pr_.numCycles; i++) { + // Mount new values to DFFs runner.tick(); + + // Set new input data. If i is equal to 0, it also mounts initial data + // to RAMs. runner.onAfterTick(i); + + // Go computing of each gate runner.run(); + + /* + // Debug printing of all the gates + runner.network().eachTask([&](Task* t) { + TaskCommon* p = dynamic_cast*>(t); + if (p == nullptr) + return; + const Label& l = t->label(); + if (t->label().cname) + LOG_DBG << l.kind << "\t" << *l.cname << "\t" + << p->DEBUG_output(); + else + LOG_DBG << l.kind << "\t" << p->DEBUG_output(); + }); + */ } - // FIXME output result packet + // Dump result packet + PlainPacket resPacket; + makeResPacket(resPacket, finder, pr_.numCycles); + writePlainPacket(pr_.outputFile, resPacket); } /**************************************************/ From 7c0c810a6bc8fc0955f92df9367648c498c9d16b Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 21:34:53 +0900 Subject: [PATCH 30/54] wip --- src/iyokan_nt_plain.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index c285dbc..86c30ef 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -990,6 +990,40 @@ void test0() PlainPacket got = readPlainPacket("_test_out"); assert(got == expectedOutPkt); } + + { + // Prepare the input packet + PlainPacket inPkt{ + {}, // ram + {}, // rom + {}, // bits + std::nullopt, // numCycles + }; + writePlainPacket("_test_in", inPkt); + + // Prepare the expected output packet + PlainPacket expectedOutPkt{ + {}, // ram + {}, // rom + {{"out", /* 2 */ {0_b, 1_b, 0_b, 0_b}}}, // bits + 3, // numCycles + }; + + Allocator alc; + Frontend frontend{ + RunParameter{ + "test/config-toml/counter-4bit.toml", // blueprintFile + "_test_in", // inputFile + "_test_out", // outputFile + 2, // numCPUWorkers + 3, // numCycles + SCHED::RANKU, // sched + }, + alc}; + frontend.run(); + PlainPacket got = readPlainPacket("_test_out"); + assert(got == expectedOutPkt); + } } } // namespace plain From 3dd5c5479d82530dcfbecc56c1787aaefc6356fa Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 22:16:35 +0900 Subject: [PATCH 31/54] wip --- src/iyokan_nt_plain.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 86c30ef..33646b1 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -1024,6 +1024,46 @@ void test0() PlainPacket got = readPlainPacket("_test_out"); assert(got == expectedOutPkt); } + + { + std::vector src(128, 0_b); + // 0x7 at byte 7 + src.at(7 * 8 + 0) = 1_b; + src.at(7 * 8 + 1) = 1_b; + src.at(7 * 8 + 2) = 1_b; + + // Prepare the input packet + PlainPacket inPkt{ + {}, // ram + {{"rom", src}}, // rom + {{"addr", {1_b, 1_b, 1_b, 0_b}}}, // bits + std::nullopt, // numCycles + }; + writePlainPacket("_test_in", inPkt); + + // Prepare the expected output packet + PlainPacket expectedOutPkt{ + {}, // ram + {}, // rom + {{"rdata", + /* 7 */ {1_b, 1_b, 1_b, 0_b, 0_b, 0_b, 0_b, 0_b}}}, // bits + 1, // numCycles + }; + + Allocator alc; + Frontend frontend{RunParameter{ + "test/config-toml/rom-4-8.toml", // blueprintFile + "_test_in", // inputFile + "_test_out", // outputFile + 2, // numCPUWorkers + 1, // numCycles + SCHED::RANKU, // sched + }, + alc}; + frontend.run(); + PlainPacket got = readPlainPacket("_test_out"); + assert(got == expectedOutPkt); + } } } // namespace plain From 88e2641a82b80097fd5be6bd01c08e1a8a1db34c Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 22:46:46 +0900 Subject: [PATCH 32/54] wip --- src/iyokan_nt_plain.cpp | 83 ++++++++++++++--------------------------- src/packet_nt.cpp | 48 ++++++++++++++++++++++++ src/packet_nt.hpp | 2 + 3 files changed, 79 insertions(+), 54 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 33646b1..c139d7d 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -956,25 +956,10 @@ void test0() } { - // Prepare the input packet - PlainPacket inPkt{ - {}, // ram - {}, // rom - { // bits - {"A", /* 0xc */ {0_b, 0_b, 1_b, 1_b}}, - {"B", /* 0xa */ {0_b, 1_b, 0_b, 1_b}}}, - std::nullopt, // numCycles - }; + auto inPkt = PlainPacket::fromTOML("test/in/test04.in"), + expectedOutPkt = PlainPacket::fromTOML("test/out/test04.out"); writePlainPacket("_test_in", inPkt); - // Prepare the expected output packet - PlainPacket expectedOutPkt{ - {}, // ram - {}, // rom - {{"out", /* 6 */ {0_b, 1_b, 1_b, 0_b}}}, // bits - 1, // numCycles - }; - Allocator alc; Frontend frontend{ RunParameter{ @@ -992,23 +977,10 @@ void test0() } { - // Prepare the input packet - PlainPacket inPkt{ - {}, // ram - {}, // rom - {}, // bits - std::nullopt, // numCycles - }; + auto inPkt = PlainPacket::fromTOML("test/in/test13.in"), + expectedOutPkt = PlainPacket::fromTOML("test/out/test13.out"); writePlainPacket("_test_in", inPkt); - // Prepare the expected output packet - PlainPacket expectedOutPkt{ - {}, // ram - {}, // rom - {{"out", /* 2 */ {0_b, 1_b, 0_b, 0_b}}}, // bits - 3, // numCycles - }; - Allocator alc; Frontend frontend{ RunParameter{ @@ -1026,30 +998,10 @@ void test0() } { - std::vector src(128, 0_b); - // 0x7 at byte 7 - src.at(7 * 8 + 0) = 1_b; - src.at(7 * 8 + 1) = 1_b; - src.at(7 * 8 + 2) = 1_b; - - // Prepare the input packet - PlainPacket inPkt{ - {}, // ram - {{"rom", src}}, // rom - {{"addr", {1_b, 1_b, 1_b, 0_b}}}, // bits - std::nullopt, // numCycles - }; + auto inPkt = PlainPacket::fromTOML("test/in/test15.in"), + expectedOutPkt = PlainPacket::fromTOML("test/out/test15.out"); writePlainPacket("_test_in", inPkt); - // Prepare the expected output packet - PlainPacket expectedOutPkt{ - {}, // ram - {}, // rom - {{"rdata", - /* 7 */ {1_b, 1_b, 1_b, 0_b, 0_b, 0_b, 0_b, 0_b}}}, // bits - 1, // numCycles - }; - Allocator alc; Frontend frontend{RunParameter{ "test/config-toml/rom-4-8.toml", // blueprintFile @@ -1064,6 +1016,29 @@ void test0() PlainPacket got = readPlainPacket("_test_out"); assert(got == expectedOutPkt); } + + /* + { + auto inPkt = PlainPacket::fromTOML("test/in/test08.in"), + expectedOutPkt = PlainPacket::fromTOML("test/out/test08.out"); + writePlainPacket("_test_in", inPkt); + + Allocator alc; + Frontend frontend{ + RunParameter{ + "test/config-toml/ram-8-16-16.toml", // blueprintFile + "_test_in", // inputFile + "_test_out", // outputFile + 2, // numCPUWorkers + 8, // numCycles + SCHED::RANKU, // sched + }, + alc}; + frontend.run(); + PlainPacket got = readPlainPacket("_test_out"); + assert(got == expectedOutPkt); + } + */ } } // namespace plain diff --git a/src/packet_nt.cpp b/src/packet_nt.cpp index 5db788c..e2862fc 100644 --- a/src/packet_nt.cpp +++ b/src/packet_nt.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -267,6 +268,53 @@ TFHEPacket PlainPacket::encrypt(const TFHEpp::SecretKey& key) const return tfhe; } +PlainPacket PlainPacket::fromTOML(const std::string& filepath) +{ + // FIXME: iyokan-packet should use this function + const auto root = toml::parse(filepath); + int numCycles = toml::find_or(root, "cycles", -1); + std::unordered_map> ram, rom, bits; + + auto parseEntries = [&root]( + std::unordered_map>& + name2bitvec, + const std::string& entryName) { + if (!root.contains(entryName)) + return; + const auto tables = + toml::find>(root, entryName); + for (const auto& table : tables) { + const auto name = toml::find(table, "name"); + const auto size = toml::find(table, "size"); + const auto bytes = + toml::find>(table, "bytes"); + + std::vector& v = name2bitvec[name]; + v.resize(size, 0_b); + auto it = v.begin(); + for (uint64_t byte : bytes) { + if (byte >= 0xffu) + LOG_S(WARNING) + << "'bytes' field expects only <256 unsinged integer, " + "but got '" + << byte << "'. Only the lower 8bits is used."; + for (int i = 0; i < 8; i++) { + if (it == v.end()) + goto end; + *it++ = ((byte >> i) & 1u) != 0 ? 1_b : 0_b; + } + } + end:; // ';' is necessary since label is followed by expression. + } + }; + + parseEntries(ram, "ram"); // [[ram]] + parseEntries(rom, "rom"); // [[rom]] + parseEntries(bits, "bits"); // [[bits]] + + return PlainPacket{ram, rom, bits, numCycles}; +} + PlainPacket TFHEPacket::decrypt(const TFHEpp::SecretKey& key) const { PlainPacket plain{{}, {}, {}, numCycles}; diff --git a/src/packet_nt.hpp b/src/packet_nt.hpp index f1fb48a..027a8ee 100644 --- a/src/packet_nt.hpp +++ b/src/packet_nt.hpp @@ -84,6 +84,8 @@ struct PlainPacket { bool operator==(const PlainPacket& rhs) const; TFHEPacket encrypt(const TFHEpp::SecretKey& key) const; + + static PlainPacket fromTOML(const std::string& filepath); }; struct TFHEPacket { From 0e1addd29550e89c9cc0ff1c7651bb4a965853ad Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 2 Nov 2021 23:15:44 +0900 Subject: [PATCH 33/54] wip --- src/iyokan_nt.cpp | 8 +++--- src/iyokan_nt.hpp | 2 +- src/iyokan_nt_plain.cpp | 60 ++++++++++++++++++++++++++--------------- src/packet_nt.cpp | 2 +- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index a165b4f..8412856 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -420,7 +420,8 @@ namespace { void make1bitRAMWithMUX(const std::string& nodeName, const std::vector& addrInputs, UID wrenInput, - size_t indexWRdata, NetworkBuilder& nb) + size_t dataWidth, size_t indexWRdata, + NetworkBuilder& nb) { /* wdata[indexWRdata] @@ -506,7 +507,7 @@ void make1bitRAMWithMUX(const std::string& nodeName, sel */ UID sel = workingIds.at(addr), mux = nb.MUX(), - ram = nb.RAM(nodeName, "ramdata", addr * inAddrWidth + indexWRdata); + ram = nb.RAM(nodeName, "ramdata", addr * dataWidth + indexWRdata); nb.connect(ram, mux); nb.connect(wdataInput, mux); nb.connect(sel, mux); @@ -587,7 +588,8 @@ void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb) // Create 1bitRAMs for (size_t i = 0; i < ram.outRdataWidth; i++) { - make1bitRAMWithMUX(ram.name, addrInputs, wrenInput, i, nb); + make1bitRAMWithMUX(ram.name, addrInputs, wrenInput, ram.outRdataWidth, + i, nb); } } diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 319f0b7..17882e8 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -111,7 +111,7 @@ class Task { // tick() resets the internal state of the task for the next cycle virtual void tick(); - // Get output value. Only available for output gates. + // Get output value. Only available for output and DFF gates. virtual void getOutput(DataHolder&); // Set input value. Only available for input gates. diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index c139d7d..178b479 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -149,6 +149,11 @@ class TaskDFF : public nt::TaskDFF { // Nothing to do, because the main process is done in // nt::TaskDFF::tick(). } + + void getOutput(DataHolder& h) override + { + h.setBit(&output()); + } }; class TaskROM : public TaskCommon { @@ -497,6 +502,19 @@ void Frontend::makeResPacket(PlainPacket& out, const TaskFinder& finder, bits.resize(atPortBit + 1); bits.at(atPortBit) = dh.getBit(); } + + // Get values of RAM + for (auto&& ram : bp_.builtinRAMs()) { + std::vector& dst = out.ram[ram.name]; + dst.clear(); + for (size_t i = 0; i < (1 << ram.inAddrWidth) * ram.outRdataWidth; + i++) { + ConfigName cname{ram.name, "ramdata", static_cast(i)}; + Task* t = finder.findByConfigName(cname); + t->getOutput(dh); + dst.push_back(dh.getBit()); + } + } } void Frontend::run() @@ -1017,28 +1035,26 @@ void test0() assert(got == expectedOutPkt); } - /* - { - auto inPkt = PlainPacket::fromTOML("test/in/test08.in"), - expectedOutPkt = PlainPacket::fromTOML("test/out/test08.out"); - writePlainPacket("_test_in", inPkt); - - Allocator alc; - Frontend frontend{ - RunParameter{ - "test/config-toml/ram-8-16-16.toml", // blueprintFile - "_test_in", // inputFile - "_test_out", // outputFile - 2, // numCPUWorkers - 8, // numCycles - SCHED::RANKU, // sched - }, - alc}; - frontend.run(); - PlainPacket got = readPlainPacket("_test_out"); - assert(got == expectedOutPkt); - } - */ + { + auto inPkt = PlainPacket::fromTOML("test/in/test08.in"), + expectedOutPkt = PlainPacket::fromTOML("test/out/test08.out"); + writePlainPacket("_test_in", inPkt); + + Allocator alc; + Frontend frontend{ + RunParameter{ + "test/config-toml/ram-8-16-16.toml", // blueprintFile + "_test_in", // inputFile + "_test_out", // outputFile + 2, // numCPUWorkers + 8, // numCycles + SCHED::RANKU, // sched + }, + alc}; + frontend.run(); + PlainPacket got = readPlainPacket("_test_out"); + assert(got == expectedOutPkt); + } } } // namespace plain diff --git a/src/packet_nt.cpp b/src/packet_nt.cpp index e2862fc..5a76b6d 100644 --- a/src/packet_nt.cpp +++ b/src/packet_nt.cpp @@ -293,7 +293,7 @@ PlainPacket PlainPacket::fromTOML(const std::string& filepath) v.resize(size, 0_b); auto it = v.begin(); for (uint64_t byte : bytes) { - if (byte >= 0xffu) + if (byte > 0xffu) LOG_S(WARNING) << "'bytes' field expects only <256 unsinged integer, " "but got '" From 92ffee0cbba278c118f9e5bd1d0a8482d7d79b9e Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 3 Nov 2021 13:26:22 +0900 Subject: [PATCH 34/54] wip --- src/iyokan_nt_plain.cpp | 97 ++++++++++------------------------------- 1 file changed, 24 insertions(+), 73 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 178b479..822eda9 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -973,88 +973,39 @@ void test0() assert(dh.getBit() == 1_b); } - { - auto inPkt = PlainPacket::fromTOML("test/in/test04.in"), - expectedOutPkt = PlainPacket::fromTOML("test/out/test04.out"); - writePlainPacket("_test_in", inPkt); - - Allocator alc; - Frontend frontend{ - RunParameter{ - "test/config-toml/addr-4bit.toml", // blueprintFile - "_test_in", // inputFile - "_test_out", // outputFile - 2, // numCPUWorkers - 1, // numCycles - SCHED::RANKU, // sched - }, - alc}; - frontend.run(); - PlainPacket got = readPlainPacket("_test_out"); - assert(got == expectedOutPkt); - } - - { - auto inPkt = PlainPacket::fromTOML("test/in/test13.in"), - expectedOutPkt = PlainPacket::fromTOML("test/out/test13.out"); - writePlainPacket("_test_in", inPkt); - - Allocator alc; - Frontend frontend{ - RunParameter{ - "test/config-toml/counter-4bit.toml", // blueprintFile - "_test_in", // inputFile - "_test_out", // outputFile - 2, // numCPUWorkers - 3, // numCycles - SCHED::RANKU, // sched - }, - alc}; - frontend.run(); - PlainPacket got = readPlainPacket("_test_out"); - assert(got == expectedOutPkt); - } + auto go = [&](const std::string& inPktPath, + const std::string& expectedOutPktPath, + const std::string& blueprintPath, int numCycles) { + const char* const reqPktPath = "_test_in"; + const char* const resPktPath = "_test_out"; - { - auto inPkt = PlainPacket::fromTOML("test/in/test15.in"), - expectedOutPkt = PlainPacket::fromTOML("test/out/test15.out"); - writePlainPacket("_test_in", inPkt); + auto inPkt = PlainPacket::fromTOML(inPktPath), + expectedOutPkt = PlainPacket::fromTOML(expectedOutPktPath); + writePlainPacket(reqPktPath, inPkt); Allocator alc; Frontend frontend{RunParameter{ - "test/config-toml/rom-4-8.toml", // blueprintFile - "_test_in", // inputFile - "_test_out", // outputFile - 2, // numCPUWorkers - 1, // numCycles - SCHED::RANKU, // sched + blueprintPath, // blueprintFile + reqPktPath, // inputFile + resPktPath, // outputFile + 2, // numCPUWorkers + numCycles, // numCycles + SCHED::RANKU, // sched }, alc}; frontend.run(); - PlainPacket got = readPlainPacket("_test_out"); + PlainPacket got = readPlainPacket(resPktPath); assert(got == expectedOutPkt); - } - - { - auto inPkt = PlainPacket::fromTOML("test/in/test08.in"), - expectedOutPkt = PlainPacket::fromTOML("test/out/test08.out"); - writePlainPacket("_test_in", inPkt); + }; - Allocator alc; - Frontend frontend{ - RunParameter{ - "test/config-toml/ram-8-16-16.toml", // blueprintFile - "_test_in", // inputFile - "_test_out", // outputFile - 2, // numCPUWorkers - 8, // numCycles - SCHED::RANKU, // sched - }, - alc}; - frontend.run(); - PlainPacket got = readPlainPacket("_test_out"); - assert(got == expectedOutPkt); - } + go("test/in/test04.in", "test/out/test04.out", + "test/config-toml/addr-4bit.toml", 1); + go("test/in/test13.in", "test/out/test13.out", + "test/config-toml/counter-4bit.toml", 3); + go("test/in/test15.in", "test/out/test15.out", + "test/config-toml/rom-4-8.toml", 1); + go("test/in/test08.in", "test/out/test08.out", + "test/config-toml/ram-8-16-16.toml", 8); } } // namespace plain From 46d0426f1a6530374627207a3c777ccb5da87298 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 3 Nov 2021 13:35:02 +0900 Subject: [PATCH 35/54] wip --- src/iyokan_nt_plain.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 822eda9..64cdadb 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -973,9 +973,9 @@ void test0() assert(dh.getBit() == 1_b); } - auto go = [&](const std::string& inPktPath, - const std::string& expectedOutPktPath, - const std::string& blueprintPath, int numCycles) { + auto go = [&](const std::string& blueprintPath, + const std::string& inPktPath, + const std::string& expectedOutPktPath, int numCycles) { const char* const reqPktPath = "_test_in"; const char* const resPktPath = "_test_out"; @@ -998,14 +998,28 @@ void test0() assert(got == expectedOutPkt); }; - go("test/in/test04.in", "test/out/test04.out", - "test/config-toml/addr-4bit.toml", 1); - go("test/in/test13.in", "test/out/test13.out", - "test/config-toml/counter-4bit.toml", 3); - go("test/in/test15.in", "test/out/test15.out", - "test/config-toml/rom-4-8.toml", 1); - go("test/in/test08.in", "test/out/test08.out", - "test/config-toml/ram-8-16-16.toml", 8); + go("test/config-toml/const-4bit.toml", "test/in/test22.in", + "test/out/test22.out", 1); + go("test/config-toml/addr-4bit.toml", "test/in/test04.in", + "test/out/test04.out", 1); + go("test/config-toml/pass-addr-pass-4bit.toml", "test/in/test04.in", + "test/out/test04.out", 1); + go("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + "test/out/test16.out", 3); + go("test/config-toml/div-8bit.toml", "test/in/test05.in", + "test/out/test05.out", 1); + go("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + "test/out/test06.out", 16); + go("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + "test/out/test07.out", 16); + go("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + "test/out/test08.out", 8); + go("test/config-toml/rom-4-8.toml", "test/in/test15.in", + "test/out/test15.out", 1); + go("test/config-toml/counter-4bit.toml", "test/in/test13.in", + "test/out/test13.out", 3); + go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", + "test/out/test09-ruby.out", 7); } } // namespace plain From 585072278c92be232a4401b5b71eb52afbc7f606 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Thu, 4 Nov 2021 00:40:58 +0900 Subject: [PATCH 36/54] wip --- src/iyokan_nt.cpp | 129 +++++++++++++++++++++++++++++ src/iyokan_nt.hpp | 44 ++++++++++ src/iyokan_nt_plain.cpp | 175 +++++++++------------------------------- 3 files changed, 209 insertions(+), 139 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 8412856..2a78a2e 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,4 +1,5 @@ #include "iyokan_nt.hpp" +#include "dataholder_nt.hpp" #include "error_nt.hpp" #include @@ -337,6 +338,134 @@ void NetworkRunner::onAfterTick(size_t currentCycle) network_.eachTask([&](Task* task) { task->onAfterTick(currentCycle); }); } +/* struct RunParameter */ + +void RunParameter::print() const +{ + LOG_S(INFO) << "Run parameters"; + LOG_S(INFO) << "\tMode: plain"; + LOG_S(INFO) << "\tBlueprint: " << blueprintFile; + LOG_S(INFO) << "\t# of CPU Workers: " << numCPUWorkers; + LOG_S(INFO) << "\t# of cycles: " << numCycles; + LOG_S(INFO) << "\tInput file (request packet): " << inputFile; + LOG_S(INFO) << "\tOutput file (result packet): " << outputFile; + LOG_S(INFO) << "\tSchedule: " << (sched == SCHED::TOPO ? "topo" : "ranku"); +} + +/* class Frontend */ + +Frontend::Frontend(const RunParameter& pr) + : pr_(pr), network_(std::nullopt), currentCycle_(0), bp_(pr_.blueprintFile) +{ +} + +void Frontend::buildNetwork(NetworkBuilder& nb) +{ + // [[file]] + for (auto&& file : bp_.files()) + readNetworkFromFile(file, nb); + + // [[builtin]] type = ram | type = mux-ram + for (auto&& ram : bp_.builtinRAMs()) { + // We ignore ram.type and always use mux-ram in plaintext mode. + makeMUXRAM(ram, nb); + } + + // [[builtin]] type = rom | type = mux-rom + for (auto&& rom : bp_.builtinROMs()) { + // We ignore rom.type and always use mux-rom in plaintext mode. + makeMUXROM(rom, nb); + } + + auto get = [&](const blueprint::Port& port) -> Task* { + Task* task = nb.finder().findByConfigName(port.cname); + if (task->label().kind != port.kind) + ERR_DIE("Invalid port: " << port.cname << " is " + << task->label().kind << ", not " + << port.kind); + return task; + }; + + // [connect] + // We need to treat "... = @..." and "@... = ..." differently from + // "..." = ...". + // First, check if ports that are connected to or from "@..." exist. + for (auto&& [key, port] : bp_.atPorts()) { + get(port); // Only checks if port exists + } + // Then, connect other ports. `get` checks if they also exist. + for (auto&& [src, dst] : bp_.edges()) { + assert(src.kind == Label::OUTPUT); + assert(dst.kind == Label::INPUT); + nb.connect(get(src)->label().uid, get(dst)->label().uid); + } + + // Set priority to each DepNode + // FIXME + + network_.emplace(nb.createNetwork()); + // FIXME check if network is valid +} + +Frontend::~Frontend() +{ +} + +void Frontend::run() +{ + DataHolder bit0, bit1; + setBit0(bit0); + setBit1(bit1); + + // Create workers + std::vector> workers = makeWorkers(); + + // Create runner and finder for the network + NetworkRunner runner{std::move(network_.value()), std::move(workers)}; + network_ = std::nullopt; + const TaskFinder& finder = runner.network().finder(); + + // Process reset cycle if @reset is used + // FIXME: Add support for --skip-reset flag + if (auto reset = bp_.at("reset"); reset && reset->kind == Label::INPUT) { + Task* t = finder.findByConfigName(reset->cname); + t->setInput(bit1); // Set reset on + runner.run(); + t->setInput(bit0); // Set reset off + } + + // Process normal cycles + for (size_t i = 0; i < pr_.numCycles; i++) { + // Mount new values to DFFs + runner.tick(); + + // Set new input data. If i is equal to 0, it also mounts initial data + // to RAMs. + runner.onAfterTick(i); + + // Go computing of each gate + runner.run(); + + /* + // Debug printing of all the gates + runner.network().eachTask([&](Task* t) { + TaskCommon* p = dynamic_cast*>(t); + if (p == nullptr) + return; + const Label& l = t->label(); + if (t->label().cname) + LOG_DBG << l.kind << "\t" << *l.cname << "\t" + << p->DEBUG_output(); + else + LOG_DBG << l.kind << "\t" << p->DEBUG_output(); + }); + */ + } + + // Dump result packet + dumpResPacket(pr_.outputFile, finder, pr_.numCycles); +} + /* makeMUXROM */ namespace { diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 17882e8..f38b71b 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -472,6 +472,50 @@ class Blueprint { const std::unordered_map& atPortWidths() const; }; +enum class SCHED { + TOPO, + RANKU, +}; + +struct RunParameter { + std::string blueprintFile, inputFile, outputFile; + int numCPUWorkers, numCycles; + SCHED sched; + + void print() const; +}; + +class Frontend { +private: + RunParameter pr_; + std::optional network_; + int currentCycle_; + Blueprint bp_; + +protected: + virtual void setBit0(DataHolder& dh) = 0; + virtual void setBit1(DataHolder& dh) = 0; + virtual void dumpResPacket(const std::string& outpath, + const TaskFinder& finder, int numCycles) = 0; + virtual std::vector> makeWorkers() = 0; + + void buildNetwork(NetworkBuilder& nb); + const RunParameter& runParam() const + { + return pr_; + } + const Blueprint& blueprint() const + { + return bp_; + } + +public: + Frontend(const RunParameter& pr); + virtual ~Frontend(); + + void run(); +}; + void readNetworkFromFile(const blueprint::File& file, NetworkBuilder& nb); void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb); void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 64cdadb..587d8a9 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -354,57 +354,29 @@ class NetworkBuilder : public nt::NetworkBuilder { } }; -enum class SCHED { - TOPO, - RANKU, -}; - -struct RunParameter { - std::string blueprintFile, inputFile, outputFile; - int numCPUWorkers, numCycles; - SCHED sched; - - void print() const - { - LOG_S(INFO) << "Run parameters"; - LOG_S(INFO) << "\tMode: plain"; - LOG_S(INFO) << "\tBlueprint: " << blueprintFile; - LOG_S(INFO) << "\t# of CPU Workers: " << numCPUWorkers; - LOG_S(INFO) << "\t# of cycles: " << numCycles; - LOG_S(INFO) << "\tInput file (request packet): " << inputFile; - LOG_S(INFO) << "\tOutput file (result packet): " << outputFile; - LOG_S(INFO) << "\tSchedule: " - << (sched == SCHED::TOPO ? "topo" : "ranku"); - } -}; - -class Frontend { +class Frontend : public nt::Frontend { private: - RunParameter pr_; - std::optional network_; PlainPacket reqPacket_; - int currentCycle_; - nt::Blueprint bp_; private: - void makeResPacket(PlainPacket& out, const TaskFinder& finder, - int numCycles); + void setBit0(DataHolder& dh) override; + void setBit1(DataHolder& dh) override; + void dumpResPacket(const std::string& outpath, const TaskFinder& finder, + int numCycles) override; + std::vector> makeWorkers() override; public: Frontend(const RunParameter& pr, Allocator& alc); - void run(); }; Frontend::Frontend(const RunParameter& pr, Allocator& alc) - : pr_(pr), - network_(std::nullopt), - reqPacket_(readPlainPacket(pr_.inputFile)), - currentCycle_(0), - bp_(pr_.blueprintFile) + : nt::Frontend(pr), reqPacket_(readPlainPacket(pr.inputFile)) { + const Blueprint& bp = blueprint(); + // Create map from ConfigName to InputSource std::map cname2isource; - for (auto&& [key, port] : bp_.atPorts()) { + for (auto&& [key, port] : bp.atPorts()) { // Find only inputs, that is, "[connect] ... = @..." if (port.kind != Label::INPUT) continue; @@ -423,70 +395,40 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) ERR_DIE("@reset cannot be set by user's input"); // Add a new entry to cname2isource - InputSource s{bp_.atPortWidths().at(atPortName), atPortBit, - &it->second}; + InputSource s{bp.atPortWidths().at(atPortName), atPortBit, &it->second}; cname2isource.emplace(port.cname, s); } + // Build the network. The instance is in nt::Frontend NetworkBuilder nb{cname2isource, reqPacket_, alc}; + buildNetwork(nb); +} - // [[file]] - for (auto&& file : bp_.files()) - readNetworkFromFile(file, nb); - - // [[builtin]] type = ram | type = mux-ram - for (auto&& ram : bp_.builtinRAMs()) { - // We ignore ram.type and always use mux-ram in plaintext mode. - makeMUXRAM(ram, nb); - } - - // [[builtin]] type = rom | type = mux-rom - for (auto&& rom : bp_.builtinROMs()) { - // We ignore rom.type and always use mux-rom in plaintext mode. - makeMUXROM(rom, nb); - } - - auto get = [&](const blueprint::Port& port) -> Task* { - Task* task = nb.finder().findByConfigName(port.cname); - if (task->label().kind != port.kind) - ERR_DIE("Invalid port: " << port.cname << " is " - << task->label().kind << ", not " - << port.kind); - return task; - }; - - // [connect] - // We need to treat "... = @..." and "@... = ..." differently from - // "..." = ...". - // First, check if ports that are connected to or from "@..." exist. - for (auto&& [key, port] : bp_.atPorts()) { - get(port); // Only checks if port exists - } - // Then, connect other ports. `get` checks if they also exist. - for (auto&& [src, dst] : bp_.edges()) { - assert(src.kind == Label::OUTPUT); - assert(dst.kind == Label::INPUT); - nb.connect(get(src)->label().uid, get(dst)->label().uid); - } - - // Set priority to each DepNode - // FIXME +void Frontend::setBit0(DataHolder& dh) +{ + static const Bit bit0 = 0_b; + dh.setBit(&bit0); +} - network_.emplace(nb.createNetwork()); - // FIXME check if network is valid +void Frontend::setBit1(DataHolder& dh) +{ + static const Bit bit1 = 1_b; + dh.setBit(&bit1); } -void Frontend::makeResPacket(PlainPacket& out, const TaskFinder& finder, - int numCycles) +void Frontend::dumpResPacket(const std::string& outpath, + const TaskFinder& finder, int numCycles) { DataHolder dh; + PlainPacket out; + const Blueprint& bp = blueprint(); // Set the current number of cycles out.numCycles = numCycles; // Get values of output @port out.bits.clear(); - for (auto&& [key, port] : bp_.atPorts()) { + for (auto&& [key, port] : bp.atPorts()) { // Find "[connect] @atPortName[atPortBit] = ..." if (port.kind != Label::OUTPUT) continue; @@ -504,7 +446,7 @@ void Frontend::makeResPacket(PlainPacket& out, const TaskFinder& finder, } // Get values of RAM - for (auto&& ram : bp_.builtinRAMs()) { + for (auto&& ram : bp.builtinRAMs()) { std::vector& dst = out.ram[ram.name]; dst.clear(); for (size_t i = 0; i < (1 << ram.inAddrWidth) * ram.outRdataWidth; @@ -515,63 +457,18 @@ void Frontend::makeResPacket(PlainPacket& out, const TaskFinder& finder, dst.push_back(dh.getBit()); } } + + // Dump the result packet + writePlainPacket(outpath, out); } -void Frontend::run() +std::vector> Frontend::makeWorkers() { - const Bit bit0 = 0_b, bit1 = 1_b; - - // Create workers + const RunParameter& pr = runParam(); std::vector> workers; - for (size_t i = 0; i < pr_.numCPUWorkers; i++) + for (size_t i = 0; i < pr.numCPUWorkers; i++) workers.emplace_back(std::make_unique()); - - // Create runner and finder for the network - NetworkRunner runner{std::move(network_.value()), std::move(workers)}; - network_ = std::nullopt; - const TaskFinder& finder = runner.network().finder(); - - // Process reset cycle if @reset is used - // FIXME: Add support for --skip-reset flag - if (auto reset = bp_.at("reset"); reset && reset->kind == Label::INPUT) { - Task* t = finder.findByConfigName(reset->cname); - t->setInput(&bit1); // Set reset on - runner.run(); - t->setInput(&bit0); // Set reset off - } - - // Process normal cycles - for (size_t i = 0; i < pr_.numCycles; i++) { - // Mount new values to DFFs - runner.tick(); - - // Set new input data. If i is equal to 0, it also mounts initial data - // to RAMs. - runner.onAfterTick(i); - - // Go computing of each gate - runner.run(); - - /* - // Debug printing of all the gates - runner.network().eachTask([&](Task* t) { - TaskCommon* p = dynamic_cast*>(t); - if (p == nullptr) - return; - const Label& l = t->label(); - if (t->label().cname) - LOG_DBG << l.kind << "\t" << *l.cname << "\t" - << p->DEBUG_output(); - else - LOG_DBG << l.kind << "\t" << p->DEBUG_output(); - }); - */ - } - - // Dump result packet - PlainPacket resPacket; - makeResPacket(resPacket, finder, pr_.numCycles); - writePlainPacket(pr_.outputFile, resPacket); + return workers; } /**************************************************/ From 264167d9a893d85684cc5e69c4499009a03d4c6d Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Thu, 4 Nov 2021 00:45:34 +0900 Subject: [PATCH 37/54] wip --- src/iyokan_nt_plain.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 587d8a9..a4825be 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -5,14 +5,6 @@ #include -template -std::string fok2(T1 t1, T2 t2) -{ - std::stringstream ss; - ss << t1 << t2; - return ss.str(); -} - namespace nt { namespace plain { From b9b48efd2056aaf5f8b4f069e5bf236e0e405d8c Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 7 Nov 2021 00:55:36 +0900 Subject: [PATCH 38/54] wip --- src/CMakeLists.txt | 2 +- src/blueprint.cpp | 3 +- src/blueprint.hpp | 82 +++++++++++++++++++++++++++++ src/iyokan_nt.cpp | 43 ++++++---------- src/iyokan_nt.hpp | 111 ++++------------------------------------ src/iyokan_nt_plain.cpp | 1 + src/label.cpp | 24 +++++++++ src/label.hpp | 41 +++++++++++++++ src/network_reader.cpp | 1 + 9 files changed, 176 insertions(+), 132 deletions(-) create mode 100644 src/blueprint.hpp create mode 100644 src/label.cpp create mode 100644 src/label.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f60d9e3..e266778 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,7 @@ endif(IYOKAN_ENABLE_CUDA) add_executable(test0 test0.cpp iyokan.cpp iyokan_plain.cpp iyokan_tfhepp.cpp error.cpp iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp - network_reader.cpp blueprint.cpp + network_reader.cpp blueprint.cpp label.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/blueprint.cpp b/src/blueprint.cpp index 965a844..ffaa9c7 100644 --- a/src/blueprint.cpp +++ b/src/blueprint.cpp @@ -1,4 +1,5 @@ -#include "iyokan_nt.hpp" +#include "blueprint.hpp" +#include "error_nt.hpp" #include #include diff --git a/src/blueprint.hpp b/src/blueprint.hpp new file mode 100644 index 0000000..b9b8559 --- /dev/null +++ b/src/blueprint.hpp @@ -0,0 +1,82 @@ +#ifndef VIRTUALSECUREPLATFORM_BLUEPRINT_HPP +#define VIRTUALSECUREPLATFORM_BLUEPRINT_HPP + +#include "label.hpp" + +#include +#include +#include +#include + +namespace nt { + +namespace blueprint { // blueprint components +struct File { + enum class TYPE { + IYOKANL1_JSON, + YOSYS_JSON, + } type; + std::string path, name; +}; + +struct BuiltinROM { + enum class TYPE { + CMUX_MEMORY, + MUX, + } type; + std::string name; + size_t inAddrWidth, outRdataWidth; +}; + +struct BuiltinRAM { + enum class TYPE { + CMUX_MEMORY, + MUX, + } type; + std::string name; + size_t inAddrWidth, inWdataWidth, outRdataWidth; +}; + +struct Port { + const char* kind; // Store a string literal + ConfigName cname; +}; +} // namespace blueprint + +class Blueprint { +private: + std::string sourceFile_, source_; + + std::vector files_; + std::vector builtinROMs_; + std::vector builtinRAMs_; + std::vector> edges_; + + std::map, blueprint::Port> atPorts_; + std::unordered_map atPortWidths_; + +private: + std::vector parsePortString(const std::string& src, + const char* const kind); + +public: + Blueprint(const std::string& fileName); + + bool needsCircuitKey() const; + const std::string& sourceFile() const; + const std::string& source() const; + const std::vector& files() const; + const std::vector& builtinROMs() const; + const std::vector& builtinRAMs() const; + const std::vector>& edges() + const; + const std::map, blueprint::Port>& atPorts() + const; + std::optional at(const std::string& portName, + int portBit = 0) const; + const std::unordered_map& atPortWidths() const; +}; + +} // namespace nt + +#endif diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 2a78a2e..dc25679 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -1,4 +1,5 @@ #include "iyokan_nt.hpp" +#include "blueprint.hpp" #include "dataholder_nt.hpp" #include "error_nt.hpp" @@ -8,27 +9,6 @@ namespace nt { -/* struct ConfigName */ -std::ostream& operator<<(std::ostream& os, const ConfigName& c) -{ - os << c.nodeName << "/" << c.portName << "[" << c.portBit << "]"; - return os; -} - -bool operator<(const ConfigName& lhs, const ConfigName& rhs) -{ - if (int res = lhs.nodeName.compare(rhs.nodeName); res != 0) - return res < 0; - if (int res = lhs.portName.compare(rhs.portName); res != 0) - return res < 0; - return lhs.portBit < rhs.portBit; -} - -/* struct Label */ -// Initialization of static variables. -const char* const Label::INPUT = "Input"; -const char* const Label::OUTPUT = "Output"; - /* class Allocator */ Allocator::Allocator() @@ -355,24 +335,29 @@ void RunParameter::print() const /* class Frontend */ Frontend::Frontend(const RunParameter& pr) - : pr_(pr), network_(std::nullopt), currentCycle_(0), bp_(pr_.blueprintFile) + : pr_(pr), + network_(std::nullopt), + currentCycle_(0), + bp_(std::make_unique(pr_.blueprintFile)) { } void Frontend::buildNetwork(NetworkBuilder& nb) { + const Blueprint& bp = blueprint(); + // [[file]] - for (auto&& file : bp_.files()) + for (auto&& file : bp.files()) readNetworkFromFile(file, nb); // [[builtin]] type = ram | type = mux-ram - for (auto&& ram : bp_.builtinRAMs()) { + for (auto&& ram : bp.builtinRAMs()) { // We ignore ram.type and always use mux-ram in plaintext mode. makeMUXRAM(ram, nb); } // [[builtin]] type = rom | type = mux-rom - for (auto&& rom : bp_.builtinROMs()) { + for (auto&& rom : bp.builtinROMs()) { // We ignore rom.type and always use mux-rom in plaintext mode. makeMUXROM(rom, nb); } @@ -390,11 +375,11 @@ void Frontend::buildNetwork(NetworkBuilder& nb) // We need to treat "... = @..." and "@... = ..." differently from // "..." = ...". // First, check if ports that are connected to or from "@..." exist. - for (auto&& [key, port] : bp_.atPorts()) { + for (auto&& [key, port] : bp.atPorts()) { get(port); // Only checks if port exists } // Then, connect other ports. `get` checks if they also exist. - for (auto&& [src, dst] : bp_.edges()) { + for (auto&& [src, dst] : bp.edges()) { assert(src.kind == Label::OUTPUT); assert(dst.kind == Label::INPUT); nb.connect(get(src)->label().uid, get(dst)->label().uid); @@ -413,6 +398,8 @@ Frontend::~Frontend() void Frontend::run() { + const Blueprint& bp = blueprint(); + DataHolder bit0, bit1; setBit0(bit0); setBit1(bit1); @@ -427,7 +414,7 @@ void Frontend::run() // Process reset cycle if @reset is used // FIXME: Add support for --skip-reset flag - if (auto reset = bp_.at("reset"); reset && reset->kind == Label::INPUT) { + if (auto reset = bp.at("reset"); reset && reset->kind == Label::INPUT) { Task* t = finder.findByConfigName(reset->cname); t->setInput(bit1); // Set reset on runner.run(); diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index f38b71b..cfda933 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -13,6 +13,7 @@ #include #include "error_nt.hpp" +#include "label.hpp" namespace nt { @@ -21,6 +22,12 @@ namespace plain { class WorkerInfo; } class DataHolder; +class Blueprint; +namespace blueprint { +class File; +class BuiltinROM; +class BuiltinRAM; +} // namespace blueprint class Allocator { public: @@ -50,39 +57,6 @@ class Allocator { } }; -using UID = uint64_t; -struct ConfigName { - std::string nodeName, portName; - int portBit; - - // Although all member variables of ConfigName are public, we make - // operator<< and operator< its friend function for clarity. - friend std::ostream& operator<<(std::ostream& os, const ConfigName& c); - friend bool operator<(const ConfigName& lhs, const ConfigName& rhs); -}; - -struct Label { - // String literals for member variable `kind`. - // If label is for inputs or outputs, these member variable must be used, - // that is, kind == Label::INPUT or kind == Label::OUTPUT. - // The instances of these variables exist in iyokan_nt.cpp. - // We CANNOT use (C++17) `inline` here, because `inline` does NOT guarantee - // that these variables have the same value in different compilation units - // (FIXME: This behaviour is confirmed only on g++-10. We need to check the - // C++ standard). - static const char* const INPUT; - static const char* const OUTPUT; - - UID uid; - const char* const kind; // Stores a string literal - std::optional cname; - - Label(UID uid, const char* const kind, std::optional cname) - : uid(uid), kind(kind), cname(std::move(cname)) - { - } -}; - class Task { private: Label label_; @@ -405,73 +379,6 @@ class NetworkRunner { void onAfterTick(size_t currentCycle); }; -namespace blueprint { // blueprint components -struct File { - enum class TYPE { - IYOKANL1_JSON, - YOSYS_JSON, - } type; - std::string path, name; -}; - -struct BuiltinROM { - enum class TYPE { - CMUX_MEMORY, - MUX, - } type; - std::string name; - size_t inAddrWidth, outRdataWidth; -}; - -struct BuiltinRAM { - enum class TYPE { - CMUX_MEMORY, - MUX, - } type; - std::string name; - size_t inAddrWidth, inWdataWidth, outRdataWidth; -}; - -struct Port { - const char* kind; // Store a string literal - ConfigName cname; -}; -} // namespace blueprint - -class Blueprint { -private: - std::string sourceFile_, source_; - - std::vector files_; - std::vector builtinROMs_; - std::vector builtinRAMs_; - std::vector> edges_; - - std::map, blueprint::Port> atPorts_; - std::unordered_map atPortWidths_; - -private: - std::vector parsePortString(const std::string& src, - const char* const kind); - -public: - Blueprint(const std::string& fileName); - - bool needsCircuitKey() const; - const std::string& sourceFile() const; - const std::string& source() const; - const std::vector& files() const; - const std::vector& builtinROMs() const; - const std::vector& builtinRAMs() const; - const std::vector>& edges() - const; - const std::map, blueprint::Port>& atPorts() - const; - std::optional at(const std::string& portName, - int portBit = 0) const; - const std::unordered_map& atPortWidths() const; -}; - enum class SCHED { TOPO, RANKU, @@ -490,7 +397,7 @@ class Frontend { RunParameter pr_; std::optional network_; int currentCycle_; - Blueprint bp_; + std::unique_ptr bp_; protected: virtual void setBit0(DataHolder& dh) = 0; @@ -506,7 +413,7 @@ class Frontend { } const Blueprint& blueprint() const { - return bp_; + return *bp_; } public: diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index a4825be..93c2912 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -1,4 +1,5 @@ #include "iyokan_nt_plain.hpp" +#include "blueprint.hpp" #include "dataholder_nt.hpp" #include "iyokan_nt.hpp" #include "packet_nt.hpp" diff --git a/src/label.cpp b/src/label.cpp new file mode 100644 index 0000000..0856af4 --- /dev/null +++ b/src/label.cpp @@ -0,0 +1,24 @@ +#include "label.hpp" + +#include + +/* struct ConfigName */ +std::ostream& operator<<(std::ostream& os, const ConfigName& c) +{ + os << c.nodeName << "/" << c.portName << "[" << c.portBit << "]"; + return os; +} + +bool operator<(const ConfigName& lhs, const ConfigName& rhs) +{ + if (int res = lhs.nodeName.compare(rhs.nodeName); res != 0) + return res < 0; + if (int res = lhs.portName.compare(rhs.portName); res != 0) + return res < 0; + return lhs.portBit < rhs.portBit; +} + +/* struct Label */ +// Initialization of static variables. +const char* const Label::INPUT = "Input"; +const char* const Label::OUTPUT = "Output"; diff --git a/src/label.hpp b/src/label.hpp new file mode 100644 index 0000000..045f7de --- /dev/null +++ b/src/label.hpp @@ -0,0 +1,41 @@ +#ifndef VIRTUALSECUREPLATFORM_CONFIG_NAME_HPP +#define VIRTUALSECUREPLATFORM_CONFIG_NAME_HPP + +#include +#include + +struct ConfigName { + std::string nodeName, portName; + int portBit; + + // Although all member variables of ConfigName are public, we make + // operator<< and operator< its friend function for clarity. + friend std::ostream& operator<<(std::ostream& os, const ConfigName& c); + friend bool operator<(const ConfigName& lhs, const ConfigName& rhs); +}; + +using UID = uint64_t; + +struct Label { + // String literals for member variable `kind`. + // If label is for inputs or outputs, these member variable must be used, + // that is, kind == Label::INPUT or kind == Label::OUTPUT. + // The instances of these variables exist in iyokan_nt.cpp. + // We CANNOT use (C++17) `inline` here, because `inline` does NOT guarantee + // that these variables have the same value in different compilation units + // (FIXME: This behaviour is confirmed only on g++-10. We need to check the + // C++ standard). + static const char* const INPUT; + static const char* const OUTPUT; + + UID uid; + const char* const kind; // Stores a string literal + std::optional cname; + + Label(UID uid, const char* const kind, std::optional cname) + : uid(uid), kind(kind), cname(std::move(cname)) + { + } +}; + +#endif diff --git a/src/network_reader.cpp b/src/network_reader.cpp index 9cef02f..d885439 100644 --- a/src/network_reader.cpp +++ b/src/network_reader.cpp @@ -1,3 +1,4 @@ +#include "blueprint.hpp" #include "iyokan_nt.hpp" #include From a24c313d643da9b3b814d0d67ef83718bf0d583d Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 7 Nov 2021 12:09:05 +0900 Subject: [PATCH 39/54] wip --- src/CMakeLists.txt | 12 ++++++------ src/iyokan_nt.hpp | 11 +++++++---- src/test0.cpp | 2 ++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e266778..ee028ed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,7 +31,7 @@ endif(IYOKAN_ENABLE_CUDA) ##### test0 add_executable(test0 - test0.cpp iyokan.cpp iyokan_plain.cpp iyokan_tfhepp.cpp error.cpp + test0.cpp iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp network_reader.cpp blueprint.cpp label.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o @@ -43,11 +43,11 @@ target_compile_options(test0 PUBLIC "$<$:${IYOKAN_CXXFLAGS_RELEA target_link_libraries(test0 ${IYOKAN_LIBS}) target_include_directories(test0 PRIVATE ${IYOKAN_INCLUDE_DIRS}) target_compile_definitions(test0 PRIVATE ${IYOKAN_COMPILE_DEFINITIONS}) -if (IYOKAN_ENABLE_CUDA) - target_sources(test0 PRIVATE iyokan_cufhe.cpp) - target_link_libraries(test0 cufhe_gpu ${CUDA_LIBRARIES}) - target_compile_definitions(test0 PRIVATE IYOKAN_CUDA_ENABLED) -endif(IYOKAN_ENABLE_CUDA) +#if (IYOKAN_ENABLE_CUDA) +# target_sources(test0 PRIVATE iyokan_cufhe.cpp) +# target_link_libraries(test0 cufhe_gpu ${CUDA_LIBRARIES}) +# target_compile_definitions(test0 PRIVATE IYOKAN_CUDA_ENABLED) +#endif(IYOKAN_ENABLE_CUDA) ##### iyokan-packet add_executable(iyokan-packet iyokan-packet.cpp error.cpp) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index cfda933..4edb089 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -34,7 +35,10 @@ class Allocator { using Index = size_t; private: - std::vector> data_; + // We use std::deque here since push_back/emplace_back of std::deque do + // not invalidate the address of its element, while ones of std::vector + // do. + std::deque data_; public: Allocator(/* optional snapshot file */); @@ -42,8 +46,7 @@ class Allocator { template T* make() { - data_.emplace_back(std::make_unique()); - std::any& v = *data_.back(); + std::any& v = data_.emplace_back(); return &v.emplace(); } @@ -51,7 +54,7 @@ class Allocator { T* get(Index index) { assert(index < data_.size()); - T* ret = std::any_cast(data_.at(index).get()); + T* ret = std::any_cast(&data_.at(index)); assert(ret != nullptr); return ret; } diff --git a/src/test0.cpp b/src/test0.cpp index 263dc34..5357c06 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -1006,4 +1006,6 @@ int main() nt::testAllocator(); nt::test0(); nt::plain::test0(); + + return 0; } From d3dcd03cb96a6f15f478f21b1683986dcdb53466 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sun, 7 Nov 2021 14:18:36 +0900 Subject: [PATCH 40/54] wip --- src/CMakeLists.txt | 2 +- src/dataholder_nt.cpp | 5 +++-- src/iyokan_nt.cpp | 6 ------ src/iyokan_nt.hpp | 35 ++++++++++++++++++++++++++++------- src/test0.cpp | 26 ++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee028ed..81c4bd6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,7 @@ endif(IYOKAN_ENABLE_CUDA) add_executable(test0 test0.cpp iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp - network_reader.cpp blueprint.cpp label.cpp + network_reader.cpp blueprint.cpp label.cpp allocator.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/dataholder_nt.cpp b/src/dataholder_nt.cpp index 73b726f..bddd2e6 100644 --- a/src/dataholder_nt.cpp +++ b/src/dataholder_nt.cpp @@ -2,11 +2,12 @@ namespace nt { +/* class DataHolder */ DataHolder::DataHolder() : dataBit_(nullptr), type_(TYPE::UND) { } -DataHolder::DataHolder(const Bit *const dataBit) +DataHolder::DataHolder(const Bit* const dataBit) : dataBit_(dataBit), type_(TYPE::BIT) { } @@ -17,7 +18,7 @@ Bit DataHolder::getBit() const return *dataBit_; } -void DataHolder::setBit(const Bit *const dataBit) +void DataHolder::setBit(const Bit* const dataBit) { dataBit_ = dataBit; type_ = TYPE::BIT; diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index dc25679..42afe7e 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -9,12 +9,6 @@ namespace nt { -/* class Allocator */ - -Allocator::Allocator() -{ -} - /* class Task */ Task::Task(Label label) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 4edb089..3dc251e 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -35,29 +35,50 @@ class Allocator { using Index = size_t; private: + // Allocator can be constructed in 2 ways: + // - With no arguments. In this case, Allocator has empty data at first, and + // make() really makes a new object. The member variable + // hasLoadedFromIStream_ is false, and indexToBeMade_ is not used. + // - With snapshot file path. In this case, Allocator reads data from the + // snapshot file, and make() returns data_[indexToBeMade_++]. The member + // variable hasLoadedFromIStream_ is true, and indexToBeMade_ indicates + // the next index of the data returned by make(). + bool hasLoadedFromIStream_; + size_t indexToBeMade_; + + // data_ has all the allocated objects in std::any. // We use std::deque here since push_back/emplace_back of std::deque do // not invalidate the address of its element, while ones of std::vector // do. std::deque data_; public: - Allocator(/* optional snapshot file */); + Allocator(); + Allocator(std::istream& is); - template - T* make() - { - std::any& v = data_.emplace_back(); - return &v.emplace(); - } + void dumpSnapshot(std::ostream& os); template T* get(Index index) { assert(index < data_.size()); + assert(!hasLoadedFromIStream_ || index < indexToBeMade_); T* ret = std::any_cast(&data_.at(index)); assert(ret != nullptr); return ret; } + + template + T* make() + { + if (hasLoadedFromIStream_) { + return std::any_cast(&data_.at(indexToBeMade_++)); + } + else { + std::any& v = data_.emplace_back(); + return &v.emplace(); + } + } }; class Task { diff --git a/src/test0.cpp b/src/test0.cpp index 5357c06..14d7aad 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -858,8 +858,11 @@ #include "iyokan_nt.hpp" #include "iyokan_nt_plain.hpp" +#include "packet_nt.hpp" #include "tfhepp_cufhe_wrapper.hpp" +#include + class TFHEppTestHelper { private: std::shared_ptr sk_; @@ -910,6 +913,7 @@ class TFHEppTestHelper { }; namespace nt { + void testAllocator() { { @@ -944,6 +948,28 @@ void testAllocator() } } +void testSnapshot() +{ + { + Allocator alc; + Bit* b0 = alc.make(); + *b0 = 0_b; + Bit* b1 = alc.make(); + *b1 = 1_b; + + std::ofstream ofs{"_test_snapshot"}; + assert(ofs); + alc.dumpSnapshot(ofs); + } + { + std::ifstream ifs{"_test_snapshot"}; + assert(ifs); + Allocator alc{ifs}; + assert(*alc.make() == 0_b); + assert(*alc.make() == 1_b); + } +} + } // namespace nt int main() From 9481594e5aff4b4c4b403021e9b95d3f47a0cbc5 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Tue, 9 Nov 2021 15:28:51 +0900 Subject: [PATCH 41/54] wip (fix Snapshot::Snapshot) --- src/allocator.cpp | 111 +++++++++++++++++++++++++++++++++++++ src/iyokan_nt.cpp | 50 ++++++++++++++++- src/iyokan_nt.hpp | 35 +++++++++++- src/iyokan_nt_plain.cpp | 119 ++++++++++++++++++++++++++++++++++++---- src/test0.cpp | 2 +- 5 files changed, 300 insertions(+), 17 deletions(-) create mode 100644 src/allocator.cpp diff --git a/src/allocator.cpp b/src/allocator.cpp new file mode 100644 index 0000000..31c5b56 --- /dev/null +++ b/src/allocator.cpp @@ -0,0 +1,111 @@ +#include "dataholder_nt.hpp" +#include "iyokan_nt.hpp" +#include "packet_nt.hpp" + +#include +#include +#include + +#include +#include +#include + +namespace { + +using namespace nt; + +template +void save(Archive& ar, const Bit& b, const std::uint32_t version) +{ + assert(version == 1); + + ar(static_cast(b)); +} + +template +void load(Archive& ar, Bit& b, const std::uint32_t version) +{ + assert(version == 1); + + bool bl; + ar(bl); + b = static_cast(b); +} + +struct Serializable { + std::variant data; + + template + void serialize(Archive& ar, const std::uint32_t version) + { + assert(version == 1); + + ar(data); + } +}; + +} // namespace + +CEREAL_CLASS_VERSION(Serializable, 1); + +namespace nt { + +/* class Allocator */ + +Allocator::Allocator() + : hasLoadedFromIStream_(false), indexToBeMade_(0), data_() +{ +} + +Allocator::Allocator(std::istream& is) + : hasLoadedFromIStream_(true), indexToBeMade_(0), data_() +{ + // Read and de-serialize data from the snapshot file + cereal::PortableBinaryInputArchive ar{is}; + size_t size; + Serializable buf; + + ar(size); + for (size_t i = 0; i < size; i++) { + ar(buf); + switch (buf.data.index()) { + case 0: { // Bit + Bit b = std::get<0>(buf.data); + data_.emplace_back(b); + break; + } + + default: + ERR_UNREACHABLE; + } + } +} + +void Allocator::dumpAllocatedData(std::ostream& os) const +{ + // FIXME: WE KNOW the code in this function is TREMENDOUSLY UGLY (and + // inefficient). We need to find some more sophisticated ways to do this. + + cereal::PortableBinaryOutputArchive ar{os}; + Serializable buf; + + // Serialization process for each type + std::unordered_map> + tyHandlers; + tyHandlers[typeid(Bit)] = [&](const std::any& any) { + const Bit* src = std::any_cast(&any); + buf.data = *src; + ar(buf); + }; + + // First serialize the size of the entries + ar(static_cast(data_.size())); + + // Dispatch + for (size_t i = 0; i < data_.size(); i++) { + const std::any& src = data_.at(i); + tyHandlers.at(src.type())(src); + } +} + +} // namespace nt diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 42afe7e..231f251 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -321,19 +321,65 @@ void RunParameter::print() const LOG_S(INFO) << "\tBlueprint: " << blueprintFile; LOG_S(INFO) << "\t# of CPU Workers: " << numCPUWorkers; LOG_S(INFO) << "\t# of cycles: " << numCycles; + LOG_S(INFO) << "\tCurrent cycle #: " << currentCycle; LOG_S(INFO) << "\tInput file (request packet): " << inputFile; LOG_S(INFO) << "\tOutput file (result packet): " << outputFile; LOG_S(INFO) << "\tSchedule: " << (sched == SCHED::TOPO ? "topo" : "ranku"); } +/* class Snapshot */ + +Snapshot::Snapshot(const RunParameter& pr, + const std::shared_ptr& alc) + : pr_(pr), alc_(alc) +{ +} + +Snapshot::Snapshot(const std::string& snapshotFile) +{ + // FIXME +} + +const RunParameter& Snapshot::getRunParam() const +{ + return pr_; +} + +const std::shared_ptr& Snapshot::getAllocator() const +{ + return alc_; +} + +void Snapshot::dump(std::ostream& os) const +{ + // FIXME +} + +void Snapshot::updateNumCycles(int numCycles) +{ + pr_.numCycles = numCycles; +} + /* class Frontend */ Frontend::Frontend(const RunParameter& pr) : pr_(pr), network_(std::nullopt), - currentCycle_(0), - bp_(std::make_unique(pr_.blueprintFile)) + currentCycle_(pr.currentCycle), + bp_(std::make_unique(pr_.blueprintFile)), + alc_(std::make_shared()) +{ + assert(alc_); +} + +Frontend::Frontend(const Snapshot& ss) + : pr_(ss.getRunParam()), + network_(std::nullopt), + currentCycle_(pr_.currentCycle), + bp_(std::make_unique(pr_.blueprintFile)), + alc_(ss.getAllocator()) { + assert(alc_); } void Frontend::buildNetwork(NetworkBuilder& nb) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 3dc251e..8346213 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -31,6 +31,11 @@ class BuiltinRAM; } // namespace blueprint class Allocator { +private: + // Prohibit copying Allcator. Probably moving it is not a problem. + Allocator(const Allocator&) = delete; + Allocator& operator=(const Allocator&) = delete; + public: using Index = size_t; @@ -56,7 +61,7 @@ class Allocator { Allocator(); Allocator(std::istream& is); - void dumpSnapshot(std::ostream& os); + void dumpAllocatedData(std::ostream& os) const; template T* get(Index index) @@ -410,18 +415,39 @@ enum class SCHED { struct RunParameter { std::string blueprintFile, inputFile, outputFile; - int numCPUWorkers, numCycles; + int numCPUWorkers, numCycles, currentCycle; SCHED sched; void print() const; }; +class Snapshot { +private: + RunParameter pr_; + std::shared_ptr alc_; + +public: + Snapshot(const RunParameter& pr, const std::shared_ptr& alc); + Snapshot(const std::string& snapshotFile); + + const RunParameter& getRunParam() const; + const std::shared_ptr& getAllocator() const; + void dump(std::ostream& os) const; + void updateNumCycles(int numCycles); +}; + class Frontend { +private: + // Prohibit copying Frontend. + Frontend(const Frontend&) = delete; + Frontend& operator=(const Frontend&) = delete; + private: RunParameter pr_; std::optional network_; int currentCycle_; std::unique_ptr bp_; + std::shared_ptr alc_; protected: virtual void setBit0(DataHolder& dh) = 0; @@ -439,9 +465,14 @@ class Frontend { { return *bp_; } + Allocator& allocator() + { + return *alc_; + } public: Frontend(const RunParameter& pr); + Frontend(const Snapshot& snapshot); virtual ~Frontend(); void run(); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 93c2912..0955e42 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -359,11 +359,11 @@ class Frontend : public nt::Frontend { std::vector> makeWorkers() override; public: - Frontend(const RunParameter& pr, Allocator& alc); + Frontend(const RunParameter& pr); }; -Frontend::Frontend(const RunParameter& pr, Allocator& alc) - : nt::Frontend(pr), reqPacket_(readPlainPacket(pr.inputFile)) +Frontend::Frontend(const RunParameter& pr) + : nt::Frontend(pr), reqPacket_(readPlainPacket(runParam().inputFile)) { const Blueprint& bp = blueprint(); @@ -393,7 +393,7 @@ Frontend::Frontend(const RunParameter& pr, Allocator& alc) } // Build the network. The instance is in nt::Frontend - NetworkBuilder nb{cname2isource, reqPacket_, alc}; + NetworkBuilder nb{cname2isource, reqPacket_, allocator()}; buildNetwork(nb); } @@ -875,14 +875,14 @@ void test0() Allocator alc; Frontend frontend{RunParameter{ - blueprintPath, // blueprintFile - reqPktPath, // inputFile - resPktPath, // outputFile - 2, // numCPUWorkers - numCycles, // numCycles - SCHED::RANKU, // sched - }, - alc}; + blueprintPath, // blueprintFile + reqPktPath, // inputFile + resPktPath, // outputFile + 2, // numCPUWorkers + numCycles, // numCycles + 0, // currentCycle + SCHED::RANKU, // sched + }}; frontend.run(); PlainPacket got = readPlainPacket(resPktPath); assert(got == expectedOutPkt); @@ -910,6 +910,101 @@ void test0() "test/out/test13.out", 3); go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", "test/out/test09-ruby.out", 7); + + { + Allocator alc; + NetworkBuilder nb{c2is, pkt, alc}; + + readNetworkFromFile( + blueprint::File{blueprint::File::TYPE::YOSYS_JSON, + "test/yosys-json/counter-4bit-yosys.json", + "counter"}, + nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tRst = finder.findByConfigName({"counter", "reset", 0}), + *tOut0 = finder.findByConfigName({"counter", "io_out", 0}), + *tOut1 = finder.findByConfigName({"counter", "io_out", 1}), + *tOut2 = finder.findByConfigName({"counter", "io_out", 2}), + *tOut3 = finder.findByConfigName({"counter", "io_out", 3}); + + tRst->setInput(&bit1); + runner.run(); + tRst->setInput(&bit0); + + // Cycle #1 + runner.tick(); + runner.run(); + // Cycle #2 + runner.tick(); + runner.run(); + // Cycle #3 + runner.tick(); + runner.run(); + + // The output is 2, that is, '0b0010' + tOut0->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut1->getOutput(dh); + assert(dh.getBit() == 1_b); + tOut2->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut3->getOutput(dh); + assert(dh.getBit() == 0_b); + + // Dump the snapshot + std::ofstream ofs{"_test_snapshot"}; + assert(ofs); + alc.dumpAllocatedData(ofs); + } + { + // Load from the snapshot + std::ifstream ifs{"_test_snapshot"}; + assert(ifs); + Allocator alc{ifs}; + + NetworkBuilder nb{c2is, pkt, alc}; + + readNetworkFromFile( + blueprint::File{blueprint::File::TYPE::YOSYS_JSON, + "test/yosys-json/counter-4bit-yosys.json", + "counter"}, + nb); + + std::vector> workers; + workers.emplace_back(std::make_unique()); + + NetworkRunner runner{nb.createNetwork(), std::move(workers)}; + auto&& finder = runner.network().finder(); + Task *tOut0 = finder.findByConfigName({"counter", "io_out", 0}), + *tOut1 = finder.findByConfigName({"counter", "io_out", 1}), + *tOut2 = finder.findByConfigName({"counter", "io_out", 2}), + *tOut3 = finder.findByConfigName({"counter", "io_out", 3}); + + // Cycle #4 + runner.tick(); + runner.run(); + // Cycle #5 + runner.tick(); + runner.run(); + // Cycle #6 + runner.tick(); + runner.run(); + + // The output is 5, that is, '0b0101' + tOut0->getOutput(dh); + assert(dh.getBit() == 1_b); + tOut1->getOutput(dh); + assert(dh.getBit() == 0_b); + tOut2->getOutput(dh); + assert(dh.getBit() == 1_b); + tOut3->getOutput(dh); + assert(dh.getBit() == 0_b); + } } } // namespace plain diff --git a/src/test0.cpp b/src/test0.cpp index 14d7aad..a3e0a83 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -959,7 +959,7 @@ void testSnapshot() std::ofstream ofs{"_test_snapshot"}; assert(ofs); - alc.dumpSnapshot(ofs); + alc.dumpAllocatedData(ofs); } { std::ifstream ifs{"_test_snapshot"}; From bc6a53eb66547363a1f7897d047805b15dec3882 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 10 Nov 2021 00:07:54 +0900 Subject: [PATCH 42/54] wip --- src/CMakeLists.txt | 2 +- src/allocator.cpp | 6 +- src/error_nt.hpp | 2 + src/iyokan_nt.cpp | 67 ++++++++---------- src/iyokan_nt.hpp | 23 +++++-- src/iyokan_nt_plain.cpp | 149 +++++++++++++++------------------------- src/test0.cpp | 9 ++- 7 files changed, 113 insertions(+), 145 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81c4bd6..c3cc20a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,7 @@ endif(IYOKAN_ENABLE_CUDA) add_executable(test0 test0.cpp iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp - network_reader.cpp blueprint.cpp label.cpp allocator.cpp + network_reader.cpp blueprint.cpp label.cpp allocator.cpp snapshot.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-9-16-16.o) diff --git a/src/allocator.cpp b/src/allocator.cpp index 31c5b56..b9d53a3 100644 --- a/src/allocator.cpp +++ b/src/allocator.cpp @@ -57,11 +57,10 @@ Allocator::Allocator() { } -Allocator::Allocator(std::istream& is) +Allocator::Allocator(cereal::PortableBinaryInputArchive& ar) : hasLoadedFromIStream_(true), indexToBeMade_(0), data_() { // Read and de-serialize data from the snapshot file - cereal::PortableBinaryInputArchive ar{is}; size_t size; Serializable buf; @@ -81,12 +80,11 @@ Allocator::Allocator(std::istream& is) } } -void Allocator::dumpAllocatedData(std::ostream& os) const +void Allocator::dumpAllocatedData(cereal::PortableBinaryOutputArchive& ar) const { // FIXME: WE KNOW the code in this function is TREMENDOUSLY UGLY (and // inefficient). We need to find some more sophisticated ways to do this. - cereal::PortableBinaryOutputArchive ar{os}; Serializable buf; // Serialization process for each type diff --git a/src/error_nt.hpp b/src/error_nt.hpp index 108833c..1a5a847 100644 --- a/src/error_nt.hpp +++ b/src/error_nt.hpp @@ -19,4 +19,6 @@ void initialize(); #define ERR_UNREACHABLE ERR_DIE("Internal error: unreachable here") +#define LOG_DBG_SCOPE(...) LOG_SCOPE_F(1, __VA_ARGS__); + #endif diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 231f251..7255ae3 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -327,39 +327,6 @@ void RunParameter::print() const LOG_S(INFO) << "\tSchedule: " << (sched == SCHED::TOPO ? "topo" : "ranku"); } -/* class Snapshot */ - -Snapshot::Snapshot(const RunParameter& pr, - const std::shared_ptr& alc) - : pr_(pr), alc_(alc) -{ -} - -Snapshot::Snapshot(const std::string& snapshotFile) -{ - // FIXME -} - -const RunParameter& Snapshot::getRunParam() const -{ - return pr_; -} - -const std::shared_ptr& Snapshot::getAllocator() const -{ - return alc_; -} - -void Snapshot::dump(std::ostream& os) const -{ - // FIXME -} - -void Snapshot::updateNumCycles(int numCycles) -{ - pr_.numCycles = numCycles; -} - /* class Frontend */ Frontend::Frontend(const RunParameter& pr) @@ -445,32 +412,43 @@ void Frontend::run() setBit1(bit1); // Create workers + LOG_DBG << "CREATE WORKERS"; std::vector> workers = makeWorkers(); // Create runner and finder for the network + LOG_DBG << "CREATE RUNNER"; NetworkRunner runner{std::move(network_.value()), std::move(workers)}; network_ = std::nullopt; const TaskFinder& finder = runner.network().finder(); // Process reset cycle if @reset is used // FIXME: Add support for --skip-reset flag - if (auto reset = bp.at("reset"); reset && reset->kind == Label::INPUT) { - Task* t = finder.findByConfigName(reset->cname); - t->setInput(bit1); // Set reset on - runner.run(); - t->setInput(bit0); // Set reset off + if (currentCycle_ == 0) { + auto reset = bp.at("reset"); + if (reset && reset->kind == Label::INPUT) { + LOG_DBG << "RESET"; + Task* t = finder.findByConfigName(reset->cname); + t->setInput(bit1); // Set reset on + runner.run(); + t->setInput(bit0); // Set reset off + } } // Process normal cycles - for (size_t i = 0; i < pr_.numCycles; i++) { + for (int i = 0; i < pr_.numCycles; i++, currentCycle_++) { + LOG_DBG_SCOPE("Cycle #%d (i = %d)", currentCycle_, i); + // Mount new values to DFFs + LOG_DBG << "TICK"; runner.tick(); // Set new input data. If i is equal to 0, it also mounts initial data // to RAMs. + LOG_DBG << "ON AFTER TICK"; runner.onAfterTick(i); // Go computing of each gate + LOG_DBG << "RUN"; runner.run(); /* @@ -490,7 +468,16 @@ void Frontend::run() } // Dump result packet - dumpResPacket(pr_.outputFile, finder, pr_.numCycles); + LOG_DBG << "DUMP RES PACKET"; + dumpResPacket(pr_.outputFile, finder, currentCycle_); + + // Dump snapshot + if (pr_.snapshotFile) { + LOG_DBG << "DUMP SNAPSHOT"; + Snapshot ss{pr_, allocatorPtr()}; + ss.updateCurrentCycle(currentCycle_); + ss.dump(pr_.snapshotFile.value()); + } } /* makeMUXROM */ diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 8346213..4c73977 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -16,9 +16,8 @@ #include "error_nt.hpp" #include "label.hpp" -namespace nt { - // Forward declarations +namespace nt { namespace plain { class WorkerInfo; } @@ -29,6 +28,13 @@ class File; class BuiltinROM; class BuiltinRAM; } // namespace blueprint +} // namespace nt +namespace cereal { +class PortableBinaryOutputArchive; +class PortableBinaryInputArchive; +} // namespace cereal + +namespace nt { class Allocator { private: @@ -59,9 +65,9 @@ class Allocator { public: Allocator(); - Allocator(std::istream& is); + Allocator(cereal::PortableBinaryInputArchive& ar); - void dumpAllocatedData(std::ostream& os) const; + void dumpAllocatedData(cereal::PortableBinaryOutputArchive& ar) const; template T* get(Index index) @@ -418,6 +424,8 @@ struct RunParameter { int numCPUWorkers, numCycles, currentCycle; SCHED sched; + std::optional snapshotFile; + void print() const; }; @@ -432,8 +440,9 @@ class Snapshot { const RunParameter& getRunParam() const; const std::shared_ptr& getAllocator() const; - void dump(std::ostream& os) const; + void updateCurrentCycle(int currentCycle); void updateNumCycles(int numCycles); + void dump(const std::string& snapshotFile) const; }; class Frontend { @@ -469,6 +478,10 @@ class Frontend { { return *alc_; } + const std::shared_ptr& allocatorPtr() const + { + return alc_; + } public: Frontend(const RunParameter& pr); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 0955e42..90a8692 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -358,12 +358,27 @@ class Frontend : public nt::Frontend { int numCycles) override; std::vector> makeWorkers() override; + // Actual constructor. + void doConstruct(); + public: Frontend(const RunParameter& pr); + Frontend(const Snapshot& ss); }; Frontend::Frontend(const RunParameter& pr) : nt::Frontend(pr), reqPacket_(readPlainPacket(runParam().inputFile)) +{ + doConstruct(); +} + +Frontend::Frontend(const Snapshot& ss) + : nt::Frontend(ss), reqPacket_(readPlainPacket(runParam().inputFile)) +{ + doConstruct(); +} + +void Frontend::doConstruct() { const Blueprint& bp = blueprint(); @@ -873,7 +888,7 @@ void test0() expectedOutPkt = PlainPacket::fromTOML(expectedOutPktPath); writePlainPacket(reqPktPath, inPkt); - Allocator alc; + LOG_DBG_SCOPE("go"); Frontend frontend{RunParameter{ blueprintPath, // blueprintFile reqPktPath, // inputFile @@ -882,6 +897,7 @@ void test0() numCycles, // numCycles 0, // currentCycle SCHED::RANKU, // sched + std::nullopt, // snapshotFile }}; frontend.run(); PlainPacket got = readPlainPacket(resPktPath); @@ -911,100 +927,47 @@ void test0() go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", "test/out/test09-ruby.out", 7); - { - Allocator alc; - NetworkBuilder nb{c2is, pkt, alc}; - - readNetworkFromFile( - blueprint::File{blueprint::File::TYPE::YOSYS_JSON, - "test/yosys-json/counter-4bit-yosys.json", - "counter"}, - nb); - - std::vector> workers; - workers.emplace_back(std::make_unique()); - - NetworkRunner runner{nb.createNetwork(), std::move(workers)}; - auto&& finder = runner.network().finder(); - Task *tRst = finder.findByConfigName({"counter", "reset", 0}), - *tOut0 = finder.findByConfigName({"counter", "io_out", 0}), - *tOut1 = finder.findByConfigName({"counter", "io_out", 1}), - *tOut2 = finder.findByConfigName({"counter", "io_out", 2}), - *tOut3 = finder.findByConfigName({"counter", "io_out", 3}); - - tRst->setInput(&bit1); - runner.run(); - tRst->setInput(&bit0); - - // Cycle #1 - runner.tick(); - runner.run(); - // Cycle #2 - runner.tick(); - runner.run(); - // Cycle #3 - runner.tick(); - runner.run(); - - // The output is 2, that is, '0b0010' - tOut0->getOutput(dh); - assert(dh.getBit() == 0_b); - tOut1->getOutput(dh); - assert(dh.getBit() == 1_b); - tOut2->getOutput(dh); - assert(dh.getBit() == 0_b); - tOut3->getOutput(dh); - assert(dh.getBit() == 0_b); - - // Dump the snapshot - std::ofstream ofs{"_test_snapshot"}; - assert(ofs); - alc.dumpAllocatedData(ofs); - } - { - // Load from the snapshot - std::ifstream ifs{"_test_snapshot"}; - assert(ifs); - Allocator alc{ifs}; - - NetworkBuilder nb{c2is, pkt, alc}; - - readNetworkFromFile( - blueprint::File{blueprint::File::TYPE::YOSYS_JSON, - "test/yosys-json/counter-4bit-yosys.json", - "counter"}, - nb); - - std::vector> workers; - workers.emplace_back(std::make_unique()); - - NetworkRunner runner{nb.createNetwork(), std::move(workers)}; - auto&& finder = runner.network().finder(); - Task *tOut0 = finder.findByConfigName({"counter", "io_out", 0}), - *tOut1 = finder.findByConfigName({"counter", "io_out", 1}), - *tOut2 = finder.findByConfigName({"counter", "io_out", 2}), - *tOut3 = finder.findByConfigName({"counter", "io_out", 3}); + auto go_ss = [&](const std::string& blueprintPath, + const std::string& inPktPath, + const std::string& expectedOutPktPath, int numCycles) { + const char* const reqPktPath = "_test_in"; + const char* const resPktPath = "_test_out"; + const char* const snapshotPath = "_test_snapshot"; - // Cycle #4 - runner.tick(); - runner.run(); - // Cycle #5 - runner.tick(); - runner.run(); - // Cycle #6 - runner.tick(); - runner.run(); + auto inPkt = PlainPacket::fromTOML(inPktPath), + expectedOutPkt = PlainPacket::fromTOML(expectedOutPktPath); + writePlainPacket(reqPktPath, inPkt); - // The output is 5, that is, '0b0101' - tOut0->getOutput(dh); - assert(dh.getBit() == 1_b); - tOut1->getOutput(dh); - assert(dh.getBit() == 0_b); - tOut2->getOutput(dh); - assert(dh.getBit() == 1_b); - tOut3->getOutput(dh); - assert(dh.getBit() == 0_b); - } + int secondNumCycles = numCycles / 2, + firstNumCycles = numCycles - secondNumCycles; + + { + LOG_DBG_SCOPE("go_ss 1st"); + Frontend frontend{RunParameter{ + blueprintPath, // blueprintFile + reqPktPath, // inputFile + resPktPath, // outputFile + 2, // numCPUWorkers + firstNumCycles, // numCycles + 0, // currentCycle + SCHED::RANKU, // sched + snapshotPath, // snapshotFile + }}; + frontend.run(); + } + { + LOG_DBG_SCOPE("go_ss 2nd"); + Snapshot ss{snapshotPath}; + ss.updateNumCycles(secondNumCycles); + Frontend frontend{ss}; + frontend.run(); + + PlainPacket got = readPlainPacket(resPktPath); + assert(got == expectedOutPkt); + } + }; + go_ss("test/config-toml/counter-4bit.toml", "test/in/test13.in", + "test/out/test13.out", 3); } } // namespace plain diff --git a/src/test0.cpp b/src/test0.cpp index a3e0a83..899882e 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -861,6 +861,9 @@ #include "packet_nt.hpp" #include "tfhepp_cufhe_wrapper.hpp" +#include +#include + #include class TFHEppTestHelper { @@ -959,12 +962,14 @@ void testSnapshot() std::ofstream ofs{"_test_snapshot"}; assert(ofs); - alc.dumpAllocatedData(ofs); + cereal::PortableBinaryOutputArchive ar{ofs}; + alc.dumpAllocatedData(ar); } { std::ifstream ifs{"_test_snapshot"}; assert(ifs); - Allocator alc{ifs}; + cereal::PortableBinaryInputArchive ar{ifs}; + Allocator alc{ar}; assert(*alc.make() == 0_b); assert(*alc.make() == 1_b); } From 175b72193613f7642d47f46ee0df97620f67849c Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 10 Nov 2021 11:04:32 +0900 Subject: [PATCH 43/54] wip --- src/iyokan_nt.cpp | 2 +- src/iyokan_nt_plain.cpp | 10 ++++++ src/snapshot.cpp | 80 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/snapshot.cpp diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 7255ae3..6a61829 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -445,7 +445,7 @@ void Frontend::run() // Set new input data. If i is equal to 0, it also mounts initial data // to RAMs. LOG_DBG << "ON AFTER TICK"; - runner.onAfterTick(i); + runner.onAfterTick(currentCycle_); // Go computing of each gate LOG_DBG << "RUN"; diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 90a8692..25d3ceb 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -966,8 +966,18 @@ void test0() assert(got == expectedOutPkt); } }; + go_ss("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + "test/out/test16.out", 3); + go_ss("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + "test/out/test06.out", 16); + go_ss("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + "test/out/test07.out", 16); + go_ss("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + "test/out/test08.out", 8); go_ss("test/config-toml/counter-4bit.toml", "test/in/test13.in", "test/out/test13.out", 3); + go_ss("test/config-toml/cahp-ruby.toml", "test/in/test09.in", + "test/out/test09-ruby.out", 7); } } // namespace plain diff --git a/src/snapshot.cpp b/src/snapshot.cpp new file mode 100644 index 0000000..b07abb2 --- /dev/null +++ b/src/snapshot.cpp @@ -0,0 +1,80 @@ +#include "iyokan_nt.hpp" + +#include +#include +#include + +#include + +namespace nt { + +/* class Snapshot */ + +Snapshot::Snapshot(const RunParameter& pr, + const std::shared_ptr& alc) + : pr_(pr), alc_(alc) +{ +} + +Snapshot::Snapshot(const std::string& snapshotFile) : pr_(), alc_(nullptr) +{ + std::ifstream ifs{snapshotFile}; + if (!ifs) + ERR_DIE("Can't open a snapshot file to read from: " << snapshotFile); + cereal::PortableBinaryInputArchive ar{ifs}; + + // Read header + std::string header; + ar(header); + if (header != "IYSS") // IYokan SnapShot + ERR_DIE( + "Can't read the snapshot file; incorrect header: " << snapshotFile); + + // Read run parameters + ar(pr_.blueprintFile, pr_.inputFile, pr_.outputFile, pr_.numCPUWorkers, + pr_.numCycles, pr_.currentCycle, pr_.sched); + + // Read allocator + alc_.reset(new Allocator(ar)); +} + +const RunParameter& Snapshot::getRunParam() const +{ + return pr_; +} + +const std::shared_ptr& Snapshot::getAllocator() const +{ + return alc_; +} + +void Snapshot::dump(const std::string& snapshotFile) const +{ + std::ofstream ofs{snapshotFile}; + if (!ofs) + ERR_DIE("Can't open a snapshot file to write in: " << snapshotFile); + cereal::PortableBinaryOutputArchive ar{ofs}; + + // Write header + // FIXME: much better way to store the header? + ar(std::string{"IYSS"}); // IYokan SnapShot + + // Serialize pr_ of class RunParameter + ar(pr_.blueprintFile, pr_.inputFile, pr_.outputFile, pr_.numCPUWorkers, + pr_.numCycles, pr_.currentCycle, pr_.sched); + + // Serialize alc_ of class Allocator + alc_->dumpAllocatedData(ar); +} + +void Snapshot::updateCurrentCycle(int currentCycle) +{ + pr_.currentCycle = currentCycle; +} + +void Snapshot::updateNumCycles(int numCycles) +{ + pr_.numCycles = numCycles; +} + +} // namespace nt From 46911c015652f9a6054ef987f74f811517184080 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 10 Nov 2021 11:17:21 +0900 Subject: [PATCH 44/54] wip --- src/iyokan_nt.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 4c73977..6aad1ad 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -452,7 +452,7 @@ class Frontend { Frontend& operator=(const Frontend&) = delete; private: - RunParameter pr_; + const RunParameter pr_; std::optional network_; int currentCycle_; std::unique_ptr bp_; From b29b5f82c0e61ea6a2b9cf5bb893999a96bc086c Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Wed, 10 Nov 2021 22:03:39 +0900 Subject: [PATCH 45/54] wip --- src/iyokan_nt.cpp | 113 ++++++++++++++++++++++++++++++++++++++-- src/iyokan_nt.hpp | 8 +++ src/iyokan_nt_plain.cpp | 18 +++---- src/label.cpp | 4 +- 4 files changed, 127 insertions(+), 16 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 6a61829..cbe3304 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -7,6 +7,94 @@ #include +namespace { +void prioritizeTaskByRanku(const nt::TaskFinder& finder) +{ + // c.f. https://en.wikipedia.org/wiki/Heterogeneous_Earliest_Finish_Time + // FIXME: Take communication costs into account + // FIXME: Tune computation costs by dynamic measurements + + using namespace nt; + + std::unordered_map compCost = { + {"DFF", 0}, {"WIRE", 0}, {"INPUT", 0}, + {"OUTPUT", 0}, {"AND", 10}, {"NAND", 10}, + {"ANDNOT", 10}, {"OR", 10}, {"NOR", 10}, + {"ORNOT", 10}, {"XOR", 10}, {"XNOR", 10}, + {"MUX", 20}, {"NOT", 0}, {"CONSTONE", 0}, + {"CONSTZERO", 0}, {"CB", 100}, {"CBInv", 100}, + {"CBWithInv", 100}, {"MUXWoSE", 20}, {"CMUXs", 10}, + {"SEI", 0}, {"GB", 10}, {"ROMUX", 10}, + {"RAMUX", 10}, {"SEI&KS", 5}, {"cufhe2tfhepp", 0}, + {"tfhepp2cufhe", 0}, {"bridge", 0}, {"RAMWriter", 0}, + {"RAMReader", 0}, {"ROM", 0}, {"SDFF", 0}, + {"RAM", 0}, + }; + + std::unordered_map + numReadyChildren; // task |-> # of ready children + std::queue que; // Initial tasks to be visited + finder.eachTask([&](UID, Task* task) { + const std::vector& children = task->children(); + + // Count the children that have no inputs to wait for + size_t n = std::count_if( + children.begin(), children.end(), + [&](Task* child) { return child->areAllInputsReady(); }); + numReadyChildren.emplace(task, n); + + // Initial nodes should be "terminals", that is, + // they have no children OR all of their children has no inputs to wait + // for. + if (children.size() == n) + que.push(task); + }); + assert(!que.empty()); + + size_t numPrioritizedTasks = 0; + while (!que.empty()) { + Task* task = que.front(); + que.pop(); + + // Calculate and set the priority for the task + int pri = 0; + for (Task* child : task->children()) + // Only take valid children (i.e., ones that have no some inputs to + // wait for) into account + if (!child->areAllInputsReady()) + pri = std::max(pri, child->priority()); + auto it = compCost.find(task->label().kind); + if (it == compCost.end()) + ERR_DIE("Internal error: compCost does not have key: " + << task->label().kind); + task->setPriority(pri + it->second); + numPrioritizedTasks++; + + if (task->areAllInputsReady()) // The end of the travel + continue; + + // Push parents into the queue if all of their children are ready + for (Task* parent : task->parents()) { + numReadyChildren.at(parent)++; + assert(parent->children().size() >= numReadyChildren.at(parent)); + if (parent->children().size() == numReadyChildren.at(parent)) + que.push(parent); + } + } + if (finder.size() > numPrioritizedTasks) { + LOG_DBG << "finder.size() " << finder.size() + << " != numPrioritizedTasks " << numPrioritizedTasks; + finder.eachTask([&](UID, Task* task) { + const Label& l = task->label(); + if (task->priority() == -1) + LOG_DBG << "\t" << l.uid << " " << l.kind << " "; + }); + ERR_DIE("Invalid network; some nodes will not be executed."); + } + assert(finder.size() == numPrioritizedTasks); +} +} // namespace + namespace nt { /* class Task */ @@ -15,7 +103,7 @@ Task::Task(Label label) : label_(std::move(label)), parents_(), children_(), - priority_(0), + priority_(-1), hasQueued_(false) { } @@ -104,6 +192,11 @@ void Task::startAsynchronously(plain::WorkerInfo&) /* class TaskFinder */ +size_t TaskFinder::size() const +{ + return byUID_.size(); +} + void TaskFinder::add(Task* task) { const Label& label = task->label(); @@ -352,6 +445,7 @@ Frontend::Frontend(const Snapshot& ss) void Frontend::buildNetwork(NetworkBuilder& nb) { const Blueprint& bp = blueprint(); + const TaskFinder& finder = nb.finder(); // [[file]] for (auto&& file : bp.files()) @@ -370,7 +464,7 @@ void Frontend::buildNetwork(NetworkBuilder& nb) } auto get = [&](const blueprint::Port& port) -> Task* { - Task* task = nb.finder().findByConfigName(port.cname); + Task* task = finder.findByConfigName(port.cname); if (task->label().kind != port.kind) ERR_DIE("Invalid port: " << port.cname << " is " << task->label().kind << ", not " @@ -392,11 +486,20 @@ void Frontend::buildNetwork(NetworkBuilder& nb) nb.connect(get(src)->label().uid, get(dst)->label().uid); } - // Set priority to each DepNode - // FIXME - + // Create the network from the builder network_.emplace(nb.createNetwork()); // FIXME check if network is valid + + // Set priority to each task + switch (pr_.sched) { + case SCHED::TOPO: + ERR_DIE("Scheduling topo is not supported anymore"); // FIXME + break; + + case SCHED::RANKU: + prioritizeTaskByRanku(network_->finder()); + break; + } } Frontend::~Frontend() diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 6aad1ad..782d642 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -143,9 +143,17 @@ class TaskFinder { std::map, Task*> byConfigName_; public: + size_t size() const; void add(Task* task); Task* findByUID(UID uid) const; Task* findByConfigName(const ConfigName& cname) const; + + template + void eachTask(F f) const + { + for (auto&& [uid, task] : byUID_) + f(uid, task); + } }; // TaskCommon can be used as base class of many "common" tasks. diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 25d3ceb..e755e3c 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -241,15 +241,15 @@ class NetworkBuilder : public nt::NetworkBuilder { to->addInput(from); } -#define DEF_COMMON_TASK(CAPName, CamelName) \ - UID CAPName() override \ - { \ - UID uid = genUID(); \ - Task##CamelName* task = nullptr; \ - task = emplaceTask( \ - Label{uid, #CamelName, std::nullopt}, currentAllocator()); \ - uid2common_.emplace(uid, task); \ - return uid; \ +#define DEF_COMMON_TASK(CAPName, CamelName) \ + UID CAPName() override \ + { \ + UID uid = genUID(); \ + Task##CamelName* task = nullptr; \ + task = emplaceTask( \ + Label{uid, #CAPName, std::nullopt}, currentAllocator()); \ + uid2common_.emplace(uid, task); \ + return uid; \ } DEF_COMMON_TASK(AND, And); DEF_COMMON_TASK(ANDNOT, Andnot); diff --git a/src/label.cpp b/src/label.cpp index 0856af4..dea2962 100644 --- a/src/label.cpp +++ b/src/label.cpp @@ -20,5 +20,5 @@ bool operator<(const ConfigName& lhs, const ConfigName& rhs) /* struct Label */ // Initialization of static variables. -const char* const Label::INPUT = "Input"; -const char* const Label::OUTPUT = "Output"; +const char* const Label::INPUT = "INPUT"; +const char* const Label::OUTPUT = "OUTPUT"; From 11a6a1c080493226d8bffb9831885325ca43f67b Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Thu, 11 Nov 2021 17:05:30 +0900 Subject: [PATCH 46/54] wip --- src/iyokan_nt.cpp | 26 ++++++-------------------- src/iyokan_nt.hpp | 3 +++ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index cbe3304..6e3bbf4 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -16,21 +16,6 @@ void prioritizeTaskByRanku(const nt::TaskFinder& finder) using namespace nt; - std::unordered_map compCost = { - {"DFF", 0}, {"WIRE", 0}, {"INPUT", 0}, - {"OUTPUT", 0}, {"AND", 10}, {"NAND", 10}, - {"ANDNOT", 10}, {"OR", 10}, {"NOR", 10}, - {"ORNOT", 10}, {"XOR", 10}, {"XNOR", 10}, - {"MUX", 20}, {"NOT", 0}, {"CONSTONE", 0}, - {"CONSTZERO", 0}, {"CB", 100}, {"CBInv", 100}, - {"CBWithInv", 100}, {"MUXWoSE", 20}, {"CMUXs", 10}, - {"SEI", 0}, {"GB", 10}, {"ROMUX", 10}, - {"RAMUX", 10}, {"SEI&KS", 5}, {"cufhe2tfhepp", 0}, - {"tfhepp2cufhe", 0}, {"bridge", 0}, {"RAMWriter", 0}, - {"RAMReader", 0}, {"ROM", 0}, {"SDFF", 0}, - {"RAM", 0}, - }; - std::unordered_map numReadyChildren; // task |-> # of ready children std::queue que; // Initial tasks to be visited @@ -63,11 +48,7 @@ void prioritizeTaskByRanku(const nt::TaskFinder& finder) // wait for) into account if (!child->areAllInputsReady()) pri = std::max(pri, child->priority()); - auto it = compCost.find(task->label().kind); - if (it == compCost.end()) - ERR_DIE("Internal error: compCost does not have key: " - << task->label().kind); - task->setPriority(pri + it->second); + task->setPriority(pri + task->getComputationCost()); numPrioritizedTasks++; if (task->areAllInputsReady()) // The end of the travel @@ -160,6 +141,11 @@ void Task::setQueued() hasQueued_ = true; } +int Task::getComputationCost() const +{ + return 0; +} + void Task::tick() { hasQueued_ = false; diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 782d642..535464f 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -113,6 +113,9 @@ class Task { void setPriority(int newPri); void setQueued(); + // Get computation cost of this task. Used for scheduling of tasks. + virtual int getComputationCost() const; + virtual void notifyOneInputReady() = 0; virtual bool areAllInputsReady() const = 0; virtual bool hasFinished() const = 0; From 1bed0b65c7990ed4d3a5fbcdccf63d9970c4098b Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Fri, 12 Nov 2021 00:48:16 +0900 Subject: [PATCH 47/54] wip --- src/iyokan_nt.cpp | 141 ++++++++++++++++++++++++++++++++++++++-------- src/iyokan_nt.hpp | 31 ++++++++++ 2 files changed, 147 insertions(+), 25 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index 6e3bbf4..be905bf 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -8,18 +8,45 @@ #include namespace { -void prioritizeTaskByRanku(const nt::TaskFinder& finder) + +// Visit tasks in the network in topological order. +template +void visitTaskTopo(const nt::Network& net, F f) { - // c.f. https://en.wikipedia.org/wiki/Heterogeneous_Earliest_Finish_Time - // FIXME: Take communication costs into account - // FIXME: Tune computation costs by dynamic measurements + using namespace nt; + std::unordered_map numReadyParents; + std::queue que; + net.eachTask([&](Task* task) { + numReadyParents.emplace(task, 0); + if (task->areAllInputsReady()) + que.push(task); + }); + while (!que.empty()) { + Task* task = que.front(); + que.pop(); + f(task); + for (Task* child : task->children()) { + if (child->areAllInputsReady()) // false parent-child relationship + continue; + numReadyParents.at(child)++; + assert(child->parents().size() >= numReadyParents.at(child)); + if (child->parents().size() == numReadyParents.at(child)) + que.push(child); + } + } +} + +// Visit tasks in the network in reversed topological order +template +void visitTaskRevTopo(const nt::Network& net, F f) +{ using namespace nt; std::unordered_map numReadyChildren; // task |-> # of ready children std::queue que; // Initial tasks to be visited - finder.eachTask([&](UID, Task* task) { + net.eachTask([&](Task* task) { const std::vector& children = task->children(); // Count the children that have no inputs to wait for @@ -36,11 +63,60 @@ void prioritizeTaskByRanku(const nt::TaskFinder& finder) }); assert(!que.empty()); - size_t numPrioritizedTasks = 0; while (!que.empty()) { Task* task = que.front(); que.pop(); + f(task); + if (task->areAllInputsReady()) // The end of the travel + continue; + // Push parents into the queue if all of their children are ready + for (Task* parent : task->parents()) { + numReadyChildren.at(parent)++; + assert(parent->children().size() >= numReadyChildren.at(parent)); + if (parent->children().size() == numReadyChildren.at(parent)) + que.push(parent); + } + } +} + +void prioritizeTaskByTopo(const nt::Network& net) +{ + using namespace nt; + + size_t numPrioritizedTasks = 0; + visitTaskTopo(net, [&](Task* task) { + // Calculate and set the priority for the task + int pri = -1; + if (!task->areAllInputsReady()) + for (Task* parent : task->parents()) + pri = std::max(pri, parent->priority()); + task->setPriority(pri + 1); + numPrioritizedTasks++; + }); + if (net.size() > numPrioritizedTasks) { + LOG_DBG << "net.size() " << net.size() << " != numPrioritizedTasks " + << numPrioritizedTasks; + net.eachTask([&](Task* task) { + const Label& l = task->label(); + if (task->priority() == -1) + LOG_DBG << "\t" << l.uid << " " << l.kind << " "; + }); + ERR_DIE("Invalid network; some nodes will not be executed."); + } + assert(net.size() == numPrioritizedTasks); +} + +void prioritizeTaskByRanku(const nt::Network& net) +{ + // c.f. https://en.wikipedia.org/wiki/Heterogeneous_Earliest_Finish_Time + // FIXME: Take communication costs into account + // FIXME: Tune computation costs by dynamic measurements + + using namespace nt; + + size_t numPrioritizedTasks = 0; + visitTaskRevTopo(net, [&](Task* task) { // Calculate and set the priority for the task int pri = 0; for (Task* child : task->children()) @@ -50,30 +126,21 @@ void prioritizeTaskByRanku(const nt::TaskFinder& finder) pri = std::max(pri, child->priority()); task->setPriority(pri + task->getComputationCost()); numPrioritizedTasks++; + }); - if (task->areAllInputsReady()) // The end of the travel - continue; - - // Push parents into the queue if all of their children are ready - for (Task* parent : task->parents()) { - numReadyChildren.at(parent)++; - assert(parent->children().size() >= numReadyChildren.at(parent)); - if (parent->children().size() == numReadyChildren.at(parent)) - que.push(parent); - } - } - if (finder.size() > numPrioritizedTasks) { - LOG_DBG << "finder.size() " << finder.size() - << " != numPrioritizedTasks " << numPrioritizedTasks; - finder.eachTask([&](UID, Task* task) { + if (net.size() > numPrioritizedTasks) { + LOG_DBG << "net.size() " << net.size() << " != numPrioritizedTasks " + << numPrioritizedTasks; + net.eachTask([&](Task* task) { const Label& l = task->label(); if (task->priority() == -1) LOG_DBG << "\t" << l.uid << " " << l.kind << " "; }); ERR_DIE("Invalid network; some nodes will not be executed."); } - assert(finder.size() == numPrioritizedTasks); + assert(net.size() == numPrioritizedTasks); } + } // namespace namespace nt { @@ -252,6 +319,27 @@ const TaskFinder& Network::finder() const return finder_; } +bool Network::checkIfValid() const +{ + bool valid = true; + eachTask([&](Task* task) { + if (!task->checkIfValid()) + valid = false; + }); + + // Check if the network is weekly connected + size_t numConnectedTasks = 0; + visitTaskTopo(*this, [&](Task*) { numConnectedTasks++; }); + if (numConnectedTasks != size()) { + LOG_S(ERROR) << "The network is not weekly connected i.e., there are " + "some nodes that cannot be visited; numConnectedTasks " + << numConnectedTasks << " != size " << size(); + valid = false; + } + + return valid; +} + /* class NetworkBuilder */ NetworkBuilder::NetworkBuilder(Allocator& alc) @@ -474,16 +562,19 @@ void Frontend::buildNetwork(NetworkBuilder& nb) // Create the network from the builder network_.emplace(nb.createNetwork()); - // FIXME check if network is valid + + // Check if network is valid + if (!network_->checkIfValid()) + ERR_DIE("Network is not valid"); // Set priority to each task switch (pr_.sched) { case SCHED::TOPO: - ERR_DIE("Scheduling topo is not supported anymore"); // FIXME + prioritizeTaskByTopo(network_.value()); break; case SCHED::RANKU: - prioritizeTaskByRanku(network_->finder()); + prioritizeTaskByRanku(network_.value()); break; } } diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 535464f..b614f83 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -116,6 +116,9 @@ class Task { // Get computation cost of this task. Used for scheduling of tasks. virtual int getComputationCost() const; + // Check if the task is valid. Returns true iff it is valid. + virtual bool checkIfValid() const = 0; + virtual void notifyOneInputReady() = 0; virtual bool areAllInputsReady() const = 0; virtual bool hasFinished() const = 0; @@ -207,6 +210,30 @@ class TaskCommon : public Task { { } + virtual bool checkIfValid() const override + { + assert(output_); + + bool valid = true; + if (getInputSize() < numMinExpectedInputs_) { + LOG_S(ERROR) << "Input size < min. expected size: " + << getInputSize() << " < " << numMinExpectedInputs_; + valid = false; + } + if (getInputSize() > numMaxExpectedInputs_) { + LOG_S(ERROR) << "Input size > max. expected size: " + << getInputSize() << " > " << numMaxExpectedInputs_; + valid = false; + } + if (getInputSize() != parents().size()) { + LOG_S(ERROR) << "Input size != parents size: " << getInputSize() + << " != " << parents().size(); + valid = false; + } + + return valid; + } + virtual void notifyOneInputReady() override { numReadyInputs_++; @@ -311,6 +338,10 @@ class Network { size_t size() const; const TaskFinder& finder() const; + // Check if the network is valid. Print error messages if necessary. Returns + // true iff it is valid. + bool checkIfValid() const; + template void eachTask(F f) const { From eb869e1f1946562046ce818b95e58f52d4ba6693 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Fri, 12 Nov 2021 18:27:33 +0900 Subject: [PATCH 48/54] wip --- src/iyokan_nt.cpp | 38 +++++++++++++++++++------------------- src/iyokan_nt.hpp | 9 ++++++--- src/iyokan_nt_plain.cpp | 2 -- src/network_reader.cpp | 28 ++++++++++++++-------------- src/snapshot.cpp | 6 ++++++ 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index be905bf..d57d1da 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -518,20 +518,25 @@ Frontend::Frontend(const Snapshot& ss) void Frontend::buildNetwork(NetworkBuilder& nb) { + LOG_DBG_SCOPE("FRONTEND BUILD NETWORK"); + const Blueprint& bp = blueprint(); const TaskFinder& finder = nb.finder(); // [[file]] + LOG_DBG << "BUILD NETWORK FROM FILE"; for (auto&& file : bp.files()) readNetworkFromFile(file, nb); // [[builtin]] type = ram | type = mux-ram + LOG_DBG << "BUILD BUILTIN RAM"; for (auto&& ram : bp.builtinRAMs()) { // We ignore ram.type and always use mux-ram in plaintext mode. makeMUXRAM(ram, nb); } // [[builtin]] type = rom | type = mux-rom + LOG_DBG << "BUILD BUILTIN ROM"; for (auto&& rom : bp.builtinROMs()) { // We ignore rom.type and always use mux-rom in plaintext mode. makeMUXROM(rom, nb); @@ -547,6 +552,7 @@ void Frontend::buildNetwork(NetworkBuilder& nb) }; // [connect] + LOG_DBG << "CONNECT"; // We need to treat "... = @..." and "@... = ..." differently from // "..." = ...". // First, check if ports that are connected to or from "@..." exist. @@ -561,13 +567,16 @@ void Frontend::buildNetwork(NetworkBuilder& nb) } // Create the network from the builder + LOG_DBG << "CREATE NETWORK FROM BUILDER"; network_.emplace(nb.createNetwork()); // Check if network is valid + LOG_DBG << "CHECK IF NETWORK IS VALID"; if (!network_->checkIfValid()) ERR_DIE("Network is not valid"); // Set priority to each task + LOG_DBG << "PRIORITIZE TASKS IN NETWORK"; switch (pr_.sched) { case SCHED::TOPO: prioritizeTaskByTopo(network_.value()); @@ -862,8 +871,8 @@ void make1bitRAMWithMUX(const std::string& nodeName, } // namespace -/* - // Iyokan-L1 JSON of MUX RAM pre-compiled (and optimized) by Yosys +extern "C" { +// Iyokan-L1 JSON of MUX RAM pre-compiled (and optimized) by Yosys extern char _binary_mux_ram_8_8_8_min_json_start[]; extern char _binary_mux_ram_8_8_8_min_json_end[]; extern char _binary_mux_ram_8_8_8_min_json_size[]; @@ -873,33 +882,24 @@ extern char _binary_mux_ram_8_16_16_min_json_size[]; extern char _binary_mux_ram_9_16_16_min_json_start[]; extern char _binary_mux_ram_9_16_16_min_json_end[]; extern char _binary_mux_ram_9_16_16_min_json_size[]; -*/ +} void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb) { assert(ram.inWdataWidth == ram.outRdataWidth); - /* -#define USE_PRECOMPILED_BINARY(addrW, dataW) \ - if (inAddrWidth == addrW && dataWidth == dataW) { \ - std::stringstream ss{std::string{ \ - _binary_mux_ram_##addrW##_##dataW##_##dataW##_min_json_start, \ - _binary_mux_ram_##addrW##_##dataW##_##dataW##_min_json_end}}; \ - IyokanL1JSONReader::read(b, ss); \ - auto net = std::make_shared( \ - std::move(b)); \ - \ - error::Stack err; \ - net->checkValid(err); \ - assert(err.empty()); \ - \ - return net; \ +#define USE_PRECOMPILED_BINARY(addrW, dataW) \ + if (ram.inAddrWidth == addrW && ram.outRdataWidth == dataW) { \ + std::stringstream ss{std::string{ \ + _binary_mux_ram_##addrW##_##dataW##_##dataW##_min_json_start, \ + _binary_mux_ram_##addrW##_##dataW##_##dataW##_min_json_end}}; \ + readPrecompiledRAMNetworkFromFile(ram.name, ss, nb, dataW); \ + return; \ } USE_PRECOMPILED_BINARY(8, 8); USE_PRECOMPILED_BINARY(8, 16); USE_PRECOMPILED_BINARY(9, 16); #undef USE_PRECOMPILED_BINARY -*/ // Create inputs std::vector addrInputs; diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index b614f83..56d4c14 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -24,9 +24,9 @@ class WorkerInfo; class DataHolder; class Blueprint; namespace blueprint { -class File; -class BuiltinROM; -class BuiltinRAM; +struct File; +struct BuiltinROM; +struct BuiltinRAM; } // namespace blueprint } // namespace nt namespace cereal { @@ -533,6 +533,9 @@ class Frontend { void run(); }; +void readPrecompiledRAMNetworkFromFile(const std::string& name, + std::istream& is, nt::NetworkBuilder& nb, + int ramDataWidth); void readNetworkFromFile(const blueprint::File& file, NetworkBuilder& nb); void makeMUXROM(const blueprint::BuiltinROM& rom, NetworkBuilder& nb); void makeMUXRAM(const blueprint::BuiltinRAM& ram, NetworkBuilder& nb); diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index e755e3c..9e7447b 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -341,8 +341,6 @@ class NetworkBuilder : public nt::NetworkBuilder { currentAllocator()); uid2common_.emplace(uid, task); - // FIXME: We need to memorize this task to make a response packet. - return uid; } }; diff --git a/src/network_reader.cpp b/src/network_reader.cpp index d885439..9b7818b 100644 --- a/src/network_reader.cpp +++ b/src/network_reader.cpp @@ -294,7 +294,7 @@ class IyokanL1JSONReader { public: template static void read(const std::string& nodeName, std::istream& is, - NetworkBuilder& builder) + NetworkBuilder& builder, std::optional ramDataWidth) { std::unordered_map id2taskId; auto addId = [&](int id, int taskId) { id2taskId.emplace(id, taskId); }; @@ -355,18 +355,13 @@ class IyokanL1JSONReader { addId(id, builder.MUX()); else { bool valid = false; - /* FIXME - // If builder.RAM() exists - if constexpr (detail::hasMethodFuncRAM) { - if (type == "RAM") { - int addr = cell.at("ramAddress").get(), - bit = cell.at("ramBit").get(); - addId(id, builder.RAM(addr, bit)); - valid = true; - } + if (type == "RAM" && ramDataWidth) { + valid = true; + int addr = cell.at("ramAddress").get(), + bit = cell.at("ramBit").get(); + addId(id, builder.RAM(nodeName, "ramdata", + addr * ramDataWidth.value() + bit)); } - */ - if (!valid) ERR_DIE("Invalid JSON of network. Invalid type: " << type); } @@ -426,7 +421,12 @@ class IyokanL1JSONReader { namespace nt { -/* readNetworkFromFile */ +void readPrecompiledRAMNetworkFromFile(const std::string& name, + std::istream& is, nt::NetworkBuilder& nb, + int ramDataWidth) +{ + IyokanL1JSONReader::read(name, is, nb, ramDataWidth); +} void readNetworkFromFile(const blueprint::File& file, nt::NetworkBuilder& nb) { @@ -440,7 +440,7 @@ void readNetworkFromFile(const blueprint::File& file, nt::NetworkBuilder& nb) << "[[file]] of type 'iyokanl1-json' is deprecated. You don't need " "to use Iyokan-L1. Use Yosys JSON directly by specifying type " "'yosys-json'."; - IyokanL1JSONReader::read(file.name, ifs, nb); + IyokanL1JSONReader::read(file.name, ifs, nb, std::nullopt); break; case blueprint::File::TYPE::YOSYS_JSON: diff --git a/src/snapshot.cpp b/src/snapshot.cpp index b07abb2..7341181 100644 --- a/src/snapshot.cpp +++ b/src/snapshot.cpp @@ -18,12 +18,16 @@ Snapshot::Snapshot(const RunParameter& pr, Snapshot::Snapshot(const std::string& snapshotFile) : pr_(), alc_(nullptr) { + LOG_DBG_SCOPE("READ SNAPSHOT"); + + LOG_DBG << "OPEN"; std::ifstream ifs{snapshotFile}; if (!ifs) ERR_DIE("Can't open a snapshot file to read from: " << snapshotFile); cereal::PortableBinaryInputArchive ar{ifs}; // Read header + LOG_DBG << "READ HEADER"; std::string header; ar(header); if (header != "IYSS") // IYokan SnapShot @@ -31,10 +35,12 @@ Snapshot::Snapshot(const std::string& snapshotFile) : pr_(), alc_(nullptr) "Can't read the snapshot file; incorrect header: " << snapshotFile); // Read run parameters + LOG_DBG << "READ RUN PARAMS"; ar(pr_.blueprintFile, pr_.inputFile, pr_.outputFile, pr_.numCPUWorkers, pr_.numCycles, pr_.currentCycle, pr_.sched); // Read allocator + LOG_DBG << "READ ALLOCATOR"; alc_.reset(new Allocator(ar)); } From 14fe0805f046d2ef2695e1a1e90ffb4172bf71f4 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Fri, 12 Nov 2021 18:33:15 +0900 Subject: [PATCH 49/54] plain done --- src/error_nt.cpp | 6 ++++++ src/test0.cpp | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/error_nt.cpp b/src/error_nt.cpp index eb10a3f..84d20e5 100644 --- a/src/error_nt.cpp +++ b/src/error_nt.cpp @@ -7,7 +7,13 @@ namespace nt::error { void initialize() { +#ifdef NDEBUG + // Release loguru::g_stderr_verbosity = loguru::Verbosity_INFO; +#else + // Debug + loguru::g_stderr_verbosity = loguru::Verbosity_1; // Show LOG_DBG messages +#endif } void abortWithBacktrace() diff --git a/src/test0.cpp b/src/test0.cpp index 899882e..7921618 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -1032,7 +1032,9 @@ int main() // testProgressGraphMaker(); // testBlueprint(); - loguru::g_stderr_verbosity = loguru::Verbosity_1; + using namespace nt; + + nt::error::initialize(); nt::testAllocator(); nt::test0(); From 809c5411bd9a533c8de4e70415cf7b479363a7d5 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 13 Nov 2021 16:37:08 +0900 Subject: [PATCH 50/54] wip --- src/CMakeLists.txt | 3 +- src/dataholder_nt.cpp | 17 + src/dataholder_nt.hpp | 8 + src/iyokan_nt.cpp | 10 + src/iyokan_nt.hpp | 14 +- src/iyokan_nt_plain.cpp | 6 +- src/iyokan_nt_tfhepp.cpp | 691 +++++++++++++++++++++++++++++++++++++++ src/iyokan_nt_tfhepp.hpp | 8 + src/packet_nt.cpp | 15 + src/packet_nt.hpp | 3 + src/test0.cpp | 4 +- 11 files changed, 772 insertions(+), 7 deletions(-) create mode 100644 src/iyokan_nt_tfhepp.cpp create mode 100644 src/iyokan_nt_tfhepp.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3cc20a..8dad3fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,8 @@ endif(IYOKAN_ENABLE_CUDA) ##### test0 add_executable(test0 test0.cpp - iyokan_nt.cpp iyokan_nt_plain.cpp error_nt.cpp packet_nt.cpp dataholder_nt.cpp + iyokan_nt.cpp iyokan_nt_plain.cpp iyokan_nt_tfhepp.cpp + error_nt.cpp packet_nt.cpp dataholder_nt.cpp network_reader.cpp blueprint.cpp label.cpp allocator.cpp snapshot.cpp ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-8-8.o ${CMAKE_CURRENT_BINARY_DIR}/mux-ram-8-16-16.o diff --git a/src/dataholder_nt.cpp b/src/dataholder_nt.cpp index bddd2e6..23d5064 100644 --- a/src/dataholder_nt.cpp +++ b/src/dataholder_nt.cpp @@ -12,6 +12,11 @@ DataHolder::DataHolder(const Bit* const dataBit) { } +DataHolder::DataHolder(const TLWELvl0* const dataTLWELvl0) + : dataTLWELvl0_(dataTLWELvl0), type_(TYPE::TLWE_LVL0) +{ +} + Bit DataHolder::getBit() const { assert(type_ == TYPE::BIT); @@ -24,4 +29,16 @@ void DataHolder::setBit(const Bit* const dataBit) type_ = TYPE::BIT; } +void DataHolder::getTLWELvl0(TLWELvl0& out) const +{ + assert(type_ == TYPE::TLWE_LVL0); + out = *dataTLWELvl0_; +} + +void DataHolder::setTLWELvl0(const TLWELvl0* const dataTLWELvl0) +{ + dataTLWELvl0_ = dataTLWELvl0; + type_ = TYPE::TLWE_LVL0; +} + } // namespace nt diff --git a/src/dataholder_nt.hpp b/src/dataholder_nt.hpp index 9f0abd8..47a7f82 100644 --- a/src/dataholder_nt.hpp +++ b/src/dataholder_nt.hpp @@ -1,6 +1,8 @@ #ifndef VIRTUALSECUREPLATFORM_DATAHOLDER_NT_HPP #define VIRTUALSECUREPLATFORM_DATAHOLDER_NT_HPP +#include "tfhepp_cufhe_wrapper.hpp" + #include #include @@ -18,19 +20,25 @@ class DataHolder { private: union { const Bit* dataBit_; + const TLWELvl0* dataTLWELvl0_; }; enum class TYPE { UND, BIT, + TLWE_LVL0, } type_; public: DataHolder(); DataHolder(const Bit* const dataBit); + DataHolder(const TLWELvl0* const dataTLWELvl0); Bit getBit() const; void setBit(const Bit* const dataBit); + + void getTLWELvl0(TLWELvl0& out) const; + void setTLWELvl0(const TLWELvl0* const dataTLWELvl0); }; } // namespace nt diff --git a/src/iyokan_nt.cpp b/src/iyokan_nt.cpp index d57d1da..c0fce73 100644 --- a/src/iyokan_nt.cpp +++ b/src/iyokan_nt.cpp @@ -233,6 +233,11 @@ bool Task::canRunPlain() const return false; } +bool Task::canRunTFHEpp() const +{ + return false; +} + void Task::onAfterTick(size_t) { // Do nothing by default. @@ -243,6 +248,11 @@ void Task::startAsynchronously(plain::WorkerInfo&) ERR_UNREACHABLE; } +void Task::startAsynchronously(tfhepp::WorkerInfo&) +{ + ERR_UNREACHABLE; +} + /* class TaskFinder */ size_t TaskFinder::size() const diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 56d4c14..71f5b11 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -19,8 +19,11 @@ // Forward declarations namespace nt { namespace plain { -class WorkerInfo; +struct WorkerInfo; } +namespace tfhepp { +struct WorkerInfo; +}; class DataHolder; class Blueprint; namespace blueprint { @@ -138,9 +141,16 @@ class Task { // Return true iff this task can be run in plaintext mode. virtual bool canRunPlain() const; + // Return true iff this task can be run in TFHEpp mode. + virtual bool canRunTFHEpp() const; + // Start this task asynchronously in plaintext mode. // Only available when canRunPlain() returns true. virtual void startAsynchronously(plain::WorkerInfo&); + + // Start this task asynchronously in TFHEpp mode. + // Only available when canRunTFHEpp() returns true. + virtual void startAsynchronously(tfhepp::WorkerInfo&); }; class TaskFinder { @@ -462,7 +472,7 @@ enum class SCHED { }; struct RunParameter { - std::string blueprintFile, inputFile, outputFile; + std::string blueprintFile, inputFile, outputFile, bkeyFile; int numCPUWorkers, numCycles, currentCycle; SCHED sched; diff --git a/src/iyokan_nt_plain.cpp b/src/iyokan_nt_plain.cpp index 9e7447b..d57ecf7 100644 --- a/src/iyokan_nt_plain.cpp +++ b/src/iyokan_nt_plain.cpp @@ -4,12 +4,10 @@ #include "iyokan_nt.hpp" #include "packet_nt.hpp" -#include - namespace nt { namespace plain { -class WorkerInfo { +struct WorkerInfo { }; class Worker : public nt::Worker { @@ -891,6 +889,7 @@ void test0() blueprintPath, // blueprintFile reqPktPath, // inputFile resPktPath, // outputFile + "", // bkeyFile 2, // numCPUWorkers numCycles, // numCycles 0, // currentCycle @@ -945,6 +944,7 @@ void test0() blueprintPath, // blueprintFile reqPktPath, // inputFile resPktPath, // outputFile + "", // bkeyFile 2, // numCPUWorkers firstNumCycles, // numCycles 0, // currentCycle diff --git a/src/iyokan_nt_tfhepp.cpp b/src/iyokan_nt_tfhepp.cpp new file mode 100644 index 0000000..6a2e9eb --- /dev/null +++ b/src/iyokan_nt_tfhepp.cpp @@ -0,0 +1,691 @@ +#include "iyokan_nt_tfhepp.hpp" +#include "blueprint.hpp" +#include "dataholder_nt.hpp" +#include "iyokan_nt.hpp" +#include "packet_nt.hpp" + +#include + +namespace { + +/* class Thread */ + +class Thread { +private: + // The C++17 keyword 'inline' is necessary here to avoid duplicate + // definition of 'pool_'. Thanks to: + // https://stackoverflow.com/questions/11709859/how-to-have-static-data-members-in-a-header-only-library + static inline std::shared_ptr pool_; + std::atomic_bool finished_; + +public: + Thread(); + + bool hasFinished() const; + static void setNumThreads(int newNumThreads); + + template + Thread& operator=(Func func) + { + finished_ = false; + pool_->enqueue([this, func]() { + func(); + finished_ = true; + }); + return *this; + } +}; + +Thread::Thread() : finished_(false) +{ + if (!pool_) { + const int DEFAULT_NUM_THREADS = 10; + pool_ = std::make_shared(DEFAULT_NUM_THREADS); + } +} + +bool Thread::hasFinished() const +{ + return finished_; +} + +void Thread::setNumThreads(int newNumThreads) +{ + pool_ = std::make_shared(newNumThreads); +} + +} // namespace + +namespace nt::tfhepp { + +struct WorkerInfo { + TFHEppBKey bkey; + + WorkerInfo(TFHEppBKey bkey) : bkey(std::move(bkey)) + { + } +}; + +class Worker : public nt::Worker { +private: + WorkerInfo wi_; + +protected: + bool canExecute(Task* task) override + { + return task->canRunTFHEpp(); + } + + void startTask(Task* task) override + { + task->startAsynchronously(wi_); + } + +public: + Worker(WorkerInfo wi) : wi_(std::move(wi)) + { + } +}; + +// struct InputSource is used by class TaskInput to set correct input value +// every cycle. +struct InputSource { + int atPortWidth, atPortBit; + std::vector* bits; +}; + +class TaskInput : public TaskCommon { +private: + std::optional source_; + +public: + TaskInput(Label label, Allocator& alc) + : TaskCommon(label, alc, 0, 1), source_(std::nullopt) + { + } + TaskInput(InputSource source, Label label, Allocator& alc) + : TaskCommon(label, alc, 0, 1), source_(source) + { + } + + void onAfterTick(size_t currentCycle) override + { + if (source_) { + // Set the output value from the source + assert(getInputSize() == 0); + InputSource& s = source_.value(); + size_t index = + (s.atPortWidth * currentCycle + s.atPortBit) % s.bits->size(); + output() = s.bits->at(index); + } + } + + void startAsynchronously(WorkerInfo&) override + { + if (getInputSize() == 1) + output() = input(0); + } + + bool hasFinished() const override + { + return true; + } + + bool canRunTFHEpp() const override + { + return true; + } + + void setInput(const DataHolder& h) override + { + // Set the input i.e., set the output value of this gate + h.getTLWELvl0(output()); + } +}; + +class TaskOutput : public TaskCommon { +public: + TaskOutput(Label label, Allocator& alc) + : TaskCommon(label, alc, 1) + { + } + + void startAsynchronously(WorkerInfo&) override + { + output() = input(0); + } + + bool hasFinished() const override + { + return true; + } + + bool canRunTFHEpp() const override + { + return true; + } + + void getOutput(DataHolder& h) override + { + h.setTLWELvl0(&output()); + } +}; + +class TaskDFF : public nt::TaskDFF { +private: + std::optional initialValue_; + +public: + TaskDFF(Label label, Allocator& alc) + : nt::TaskDFF(std::move(label), alc), + initialValue_(std::nullopt) + { + } + + TaskDFF(const TLWELvl0& initialValue, Label label, Allocator& alc) + : nt::TaskDFF(std::move(label), alc), + initialValue_(initialValue) + { + } + + void onAfterTick(size_t currentCycle) override + { + if (currentCycle == 0 && initialValue_) + output() = initialValue_.value(); + } + + bool canRunTFHEpp() const override + { + return true; + } + + void startAsynchronously(WorkerInfo&) override + { + // Nothing to do, because the main process is done in + // nt::TaskDFF::tick(). + } + + void getOutput(DataHolder& h) override + { + h.setTLWELvl0(&output()); + } +}; + +class TaskROM : public TaskCommon { +public: + TaskROM(const TLWELvl0& value, Label label, Allocator& alc) + : TaskCommon(label, alc, 0) + { + output() = value; + } + + void startAsynchronously(WorkerInfo&) override + { + } + + bool hasFinished() const override + { + return true; + } + + bool canRunTFHEpp() const override + { + return true; + } +}; + +#define DEF_TASK(CamelName, inputSize, expr) \ + class Task##CamelName : public TaskCommon { \ + private: \ + Thread thr_; \ + \ + public: \ + Task##CamelName(Label label, Allocator& alc) \ + : TaskCommon(std::move(label), alc, inputSize) \ + { \ + } \ + void startAsynchronously(WorkerInfo& wi) override \ + { \ + thr_ = [&]() { \ + const GateKeyFFT& gk = *wi.bkey.gk; \ + (void)gk; /* Suppress warning 'unused variable' */ \ + (expr); \ + }; \ + } \ + bool hasFinished() const override \ + { \ + return thr_.hasFinished(); \ + } \ + bool canRunTFHEpp() const override \ + { \ + return true; \ + } \ + }; +DEF_TASK(And, 2, TFHEpp::HomAND(output(), input(0), input(1), gk)); +DEF_TASK(Andnot, 2, TFHEpp::HomANDYN(output(), input(0), input(1), gk)); +DEF_TASK(ConstOne, 0, TFHEpp::HomCONSTANTONE(output())); +DEF_TASK(ConstZero, 0, TFHEpp::HomCONSTANTZERO(output())); +DEF_TASK(Mux, 3, TFHEpp::HomMUX(output(), input(2), input(1), input(0), gk)); +DEF_TASK(Nand, 2, TFHEpp::HomNAND(output(), input(0), input(1), gk)); +DEF_TASK(Nmux, 3, TFHEpp::HomNMUX(output(), input(2), input(1), input(0), gk)); +DEF_TASK(Nor, 2, TFHEpp::HomNOR(output(), input(0), input(1), gk)); +DEF_TASK(Not, 1, TFHEpp::HomNOT(output(), input(0))); +DEF_TASK(Or, 2, TFHEpp::HomOR(output(), input(0), input(1), gk)); +DEF_TASK(Ornot, 2, TFHEpp::HomORYN(output(), input(0), input(1), gk)); +DEF_TASK(Xnor, 2, TFHEpp::HomXNOR(output(), input(0), input(1), gk)); +DEF_TASK(Xor, 2, TFHEpp::HomXOR(output(), input(0), input(1), gk)); +#undef DEF_TASK + +class NetworkBuilder : public nt::NetworkBuilder { +private: + std::unordered_map*> uid2common_; + UID nextUID_; + const TFHEPacket* const reqPacket_; + const std::map* const cname2isource_; + +private: + UID genUID() + { + return nextUID_++; + } + +public: + NetworkBuilder(const std::map& cname2isource, + const TFHEPacket& reqPacket, Allocator& alc) + : nt::NetworkBuilder(alc), + uid2common_(), + nextUID_(0), + reqPacket_(&reqPacket), + cname2isource_(&cname2isource) + { + } + + ~NetworkBuilder() + { + } + + void connect(UID fromUID, UID toUID) override + { + auto &from = uid2common_.at(fromUID), &to = uid2common_.at(toUID); + to->addInput(from); + } + +#define DEF_COMMON_TASK(CAPName, CamelName) \ + UID CAPName() override \ + { \ + UID uid = genUID(); \ + Task##CamelName* task = nullptr; \ + task = emplaceTask( \ + Label{uid, #CAPName, std::nullopt}, currentAllocator()); \ + uid2common_.emplace(uid, task); \ + return uid; \ + } + DEF_COMMON_TASK(AND, And); + DEF_COMMON_TASK(ANDNOT, Andnot); + DEF_COMMON_TASK(CONSTONE, ConstOne); + DEF_COMMON_TASK(CONSTZERO, ConstZero); + DEF_COMMON_TASK(DFF, DFF); + DEF_COMMON_TASK(MUX, Mux); + DEF_COMMON_TASK(NAND, Nand); + DEF_COMMON_TASK(NMUX, Nmux); + DEF_COMMON_TASK(NOR, Nor); + DEF_COMMON_TASK(NOT, Not); + DEF_COMMON_TASK(OR, Or); + DEF_COMMON_TASK(ORNOT, Ornot); + DEF_COMMON_TASK(XNOR, Xnor); + DEF_COMMON_TASK(XOR, Xor); +#undef DEF_COMMON_TASK + + UID SDFF0() override + { + TLWELvl0 tlwe; + TFHEpp::HomCONSTANTZERO(tlwe); + UID uid = genUID(); + TaskDFF* task = emplaceTask( + tlwe, Label{uid, "SDFF0", std::nullopt}, currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } + + UID SDFF1() override + { + TLWELvl0 tlwe; + TFHEpp::HomCONSTANTONE(tlwe); + UID uid = genUID(); + TaskDFF* task = emplaceTask( + tlwe, Label{uid, "SDFF1", std::nullopt}, currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } + + UID INPUT(const std::string& nodeName, const std::string& portName, + int portBit) override + { + Allocator& alc = currentAllocator(); + UID uid = genUID(); + ConfigName cname = ConfigName{nodeName, portName, portBit}; + Label label{uid, Label::INPUT, cname}; + TaskInput* task = nullptr; + if (auto it = cname2isource_->find(cname); it != cname2isource_->end()) + task = emplaceTask(it->second, label, alc); + else + task = emplaceTask(label, alc); + uid2common_.emplace(uid, task); + return uid; + } + + UID OUTPUT(const std::string& nodeName, const std::string& portName, + int portBit) override + { + UID uid = genUID(); + TaskOutput* task = nullptr; + task = emplaceTask( + Label{uid, Label::OUTPUT, ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } + + UID ROM(const std::string& nodeName, const std::string& portName, + int portBit) override + { + assert(reqPacket_ != nullptr); + assert(portName == "romdata"); + + UID uid = genUID(); + TaskROM* task = emplaceTask( + reqPacket_->romInTLWE.at(nodeName).at(portBit), + Label{uid, "ROM", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + uid2common_.emplace(uid, task); + return uid; + } + + UID RAM(const std::string& nodeName, const std::string& portName, + int portBit) override + { + assert(reqPacket_ != nullptr); + assert(portName == "ramdata"); + + UID uid = genUID(); + TaskDFF* task = emplaceTask( + reqPacket_->ramInTLWE.at(nodeName).at(portBit), + Label{uid, "RAM", ConfigName{nodeName, portName, portBit}}, + currentAllocator()); + uid2common_.emplace(uid, task); + + return uid; + } +}; + +class Frontend : public nt::Frontend { +private: + TFHEPacket reqPacket_; + TFHEppBKey bkey_; + +private: + void setBit0(DataHolder& dh) override; + void setBit1(DataHolder& dh) override; + void dumpResPacket(const std::string& outpath, const TaskFinder& finder, + int numCycles) override; + std::vector> makeWorkers() override; + + // Actual constructor. + void doConstruct(); + +public: + Frontend(const RunParameter& pr); + Frontend(const Snapshot& ss); +}; + +Frontend::Frontend(const RunParameter& pr) + : nt::Frontend(pr), reqPacket_(readTFHEPacket(runParam().inputFile)) +{ + doConstruct(); +} + +Frontend::Frontend(const Snapshot& ss) + : nt::Frontend(ss), reqPacket_(readTFHEPacket(runParam().inputFile)) +{ + doConstruct(); +} + +void Frontend::doConstruct() +{ + const Blueprint& bp = blueprint(); + + // Create map from ConfigName to InputSource + std::map cname2isource; + for (auto&& [key, port] : bp.atPorts()) { + // Find only inputs, that is, "[connect] ... = @..." + if (port.kind != Label::INPUT) + continue; + + // Get "@atPortName[atPortBit]" + auto& [atPortName, atPortBit] = key; + + // Check if reqPacket_ contains input data for @atPortName + auto it = reqPacket_.bits.find(atPortName); + if (it == reqPacket_.bits.end()) + continue; + + // Die if users try to set the value of @reset[0] since it is set only + // by system + if (atPortName == "reset") + ERR_DIE("@reset cannot be set by user's input"); + + // Add a new entry to cname2isource + InputSource s{bp.atPortWidths().at(atPortName), atPortBit, &it->second}; + cname2isource.emplace(port.cname, s); + } + + // Build the network. The instance is in nt::Frontend + NetworkBuilder nb{cname2isource, reqPacket_, allocator()}; + buildNetwork(nb); + + // Read bkey + readTFHEppBKey(bkey_, runParam().bkeyFile); +} + +void Frontend::setBit0(DataHolder& dh) +{ + static TLWELvl0 tlwe; + TFHEpp::HomCONSTANTZERO(tlwe); + dh.setTLWELvl0(&tlwe); +} + +void Frontend::setBit1(DataHolder& dh) +{ + static TLWELvl0 tlwe; + TFHEpp::HomCONSTANTONE(tlwe); + dh.setTLWELvl0(&tlwe); +} + +void Frontend::dumpResPacket(const std::string& outpath, + const TaskFinder& finder, int numCycles) +{ + DataHolder dh; + TFHEPacket out; + const Blueprint& bp = blueprint(); + + // Set the current number of cycles + out.numCycles = numCycles; + + // Get values of output @port + out.bits.clear(); + for (auto&& [key, port] : bp.atPorts()) { + // Find "[connect] @atPortName[atPortBit] = ..." + if (port.kind != Label::OUTPUT) + continue; + auto& [atPortName, atPortBit] = key; + + // Get the value + Task* t = finder.findByConfigName(port.cname); + t->getOutput(dh); + + // Assign the value to the corresponding bit of the response packet + auto& bits = out.bits[atPortName]; + if (bits.size() < atPortBit + 1) + bits.resize(atPortBit + 1); + dh.getTLWELvl0(bits.at(atPortBit)); + } + + /* + FIXME + // Get values of RAM + for (auto&& ram : bp.builtinRAMs()) { + std::vector& dst = out.ram[ram.name]; + dst.clear(); + for (size_t i = 0; i < (1 << ram.inAddrWidth) * ram.outRdataWidth; + i++) { + ConfigName cname{ram.name, "ramdata", static_cast(i)}; + Task* t = finder.findByConfigName(cname); + t->getOutput(dh); + dst.push_back(dh.getBit()); + } + } + */ + + // Dump the result packet + writeTFHEPacket(outpath, out); +} + +std::vector> Frontend::makeWorkers() +{ + const RunParameter& pr = runParam(); + std::vector> workers; + for (size_t i = 0; i < pr.numCPUWorkers; i++) + workers.emplace_back(std::make_unique(bkey_)); + return workers; +} + +/**************************************************/ +/***** TEST ***************************************/ +/**************************************************/ + +void test0() +{ + // Set # of CPU cores as # of threads + int numCPUCores = std::thread::hardware_concurrency(); + Thread::setNumThreads(numCPUCores); + + // Prepare secret and bootstrapping keys + const char* const bkeyPath = "_test_bkey"; + std::optional sk; + { + LOG_DBG_SCOPE("GENERATE KEYS"); + LOG_DBG << "GENERATE SECRET KEY"; + sk.emplace(); + LOG_DBG << "GENERATE BOOTSTRAPPING KEY"; + writeTFHEppBKey(bkeyPath, TFHEppBKey{*sk}); + } + + auto go = [&](const std::string& blueprintPath, + const std::string& inPktPath, + const std::string& expectedOutPktPath, int numCycles) { + const char* const reqPktPath = "_test_in"; + const char* const resPktPath = "_test_out"; + + auto inPlainPkt = PlainPacket::fromTOML(inPktPath), + expectedOutPlainPkt = PlainPacket::fromTOML(expectedOutPktPath); + auto inPkt = inPlainPkt.encrypt(*sk); + writeTFHEPacket(reqPktPath, inPkt); + + LOG_DBG_SCOPE("go"); + Frontend frontend{RunParameter{ + blueprintPath, // blueprintFile + reqPktPath, // inputFile + resPktPath, // outputFile + bkeyPath, // bkeyFile + numCPUCores, // numCPUWorkers + numCycles, // numCycles + 0, // currentCycle + SCHED::RANKU, // sched + std::nullopt, // snapshotFile + }}; + frontend.run(); + + TFHEPacket got = readTFHEPacket(resPktPath); + PlainPacket gotPlain = got.decrypt(*sk); + assert(gotPlain == expectedOutPlainPkt); + }; + + // go("test/config-toml/const-4bit.toml", "test/in/test22.in", + // "test/out/test22.out", 1); + // go("test/config-toml/addr-4bit.toml", "test/in/test04.in", + // "test/out/test04.out", 1); + // go("test/config-toml/pass-addr-pass-4bit.toml", "test/in/test04.in", + // "test/out/test04.out", 1); + // go("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + // "test/out/test16.out", 3); + // go("test/config-toml/div-8bit.toml", "test/in/test05.in", + // "test/out/test05.out", 1); + // go("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + // "test/out/test06.out", 16); + // go("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + // "test/out/test07.out", 16); + go("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + "test/out/test08.out", 8); + // go("test/config-toml/rom-4-8.toml", "test/in/test15.in", + // "test/out/test15.out", 1); + // go("test/config-toml/counter-4bit.toml", "test/in/test13.in", + // "test/out/test13.out", 3); + // go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", + // "test/out/test09-ruby.out", 7); + + /* + auto go_ss = [&](const std::string& blueprintPath, + const std::string& inPktPath, + const std::string& expectedOutPktPath, int numCycles) { + const char* const reqPktPath = "_test_in"; + const char* const resPktPath = "_test_out"; + const char* const snapshotPath = "_test_snapshot"; + + auto inPkt = PlainPacket::fromTOML(inPktPath), + expectedOutPkt = PlainPacket::fromTOML(expectedOutPktPath); + writePlainPacket(reqPktPath, inPkt); + + int secondNumCycles = numCycles / 2, + firstNumCycles = numCycles - secondNumCycles; + + { + LOG_DBG_SCOPE("go_ss 1st"); + Frontend frontend{RunParameter{ + blueprintPath, // blueprintFile + reqPktPath, // inputFile + resPktPath, // outputFile + 2, // numCPUWorkers + firstNumCycles, // numCycles + 0, // currentCycle + SCHED::RANKU, // sched + snapshotPath, // snapshotFile + }}; + frontend.run(); + } + { + LOG_DBG_SCOPE("go_ss 2nd"); + Snapshot ss{snapshotPath}; + ss.updateNumCycles(secondNumCycles); + Frontend frontend{ss}; + frontend.run(); + + PlainPacket got = readPlainPacket(resPktPath); + assert(got == expectedOutPkt); + } + }; + go_ss("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + "test/out/test16.out", 3); + go_ss("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + "test/out/test06.out", 16); + go_ss("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + "test/out/test07.out", 16); + go_ss("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + "test/out/test08.out", 8); + go_ss("test/config-toml/counter-4bit.toml", "test/in/test13.in", + "test/out/test13.out", 3); + go_ss("test/config-toml/cahp-ruby.toml", "test/in/test09.in", + "test/out/test09-ruby.out", 7); + */ +} + +} // namespace nt::tfhepp diff --git a/src/iyokan_nt_tfhepp.hpp b/src/iyokan_nt_tfhepp.hpp new file mode 100644 index 0000000..0b58ddc --- /dev/null +++ b/src/iyokan_nt_tfhepp.hpp @@ -0,0 +1,8 @@ +#ifndef VIRTUALSECUREPLATFORM_IYOKAN_NT_TFHEPP_HPP +#define VIRTUALSECUREPLATFORM_IYOKAN_NT_TFHEPP_HPP + +namespace nt::tfhepp { +void test0(); +} + +#endif diff --git a/src/packet_nt.cpp b/src/packet_nt.cpp index 5a76b6d..1029f59 100644 --- a/src/packet_nt.cpp +++ b/src/packet_nt.cpp @@ -361,6 +361,11 @@ TFHEPacket readTFHEPacket(const std::string& path) return readFromArchive(path); } +void readTFHEppBKey(TFHEppBKey& out, const std::string& path) +{ + out = readFromArchive(path); +} + void writePlainPacket(std::ostream& os, const PlainPacket& pkt) { writeToArchive(os, pkt); @@ -376,4 +381,14 @@ void writeTFHEPacket(std::ostream& os, const TFHEPacket& pkt) writeToArchive(os, pkt); } +void writeTFHEPacket(const std::string& path, const TFHEPacket& pkt) +{ + writeToArchive(path, pkt); +} + +void writeTFHEppBKey(const std::string& path, const TFHEppBKey& bkey) +{ + writeToArchive(path, bkey); +} + } // namespace nt diff --git a/src/packet_nt.hpp b/src/packet_nt.hpp index 027a8ee..8208c62 100644 --- a/src/packet_nt.hpp +++ b/src/packet_nt.hpp @@ -132,9 +132,12 @@ PlainPacket readPlainPacket(std::istream& is); PlainPacket readPlainPacket(const std::string& path); TFHEPacket readTFHEPacket(std::istream& is); TFHEPacket readTFHEPacket(const std::string& path); +void readTFHEppBKey(TFHEppBKey& out, const std::string& path); void writePlainPacket(std::ostream& os, const PlainPacket& pkt); void writePlainPacket(const std::string& path, const PlainPacket& pkt); void writeTFHEPacket(std::ostream& os, const TFHEPacket& pkt); +void writeTFHEPacket(const std::string& path, const TFHEPacket& pkt); +void writeTFHEppBKey(const std::string& path, const TFHEppBKey& bkey); } // namespace nt diff --git a/src/test0.cpp b/src/test0.cpp index 7921618..b21d339 100644 --- a/src/test0.cpp +++ b/src/test0.cpp @@ -858,6 +858,7 @@ #include "iyokan_nt.hpp" #include "iyokan_nt_plain.hpp" +#include "iyokan_nt_tfhepp.hpp" #include "packet_nt.hpp" #include "tfhepp_cufhe_wrapper.hpp" @@ -1038,7 +1039,8 @@ int main() nt::testAllocator(); nt::test0(); - nt::plain::test0(); + // nt::plain::test0(); + nt::tfhepp::test0(); return 0; } From cbaca2be01cd0152cf2a0c9370a463449a56304b Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 13 Nov 2021 22:33:18 +0900 Subject: [PATCH 51/54] wip --- src/iyokan_nt_tfhepp.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/iyokan_nt_tfhepp.cpp b/src/iyokan_nt_tfhepp.cpp index 6a2e9eb..88706eb 100644 --- a/src/iyokan_nt_tfhepp.cpp +++ b/src/iyokan_nt_tfhepp.cpp @@ -247,8 +247,7 @@ class TaskROM : public TaskCommon { void startAsynchronously(WorkerInfo& wi) override \ { \ thr_ = [&]() { \ - const GateKeyFFT& gk = *wi.bkey.gk; \ - (void)gk; /* Suppress warning 'unused variable' */ \ + [[maybe_unused]] const GateKeyFFT& gk = *wi.bkey.gk; \ (expr); \ }; \ } \ @@ -530,21 +529,18 @@ void Frontend::dumpResPacket(const std::string& outpath, dh.getTLWELvl0(bits.at(atPortBit)); } - /* - FIXME // Get values of RAM for (auto&& ram : bp.builtinRAMs()) { - std::vector& dst = out.ram[ram.name]; - dst.clear(); - for (size_t i = 0; i < (1 << ram.inAddrWidth) * ram.outRdataWidth; - i++) { + std::vector& dst = out.ramInTLWE[ram.name]; + size_t size = (1 << ram.inAddrWidth) * ram.outRdataWidth; + dst.resize(size); + for (size_t i = 0; i < size; i++) { ConfigName cname{ram.name, "ramdata", static_cast(i)}; Task* t = finder.findByConfigName(cname); t->getOutput(dh); - dst.push_back(dh.getBit()); + dh.getTLWELvl0(dst.at(i)); } } - */ // Dump the result packet writeTFHEPacket(outpath, out); From 9f180676b4aca6c1a624634e16f7acfe4817cdaa Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 13 Nov 2021 23:03:05 +0900 Subject: [PATCH 52/54] wip --- src/iyokan_nt_tfhepp.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/iyokan_nt_tfhepp.cpp b/src/iyokan_nt_tfhepp.cpp index 88706eb..22730f0 100644 --- a/src/iyokan_nt_tfhepp.cpp +++ b/src/iyokan_nt_tfhepp.cpp @@ -234,7 +234,7 @@ class TaskROM : public TaskCommon { } }; -#define DEF_TASK(CamelName, inputSize, expr) \ +#define DEF_TASK(CamelName, inputSize, compCost, expr) \ class Task##CamelName : public TaskCommon { \ private: \ Thread thr_; \ @@ -259,20 +259,26 @@ class TaskROM : public TaskCommon { { \ return true; \ } \ + int getComputationCost() const override \ + { \ + return compCost; \ + } \ }; -DEF_TASK(And, 2, TFHEpp::HomAND(output(), input(0), input(1), gk)); -DEF_TASK(Andnot, 2, TFHEpp::HomANDYN(output(), input(0), input(1), gk)); -DEF_TASK(ConstOne, 0, TFHEpp::HomCONSTANTONE(output())); -DEF_TASK(ConstZero, 0, TFHEpp::HomCONSTANTZERO(output())); -DEF_TASK(Mux, 3, TFHEpp::HomMUX(output(), input(2), input(1), input(0), gk)); -DEF_TASK(Nand, 2, TFHEpp::HomNAND(output(), input(0), input(1), gk)); -DEF_TASK(Nmux, 3, TFHEpp::HomNMUX(output(), input(2), input(1), input(0), gk)); -DEF_TASK(Nor, 2, TFHEpp::HomNOR(output(), input(0), input(1), gk)); -DEF_TASK(Not, 1, TFHEpp::HomNOT(output(), input(0))); -DEF_TASK(Or, 2, TFHEpp::HomOR(output(), input(0), input(1), gk)); -DEF_TASK(Ornot, 2, TFHEpp::HomORYN(output(), input(0), input(1), gk)); -DEF_TASK(Xnor, 2, TFHEpp::HomXNOR(output(), input(0), input(1), gk)); -DEF_TASK(Xor, 2, TFHEpp::HomXOR(output(), input(0), input(1), gk)); +DEF_TASK(And, 2, 10, TFHEpp::HomAND(output(), input(0), input(1), gk)); +DEF_TASK(Andnot, 2, 10, TFHEpp::HomANDYN(output(), input(0), input(1), gk)); +DEF_TASK(ConstOne, 0, 0, TFHEpp::HomCONSTANTONE(output())); +DEF_TASK(ConstZero, 0, 0, TFHEpp::HomCONSTANTZERO(output())); +DEF_TASK(Mux, 3, 20, + TFHEpp::HomMUX(output(), input(2), input(1), input(0), gk)); +DEF_TASK(Nand, 2, 10, TFHEpp::HomNAND(output(), input(0), input(1), gk)); +DEF_TASK(Nmux, 3, 20, + TFHEpp::HomNMUX(output(), input(2), input(1), input(0), gk)); +DEF_TASK(Nor, 2, 10, TFHEpp::HomNOR(output(), input(0), input(1), gk)); +DEF_TASK(Not, 1, 0, TFHEpp::HomNOT(output(), input(0))); +DEF_TASK(Or, 2, 10, TFHEpp::HomOR(output(), input(0), input(1), gk)); +DEF_TASK(Ornot, 2, 10, TFHEpp::HomORYN(output(), input(0), input(1), gk)); +DEF_TASK(Xnor, 2, 10, TFHEpp::HomXNOR(output(), input(0), input(1), gk)); +DEF_TASK(Xor, 2, 10, TFHEpp::HomXOR(output(), input(0), input(1), gk)); #undef DEF_TASK class NetworkBuilder : public nt::NetworkBuilder { From bc7fd5d568c2c0dc45eeccdb008640e9f1ce94db Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 13 Nov 2021 23:25:45 +0900 Subject: [PATCH 53/54] wip --- src/allocator.cpp | 13 ++++++++++++- src/iyokan_nt.hpp | 8 ++++++++ src/iyokan_nt_tfhepp.cpp | 37 +++++++++++++++++++------------------ src/snapshot.cpp | 7 +++---- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/allocator.cpp b/src/allocator.cpp index b9d53a3..c10fb1b 100644 --- a/src/allocator.cpp +++ b/src/allocator.cpp @@ -33,7 +33,7 @@ void load(Archive& ar, Bit& b, const std::uint32_t version) } struct Serializable { - std::variant data; + std::variant data; template void serialize(Archive& ar, const std::uint32_t version) @@ -74,6 +74,12 @@ Allocator::Allocator(cereal::PortableBinaryInputArchive& ar) break; } + case 1: { // TLWELvl0 + const TLWELvl0& tlwe = std::get<1>(buf.data); + data_.emplace_back(tlwe); + break; + } + default: ERR_UNREACHABLE; } @@ -95,6 +101,11 @@ void Allocator::dumpAllocatedData(cereal::PortableBinaryOutputArchive& ar) const buf.data = *src; ar(buf); }; + tyHandlers[typeid(TLWELvl0)] = [&](const std::any& any) { + const TLWELvl0* src = std::any_cast(&any); + buf.data = *src; + ar(buf); + }; // First serialize the size of the entries ar(static_cast(data_.size())); diff --git a/src/iyokan_nt.hpp b/src/iyokan_nt.hpp index 71f5b11..ec597e3 100644 --- a/src/iyokan_nt.hpp +++ b/src/iyokan_nt.hpp @@ -479,6 +479,14 @@ struct RunParameter { std::optional snapshotFile; void print() const; + + // For cereal + template + void serialize(Archive& ar) + { + ar(blueprintFile, inputFile, outputFile, bkeyFile, numCPUWorkers, + numCycles, currentCycle, sched, snapshotFile); + } }; class Snapshot { diff --git a/src/iyokan_nt_tfhepp.cpp b/src/iyokan_nt_tfhepp.cpp index 22730f0..a82d009 100644 --- a/src/iyokan_nt_tfhepp.cpp +++ b/src/iyokan_nt_tfhepp.cpp @@ -626,8 +626,8 @@ void test0() // "test/out/test06.out", 16); // go("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", // "test/out/test07.out", 16); - go("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", - "test/out/test08.out", 8); + // go("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + // "test/out/test08.out", 8); // go("test/config-toml/rom-4-8.toml", "test/in/test15.in", // "test/out/test15.out", 1); // go("test/config-toml/counter-4bit.toml", "test/in/test13.in", @@ -635,7 +635,6 @@ void test0() // go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", // "test/out/test09-ruby.out", 7); - /* auto go_ss = [&](const std::string& blueprintPath, const std::string& inPktPath, const std::string& expectedOutPktPath, int numCycles) { @@ -643,9 +642,10 @@ void test0() const char* const resPktPath = "_test_out"; const char* const snapshotPath = "_test_snapshot"; - auto inPkt = PlainPacket::fromTOML(inPktPath), - expectedOutPkt = PlainPacket::fromTOML(expectedOutPktPath); - writePlainPacket(reqPktPath, inPkt); + auto inPlainPkt = PlainPacket::fromTOML(inPktPath), + expectedOutPlainPkt = PlainPacket::fromTOML(expectedOutPktPath); + auto inPkt = inPlainPkt.encrypt(*sk); + writeTFHEPacket(reqPktPath, inPkt); int secondNumCycles = numCycles / 2, firstNumCycles = numCycles - secondNumCycles; @@ -656,7 +656,8 @@ void test0() blueprintPath, // blueprintFile reqPktPath, // inputFile resPktPath, // outputFile - 2, // numCPUWorkers + bkeyPath, // bkeyFile + numCPUCores, // numCPUWorkers firstNumCycles, // numCycles 0, // currentCycle SCHED::RANKU, // sched @@ -671,23 +672,23 @@ void test0() Frontend frontend{ss}; frontend.run(); - PlainPacket got = readPlainPacket(resPktPath); - assert(got == expectedOutPkt); + TFHEPacket got = readTFHEPacket(resPktPath); + PlainPacket gotPlain = got.decrypt(*sk); + assert(gotPlain == expectedOutPlainPkt); } }; - go_ss("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", - "test/out/test16.out", 3); - go_ss("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", - "test/out/test06.out", 16); - go_ss("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", - "test/out/test07.out", 16); - go_ss("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", - "test/out/test08.out", 8); + // go_ss("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + // "test/out/test16.out", 3); + // go_ss("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + // "test/out/test06.out", 16); + // go_ss("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + // "test/out/test07.out", 16); + // go_ss("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + // "test/out/test08.out", 8); go_ss("test/config-toml/counter-4bit.toml", "test/in/test13.in", "test/out/test13.out", 3); go_ss("test/config-toml/cahp-ruby.toml", "test/in/test09.in", "test/out/test09-ruby.out", 7); - */ } } // namespace nt::tfhepp diff --git a/src/snapshot.cpp b/src/snapshot.cpp index 7341181..366bd84 100644 --- a/src/snapshot.cpp +++ b/src/snapshot.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -36,8 +37,7 @@ Snapshot::Snapshot(const std::string& snapshotFile) : pr_(), alc_(nullptr) // Read run parameters LOG_DBG << "READ RUN PARAMS"; - ar(pr_.blueprintFile, pr_.inputFile, pr_.outputFile, pr_.numCPUWorkers, - pr_.numCycles, pr_.currentCycle, pr_.sched); + ar(pr_); // Read allocator LOG_DBG << "READ ALLOCATOR"; @@ -66,8 +66,7 @@ void Snapshot::dump(const std::string& snapshotFile) const ar(std::string{"IYSS"}); // IYokan SnapShot // Serialize pr_ of class RunParameter - ar(pr_.blueprintFile, pr_.inputFile, pr_.outputFile, pr_.numCPUWorkers, - pr_.numCycles, pr_.currentCycle, pr_.sched); + ar(pr_); // Serialize alc_ of class Allocator alc_->dumpAllocatedData(ar); From c9ae801fbe768db174fb40982c90a32793324a70 Mon Sep 17 00:00:00 2001 From: Ushitora Anqou Date: Sat, 13 Nov 2021 23:29:05 +0900 Subject: [PATCH 54/54] wip --- src/iyokan_nt_tfhepp.cpp | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/iyokan_nt_tfhepp.cpp b/src/iyokan_nt_tfhepp.cpp index a82d009..5e1c790 100644 --- a/src/iyokan_nt_tfhepp.cpp +++ b/src/iyokan_nt_tfhepp.cpp @@ -612,28 +612,28 @@ void test0() assert(gotPlain == expectedOutPlainPkt); }; - // go("test/config-toml/const-4bit.toml", "test/in/test22.in", - // "test/out/test22.out", 1); - // go("test/config-toml/addr-4bit.toml", "test/in/test04.in", - // "test/out/test04.out", 1); - // go("test/config-toml/pass-addr-pass-4bit.toml", "test/in/test04.in", - // "test/out/test04.out", 1); - // go("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", - // "test/out/test16.out", 3); - // go("test/config-toml/div-8bit.toml", "test/in/test05.in", - // "test/out/test05.out", 1); - // go("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", - // "test/out/test06.out", 16); - // go("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", - // "test/out/test07.out", 16); - // go("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", - // "test/out/test08.out", 8); - // go("test/config-toml/rom-4-8.toml", "test/in/test15.in", - // "test/out/test15.out", 1); - // go("test/config-toml/counter-4bit.toml", "test/in/test13.in", - // "test/out/test13.out", 3); - // go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", - // "test/out/test09-ruby.out", 7); + go("test/config-toml/const-4bit.toml", "test/in/test22.in", + "test/out/test22.out", 1); + go("test/config-toml/addr-4bit.toml", "test/in/test04.in", + "test/out/test04.out", 1); + go("test/config-toml/pass-addr-pass-4bit.toml", "test/in/test04.in", + "test/out/test04.out", 1); + go("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + "test/out/test16.out", 3); + go("test/config-toml/div-8bit.toml", "test/in/test05.in", + "test/out/test05.out", 1); + go("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + "test/out/test06.out", 16); + go("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + "test/out/test07.out", 16); + go("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + "test/out/test08.out", 8); + go("test/config-toml/rom-4-8.toml", "test/in/test15.in", + "test/out/test15.out", 1); + go("test/config-toml/counter-4bit.toml", "test/in/test13.in", + "test/out/test13.out", 3); + go("test/config-toml/cahp-ruby.toml", "test/in/test09.in", + "test/out/test09-ruby.out", 7); auto go_ss = [&](const std::string& blueprintPath, const std::string& inPktPath, @@ -677,14 +677,14 @@ void test0() assert(gotPlain == expectedOutPlainPkt); } }; - // go_ss("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", - // "test/out/test16.out", 3); - // go_ss("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", - // "test/out/test06.out", 16); - // go_ss("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", - // "test/out/test07.out", 16); - // go_ss("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", - // "test/out/test08.out", 8); + go_ss("test/config-toml/addr-register-4bit.toml", "test/in/test16.in", + "test/out/test16.out", 3); + go_ss("test/config-toml/ram-addr8bit.toml", "test/in/test06.in", + "test/out/test06.out", 16); + go_ss("test/config-toml/ram-addr9bit.toml", "test/in/test07.in", + "test/out/test07.out", 16); + go_ss("test/config-toml/ram-8-16-16.toml", "test/in/test08.in", + "test/out/test08.out", 8); go_ss("test/config-toml/counter-4bit.toml", "test/in/test13.in", "test/out/test13.out", 3); go_ss("test/config-toml/cahp-ruby.toml", "test/in/test09.in",