From 6baa37b830dfe14a122800b5faf92daa0e29a03b Mon Sep 17 00:00:00 2001 From: Morten Borup Petersen Date: Sun, 24 Sep 2023 11:52:37 +0200 Subject: [PATCH] Reformat to LLVM-style formatting --- .clang-format | 63 +- components/vsrtl_adderandreg.h | 40 +- components/vsrtl_aluandreg.h | 26 +- components/vsrtl_counter.h | 51 +- components/vsrtl_enumandmux.h | 62 +- components/vsrtl_fulladder.h | 55 +- components/vsrtl_manynestedcomponents.h | 45 +- components/vsrtl_nestedexponenter.h | 63 +- components/vsrtl_rannumgen.h | 75 +- components/vsrtl_registerfilecmp.h | 52 +- components/vsrtl_xornetwork.h | 72 +- core/vsrtl_adder.h | 19 +- core/vsrtl_addressspace.h | 321 +++--- core/vsrtl_alu.h | 97 +- core/vsrtl_collator.h | 29 +- core/vsrtl_comparator.h | 29 +- core/vsrtl_component.h | 469 ++++---- core/vsrtl_constant.h | 59 +- core/vsrtl_core.h | 2 +- core/vsrtl_decollator.h | 19 +- core/vsrtl_design.h | 381 ++++--- core/vsrtl_enum.h | 6 +- core/vsrtl_logicgate.h | 102 +- core/vsrtl_memory.h | 295 ++--- core/vsrtl_multiplexer.h | 165 +-- core/vsrtl_port.h | 227 ++-- core/vsrtl_register.h | 368 +++--- core/vsrtl_registerfile.h | 61 +- core/vsrtl_shift.h | 38 +- core/vsrtl_wire.h | 56 +- graphics/gallantsignalwrapper.h | 32 +- graphics/vsrtl_componentborder.h | 291 ++--- graphics/vsrtl_componentbutton.h | 82 +- graphics/vsrtl_componentgraphic.cpp | 1125 +++++++++--------- graphics/vsrtl_componentgraphic.h | 524 +++++---- graphics/vsrtl_graphics_defines.h | 23 +- graphics/vsrtl_graphics_util.cpp | 4 +- graphics/vsrtl_graphics_util.h | 168 ++- graphics/vsrtl_graphicsbase.h | 105 +- graphics/vsrtl_graphicsbaseitem.h | 286 ++--- graphics/vsrtl_gridcomponent.cpp | 587 +++++----- graphics/vsrtl_gridcomponent.h | 298 ++--- graphics/vsrtl_label.cpp | 188 +-- graphics/vsrtl_label.h | 193 ++-- graphics/vsrtl_labeleditdialog.h | 94 +- graphics/vsrtl_mainwindow.cpp | 227 ++-- graphics/vsrtl_mainwindow.h | 18 +- graphics/vsrtl_multiplexergraphic.cpp | 62 +- graphics/vsrtl_multiplexergraphic.h | 11 +- graphics/vsrtl_netlist.cpp | 185 +-- graphics/vsrtl_netlist.h | 34 +- graphics/vsrtl_netlistdelegate.cpp | 96 +- graphics/vsrtl_netlistdelegate.h | 16 +- graphics/vsrtl_netlistmodel.cpp | 183 +-- graphics/vsrtl_netlistmodel.h | 44 +- graphics/vsrtl_netlistmodelbase.cpp | 30 +- graphics/vsrtl_netlistmodelbase.h | 151 +-- graphics/vsrtl_netlistview.h | 48 +- graphics/vsrtl_parameterdialog.cpp | 131 ++- graphics/vsrtl_parameterdialog.h | 15 +- graphics/vsrtl_placeroute.cpp | 208 ++-- graphics/vsrtl_placeroute.h | 35 +- graphics/vsrtl_portgraphic.cpp | 826 +++++++------- graphics/vsrtl_portgraphic.h | 302 ++--- graphics/vsrtl_qt_serializers.h | 46 +- graphics/vsrtl_radix.cpp | 275 ++--- graphics/vsrtl_radix.h | 13 +- graphics/vsrtl_registermodel.cpp | 236 ++-- graphics/vsrtl_registermodel.h | 45 +- graphics/vsrtl_scene.cpp | 347 +++--- graphics/vsrtl_scene.h | 114 +- graphics/vsrtl_shape.cpp | 273 +++-- graphics/vsrtl_shape.h | 90 +- graphics/vsrtl_simqobject.h | 29 +- graphics/vsrtl_treeitem.cpp | 131 +-- graphics/vsrtl_treeitem.h | 61 +- graphics/vsrtl_valuelabel.cpp | 159 +-- graphics/vsrtl_valuelabel.h | 45 +- graphics/vsrtl_view.cpp | 119 +- graphics/vsrtl_view.h | 40 +- graphics/vsrtl_widget.cpp | 344 +++--- graphics/vsrtl_widget.h | 104 +- graphics/vsrtl_wiregraphic.cpp | 918 ++++++++------- graphics/vsrtl_wiregraphic.h | 575 +++++----- interface/vsrtl_binutils.h | 81 +- interface/vsrtl_defines.h | 21 +- interface/vsrtl_gfxobjecttypes.h | 117 +- interface/vsrtl_interface.cpp | 34 +- interface/vsrtl_interface.h | 1397 ++++++++++++----------- interface/vsrtl_parameter.h | 72 +- interface/vsrtl_vcdfile.cpp | 118 +- interface/vsrtl_vcdfile.h | 54 +- test/tst_adderandreg.cpp | 25 +- test/tst_aluandreg.cpp | 25 +- test/tst_counter.cpp | 42 +- test/tst_enumandmux.cpp | 6 +- test/tst_leros.cpp | 215 ++-- test/tst_memory.cpp | 162 +-- test/tst_nestedcomponent.cpp | 21 +- test/tst_rannumgen.cpp | 30 +- test/tst_registerfile.cpp | 6 +- 101 files changed, 8533 insertions(+), 7826 deletions(-) diff --git a/.clang-format b/.clang-format index 7cd90a4..a74fda4 100644 --- a/.clang-format +++ b/.clang-format @@ -1,61 +1,2 @@ -Language: Cpp -# BasedOnStyle: Chromium -AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: true -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: true -BinPackArguments: true -BinPackParameters: true -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakStringLiterals: true -ColumnLimit: 120 -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -ExperimentalAutoDetectBinPacking: false -IncludeCategories: - - Regex: '^<.*\.h>' - Priority: 1 - - Regex: '^<.*' - Priority: 2 - - Regex: '.*' - Priority: 3 -IndentCaseLabels: true -IndentWidth: 4 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: false -MaxEmptyLinesToKeep: 1 -PenaltyBreakBeforeFirstCallParameter: 1 -PointerAlignment: Left -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 4 -UseTab: Never \ No newline at end of file +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/components/vsrtl_adderandreg.h b/components/vsrtl_adderandreg.h index aab6033..921fb4f 100644 --- a/components/vsrtl_adderandreg.h +++ b/components/vsrtl_adderandreg.h @@ -11,27 +11,27 @@ namespace core { class AdderAndReg : public Design { public: - AdderAndReg() : Design("Adder and Register") { - // Connect objects - 4 >> adder->op1; - reg->out >> adder->op2; - adder->out >> reg->in; - } - static constexpr int m_cVal = 4; + AdderAndReg() : Design("Adder and Register") { + // Connect objects + 4 >> adder->op1; + reg->out >> adder->op2; + adder->out >> reg->in; + } + static constexpr int m_cVal = 4; - // Create objects - SUBCOMPONENT(adder, Adder<32>); - SUBCOMPONENT(reg, Register<32>); + // Create objects + SUBCOMPONENT(adder, Adder<32>); + SUBCOMPONENT(reg, Register<32>); - PARAMETER(a, int, 1); - PARAMETER(b, std::string, "abc"); - PARAMETER(c, bool, true); - /* - PARAMETER(d, std::vector, 123); - PARAMETER(e, std::vector, TYPE({"abc", "def", "eh"})); - */ + PARAMETER(a, int, 1); + PARAMETER(b, std::string, "abc"); + PARAMETER(c, bool, true); + /* +PARAMETER(d, std::vector, 123); +PARAMETER(e, std::vector, TYPE({"abc", "def", "eh"})); +*/ }; -} // namespace core -} // namespace vsrtl -#endif // VSRTL_ADDERANDREG_H +} // namespace core +} // namespace vsrtl +#endif // VSRTL_ADDERANDREG_H diff --git a/components/vsrtl_aluandreg.h b/components/vsrtl_aluandreg.h index a45d0ce..ad2a355 100644 --- a/components/vsrtl_aluandreg.h +++ b/components/vsrtl_aluandreg.h @@ -13,19 +13,19 @@ namespace core { class ALUAndReg : public Design { public: - ALUAndReg() : Design("ALU and Register") { - // Connect objects - 4 >> alu->op1; - reg->out >> alu->op2; - ALU_OPCODE::ADD >> alu->ctrl; - alu->out >> reg->in; - } - static constexpr int m_cVal = 4; + ALUAndReg() : Design("ALU and Register") { + // Connect objects + 4 >> alu->op1; + reg->out >> alu->op2; + ALU_OPCODE::ADD >> alu->ctrl; + alu->out >> reg->in; + } + static constexpr int m_cVal = 4; - // Create objects - SUBCOMPONENT(alu, ALU<32>); - SUBCOMPONENT(reg, Register<32>); + // Create objects + SUBCOMPONENT(alu, ALU<32>); + SUBCOMPONENT(reg, Register<32>); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/components/vsrtl_counter.h b/components/vsrtl_counter.h index df31c6a..b15f0f9 100644 --- a/components/vsrtl_counter.h +++ b/components/vsrtl_counter.h @@ -13,35 +13,36 @@ namespace core { template class Counter : public Design { - static_assert((sizeof(VSRTL_VT_U) * CHAR_BIT) >= width, "Counter width greater than VSRTL valuetype width"); + static_assert((sizeof(VSRTL_VT_U) * CHAR_BIT) >= width, + "Counter width greater than VSRTL valuetype width"); public: - Counter() : Design(std::to_string(width) + " bit counter") { - // Connect - 0 >> adders[0]->Cin; - 1 >> adders[0]->A; - regs[0]->out >> adders[0]->B; - regs[0]->out >> *value->in[0]; - adders[0]->S >> regs[0]->in; - - for (unsigned i = 1; i < width; i++) { - adders[i - 1]->Cout >> adders[i]->Cin; - regs[i]->out >> adders[i]->A; - regs[i]->out >> *value->in[i]; - 0 >> adders[i]->B; - adders[i]->S >> regs[i]->in; - } - - value->out >> outputReg->in; + Counter() : Design(std::to_string(width) + " bit counter") { + // Connect + 0 >> adders[0]->Cin; + 1 >> adders[0]->A; + regs[0]->out >> adders[0]->B; + regs[0]->out >> *value->in[0]; + adders[0]->S >> regs[0]->in; + + for (unsigned i = 1; i < width; i++) { + adders[i - 1]->Cout >> adders[i]->Cin; + regs[i]->out >> adders[i]->A; + regs[i]->out >> *value->in[i]; + 0 >> adders[i]->B; + adders[i]->S >> regs[i]->in; } - SUBCOMPONENTS(adders, FullAdder, width); - SUBCOMPONENTS(regs, Register<1>, width); - SUBCOMPONENT(outputReg, Register); - SUBCOMPONENT(value, Collator); + value->out >> outputReg->in; + } + + SUBCOMPONENTS(adders, FullAdder, width); + SUBCOMPONENTS(regs, Register<1>, width); + SUBCOMPONENT(outputReg, Register); + SUBCOMPONENT(value, Collator); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_NBITADDER_H +#endif // VSRTL_NBITADDER_H diff --git a/components/vsrtl_enumandmux.h b/components/vsrtl_enumandmux.h index 1f3f279..087a7bd 100644 --- a/components/vsrtl_enumandmux.h +++ b/components/vsrtl_enumandmux.h @@ -14,36 +14,36 @@ Enum(TestEnum, A, B, C, D, E, F); class EnumAndMux : public Design { public: - EnumAndMux() : Design("Enum and Multiplexer") { - // Connect objects - 1 >> mux->get(TestEnum::A); - 2 >> mux->get(TestEnum::B); - 3 >> mux->get(TestEnum::E); - 0xDEADBEEF >> mux->others(); - - reg->out >> mux->select; - - 1 >> adder->op1; - reg->out >> adder->op2; - - (TestEnum::_size() - 1) >> cmp->op1; - reg->out >> cmp->op2; - - // Register next-state input mux - cmp->out >> regIn_mux->select; - 0 >> *regIn_mux->ins[1]; - adder->out >> *regIn_mux->ins[0]; - regIn_mux->out >> reg->in; - } - static constexpr int width = 32; - - // Create objects - SUBCOMPONENT(mux, TYPE(EnumMultiplexer)); - SUBCOMPONENT(adder, Adder); - SUBCOMPONENT(reg, Register); - SUBCOMPONENT(regIn_mux, TYPE(Multiplexer<2, TestEnum::width()>)); - SUBCOMPONENT(cmp, Eq); + EnumAndMux() : Design("Enum and Multiplexer") { + // Connect objects + 1 >> mux->get(TestEnum::A); + 2 >> mux->get(TestEnum::B); + 3 >> mux->get(TestEnum::E); + 0xDEADBEEF >> mux->others(); + + reg->out >> mux->select; + + 1 >> adder->op1; + reg->out >> adder->op2; + + (TestEnum::_size() - 1) >> cmp->op1; + reg->out >> cmp->op2; + + // Register next-state input mux + cmp->out >> regIn_mux->select; + 0 >> *regIn_mux->ins[1]; + adder->out >> *regIn_mux->ins[0]; + regIn_mux->out >> reg->in; + } + static constexpr int width = 32; + + // Create objects + SUBCOMPONENT(mux, TYPE(EnumMultiplexer)); + SUBCOMPONENT(adder, Adder); + SUBCOMPONENT(reg, Register); + SUBCOMPONENT(regIn_mux, TYPE(Multiplexer<2, TestEnum::width()>)); + SUBCOMPONENT(cmp, Eq); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/components/vsrtl_fulladder.h b/components/vsrtl_fulladder.h index 73dfdcf..597abdc 100644 --- a/components/vsrtl_fulladder.h +++ b/components/vsrtl_fulladder.h @@ -9,41 +9,42 @@ namespace core { class FullAdder : public Component { public: - FullAdder(const std::string& name, SimComponent* parent) : Component(name, parent) { - A >> *xor1->in[0]; - B >> *xor1->in[1]; + FullAdder(const std::string &name, SimComponent *parent) + : Component(name, parent) { + A >> *xor1->in[0]; + B >> *xor1->in[1]; - xor1->out >> *xor2->in[0]; - Cin >> *xor2->in[1]; + xor1->out >> *xor2->in[0]; + Cin >> *xor2->in[1]; - xor1->out >> *and1->in[0]; - Cin >> *and1->in[1]; + xor1->out >> *and1->in[0]; + Cin >> *and1->in[1]; - A >> *and2->in[0]; - B >> *and2->in[1]; + A >> *and2->in[0]; + B >> *and2->in[1]; - and1->out >> *or1->in[0]; - and2->out >> *or1->in[1]; + and1->out >> *or1->in[0]; + and2->out >> *or1->in[1]; - or1->out >> Cout; - xor2->out >> S; - } + or1->out >> Cout; + xor2->out >> S; + } - INPUTPORT(A, 1); - INPUTPORT(B, 1); - INPUTPORT(Cin, 1); + INPUTPORT(A, 1); + INPUTPORT(B, 1); + INPUTPORT(Cin, 1); - OUTPUTPORT(S, 1); - OUTPUTPORT(Cout, 1); + OUTPUTPORT(S, 1); + OUTPUTPORT(Cout, 1); - SUBCOMPONENT(xor1, TYPE(Xor<1, 2>)); - SUBCOMPONENT(xor2, TYPE(Xor<1, 2>)); - SUBCOMPONENT(and1, TYPE(And<1, 2>)); - SUBCOMPONENT(and2, TYPE(And<1, 2>)); - SUBCOMPONENT(or1, TYPE(Or<1, 2>)); + SUBCOMPONENT(xor1, TYPE(Xor<1, 2>)); + SUBCOMPONENT(xor2, TYPE(Xor<1, 2>)); + SUBCOMPONENT(and1, TYPE(And<1, 2>)); + SUBCOMPONENT(and2, TYPE(And<1, 2>)); + SUBCOMPONENT(or1, TYPE(Or<1, 2>)); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_FULLADDER_H +#endif // VSRTL_FULLADDER_H diff --git a/components/vsrtl_manynestedcomponents.h b/components/vsrtl_manynestedcomponents.h index d3e886e..b06c3f3 100644 --- a/components/vsrtl_manynestedcomponents.h +++ b/components/vsrtl_manynestedcomponents.h @@ -8,35 +8,36 @@ namespace core { class DoubleNestedExponenter : public Component { public: - DoubleNestedExponenter(const std::string& name, SimComponent* parent) : Component(name, parent) { - in >> exp1->expIn; - exp1->out >> exp2->expIn; + DoubleNestedExponenter(const std::string &name, SimComponent *parent) + : Component(name, parent) { + in >> exp1->expIn; + exp1->out >> exp2->expIn; - exp2->out >> out; - } - INPUTPORT(in, 32); - OUTPUTPORT(out, 32); + exp2->out >> out; + } + INPUTPORT(in, 32); + OUTPUTPORT(out, 32); private: - SUBCOMPONENT(exp1, Exponenter); - SUBCOMPONENT(exp2, Exponenter); + SUBCOMPONENT(exp1, Exponenter); + SUBCOMPONENT(exp2, Exponenter); }; class ManyNestedComponents : public Design { public: - static constexpr int m_cVal = 4; - ManyNestedComponents() : Design("Many nested components") { - // Connect objects - exp1->out >> exp2->in; - exp2->out >> exp1->in; - } - - // Create objects - SUBCOMPONENT(exp1, DoubleNestedExponenter); - SUBCOMPONENT(exp2, DoubleNestedExponenter); + static constexpr int m_cVal = 4; + ManyNestedComponents() : Design("Many nested components") { + // Connect objects + exp1->out >> exp2->in; + exp2->out >> exp1->in; + } + + // Create objects + SUBCOMPONENT(exp1, DoubleNestedExponenter); + SUBCOMPONENT(exp2, DoubleNestedExponenter); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_MANYNESTEDCOMPONENTS_H +#endif // VSRTL_MANYNESTEDCOMPONENTS_H diff --git a/components/vsrtl_nestedexponenter.h b/components/vsrtl_nestedexponenter.h index fc695f4..bd9a590 100644 --- a/components/vsrtl_nestedexponenter.h +++ b/components/vsrtl_nestedexponenter.h @@ -12,44 +12,45 @@ namespace core { class Exponenter : public Component { public: - Exponenter(const std::string& name, SimComponent* parent) : Component(name, parent) { - mul->out >> expReg->in; + Exponenter(const std::string &name, SimComponent *parent) + : Component(name, parent) { + mul->out >> expReg->in; - expIn >> mul->op1; - expIn >> mul->op2; - (ALU_OPCODE::MUL) >> mul->ctrl; + expIn >> mul->op1; + expIn >> mul->op2; + (ALU_OPCODE::MUL) >> mul->ctrl; - expReg->out >> out; - } - INPUTPORT(expIn, 32); - OUTPUTPORT(out, 32); + expReg->out >> out; + } + INPUTPORT(expIn, 32); + OUTPUTPORT(out, 32); - SUBCOMPONENT(expReg, Register<32>); - SUBCOMPONENT(mul, ALU<32>); + SUBCOMPONENT(expReg, Register<32>); + SUBCOMPONENT(mul, ALU<32>); }; class NestedExponenter : public Design { public: - NestedExponenter() : Design("Nested Exponenter") { - exp->out >> adder->op1; - reg->out >> adder->op2; - (ALU_OPCODE::ADD) >> adder->ctrl; - - add2->out >> reg->in; - adder->out >> exp->expIn; - - adder->out >> add2->op1; - 2 >> add2->op2; - (ALU_OPCODE::ADD) >> add2->ctrl; - } - // Create objects - SUBCOMPONENT(exp, Exponenter); - SUBCOMPONENT(reg, Register<32>); - SUBCOMPONENT(adder, ALU<32>); - SUBCOMPONENT(add2, ALU<32>); + NestedExponenter() : Design("Nested Exponenter") { + exp->out >> adder->op1; + reg->out >> adder->op2; + (ALU_OPCODE::ADD) >> adder->ctrl; + + add2->out >> reg->in; + adder->out >> exp->expIn; + + adder->out >> add2->op1; + 2 >> add2->op2; + (ALU_OPCODE::ADD) >> add2->ctrl; + } + // Create objects + SUBCOMPONENT(exp, Exponenter); + SUBCOMPONENT(reg, Register<32>); + SUBCOMPONENT(adder, ALU<32>); + SUBCOMPONENT(add2, ALU<32>); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_NESTEDEXPONENTER_H +#endif // VSRTL_NESTEDEXPONENTER_H diff --git a/components/vsrtl_rannumgen.h b/components/vsrtl_rannumgen.h index 7c2f7b3..149c79e 100644 --- a/components/vsrtl_rannumgen.h +++ b/components/vsrtl_rannumgen.h @@ -14,51 +14,52 @@ namespace core { class RanNumGen : public Design { public: - RanNumGen() : Design("Random Number Generator") { - // Connect objects - rngResReg->out >> sh1->in; + RanNumGen() : Design("Random Number Generator") { + // Connect objects + rngResReg->out >> sh1->in; - rngResReg->out >> *xOr1->in[0]; - sh1->out >> *xOr1->in[1]; + rngResReg->out >> *xOr1->in[0]; + sh1->out >> *xOr1->in[1]; - xOr1->out >> *xOr2->in[0]; - xOr1->out >> sh2->in; - sh2->out >> *xOr2->in[1]; + xOr1->out >> *xOr2->in[0]; + xOr1->out >> sh2->in; + sh2->out >> *xOr2->in[1]; - xOr2->out >> *xOr3->in[0]; - xOr2->out >> sh3->in; - sh3->out >> *xOr3->in[1]; + xOr2->out >> *xOr3->in[0]; + xOr2->out >> sh3->in; + sh3->out >> *xOr3->in[1]; - xOr3->out >> *mux->ins[1]; + xOr3->out >> *mux->ins[1]; - mux->out >> rngResReg->in; + mux->out >> rngResReg->in; - // Initialization selection. The RNG must be supplied with a seed value - in this case, select 'init' - // constant as the input of the rng register in the first clock cycle, and the RNG circuit for all others. - 0x13fb27a3 >> *mux->ins[0]; - orr->out >> selReg->in; - selReg->out >> *orr->in[0]; - 1 >> *orr->in[1]; - selReg->out >> mux->select; - } - static constexpr int m_cVal = 4; + // Initialization selection. The RNG must be supplied with a seed value - in + // this case, select 'init' constant as the input of the rng register in the + // first clock cycle, and the RNG circuit for all others. + 0x13fb27a3 >> *mux->ins[0]; + orr->out >> selReg->in; + selReg->out >> *orr->in[0]; + 1 >> *orr->in[1]; + selReg->out >> mux->select; + } + static constexpr int m_cVal = 4; - // Create objects - SUBCOMPONENT(sh1, Shift<32>, ShiftType::sl, 13); - SUBCOMPONENT(sh2, Shift<32>, ShiftType::srl, 17); - SUBCOMPONENT(sh3, Shift<32>, ShiftType::sl, 5); - SUBCOMPONENT(xOr1, TYPE(Xor<32, 2>)); - SUBCOMPONENT(xOr2, TYPE(Xor<32, 2>)); - SUBCOMPONENT(xOr3, TYPE(Xor<32, 2>)); - SUBCOMPONENT(rngResReg, Register<32>); - SUBCOMPONENT(mux, TYPE(Multiplexer<2, 32>)); + // Create objects + SUBCOMPONENT(sh1, Shift<32>, ShiftType::sl, 13); + SUBCOMPONENT(sh2, Shift<32>, ShiftType::srl, 17); + SUBCOMPONENT(sh3, Shift<32>, ShiftType::sl, 5); + SUBCOMPONENT(xOr1, TYPE(Xor<32, 2>)); + SUBCOMPONENT(xOr2, TYPE(Xor<32, 2>)); + SUBCOMPONENT(xOr3, TYPE(Xor<32, 2>)); + SUBCOMPONENT(rngResReg, Register<32>); + SUBCOMPONENT(mux, TYPE(Multiplexer<2, 32>)); - // Initialization objects for first clock cycle - SUBCOMPONENT(orr, TYPE(Or<1, 2>)); - SUBCOMPONENT(selReg, Register<1>); + // Initialization objects for first clock cycle + SUBCOMPONENT(orr, TYPE(Or<1, 2>)); + SUBCOMPONENT(selReg, Register<1>); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_RANNUMGEN_H +#endif // VSRTL_RANNUMGEN_H diff --git a/components/vsrtl_registerfilecmp.h b/components/vsrtl_registerfilecmp.h index 8db1279..53a9df0 100644 --- a/components/vsrtl_registerfilecmp.h +++ b/components/vsrtl_registerfilecmp.h @@ -10,35 +10,35 @@ namespace core { class RegisterFileTester : public Design { public: - static constexpr unsigned int regFiles = 1; - - RegisterFileTester() : Design("Registerfile Tester") { - for (unsigned i = 0; i < regFiles; i++) { - idx_reg->out >> regs[i]->rd_idx; - idx_adder->out >> regs[i]->wr_idx; - reg_adder->out >> regs[i]->wr_data; - 1 >> regs[i]->wr_en; - - if (i == 0) { - idx_adder->out >> idx_reg->in; - 1 >> idx_adder->op1; - idx_reg->out >> idx_adder->op2; - 1 >> reg_adder->op1; - regs[i]->rd_data >> reg_adder->op2; - } - } + static constexpr unsigned int regFiles = 1; + + RegisterFileTester() : Design("Registerfile Tester") { + for (unsigned i = 0; i < regFiles; i++) { + idx_reg->out >> regs[i]->rd_idx; + idx_adder->out >> regs[i]->wr_idx; + reg_adder->out >> regs[i]->wr_data; + 1 >> regs[i]->wr_en; + + if (i == 0) { + idx_adder->out >> idx_reg->in; + 1 >> idx_adder->op1; + idx_reg->out >> idx_adder->op2; + 1 >> reg_adder->op1; + regs[i]->rd_data >> reg_adder->op2; + } } - static constexpr unsigned int regSize = 32; + } + static constexpr unsigned int regSize = 32; - // Create objects - SUBCOMPONENTS(regs, TYPE(RegisterFile), regFiles); + // Create objects + SUBCOMPONENTS(regs, TYPE(RegisterFile), regFiles); - SUBCOMPONENT(idx_adder, Adder); - SUBCOMPONENT(reg_adder, Adder); - SUBCOMPONENT(idx_reg, Register); + SUBCOMPONENT(idx_adder, Adder); + SUBCOMPONENT(reg_adder, Adder); + SUBCOMPONENT(idx_reg, Register); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_REGISTERFILECMP_H +#endif // VSRTL_REGISTERFILECMP_H diff --git a/components/vsrtl_xornetwork.h b/components/vsrtl_xornetwork.h index 1e7262e..ef3fb44 100644 --- a/components/vsrtl_xornetwork.h +++ b/components/vsrtl_xornetwork.h @@ -13,45 +13,45 @@ namespace core { class XorNetwork : public Design { public: - static constexpr unsigned int rows = 100; - static constexpr unsigned int cols = 50; - - XorNetwork() : Design("XOr Network") { - // Driver setup - 0x1234abcd >> adder->op1; - seedReg->out >> adder->op2; - adder->out >> seedReg->in; - adder->out >> decol->in; - - // Muxes, Registers & first XOR column setup - for (size_t i = 0; i < rows; i++) { - const size_t xor1 = i; - const size_t xor2 = (i + rows / 2) % rows; - *decol->out[i] >> *xors[xor1]->in[0]; - *decol->out[i] >> *xors[xor2]->in[1]; - } + static constexpr unsigned int rows = 100; + static constexpr unsigned int cols = 50; + + XorNetwork() : Design("XOr Network") { + // Driver setup + 0x1234abcd >> adder->op1; + seedReg->out >> adder->op2; + adder->out >> seedReg->in; + adder->out >> decol->in; + + // Muxes, Registers & first XOR column setup + for (size_t i = 0; i < rows; i++) { + const size_t xor1 = i; + const size_t xor2 = (i + rows / 2) % rows; + *decol->out[i] >> *xors[xor1]->in[0]; + *decol->out[i] >> *xors[xor2]->in[1]; + } - // XOR columns - for (size_t i = 0; i <= cols - 2; i++) { - for (size_t k = 0; k < rows; k++) { - const size_t fromXor = i * rows + k; - const size_t xor1 = fromXor + rows; - size_t xor2 = (fromXor + rows + rows / 2); - if (xor2 >= rows * (i + 2)) { - xor2 -= rows; - } - xors[i * rows + k]->out >> *xors[xor1]->in[0]; - xors[i * rows + k]->out >> *xors[xor2]->in[1]; - } + // XOR columns + for (size_t i = 0; i <= cols - 2; i++) { + for (size_t k = 0; k < rows; k++) { + const size_t fromXor = i * rows + k; + const size_t xor1 = fromXor + rows; + size_t xor2 = (fromXor + rows + rows / 2); + if (xor2 >= rows * (i + 2)) { + xor2 -= rows; } + xors[i * rows + k]->out >> *xors[xor1]->in[0]; + xors[i * rows + k]->out >> *xors[xor2]->in[1]; + } } + } - // Create objects - SUBCOMPONENTS(xors, TYPE(Or<1, 2>), cols* rows); + // Create objects + SUBCOMPONENTS(xors, TYPE(Or<1, 2>), cols *rows); - SUBCOMPONENT(seedReg, Register); - SUBCOMPONENT(decol, Decollator); - SUBCOMPONENT(adder, Adder); + SUBCOMPONENT(seedReg, Register); + SUBCOMPONENT(decol, Decollator); + SUBCOMPONENT(adder, Adder); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/core/vsrtl_adder.h b/core/vsrtl_adder.h index 2ea5994..d821ff3 100644 --- a/core/vsrtl_adder.h +++ b/core/vsrtl_adder.h @@ -11,14 +11,15 @@ namespace core { template class Adder : public Component { public: - SetGraphicsType(Adder); - Adder(const std::string& name, SimComponent* parent) : Component(name, parent) { - out << [=] { return op1.sValue() + op2.sValue(); }; - } + SetGraphicsType(Adder); + Adder(const std::string &name, SimComponent *parent) + : Component(name, parent) { + out << [=] { return op1.sValue() + op2.sValue(); }; + } - INPUTPORT(op1, W); - INPUTPORT(op2, W); - OUTPUTPORT(out, W); + INPUTPORT(op1, W); + INPUTPORT(op2, W); + OUTPUTPORT(out, W); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/core/vsrtl_addressspace.h b/core/vsrtl_addressspace.h index 1501098..2581e3c 100644 --- a/core/vsrtl_addressspace.h +++ b/core/vsrtl_addressspace.h @@ -13,191 +13,210 @@ namespace core { /** * @brief The AddressSpace class - * The AddressSpace class manages a sparse array datastructure used to describe a byte-addressable memory of unbounded - * size (up to UINT32_MAX keys), intended for use as instruction/data memory. Furthermore, initialization memories can - * be added, which will be re-written to the sparse array upon resetting the memory. + * The AddressSpace class manages a sparse array datastructure used to describe + * a byte-addressable memory of unbounded size (up to UINT32_MAX keys), intended + * for use as instruction/data memory. Furthermore, initialization memories can + * be added, which will be re-written to the sparse array upon resetting the + * memory. * */ class AddressSpace { public: - enum class RegionType { Program, IO }; - virtual ~AddressSpace() {} - - virtual void writeMem(VSRTL_VT_U address, VSRTL_VT_U value, int bytes) { - // writes value from the given address start, and up to $size bytes of - // $value - for (int i = 0; i < bytes; i++) { - m_data[address++] = value & 0xFF; - value >>= 8; - } + enum class RegionType { Program, IO }; + virtual ~AddressSpace() {} + + virtual void writeMem(VSRTL_VT_U address, VSRTL_VT_U value, int bytes) { + // writes value from the given address start, and up to $size bytes of + // $value + for (int i = 0; i < bytes; i++) { + m_data[address++] = value & 0xFF; + value >>= 8; } + } - virtual VSRTL_VT_U readMem(VSRTL_VT_U address, unsigned bytes) { - VSRTL_VT_U value = 0; - for (unsigned i = 0; i < bytes; i++) { - value |= static_cast(m_data[address++]) << (i * CHAR_BIT); - } - - return value; + virtual VSRTL_VT_U readMem(VSRTL_VT_U address, unsigned bytes) { + VSRTL_VT_U value = 0; + for (unsigned i = 0; i < bytes; i++) { + value |= static_cast(m_data[address++]) << (i * CHAR_BIT); } - virtual VSRTL_VT_U readMemConst(VSRTL_VT_U address, unsigned bytes) const { - VSRTL_VT_U value = 0; - for (unsigned i = 0; i < bytes; i++) { - auto it = m_data.find(address); - if (it != m_data.end()) { - value |= static_cast(it->second) << (i * CHAR_BIT); - } - address++; - } - return value; + return value; + } + + virtual VSRTL_VT_U readMemConst(VSRTL_VT_U address, unsigned bytes) const { + VSRTL_VT_U value = 0; + for (unsigned i = 0; i < bytes; i++) { + auto it = m_data.find(address); + if (it != m_data.end()) { + value |= static_cast(it->second) << (i * CHAR_BIT); + } + address++; } - - virtual bool contains(const VSRTL_VT_U& address) const { return m_data.count(address) > 0; } - virtual RegionType regionType(const VSRTL_VT_U& /* address */) const { return RegionType::Program; } - - /** - * @brief addInitializationMemory - * The specified program will be added as a memory segment which will be loaded into this memory once it is reset. - */ - template - void addInitializationMemory(const VSRTL_VT_U& startAddr, T* program, const size_t& n) { - auto& mem = m_initializationMemories.emplace_back(); - VSRTL_VT_U addr = startAddr; - for (size_t i = 0; i < n; i++) { - // Add to initialization memories for future rewriting upon reset - mem.writeMem(addr, program[i], sizeof(T)); - addr += sizeof(T); - } + return value; + } + + virtual bool contains(const VSRTL_VT_U &address) const { + return m_data.count(address) > 0; + } + virtual RegionType regionType(const VSRTL_VT_U & /* address */) const { + return RegionType::Program; + } + + /** + * @brief addInitializationMemory + * The specified program will be added as a memory segment which will be + * loaded into this memory once it is reset. + */ + template + void addInitializationMemory(const VSRTL_VT_U &startAddr, T *program, + const size_t &n) { + auto &mem = m_initializationMemories.emplace_back(); + VSRTL_VT_U addr = startAddr; + for (size_t i = 0; i < n; i++) { + // Add to initialization memories for future rewriting upon reset + mem.writeMem(addr, program[i], sizeof(T)); + addr += sizeof(T); } + } - void clearInitializationMemories() { m_initializationMemories.clear(); } + void clearInitializationMemories() { m_initializationMemories.clear(); } - virtual void reset() { - m_data.clear(); - for (const auto& mem : m_initializationMemories) { - for (const auto& memData : mem.m_data) { - writeMem(memData.first, memData.second, sizeof(memData.second)); - } - } + virtual void reset() { + m_data.clear(); + for (const auto &mem : m_initializationMemories) { + for (const auto &memData : mem.m_data) { + writeMem(memData.first, memData.second, sizeof(memData.second)); + } } + } private: - std::unordered_map m_data; - std::vector m_initializationMemories; + std::unordered_map m_data; + std::vector m_initializationMemories; }; struct IOFunctors { - /** - * @brief ioWrite - * Function pointer to an IO write function. - * - @param 1: adress offset relative to the base address of the component - * - @param 2: value to write - * - @param 3: byte-width of write - */ - std::function ioWrite; - /** - * @brief ioRead - * Function pointer to an IO read function. - * - @param 1: adress offset relative to the base address of the component - * - @param 3: byte-width of read - * - @return read value from peripheral - */ - std::function ioRead; + /** + * @brief ioWrite + * Function pointer to an IO write function. + * - @param 1: adress offset relative to the base address of the component + * - @param 2: value to write + * - @param 3: byte-width of write + */ + std::function ioWrite; + /** + * @brief ioRead + * Function pointer to an IO read function. + * - @param 1: adress offset relative to the base address of the component + * - @param 3: byte-width of read + * - @return read value from peripheral + */ + std::function ioRead; }; /** * @brief The AddressSpaceMM class - * Extends the AddressSpace with the capabilites of having separate memory regions in the address space wherein - * read/writes should be forwarded to somewhere else. Used for registerring memory-mapped peripherals. + * Extends the AddressSpace with the capabilites of having separate memory + * regions in the address space wherein read/writes should be forwarded to + * somewhere else. Used for registerring memory-mapped peripherals. */ class AddressSpaceMM : public AddressSpace { public: - struct MMapValue { - VSRTL_VT_U base; - unsigned size; - IOFunctors io; - }; - - virtual void writeMem(VSRTL_VT_U address, VSRTL_VT_U value, int size = sizeof(VSRTL_VT_U)) override { - if (auto* mmapregion = findMMapRegion(address)) { - mmapregion->io.ioWrite(address - mmapregion->base, value, size); - } else { - AddressSpace::writeMem(address, value, size); - } + struct MMapValue { + VSRTL_VT_U base; + unsigned size; + IOFunctors io; + }; + + virtual void writeMem(VSRTL_VT_U address, VSRTL_VT_U value, + int size = sizeof(VSRTL_VT_U)) override { + if (auto *mmapregion = findMMapRegion(address)) { + mmapregion->io.ioWrite(address - mmapregion->base, value, size); + } else { + AddressSpace::writeMem(address, value, size); } + } - virtual VSRTL_VT_U readMem(VSRTL_VT_U address, unsigned width) override { - if (auto* mmapregion = findMMapRegion(address)) { - return mmapregion->io.ioRead(address - mmapregion->base, width); - } else { - return AddressSpace::readMem(address, width); - } + virtual VSRTL_VT_U readMem(VSRTL_VT_U address, unsigned width) override { + if (auto *mmapregion = findMMapRegion(address)) { + return mmapregion->io.ioRead(address - mmapregion->base, width); + } else { + return AddressSpace::readMem(address, width); } - - virtual VSRTL_VT_U readMemConst(VSRTL_VT_U address, unsigned width) const override { - if (auto* mmapregion = findMMapRegion(address)) { - return mmapregion->io.ioRead(address - mmapregion->base, width); - } else { - return AddressSpace::readMemConst(address, width); - } + } + + virtual VSRTL_VT_U readMemConst(VSRTL_VT_U address, + unsigned width) const override { + if (auto *mmapregion = findMMapRegion(address)) { + return mmapregion->io.ioRead(address - mmapregion->base, width); + } else { + return AddressSpace::readMemConst(address, width); } - - RegionType regionType(const VSRTL_VT_U& address) const override { - if (auto* mmapregion = findMMapRegion(address)) { - (void)mmapregion; - return RegionType::IO; - } else { - return RegionType::Program; - } + } + + RegionType regionType(const VSRTL_VT_U &address) const override { + if (auto *mmapregion = findMMapRegion(address)) { + (void)mmapregion; + return RegionType::IO; + } else { + return RegionType::Program; } - - /** - * @brief addRegion - * Registers a memory mapped region at @param start with @param size. - */ - void addIORegion(const VSRTL_VT_U& baseAddr, const unsigned& size, const IOFunctors& io) { - // Assert that there lies no memory regions within the region that we are about to insert - assert(findMMapRegion(baseAddr) == nullptr && findMMapRegion(baseAddr + size - 1) == nullptr && - "Tried to add memory mapped region which overlaps with some other region"); - assert(size > 0); - m_mmapRegions[baseAddr + size - 1] = MMapValue{baseAddr, size, io}; - } - void removeIORegion(const VSRTL_VT_U& baseAddr, const unsigned& size) { - auto it = m_mmapRegions.find(baseAddr + size - 1); - assert(it != m_mmapRegions.end() && "Tried to remove non-existing memory mapped region"); - m_mmapRegions.erase(it); + } + + /** + * @brief addRegion + * Registers a memory mapped region at @param start with @param size. + */ + void addIORegion(const VSRTL_VT_U &baseAddr, const unsigned &size, + const IOFunctors &io) { + // Assert that there lies no memory regions within the region that we are + // about to insert + assert(findMMapRegion(baseAddr) == nullptr && + findMMapRegion(baseAddr + size - 1) == nullptr && + "Tried to add memory mapped region which overlaps with some other " + "region"); + assert(size > 0); + m_mmapRegions[baseAddr + size - 1] = MMapValue{baseAddr, size, io}; + } + void removeIORegion(const VSRTL_VT_U &baseAddr, const unsigned &size) { + auto it = m_mmapRegions.find(baseAddr + size - 1); + assert(it != m_mmapRegions.end() && + "Tried to remove non-existing memory mapped region"); + m_mmapRegions.erase(it); + } + + /** + * @brief findMMapRegion + * Attempts to locate the memory mapped region which @param address resides + * in. If located, returns I/O capabilities to this region, else returns + * nullptr. + */ + const MMapValue *findMMapRegion(const VSRTL_VT_U &address) const { + if (m_mmapRegions.empty()) { + return nullptr; } - /** - * @brief findMMapRegion - * Attempts to locate the memory mapped region which @param address resides in. If located, returns I/O capabilities - * to this region, else returns nullptr. - */ - const MMapValue* findMMapRegion(const VSRTL_VT_U& address) const { - if (m_mmapRegions.empty()) { - return nullptr; - } - - auto it = m_mmapRegions.lower_bound(address); - if (it == m_mmapRegions.end()) { - return nullptr; - } else if (address < it->second.base) { - return nullptr; - } - - return &it->second; + auto it = m_mmapRegions.lower_bound(address); + if (it == m_mmapRegions.end()) { + return nullptr; + } else if (address < it->second.base) { + return nullptr; } + return &it->second; + } + private: - /** - * @brief m_mmapRegions - * Map of memory-mapped regions. Key is the last address of the region. Might seem like a weird choice (instead of - * base address of the region), however, this logic works better with std::map::lower_bound. Value represents the - * size of the region (used to determine indexing into the region) as well as I/O functions. - */ - std::map m_mmapRegions; + /** + * @brief m_mmapRegions + * Map of memory-mapped regions. Key is the last address of the region. Might + * seem like a weird choice (instead of base address of the region), however, + * this logic works better with std::map::lower_bound. Value represents the + * size of the region (used to determine indexing into the region) as well as + * I/O functions. + */ + std::map m_mmapRegions; }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/core/vsrtl_alu.h b/core/vsrtl_alu.h index 8ed05af..f9f19e8 100644 --- a/core/vsrtl_alu.h +++ b/core/vsrtl_alu.h @@ -1,72 +1,73 @@ #pragma once -#include #include "../interface/vsrtl_binutils.h" #include "vsrtl_component.h" #include "vsrtl_defines.h" #include "vsrtl_enum.h" #include "vsrtl_port.h" +#include #include "../interface/vsrtl_gfxobjecttypes.h" namespace vsrtl { namespace core { -Enum(ALU_OPCODE, ADD, SUB, MUL, DIV, AND, OR, XOR, SL, SRA, SRL, LUI, LT, LTU, EQ); +Enum(ALU_OPCODE, ADD, SUB, MUL, DIV, AND, OR, XOR, SL, SRA, SRL, LUI, LT, LTU, + EQ); template class ALU : public Component { public: - SetGraphicsType(ALU); - ALU(const std::string& name, SimComponent* parent) : Component(name, parent) { - out << ([=] { return calculateOutput(); }); - } + SetGraphicsType(ALU); + ALU(const std::string &name, SimComponent *parent) : Component(name, parent) { + out << ([=] { return calculateOutput(); }); + } - void propagate() { calculateOutput(); } + void propagate() { calculateOutput(); } - INPUTPORT(op1, W); - INPUTPORT(op2, W); - INPUTPORT(ctrl, ALU_OPCODE::width()); + INPUTPORT(op1, W); + INPUTPORT(op2, W); + INPUTPORT(ctrl, ALU_OPCODE::width()); - OUTPUTPORT(out, W); + OUTPUTPORT(out, W); private: - VSRTL_VT_U calculateOutput() { - const auto uop1 = op1.uValue(); - const auto uop2 = op2.uValue(); - const auto _op1 = op1.sValue(); - const auto _op2 = op2.sValue(); - Switch(ctrl, ALU_OPCODE) { - case ALU_OPCODE::ADD: - return uop1 + uop2; - case ALU_OPCODE::SUB: - return uop1 - uop2; - case ALU_OPCODE::MUL: - return uop1 * uop2; - case ALU_OPCODE::DIV: - return uop1 / uop2; - case ALU_OPCODE::AND: - return uop1 & uop2; - case ALU_OPCODE::OR: - return uop1 | uop2; - case ALU_OPCODE::XOR: - return uop1 ^ uop2; - case ALU_OPCODE::SL: - return uop1 << uop2; - case ALU_OPCODE::SRA: - return _op1 >> uop2; - case ALU_OPCODE::SRL: - return uop1 >> uop2; - case ALU_OPCODE::LUI: - return uop2; - case ALU_OPCODE::LT: - return _op1 < _op2 ? 1 : 0; - case ALU_OPCODE::LTU: - return uop1 < uop2 ? 1 : 0; - default: - throw std::runtime_error("Invalid ALU opcode"); - } + VSRTL_VT_U calculateOutput() { + const auto uop1 = op1.uValue(); + const auto uop2 = op2.uValue(); + const auto _op1 = op1.sValue(); + const auto _op2 = op2.sValue(); + Switch(ctrl, ALU_OPCODE) { + case ALU_OPCODE::ADD: + return uop1 + uop2; + case ALU_OPCODE::SUB: + return uop1 - uop2; + case ALU_OPCODE::MUL: + return uop1 * uop2; + case ALU_OPCODE::DIV: + return uop1 / uop2; + case ALU_OPCODE::AND: + return uop1 & uop2; + case ALU_OPCODE::OR: + return uop1 | uop2; + case ALU_OPCODE::XOR: + return uop1 ^ uop2; + case ALU_OPCODE::SL: + return uop1 << uop2; + case ALU_OPCODE::SRA: + return _op1 >> uop2; + case ALU_OPCODE::SRL: + return uop1 >> uop2; + case ALU_OPCODE::LUI: + return uop2; + case ALU_OPCODE::LT: + return _op1 < _op2 ? 1 : 0; + case ALU_OPCODE::LTU: + return uop1 < uop2 ? 1 : 0; + default: + throw std::runtime_error("Invalid ALU opcode"); } + } }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/core/vsrtl_collator.h b/core/vsrtl_collator.h index 7c23396..94ac43c 100644 --- a/core/vsrtl_collator.h +++ b/core/vsrtl_collator.h @@ -19,20 +19,21 @@ namespace core { template class Collator : public Component { public: - Collator(const std::string& name, SimComponent* parent) : Component(name, parent) { - out << [=] { - VSRTL_VT_U value = 0; - for (unsigned i = 0; i < W; i++) { - value |= static_cast(*in[i]) << i; - } - return value; - }; - } - OUTPUTPORT(out, W); - INPUTPORTS(in, 1, W); + Collator(const std::string &name, SimComponent *parent) + : Component(name, parent) { + out << [=] { + VSRTL_VT_U value = 0; + for (unsigned i = 0; i < W; i++) { + value |= static_cast(*in[i]) << i; + } + return value; + }; + } + OUTPUTPORT(out, W); + INPUTPORTS(in, 1, W); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_COLLATOR_H +#endif // VSRTL_COLLATOR_H diff --git a/core/vsrtl_comparator.h b/core/vsrtl_comparator.h index d717171..7c958cb 100644 --- a/core/vsrtl_comparator.h +++ b/core/vsrtl_comparator.h @@ -6,17 +6,18 @@ namespace vsrtl { namespace core { -#define CMP_COMPONENT(classname, valFunc, op) \ - template \ - class classname : public Component { \ - public: \ - classname(const std::string& name, SimComponent* parent) : Component(name, parent) { \ - out << [=] { return op1.valFunc() op op2.valFunc(); }; \ - } \ - OUTPUTPORT(out, 1); \ - INPUTPORT(op1, W); \ - INPUTPORT(op2, W); \ - }; +#define CMP_COMPONENT(classname, valFunc, op) \ + template \ + class classname : public Component { \ + public: \ + classname(const std::string &name, SimComponent *parent) \ + : Component(name, parent) { \ + out << [=] { return op1.valFunc() op op2.valFunc(); }; \ + } \ + OUTPUTPORT(out, 1); \ + INPUTPORT(op1, W); \ + INPUTPORT(op2, W); \ + }; CMP_COMPONENT(Sge, sValue, >=) CMP_COMPONENT(Slt, sValue, <) @@ -24,7 +25,7 @@ CMP_COMPONENT(Uge, uValue, >=) CMP_COMPONENT(Ult, uValue, <) CMP_COMPONENT(Eq, uValue, ==) -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_COMPARATOR_H +#endif // VSRTL_COMPARATOR_H diff --git a/core/vsrtl_component.h b/core/vsrtl_component.h index 983c7d4..daf9be0 100644 --- a/core/vsrtl_component.h +++ b/core/vsrtl_component.h @@ -21,248 +21,295 @@ namespace vsrtl { namespace core { -/// To allow for invokations of subcomponents with types having more than one template parameter, it is necessary to be -/// able to join the templated type, without having the preprocessor deduce the ',' in the type list as a separator in -/// the actual macro. For these cases, we may say SUBCOMPONENT(xor1, TYPE(Xor<1,2>)); +/// To allow for invokations of subcomponents with types having more than one +/// template parameter, it is necessary to be able to join the templated type, +/// without having the preprocessor deduce the ',' in the type list as a +/// separator in the actual macro. For these cases, we may say +/// SUBCOMPONENT(xor1, TYPE(Xor<1,2>)); #define TYPE(...) __VA_ARGS__ -#define INPUTPORT(name, W) Port& name = this->template createInputPort(#name) -#define INPUTPORT_ENUM(name, E_t) Port& name = this->template createInputPort(#name) -#define INPUTPORTS(name, W, N) std::vector*> name = this->template createInputPorts("in", N) +#define INPUTPORT(name, W) \ + Port &name = this->template createInputPort(#name) +#define INPUTPORT_ENUM(name, E_t) \ + Port &name = \ + this->template createInputPort(#name) +#define INPUTPORTS(name, W, N) \ + std::vector *> name = this->template createInputPorts("in", N) -#define OUTPUTPORT(name, W) Port& name = this->template createOutputPort(#name) -#define OUTPUTPORT_ENUM(name, E_t) Port& name = this->template createOutputPort(#name) -#define OUTPUTPORTS(name, W, N) std::vector*> name = this->template createOutputPorts("in", N) +#define OUTPUTPORT(name, W) \ + Port &name = this->template createOutputPort(#name) +#define OUTPUTPORT_ENUM(name, E_t) \ + Port &name = \ + this->template createOutputPort(#name) +#define OUTPUTPORTS(name, W, N) \ + std::vector *> name = this->template createOutputPorts("in", N) class Component : public SimComponent { public: - Component(const std::string& displayName, SimComponent* parent) : SimComponent(displayName, parent) {} - /** - * @brief getBaseType - * Used to identify the component type, which is used when determining how to draw a component. Introduced to avoid - * intermediate base classes for many (All) components, which is a template class. For instance, it is desireable to - * identify all instances of "Constant<...>" objects, but without introducing a "BaseConstant" class. - * @return String identifier for the component type - */ + Component(const std::string &displayName, SimComponent *parent) + : SimComponent(displayName, parent) {} + /** + * @brief getBaseType + * Used to identify the component type, which is used when determining how to + * draw a component. Introduced to avoid intermediate base classes for many + * (All) components, which is a template class. For instance, it is desireable + * to identify all instances of "Constant<...>" objects, but without + * introducing a "BaseConstant" class. + * @return String identifier for the component type + */ - const GraphicsType* getGraphicsType() const override { return GraphicsTypeFor(Component); } - virtual void resetPropagation() { - if (m_propagationState == PropagationState::unpropagated) { - // Constants (components with no inputs) are always propagated - } else { - m_propagationState = PropagationState::unpropagated; - for (const auto& i : getPorts()) - i->resetPropagation(); - for (const auto& o : getPorts()) - o->resetPropagation(); - } + const GraphicsType *getGraphicsType() const override { + return GraphicsTypeFor(Component); + } + virtual void resetPropagation() { + if (m_propagationState == PropagationState::unpropagated) { + // Constants (components with no inputs) are always propagated + } else { + m_propagationState = PropagationState::unpropagated; + for (const auto &i : getPorts()) + i->resetPropagation(); + for (const auto &o : getPorts()) + o->resetPropagation(); } - bool isPropagated() const { return m_propagationState == PropagationState::propagated; } - void setSensitiveTo(const PortBase* p) { m_sensitivityList.push_back(p); } - void setSensitiveTo(const PortBase& p) { setSensitiveTo(&p); } + } + bool isPropagated() const { + return m_propagationState == PropagationState::propagated; + } + void setSensitiveTo(const PortBase *p) { m_sensitivityList.push_back(p); } + void setSensitiveTo(const PortBase &p) { setSensitiveTo(&p); } - template - Port& createInputPort(const std::string& name) { - return createPort(name, m_inputPorts, vsrtl::SimPort::PortType::in); - } - template - Port& createOutputPort(const std::string& name) { - return createPort(name, m_outputPorts, vsrtl::SimPort::PortType::out); - } + template + Port &createInputPort(const std::string &name) { + return createPort(name, m_inputPorts, vsrtl::SimPort::PortType::in); + } + template + Port &createOutputPort(const std::string &name) { + return createPort(name, m_outputPorts, + vsrtl::SimPort::PortType::out); + } - template - std::vector*> createInputPorts(const std::string& name, unsigned int n) { - return createPorts(name, m_inputPorts, vsrtl::SimPort::PortType::in, n); - } + template + std::vector *> createInputPorts(const std::string &name, + unsigned int n) { + return createPorts(name, m_inputPorts, vsrtl::SimPort::PortType::in, n); + } - template - std::vector*> createOutputPorts(const std::string& name, unsigned int n) { - return createPorts(name, m_outputPorts, vsrtl::SimPort::PortType::out, n); - } + template + std::vector *> createOutputPorts(const std::string &name, + unsigned int n) { + return createPorts(name, m_outputPorts, vsrtl::SimPort::PortType::out, + n); + } - void propagateComponent(std::vector& propagationStack) { - // Component has already been propagated - if (m_propagationState == PropagationState::propagated) - return; + void propagateComponent(std::vector &propagationStack) { + // Component has already been propagated + if (m_propagationState == PropagationState::propagated) + return; - if (isSynchronous()) { - // Registers are implicitely clocked by calling propagate() on its output ports. - /** @remark register be saved before propagateComponent reaches the register ! */ - m_propagationState = PropagationState::propagated; - for (const auto& s : getPorts()) { - s->propagate(propagationStack); - } - } else { - /* A circuit should initially ask its subcomponents to propagate. Some subcomponents may be able to - * propagate and some may not. Furthermore, This subcomponent (X) may be dependent on some of its internal - * subcomponents to propagate. - * Example: - * Port Y is propagated, and now asks X to propagate. - * X contains two subcomponents, B and A. A has all of its inputs (Y) propagated. B is reliant on 'z' to be - * propagated. However, 'z' is dependent on A being propagated. - * - * 1. - * X is trying to propagate, will initially ask its subcomponents to propagate. - * - * 1. - * Asking A to propagate, will make port (h) propagated. - * Asking B to propagate is invalid, because 'z' is unpropagated. - * - * 2. - * Component X will then check whether all of its input ports are propagated (z, i). 'i' is propagated, but - * 'z' is not, so the propagation algorithm will ask the parent component of port 'z' to propagate (C). - * - * 3. - * C has one input which attaches to port 'h' of A - which is now propagated. So C may propagate, in turn - * making 'z' propagated. - * - * 4. - * All inputs have now been propagated to X. It will then again ask its subcomponents to try to - * propagate. - * - * 5. - * - * y - * + X - * | +--------------+ - * | | +------+ | - * | | | | | - * | | | B | | - * | z | | | | +------+ - * | +---->----> | | | | - * | | | +------+ | | | - * | | | | +---> C +----+ - * | | | +------+ | | | | | - * | | i | | | h | | +------+ | - * +--------->----> A +--------+ | - * | | | | | | - * | | | | | | - * | | +------+ | | - * | | | | - * | +--------------+ | - * | | - * +-----------------------------------------+ - * - */ + if (isSynchronous()) { + // Registers are implicitely clocked by calling propagate() on its output + // ports. + /** @remark register be saved before propagateComponent reaches the + * register ! */ + m_propagationState = PropagationState::propagated; + for (const auto &s : getPorts()) { + s->propagate(propagationStack); + } + } else { + /* A circuit should initially ask its subcomponents to propagate. Some + * subcomponents may be able to propagate and some may not. Furthermore, + * This subcomponent (X) may be dependent on some of its internal + * subcomponents to propagate. + * Example: + * Port Y is propagated, and now asks X to propagate. + * X contains two subcomponents, B and A. A has all of its inputs (Y) + * propagated. B is reliant on 'z' to be propagated. However, 'z' is + * dependent on A being propagated. + * + * 1. + * X is trying to propagate, will initially ask its subcomponents to + * propagate. + * + * 1. + * Asking A to propagate, will make port (h) propagated. + * Asking B to propagate is invalid, because 'z' is unpropagated. + * + * 2. + * Component X will then check whether all of its input ports are + * propagated (z, i). 'i' is propagated, but 'z' is not, so the + * propagation algorithm will ask the parent component of port 'z' to + * propagate (C). + * + * 3. + * C has one input which attaches to port 'h' of A - which is now + * propagated. So C may propagate, in turn making 'z' propagated. + * + * 4. + * All inputs have now been propagated to X. It will then again ask its + * subcomponents to try to propagate. + * + * 5. + * + * y + * + X + * | +--------------+ + * | | +------+ | + * | | | | | + * | | | B | | + * | z | | | | +------+ + * | +---->----> | | | | + * | | | +------+ | | | + * | | | | +---> C +----+ + * | | | +------+ | | | | | + * | | i | | | h | | +------+ | + * +--------->----> A +--------+ | + * | | | | | | + * | | | | | | + * | | +------+ | | + * | | | | + * | +--------------+ | + * | | + * +-----------------------------------------+ + * + */ - for (const auto& sc : getSubComponents()) - sc->propagateComponent(propagationStack); + for (const auto &sc : getSubComponents()) + sc->propagateComponent(propagationStack); - // All sequential logic must have their inputs propagated before they themselves can propagate. If this is - // not the case, the function will return. Iff the circuit is correctly connected, this component will at a - // later point be visited, given that the input port which is currently not yet propagated, will become - // propagated at some point, signalling its connected components to propagate. - for (const auto& input : getPorts()) { - if (!input->isPropagated()) - return; - } - // Furthermore, we check whether any additional signals added to the sensitivity list are propagated. - for (const auto& sens : m_sensitivityList) { - if (!sens->isPropagated()) - return; - } + // All sequential logic must have their inputs propagated before they + // themselves can propagate. If this is not the case, the function will + // return. Iff the circuit is correctly connected, this component will at + // a later point be visited, given that the input port which is currently + // not yet propagated, will become propagated at some point, signalling + // its connected components to propagate. + for (const auto &input : getPorts()) { + if (!input->isPropagated()) + return; + } + // Furthermore, we check whether any additional signals added to the + // sensitivity list are propagated. + for (const auto &sens : m_sensitivityList) { + if (!sens->isPropagated()) + return; + } - for (const auto& sc : getSubComponents()) - sc->propagateComponent(propagationStack); + for (const auto &sc : getSubComponents()) + sc->propagateComponent(propagationStack); - // At this point, all input ports are assured to be propagated. In this case, it is safe to propagate - // the outputs of the component. - for (const auto& s : getPorts()) { - s->propagate(propagationStack); - } - m_propagationState = PropagationState::propagated; + // At this point, all input ports are assured to be propagated. In this + // case, it is safe to propagate the outputs of the component. + for (const auto &s : getPorts()) { + s->propagate(propagationStack); + } + m_propagationState = PropagationState::propagated; - // if any internal values have changed... - // @todo: implement granular change signal emission - if (getDesign()->signalsEnabled()) { - changed.Emit(); - } - } + // if any internal values have changed... + // @todo: implement granular change signal emission + if (getDesign()->signalsEnabled()) { + changed.Emit(); + } + } - // Signal all connected components of the current component to propagate - for (const auto& out : getPorts()) { - for (const auto& in : out->getOutputPorts()) { - // With the input port of the connected component propagated, the parent component may be propagated. - // This will succeed if all input components to the parent component has been propagated. - in->getParent()->propagateComponent(propagationStack); + // Signal all connected components of the current component to propagate + for (const auto &out : getPorts()) { + for (const auto &in : out->getOutputPorts()) { + // With the input port of the connected component propagated, the parent + // component may be propagated. This will succeed if all input + // components to the parent component has been propagated. + in->getParent()->propagateComponent(propagationStack); - // To facilitate output -> output connections, we need to trigger propagation in the output's parent - // aswell - /** - * IN IN OUT OUT - * _____________ - * | _____ | - * | | | | - * | | ->---> - * | |____| | - * |____________| - * - */ - for (const auto& inout : in->getOutputPorts()) - inout->getParent()->propagateComponent(propagationStack); - } - } + // To facilitate output -> output connections, we need to trigger + // propagation in the output's parent aswell + /** + * IN IN OUT OUT + * _____________ + * | _____ | + * | | | | + * | | ->---> + * | |____| | + * |____________| + * + */ + for (const auto &inout : in->getOutputPorts()) + inout->getParent()->propagateComponent(propagationStack); + } } + } - void initialize() { - if (m_inputPorts.size() == 0 && !hasSubcomponents() && m_sensitivityList.empty()) { - // Component has no input ports - ie. component is a constant. propagate all output ports and set component - // as propagated. - for (const auto& p : getPorts()) - p->propagateConstant(); - m_propagationState = PropagationState::propagated; - } + void initialize() { + if (m_inputPorts.size() == 0 && !hasSubcomponents() && + m_sensitivityList.empty()) { + // Component has no input ports - ie. component is a constant. propagate + // all output ports and set component as propagated. + for (const auto &p : getPorts()) + p->propagateConstant(); + m_propagationState = PropagationState::propagated; } + } - virtual void verifyComponent() const { - for (const auto& ip : getPorts()) { - if (!ip->isConnected()) { - throw std::runtime_error("Component: '" + getName() + "' has unconnected input '" + ip->getName()); - } - } - for (const auto& op : getPorts()) { - if (!op->isConnected()) { - throw std::runtime_error("Component: '" + getName() + "' has unconnected output '" + op->getName()); - } - } + virtual void verifyComponent() const { + for (const auto &ip : getPorts()) { + if (!ip->isConnected()) { + throw std::runtime_error("Component: '" + getName() + + "' has unconnected input '" + ip->getName()); + } + } + for (const auto &op : getPorts()) { + if (!op->isConnected()) { + throw std::runtime_error("Component: '" + getName() + + "' has unconnected output '" + op->getName()); + } } + } protected: - template - Port& createPort(const std::string& name, std::set, PortBaseCompT>& container, - vsrtl::SimPort::PortType type) { - verifyIsUniquePortName(name); - Port* port; - if constexpr (std::is_void::value) { - port = static_cast*>((*container.emplace(std::make_unique>(name, this, type)).first).get()); - } else { - port = static_cast*>( - (*container.emplace(std::make_unique>(name, this, type)).first).get()); - } - return *port; + template + Port & + createPort(const std::string &name, + std::set, PortBaseCompT> &container, + vsrtl::SimPort::PortType type) { + verifyIsUniquePortName(name); + Port *port; + if constexpr (std::is_void::value) { + port = static_cast *>( + (*container.emplace(std::make_unique>(name, this, type)) + .first) + .get()); + } else { + port = static_cast *>( + (*container + .emplace(std::make_unique>(name, this, type)) + .first) + .get()); } + return *port; + } - template - std::vector*> createPorts(const std::string& name, - std::set, PortBaseCompT>& container, - vsrtl::SimPort::PortType type, unsigned int n) { - std::vector*> ports; - Port* port; - for (unsigned int i = 0; i < n; i++) { - std::string i_name = name + "_" + std::to_string(i); - verifyIsUniquePortName(i_name); - port = static_cast*>( - (*container.emplace(std::make_unique>(i_name.c_str(), this, type)).first).get()); - ports.push_back(port); - } - return ports; + template + std::vector *> + createPorts(const std::string &name, + std::set, PortBaseCompT> &container, + vsrtl::SimPort::PortType type, unsigned int n) { + std::vector *> ports; + Port *port; + for (unsigned int i = 0; i < n; i++) { + std::string i_name = name + "_" + std::to_string(i); + verifyIsUniquePortName(i_name); + port = static_cast *>( + (*container + .emplace(std::make_unique>(i_name.c_str(), this, type)) + .first) + .get()); + ports.push_back(port); } + return ports; + } - std::vector m_sensitivityList; - PropagationState m_propagationState = PropagationState::unpropagated; + std::vector m_sensitivityList; + PropagationState m_propagationState = PropagationState::unpropagated; }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_COMPONENT_H +#endif // VSRTL_COMPONENT_H diff --git a/core/vsrtl_constant.h b/core/vsrtl_constant.h index 11573a8..a70dfa4 100644 --- a/core/vsrtl_constant.h +++ b/core/vsrtl_constant.h @@ -12,11 +12,13 @@ namespace core { namespace { constexpr bool valueFitsInBitWidth(unsigned int width, int value) { - const int v = value < 0 ? -value : value; - unsigned v_width = ceillog2(v) + ((bitcount(value) == 1) && (value != 0) && (value != 1) ? 1 : 0); - return v_width <= width; + const int v = value < 0 ? -value : value; + unsigned v_width = + ceillog2(v) + + ((bitcount(value) == 1) && (value != 0) && (value != 1) ? 1 : 0); + return v_width <= width; } -} // namespace +} // namespace /** * @param width Must be able to contain the signed bitfield of value @@ -25,39 +27,42 @@ constexpr bool valueFitsInBitWidth(unsigned int width, int value) { template class Constant : public Component { public: - SetGraphicsType(Constant); - Constant(const std::string& name, SimComponent* parent, VSRTL_VT_U value = 0) : Component(name, parent) { - m_value = value; - if (!valueFitsInBitWidth(W, m_value)) { - throw std::runtime_error("Value does not fit inside provided bit-width"); - } - - out << ([=] { return m_value; }); + SetGraphicsType(Constant); + Constant(const std::string &name, SimComponent *parent, VSRTL_VT_U value = 0) + : Component(name, parent) { + m_value = value; + if (!valueFitsInBitWidth(W, m_value)) { + throw std::runtime_error("Value does not fit inside provided bit-width"); } - OUTPUTPORT(out, W); + out << ([=] { return m_value; }); + } + + OUTPUTPORT(out, W); private: - VSRTL_VT_U m_value; + VSRTL_VT_U m_value; }; template -void operator>>(VSRTL_VT_S c, Port& toThis) { - // A constant should be created as a child of the shared parent between the component to be connected and the newly - // created constant - auto* parent = toThis.getParent()->template getParent(); - auto* constant = parent->template create_component>( - "constant id" + std::to_string(parent->reserveConstantId()) + "v" + std::to_string(c), c); - constant->out >> toThis; +void operator>>(VSRTL_VT_S c, Port &toThis) { + // A constant should be created as a child of the shared parent between the + // component to be connected and the newly created constant + auto *parent = toThis.getParent()->template getParent(); + auto *constant = parent->template create_component>( + "constant id" + std::to_string(parent->reserveConstantId()) + "v" + + std::to_string(c), + c); + constant->out >> toThis; } template -void operator>>(VSRTL_VT_U c, std::vector*> toThis) { - for (auto& p : toThis) - c >> *p; +void operator>>(VSRTL_VT_U c, std::vector *> toThis) { + for (auto &p : toThis) + c >> *p; } -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // CONSTANT_H +#endif // CONSTANT_H diff --git a/core/vsrtl_core.h b/core/vsrtl_core.h index 275546d..5fe9468 100644 --- a/core/vsrtl_core.h +++ b/core/vsrtl_core.h @@ -12,4 +12,4 @@ #include "vsrtl_multiplexer.h" #include "vsrtl_register.h" -#endif // VSRTL_CORE_H +#endif // VSRTL_CORE_H diff --git a/core/vsrtl_decollator.h b/core/vsrtl_decollator.h index e1b8323..d2f4d86 100644 --- a/core/vsrtl_decollator.h +++ b/core/vsrtl_decollator.h @@ -18,17 +18,18 @@ namespace core { template class Decollator : public Component { public: - Decollator(const std::string& name, SimComponent* parent) : Component(name, parent) { - for (int i = 0; i < W; i++) { - *out[i] << [=] { return (VT_U(in) >> i) & 0b1; }; - } + Decollator(const std::string &name, SimComponent *parent) + : Component(name, parent) { + for (int i = 0; i < W; i++) { + *out[i] << [=] { return (VT_U(in) >> i) & 0b1; }; } + } - OUTPUTPORTS(out, 1, W); - INPUTPORT(in, W); + OUTPUTPORTS(out, 1, W); + INPUTPORT(in, W); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_DECOLLATOR_H +#endif // VSRTL_DECOLLATOR_H diff --git a/core/vsrtl_design.h b/core/vsrtl_design.h index 2dc4358..04b3219 100644 --- a/core/vsrtl_design.h +++ b/core/vsrtl_design.h @@ -15,9 +15,12 @@ namespace vsrtl { namespace core { // An ADDRESSSPACE instance defines a distinct address space. Multiple memory -// components may be linked to the same address space to provide separate access ports to a shared address space. -#define ADDRESSSPACE(name) AddressSpace* name = this->createMemory() -#define ADDRESSSPACEMM(name) AddressSpaceMM* name = this->createMemory() +// components may be linked to the same address space to provide separate access +// ports to a shared address space. +#define ADDRESSSPACE(name) \ + AddressSpace *name = this->createMemory() +#define ADDRESSSPACEMM(name) \ + AddressSpaceMM *name = this->createMemory() /** * @brief The Design class @@ -25,209 +28,221 @@ namespace core { */ class Design : public SimDesign { public: - Design(const std::string& name) : SimDesign(name, nullptr) {} - - /** - * @brief clock - * Simulates clocking the circuit. Registers are clocked and the propagation algorithm is run - * @pre A call to propagate() must be done, to set the initial state of the circuit - */ - void clock() override { - if (!isVerifiedAndInitialized()) { - throw std::runtime_error("Design was not verified and initialized before clocking."); - } - - // Save register values (to correctly clock register -> register connections) - for (const auto& reg : m_clockedComponents) { - reg->save(); - } - - ClockedComponent::pushReversibleCycle(); - m_cycleCount++; - propagateDesign(); - SimDesign::clock(); + Design(const std::string &name) : SimDesign(name, nullptr) {} + + /** + * @brief clock + * Simulates clocking the circuit. Registers are clocked and the propagation + * algorithm is run + * @pre A call to propagate() must be done, to set the initial state of the + * circuit + */ + void clock() override { + if (!isVerifiedAndInitialized()) { + throw std::runtime_error( + "Design was not verified and initialized before clocking."); } - void reverse() override { - if (canReverse()) { - if (!isVerifiedAndInitialized()) { - throw std::runtime_error("Design was not verified and initialized before reversing."); - } - // Clock registers - for (const auto& reg : m_clockedComponents) { - reg->reverse(); - } - ClockedComponent::popReversibleCycle(); - m_cycleCount--; - propagateDesign(); - SimDesign::reverse(); - } + // Save register values (to correctly clock register -> register + // connections) + for (const auto ® : m_clockedComponents) { + reg->save(); } - void propagate() override { propagateDesign(); } - - /** - * @brief reset - * Resets the circuit, setting all registers to 0 and propagates the circuit. Constants might have an affect on the - * circuit in terms of not all component values being 0. - */ - void reset() override { - // Reset all memories, clearing the sparse arrays and rewriting any initialization data - for (const auto& memory : m_memories) { - memory->reset(); - } - - // reset all registers - // propagate everything combinational - for (const auto& reg : m_clockedComponents) - reg->reset(); - propagateDesign(); - ClockedComponent::resetReverseStackCount(); - m_cycleCount = 0; - SimDesign::reset(); + ClockedComponent::pushReversibleCycle(); + m_cycleCount++; + propagateDesign(); + SimDesign::clock(); + } + + void reverse() override { + if (canReverse()) { + if (!isVerifiedAndInitialized()) { + throw std::runtime_error( + "Design was not verified and initialized before reversing."); + } + // Clock registers + for (const auto ® : m_clockedComponents) { + reg->reverse(); + } + ClockedComponent::popReversibleCycle(); + m_cycleCount--; + propagateDesign(); + SimDesign::reverse(); } - - bool canReverse() const override { return ClockedComponent::canReverse(); } - /** - * @brief setReverseStackSize - * Sets the maximum number of reversible cycles to @param size and updates all clocked components to reflect the new - * reverse stack size. - */ - void setReverseStackSize(unsigned size) { - ClockedComponent::setReverseStackSize(size); - for (const auto& c : m_clockedComponents) { - c->reverseStackSizeChanged(); - } + } + + void propagate() override { propagateDesign(); } + + /** + * @brief reset + * Resets the circuit, setting all registers to 0 and propagates the circuit. + * Constants might have an affect on the circuit in terms of not all component + * values being 0. + */ + void reset() override { + // Reset all memories, clearing the sparse arrays and rewriting any + // initialization data + for (const auto &memory : m_memories) { + memory->reset(); } - void createPropagationStack() { - // The circuit is traversed to find the sequence of which ports may be propagated, such that all input - // dependencies for each component are met when a port is propagated. With this, propagateDesign() may - // sequentially ierate through the propagation stack to propagate the value of each port - for (const auto& reg : m_clockedComponents) - reg->propagateComponent(m_propagationStack); + // reset all registers + // propagate everything combinational + for (const auto ® : m_clockedComponents) + reg->reset(); + propagateDesign(); + ClockedComponent::resetReverseStackCount(); + m_cycleCount = 0; + SimDesign::reset(); + } + + bool canReverse() const override { return ClockedComponent::canReverse(); } + /** + * @brief setReverseStackSize + * Sets the maximum number of reversible cycles to @param size and updates all + * clocked components to reflect the new reverse stack size. + */ + void setReverseStackSize(unsigned size) { + ClockedComponent::setReverseStackSize(size); + for (const auto &c : m_clockedComponents) { + c->reverseStackSizeChanged(); } - - void propagateDesign() { - for (const auto& p : m_propagationStack) - p->setPortValue(); + } + + void createPropagationStack() { + // The circuit is traversed to find the sequence of which ports may be + // propagated, such that all input dependencies for each component are met + // when a port is propagated. With this, propagateDesign() may sequentially + // ierate through the propagation stack to propagate the value of each port + for (const auto ® : m_clockedComponents) + reg->propagateComponent(m_propagationStack); + } + + void propagateDesign() { + for (const auto &p : m_propagationStack) + p->setPortValue(); + } + + void setSynchronousValue(SimSynchronous *c, VSRTL_VT_U addr, + VSRTL_VT_U value) override { + c->forceValue(addr, value); + // Given the new output value of the register, the circuit must be + // repropagated + propagateDesign(); + } + + /** + * @brief verifyAndInitialize + * Calls verifyDesign() to ensure that all the required inputs for each + * initialized object have been set, and propagates the circuit to set the + * initial state. + */ + void verifyAndInitialize() override { + if (isVerifiedAndInitialized()) + return; + + createComponentGraph(); + + for (const auto &c : m_componentGraph) { + auto *comp = c.first->cast(); + if (!comp) { + if (c.first->cast()) + continue; + else + assert(false && "Trying to verify unknown component"); + } + // Verify that all components has no undefined input signals + comp->verifyComponent(); + // Initialize the component + comp->initialize(); } - void setSynchronousValue(SimSynchronous* c, VSRTL_VT_U addr, VSRTL_VT_U value) override { - c->forceValue(addr, value); - // Given the new output value of the register, the circuit must be repropagated - propagateDesign(); + if (detectCombinationalLoop()) { + throw std::runtime_error("Combinational loop detected in circuit"); } - /** - * @brief verifyAndInitialize - * Calls verifyDesign() to ensure that all the required inputs for each initialized object have been set, and - * propagates the circuit to set the initial state. - */ - void verifyAndInitialize() override { - if (isVerifiedAndInitialized()) - return; - - createComponentGraph(); - - for (const auto& c : m_componentGraph) { - auto* comp = c.first->cast(); - if (!comp) { - if (c.first->cast()) - continue; - else - assert(false && "Trying to verify unknown component"); - } - // Verify that all components has no undefined input signals - comp->verifyComponent(); - // Initialize the component - comp->initialize(); - } - - if (detectCombinationalLoop()) { - throw std::runtime_error("Combinational loop detected in circuit"); - } - - // Traverse the graph to create the optimal propagation sequence - createPropagationStack(); - - // Reset the circuit to propagate initial state - // @todo this should be changed, such that ports initially have a value of "X" until they are assigned - reset(); - - SimDesign::verifyAndInitialize(); + // Traverse the graph to create the optimal propagation sequence + createPropagationStack(); + + // Reset the circuit to propagate initial state + // @todo this should be changed, such that ports initially have a value of + // "X" until they are assigned + reset(); + + SimDesign::verifyAndInitialize(); + } + + bool cycleUtil(SimComponent *c, std::map &visited, + std::map &recurseStack) { + visited[c] = true; + recurseStack[c] = true; + if (!dynamic_cast(c)) // Graph is cut at registers + return false; + + for (const auto &neighbour : m_componentGraph[c]) { + if (!visited[neighbour] && cycleUtil(neighbour, visited, recurseStack)) { + return true; + } else if (recurseStack[neighbour]) { + return true; + } } - bool cycleUtil(SimComponent* c, std::map& visited, - std::map& recurseStack) { - visited[c] = true; - recurseStack[c] = true; - if (!dynamic_cast(c)) // Graph is cut at registers - return false; - - for (const auto& neighbour : m_componentGraph[c]) { - if (!visited[neighbour] && cycleUtil(neighbour, visited, recurseStack)) { - return true; - } else if (recurseStack[neighbour]) { - return true; - } - } - - recurseStack[c] = false; - return false; - } + recurseStack[c] = false; + return false; + } - bool detectCombinationalLoop() { - std::map visited; - for (const auto& cptr : m_componentGraph) { - visited[cptr.first] = false; - } - std::map recurseStack; - - for (const auto& c : m_componentGraph) { - if (visited[c.first] == false) { - return cycleUtil(c.first, visited, recurseStack); - } else { - return false; - } - } - return false; + bool detectCombinationalLoop() { + std::map visited; + for (const auto &cptr : m_componentGraph) { + visited[cptr.first] = false; } + std::map recurseStack; - template - T* createMemory() { - static_assert(std::is_base_of::value); - auto sptr = std::make_unique(); - auto* ptr = sptr.get(); - m_memories.push_back(std::move(sptr)); - return ptr; + for (const auto &c : m_componentGraph) { + if (visited[c.first] == false) { + return cycleUtil(c.first, visited, recurseStack); + } else { + return false; + } } + return false; + } + + template + T *createMemory() { + static_assert(std::is_base_of::value); + auto sptr = std::make_unique(); + auto *ptr = sptr.get(); + m_memories.push_back(std::move(sptr)); + return ptr; + } private: - void createComponentGraph() { - m_componentGraph.clear(); - getComponentGraph(m_componentGraph); - - // Gather all registers in the design - for (const auto& c : m_componentGraph) { - if (auto* cc = dynamic_cast(c.first)) { - m_clockedComponents.insert(cc); - } - if (auto* rb = dynamic_cast(c.first)) { - m_registers.insert(rb); - } - } + void createComponentGraph() { + m_componentGraph.clear(); + getComponentGraph(m_componentGraph); + + // Gather all registers in the design + for (const auto &c : m_componentGraph) { + if (auto *cc = dynamic_cast(c.first)) { + m_clockedComponents.insert(cc); + } + if (auto *rb = dynamic_cast(c.first)) { + m_registers.insert(rb); + } } + } - std::map> m_componentGraph; - std::set m_registers; - std::set m_clockedComponents; - std::vector> m_memories; + std::map> m_componentGraph; + std::set m_registers; + std::set m_clockedComponents; + std::vector> m_memories; - std::vector m_propagationStack; + std::vector m_propagationStack; }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_DESIGN_H +#endif // VSRTL_DESIGN_H diff --git a/core/vsrtl_enum.h b/core/vsrtl_enum.h index 195c4f7..fd9de72 100644 --- a/core/vsrtl_enum.h +++ b/core/vsrtl_enum.h @@ -14,7 +14,7 @@ namespace core { #define Switch(signal, enumname) switch (signal.uValue()) -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_ENUM_H +#endif // VSRTL_ENUM_H diff --git a/core/vsrtl_logicgate.h b/core/vsrtl_logicgate.h index d0b0d01..0392a53 100644 --- a/core/vsrtl_logicgate.h +++ b/core/vsrtl_logicgate.h @@ -9,79 +9,85 @@ namespace core { template class LogicGate : public Component { public: - LogicGate(const std::string& name, SimComponent* parent) : Component(name, parent) {} - OUTPUTPORT(out, W); - INPUTPORTS(in, W, nInputs); + LogicGate(const std::string &name, SimComponent *parent) + : Component(name, parent) {} + OUTPUTPORT(out, W); + INPUTPORTS(in, W, nInputs); }; template class And : public LogicGate { public: - SetGraphicsType(And) And(const std::string& name, SimComponent* parent) : LogicGate(name, parent) { - this->out << [=] { - auto v = this->in[0]->uValue(); - for (unsigned i = 1; i < this->in.size(); i++) { - v = v & this->in[i]->uValue(); - } - return v; - }; - } + SetGraphicsType(And) And(const std::string &name, SimComponent *parent) + : LogicGate(name, parent) { + this->out << [=] { + auto v = this->in[0]->uValue(); + for (unsigned i = 1; i < this->in.size(); i++) { + v = v & this->in[i]->uValue(); + } + return v; + }; + } }; template class Nand : public LogicGate { public: - SetGraphicsType(Nand) Nand(const std::string& name, SimComponent* parent) : LogicGate(name, parent) { - this->out << [=] { - auto v = this->in[0]->uValue(); - for (unsigned i = 1; i < this->in.size(); i++) { - v = v & this->in[i]->uValue(); - } - return ~v; - }; - } + SetGraphicsType(Nand) Nand(const std::string &name, SimComponent *parent) + : LogicGate(name, parent) { + this->out << [=] { + auto v = this->in[0]->uValue(); + for (unsigned i = 1; i < this->in.size(); i++) { + v = v & this->in[i]->uValue(); + } + return ~v; + }; + } }; template class Or : public LogicGate { public: - SetGraphicsType(Or); - Or(const std::string& name, SimComponent* parent) : LogicGate(name, parent) { - this->out << [=] { - auto v = this->in[0]->uValue(); - for (unsigned i = 1; i < this->in.size(); i++) { - v = v | this->in[i]->uValue(); - } - return v; - }; - } + SetGraphicsType(Or); + Or(const std::string &name, SimComponent *parent) + : LogicGate(name, parent) { + this->out << [=] { + auto v = this->in[0]->uValue(); + for (unsigned i = 1; i < this->in.size(); i++) { + v = v | this->in[i]->uValue(); + } + return v; + }; + } }; template class Xor : public LogicGate { public: - SetGraphicsType(Xor); - Xor(const std::string& name, SimComponent* parent) : LogicGate(name, parent) { - this->out << [=] { - auto v = this->in[0]->uValue(); - for (unsigned i = 1; i < this->in.size(); i++) { - v = v ^ this->in[i]->uValue(); - } - return v; - }; - } + SetGraphicsType(Xor); + Xor(const std::string &name, SimComponent *parent) + : LogicGate(name, parent) { + this->out << [=] { + auto v = this->in[0]->uValue(); + for (unsigned i = 1; i < this->in.size(); i++) { + v = v ^ this->in[i]->uValue(); + } + return v; + }; + } }; template class Not : public LogicGate { public: - SetGraphicsType(Not); - Not(const std::string& name, SimComponent* parent) : LogicGate(name, parent) { - this->out << [=] { return ~this->in[0]->uValue(); }; - } + SetGraphicsType(Not); + Not(const std::string &name, SimComponent *parent) + : LogicGate(name, parent) { + this->out << [=] { return ~this->in[0]->uValue(); }; + } }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_LOGICGATE_H +#endif // VSRTL_LOGICGATE_H diff --git a/core/vsrtl_memory.h b/core/vsrtl_memory.h index f6d5d35..c36e2e9 100644 --- a/core/vsrtl_memory.h +++ b/core/vsrtl_memory.h @@ -14,189 +14,214 @@ namespace vsrtl { namespace core { -// A memory eviction contains information about data that was overwritten in a given cycle. +// A memory eviction contains information about data that was overwritten in a +// given cycle. struct MemoryEviction { - long long cycle; - VSRTL_VT_U addr; - VSRTL_VT_U data; - VSRTL_VT_U width; + long long cycle; + VSRTL_VT_U addr; + VSRTL_VT_U data; + VSRTL_VT_U width; }; template class BaseMemory { public: - BaseMemory() {} + BaseMemory() {} - void setMemory(AddressSpace* mem) { m_memory = mem; } - const AddressSpace* memory() const { return m_memory; } - virtual AddressSpace::RegionType accessRegion() const = 0; + void setMemory(AddressSpace *mem) { m_memory = mem; } + const AddressSpace *memory() const { return m_memory; } + virtual AddressSpace::RegionType accessRegion() const = 0; - VSRTL_VT_U read(VSRTL_VT_U address, int size, unsigned wordShift) { - return m_memory->readMem(byteIndexed ? address : address << wordShift, size); - } + VSRTL_VT_U read(VSRTL_VT_U address, int size, unsigned wordShift) { + return m_memory->readMem(byteIndexed ? address : address << wordShift, + size); + } - void write(VSRTL_VT_U address, VSRTL_VT_U value, int size, unsigned wordShift) { - m_memory->writeMem(byteIndexed ? address : address << wordShift, value, size); - } + void write(VSRTL_VT_U address, VSRTL_VT_U value, int size, + unsigned wordShift) { + m_memory->writeMem(byteIndexed ? address : address << wordShift, value, + size); + } - // Width-independent accessors to memory in- and output signals. - virtual VSRTL_VT_U addressSig() const = 0; - virtual VSRTL_VT_U wrEnSig() const = 0; - virtual VSRTL_VT_U opSig() const { return 0; }; + // Width-independent accessors to memory in- and output signals. + virtual VSRTL_VT_U addressSig() const = 0; + virtual VSRTL_VT_U wrEnSig() const = 0; + virtual VSRTL_VT_U opSig() const { return 0; }; protected: - AddressSpace* m_memory = nullptr; + AddressSpace *m_memory = nullptr; }; -template +template class WrMemory : public ClockedComponent, public BaseMemory { public: - SetGraphicsType(Component); - WrMemory(const std::string& name, SimComponent* parent) : ClockedComponent(name, parent) {} - void reset() override { m_reverseStack.clear(); } - AddressSpace::RegionType accessRegion() const override { return this->memory()->regionType(addr.uValue()); } - - void save() override { - constexpr unsigned wordshift = ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT); - const bool writeEnable = static_cast(wr_en); - if (writeEnable) { - const VSRTL_VT_U addr_v = addr.uValue(); - const VSRTL_VT_U data_in_v = data_in.uValue(); - const VSRTL_VT_U data_out_v = this->read(addr_v, dataWidth / CHAR_BIT, wordshift); - const VSRTL_VT_U wr_width_v = wr_width.uValue(); - // save() is called prior to cycle incrementation; WrMemory::reverse() relies on an eviction being listed - // for the cycle which the 'reverse' call happened in.´ - const auto cycle = getDesign()->getCycleCount() + 1; - auto ev = MemoryEviction({cycle, addr_v, data_out_v, wr_width_v}); - saveToStack(ev); - this->write(addr_v, data_in_v, wr_width_v, wordshift); - } + SetGraphicsType(Component); + WrMemory(const std::string &name, SimComponent *parent) + : ClockedComponent(name, parent) {} + void reset() override { m_reverseStack.clear(); } + AddressSpace::RegionType accessRegion() const override { + return this->memory()->regionType(addr.uValue()); + } + + void save() override { + constexpr unsigned wordshift = + ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT); + const bool writeEnable = static_cast(wr_en); + if (writeEnable) { + const VSRTL_VT_U addr_v = addr.uValue(); + const VSRTL_VT_U data_in_v = data_in.uValue(); + const VSRTL_VT_U data_out_v = + this->read(addr_v, dataWidth / CHAR_BIT, wordshift); + const VSRTL_VT_U wr_width_v = wr_width.uValue(); + // save() is called prior to cycle incrementation; WrMemory::reverse() + // relies on an eviction being listed for the cycle which the 'reverse' + // call happened in.´ + const auto cycle = getDesign()->getCycleCount() + 1; + auto ev = MemoryEviction({cycle, addr_v, data_out_v, wr_width_v}); + saveToStack(ev); + this->write(addr_v, data_in_v, wr_width_v, wordshift); } - - void reverse() override { - if (m_reverseStack.size() > 0) { - auto lastEviction = m_reverseStack.front(); - auto cycle = getDesign()->getCycleCount(); - if (lastEviction.cycle == cycle) { - this->write(lastEviction.addr, lastEviction.data, lastEviction.width, - ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); - m_reverseStack.pop_front(); - } - } + } + + void reverse() override { + if (m_reverseStack.size() > 0) { + auto lastEviction = m_reverseStack.front(); + auto cycle = getDesign()->getCycleCount(); + if (lastEviction.cycle == cycle) { + this->write(lastEviction.addr, lastEviction.data, lastEviction.width, + ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); + m_reverseStack.pop_front(); + } } + } - virtual VSRTL_VT_U addressSig() const override { return addr.uValue(); }; - virtual VSRTL_VT_U wrEnSig() const override { return wr_en.uValue(); }; + virtual VSRTL_VT_U addressSig() const override { return addr.uValue(); }; + virtual VSRTL_VT_U wrEnSig() const override { return wr_en.uValue(); }; - void forceValue(VSRTL_VT_U address, VSRTL_VT_U value) override { - this->write(address, value, dataWidth / CHAR_BIT, ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); - } + void forceValue(VSRTL_VT_U address, VSRTL_VT_U value) override { + this->write(address, value, dataWidth / CHAR_BIT, + ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); + } - INPUTPORT(addr, addrWidth); - INPUTPORT(data_in, dataWidth); - INPUTPORT(wr_width, ceillog2(dataWidth / CHAR_BIT + 1)); // # bytes - INPUTPORT(wr_en, 1); + INPUTPORT(addr, addrWidth); + INPUTPORT(data_in, dataWidth); + INPUTPORT(wr_width, ceillog2(dataWidth / CHAR_BIT + 1)); // # bytes + INPUTPORT(wr_en, 1); protected: - void reverseStackSizeChanged() override { - if (reverseStackSize() < m_reverseStack.size()) { - m_reverseStack.resize(m_reverseStack.size()); - } + void reverseStackSizeChanged() override { + if (reverseStackSize() < m_reverseStack.size()) { + m_reverseStack.resize(m_reverseStack.size()); } + } private: - void saveToStack(MemoryEviction v) { - m_reverseStack.push_front(v); - if (m_reverseStack.size() > reverseStackSize()) { - m_reverseStack.pop_back(); - } + void saveToStack(MemoryEviction v) { + m_reverseStack.push_front(v); + if (m_reverseStack.size() > reverseStackSize()) { + m_reverseStack.pop_back(); } + } - std::deque m_reverseStack; + std::deque m_reverseStack; }; -template +template class MemorySyncRd : public WrMemory { public: - MemorySyncRd(const std::string& name, SimComponent* parent) - : WrMemory(name, parent) { - data_out << [=] { - return this->read(this->addr.uValue(), dataWidth / CHAR_BIT, - ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); - }; - } + MemorySyncRd(const std::string &name, SimComponent *parent) + : WrMemory(name, parent) { + data_out << [=] { + return this->read( + this->addr.uValue(), dataWidth / CHAR_BIT, + ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); + }; + } - OUTPUTPORT(data_out, dataWidth); + OUTPUTPORT(data_out, dataWidth); private: - std::unordered_map m_memory; + std::unordered_map m_memory; }; -template +template class RdMemory : public Component, public BaseMemory { - template - friend class Memory; + template + friend class Memory; public: - SetGraphicsType(ClockedComponent); - RdMemory(const std::string& name, SimComponent* parent) : Component(name, parent) { - data_out << [=] { - auto _addr = addr.uValue(); - auto val = - this->read(_addr, dataWidth / CHAR_BIT, ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); - return val; - }; - } - - AddressSpace::RegionType accessRegion() const override { return this->memory()->regionType(addr.uValue()); } - - virtual VSRTL_VT_U addressSig() const override { return addr.uValue(); }; - virtual VSRTL_VT_U wrEnSig() const override { return 0; }; - - INPUTPORT(addr, addrWidth); - OUTPUTPORT(data_out, dataWidth); + SetGraphicsType(ClockedComponent); + RdMemory(const std::string &name, SimComponent *parent) + : Component(name, parent) { + data_out << [=] { + auto _addr = addr.uValue(); + auto val = this->read( + _addr, dataWidth / CHAR_BIT, + ceillog2((byteIndexed ? addrWidth : dataWidth) / CHAR_BIT)); + return val; + }; + } + + AddressSpace::RegionType accessRegion() const override { + return this->memory()->regionType(addr.uValue()); + } + + virtual VSRTL_VT_U addressSig() const override { return addr.uValue(); }; + virtual VSRTL_VT_U wrEnSig() const override { return 0; }; + + INPUTPORT(addr, addrWidth); + OUTPUTPORT(data_out, dataWidth); }; -template +template class MemoryAsyncRd : public Component { public: - SetGraphicsType(ClockedComponent); - MemoryAsyncRd(const std::string& name, SimComponent* parent) : Component(name, parent) { - addr >> _wr_mem->addr; - wr_en >> _wr_mem->wr_en; - data_in >> _wr_mem->data_in; - wr_width >> _wr_mem->wr_width; - - addr >> _rd_mem->addr; - _rd_mem->data_out >> data_out; - } - - void setMemory(AddressSpace* mem) { - _wr_mem->setMemory(mem); - _rd_mem->setMemory(mem); - } - - AddressSpace::RegionType accessRegion() const { return this->memory()->regionType(addr.uValue()); } - - const AddressSpace* memory() const { return _wr_mem->memory(); } - - SUBCOMPONENT(_rd_mem, TYPE(RdMemory)); - SUBCOMPONENT(_wr_mem, TYPE(WrMemory)); - - INPUTPORT(addr, addrWidth); - INPUTPORT(data_in, dataWidth); - INPUTPORT(wr_en, 1); - INPUTPORT(wr_width, ceillog2(dataWidth / CHAR_BIT + 1)); // # bytes - OUTPUTPORT(data_out, dataWidth); + SetGraphicsType(ClockedComponent); + MemoryAsyncRd(const std::string &name, SimComponent *parent) + : Component(name, parent) { + addr >> _wr_mem->addr; + wr_en >> _wr_mem->wr_en; + data_in >> _wr_mem->data_in; + wr_width >> _wr_mem->wr_width; + + addr >> _rd_mem->addr; + _rd_mem->data_out >> data_out; + } + + void setMemory(AddressSpace *mem) { + _wr_mem->setMemory(mem); + _rd_mem->setMemory(mem); + } + + AddressSpace::RegionType accessRegion() const { + return this->memory()->regionType(addr.uValue()); + } + + const AddressSpace *memory() const { return _wr_mem->memory(); } + + SUBCOMPONENT(_rd_mem, TYPE(RdMemory)); + SUBCOMPONENT(_wr_mem, TYPE(WrMemory)); + + INPUTPORT(addr, addrWidth); + INPUTPORT(data_in, dataWidth); + INPUTPORT(wr_en, 1); + INPUTPORT(wr_width, ceillog2(dataWidth / CHAR_BIT + 1)); // # bytes + OUTPUTPORT(data_out, dataWidth); }; -template +template class ROM : public RdMemory { public: - ROM(const std::string& name, SimComponent* parent) : RdMemory(name, parent) {} + ROM(const std::string &name, SimComponent *parent) + : RdMemory(name, parent) {} }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // MEMORY_H +#endif // MEMORY_H diff --git a/core/vsrtl_multiplexer.h b/core/vsrtl_multiplexer.h index 09eabfe..d388e91 100644 --- a/core/vsrtl_multiplexer.h +++ b/core/vsrtl_multiplexer.h @@ -1,123 +1,128 @@ #ifndef VSRTL_MULTIPLEXER_H #define VSRTL_MULTIPLEXER_H -#include #include "vsrtl_component.h" #include "vsrtl_defines.h" #include "vsrtl_enum.h" +#include namespace vsrtl { namespace core { class MultiplexerBase : public Component { public: - SetGraphicsType(Multiplexer); - MultiplexerBase(const std::string& name, SimComponent* parent) : Component(name, parent) {} + SetGraphicsType(Multiplexer); + MultiplexerBase(const std::string &name, SimComponent *parent) + : Component(name, parent) {} - virtual std::vector getIns() = 0; - virtual PortBase* getSelect() = 0; - virtual PortBase* getOut() = 0; + virtual std::vector getIns() = 0; + virtual PortBase *getSelect() = 0; + virtual PortBase *getOut() = 0; }; template class Multiplexer : public MultiplexerBase { public: - Multiplexer(const std::string& name, SimComponent* parent) : MultiplexerBase(name, parent) { - setSpecialPort(GFX_MUX_SELECT, &select); - out << [=] { return ins.at(select.uValue())->uValue(); }; + Multiplexer(const std::string &name, SimComponent *parent) + : MultiplexerBase(name, parent) { + setSpecialPort(GFX_MUX_SELECT, &select); + out << [=] { return ins.at(select.uValue())->uValue(); }; + } + + std::vector getIns() override { + std::vector ins_base; + for (const auto &in : ins) + ins_base.push_back(in); + return ins_base; + } + + virtual Port &get(unsigned idx) { + if (idx >= ins.size()) { + throw std::runtime_error("Requested index out of multiplexer range"); } - - std::vector getIns() override { - std::vector ins_base; - for (const auto& in : ins) - ins_base.push_back(in); - return ins_base; + return *ins[idx]; + } + + /** + * @brief others + * @return a vector of all ports which has not been connected + */ + std::vector *> others() { + std::vector *> vec; + for (const auto &port : ins) { + if (!port->getInputPort()) { + vec.push_back(port); + } } + return vec; + } - virtual Port& get(unsigned idx) { - if (idx >= ins.size()) { - throw std::runtime_error("Requested index out of multiplexer range"); - } - return *ins[idx]; - } + PortBase *getSelect() override { return &select; } + PortBase *getOut() override { return &out; } - /** - * @brief others - * @return a vector of all ports which has not been connected - */ - std::vector*> others() { - std::vector*> vec; - for (const auto& port : ins) { - if (!port->getInputPort()) { - vec.push_back(port); - } - } - return vec; - } - - PortBase* getSelect() override { return &select; } - PortBase* getOut() override { return &out; } - - OUTPUTPORT(out, W); - INPUTPORT(select, ceillog2(N)); - INPUTPORTS(ins, W, N); + OUTPUTPORT(out, W); + INPUTPORT(select, ceillog2(N)); + INPUTPORTS(ins, W, N); }; /** @class EnumMultiplexer * A multiplexer which may be initialized with a VSRTL Enum. - * The select signal and number of input ports will be inferred based on the enum type. + * The select signal and number of input ports will be inferred based on the + * enum type. * */ template class EnumMultiplexer : public MultiplexerBase { public: - EnumMultiplexer(const std::string& name, SimComponent* parent) : MultiplexerBase(name, parent) { - setSpecialPort(GFX_MUX_SELECT, &select); - for (auto v : E_t::_values()) { - m_enumToPort[v] = this->ins.at(v); - } - out << [=] { return ins.at(select.uValue())->uValue(); }; + EnumMultiplexer(const std::string &name, SimComponent *parent) + : MultiplexerBase(name, parent) { + setSpecialPort(GFX_MUX_SELECT, &select); + for (auto v : E_t::_values()) { + m_enumToPort[v] = this->ins.at(v); } + out << [=] { return ins.at(select.uValue())->uValue(); }; + } - Port& get(unsigned enumIdx) { - if (m_enumToPort.count(enumIdx) != 1) { - throw std::runtime_error("Requested index out of Enum range"); - } - if (m_enumToPort[enumIdx] == nullptr) { - throw std::runtime_error("Requested enum index not associated with any port"); - } - return *m_enumToPort[enumIdx]; + Port &get(unsigned enumIdx) { + if (m_enumToPort.count(enumIdx) != 1) { + throw std::runtime_error("Requested index out of Enum range"); } - - std::vector getIns() override { - std::vector ins_base; - for (const auto& in : ins) - ins_base.push_back(in); - return ins_base; + if (m_enumToPort[enumIdx] == nullptr) { + throw std::runtime_error( + "Requested enum index not associated with any port"); } - - std::vector*> others() { - std::vector*> vec; - for (const auto& port : ins) { - if (!port->getInputPort()) { - vec.push_back(port); - } - } - return vec; + return *m_enumToPort[enumIdx]; + } + + std::vector getIns() override { + std::vector ins_base; + for (const auto &in : ins) + ins_base.push_back(in); + return ins_base; + } + + std::vector *> others() { + std::vector *> vec; + for (const auto &port : ins) { + if (!port->getInputPort()) { + vec.push_back(port); + } } + return vec; + } - PortBase* getSelect() override { return &select; } - PortBase* getOut() override { return &out; } + PortBase *getSelect() override { return &select; } + PortBase *getOut() override { return &out; } - OUTPUTPORT(out, W); - INPUTPORT_ENUM(select, E_t); - INPUTPORTS(ins, W, E_t::_size()); + OUTPUTPORT(out, W); + INPUTPORT_ENUM(select, E_t); + INPUTPORTS(ins, W, E_t::_size()); private: - std::map*> m_enumToPort; + std::map *> m_enumToPort; }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_MULTIPLEXER_H +#endif // VSRTL_MULTIPLEXER_H diff --git a/core/vsrtl_port.h b/core/vsrtl_port.h index b71ad1c..e7633c2 100644 --- a/core/vsrtl_port.h +++ b/core/vsrtl_port.h @@ -1,11 +1,11 @@ #ifndef VSRTL_SIGNAL_H #define VSRTL_SIGNAL_H -#include #include #include #include #include +#include #include #include @@ -25,129 +25,150 @@ enum class PropagationState { unpropagated, propagated, constant }; */ class PortBase : public SimPort { public: - PortBase(const std::string& name, SimComponent* parent, PortType type) : SimPort(name, parent, type) { - assert(parent != nullptr); - } - - bool isPropagated() const { return m_propagationState != PropagationState::unpropagated; } - bool isConstant() const override { return m_propagationState == PropagationState::constant; } - void resetPropagation() { - m_propagationState = - m_propagationState == PropagationState::constant ? m_propagationState : PropagationState::unpropagated; - } - - virtual void propagate(std::vector& propagationStack) = 0; - virtual void propagateConstant() = 0; - virtual void setPortValue() = 0; - virtual bool isConnected() const = 0; - - /** - * @brief stringValue - * A port may define special string formatting to be displayed in the graphical library. If so, owning components - * should set the string value function to provide such values. - */ - virtual bool isEnumPort() const override { return false; } - virtual std::string valueToEnumString() const override { throw std::runtime_error("This is not an enum port!"); } - virtual VSRTL_VT_U enumStringToValue(const char*) const override { - throw std::runtime_error("This is not an enum port!"); - } + PortBase(const std::string &name, SimComponent *parent, PortType type) + : SimPort(name, parent, type) { + assert(parent != nullptr); + } + + bool isPropagated() const { + return m_propagationState != PropagationState::unpropagated; + } + bool isConstant() const override { + return m_propagationState == PropagationState::constant; + } + void resetPropagation() { + m_propagationState = m_propagationState == PropagationState::constant + ? m_propagationState + : PropagationState::unpropagated; + } + + virtual void propagate(std::vector &propagationStack) = 0; + virtual void propagateConstant() = 0; + virtual void setPortValue() = 0; + virtual bool isConnected() const = 0; + + /** + * @brief stringValue + * A port may define special string formatting to be displayed in the + * graphical library. If so, owning components should set the string value + * function to provide such values. + */ + virtual bool isEnumPort() const override { return false; } + virtual std::string valueToEnumString() const override { + throw std::runtime_error("This is not an enum port!"); + } + virtual VSRTL_VT_U enumStringToValue(const char *) const override { + throw std::runtime_error("This is not an enum port!"); + } protected: - PropagationState m_propagationState = PropagationState::unpropagated; + PropagationState m_propagationState = PropagationState::unpropagated; }; template class Port : public PortBase { public: - Port(const std::string& name, SimComponent* parent, PortType type) : PortBase(name, parent, type) {} - bool isConnected() const override { return m_inputPort != nullptr || m_propagationFunction; } - - // Port connections are doubly linked - void operator>>(Port& toThis) { - m_outputPorts.push_back(&toThis); - if (toThis.m_inputPort != nullptr) { - throw std::runtime_error( - "Failed trying to connect port '" + getParent()->getName() + ":" + getName() + "' to port '" + - toThis.getParent()->getName() + ":" + toThis.getName() + ". Port is already connected to '" + - toThis.getInputPort()->getParent()->getName() + ":" + toThis.getInputPort()->getName()); - } - toThis.m_inputPort = this; + Port(const std::string &name, SimComponent *parent, PortType type) + : PortBase(name, parent, type) {} + bool isConnected() const override { + return m_inputPort != nullptr || m_propagationFunction; + } + + // Port connections are doubly linked + void operator>>(Port &toThis) { + m_outputPorts.push_back(&toThis); + if (toThis.m_inputPort != nullptr) { + throw std::runtime_error( + "Failed trying to connect port '" + getParent()->getName() + ":" + + getName() + "' to port '" + toThis.getParent()->getName() + ":" + + toThis.getName() + ". Port is already connected to '" + + toThis.getInputPort()->getParent()->getName() + ":" + + toThis.getInputPort()->getName()); } - - void operator>>(const std::vector*>& toThis) { - for (auto& p : toThis) - *this >> *p; + toThis.m_inputPort = this; + } + + void operator>>(const std::vector *> &toThis) { + for (auto &p : toThis) + *this >> *p; + } + + VSRTL_VT_U uValue() const override { return m_value & generateBitmask(W); } + VSRTL_VT_S sValue() const override { return signextend(m_value); } + unsigned int getWidth() const override { return W; } + + explicit operator VSRTL_VT_S() const { return signextend(m_value); } + + void setPortValue() override { + auto prePropagateValue = m_value; + if (m_propagationFunction) { + m_value = m_propagationFunction(); + } else { + m_value = getInputPort>()->uValue(); } - - VSRTL_VT_U uValue() const override { return m_value & generateBitmask(W); } - VSRTL_VT_S sValue() const override { return signextend(m_value); } - unsigned int getWidth() const override { return W; } - - explicit operator VSRTL_VT_S() const { return signextend(m_value); } - - void setPortValue() override { - auto prePropagateValue = m_value; - if (m_propagationFunction) { - m_value = m_propagationFunction(); - } else { - m_value = getInputPort>()->uValue(); - } - if (m_value != prePropagateValue) { - // Signal all watcher of this port that the port value changed - if (getDesign()->signalsEnabled()) { - changed.Emit(); - } - } + if (m_value != prePropagateValue) { + // Signal all watcher of this port that the port value changed + if (getDesign()->signalsEnabled()) { + changed.Emit(); + } } - - void propagate(std::vector& propagationStack) override { - if (m_propagationState == PropagationState::unpropagated) { - propagationStack.push_back(this); - // Propagate the value to the ports which connect to this - for (const auto& port : getOutputPorts>()) - port->propagate(propagationStack); - m_propagationState = PropagationState::propagated; - } + } + + void propagate(std::vector &propagationStack) override { + if (m_propagationState == PropagationState::unpropagated) { + propagationStack.push_back(this); + // Propagate the value to the ports which connect to this + for (const auto &port : getOutputPorts>()) + port->propagate(propagationStack); + m_propagationState = PropagationState::propagated; } - - void propagateConstant() override { - m_propagationState = PropagationState::constant; - setPortValue(); - for (const auto& port : getOutputPorts>()) - port->propagateConstant(); + } + + void propagateConstant() override { + m_propagationState = PropagationState::constant; + setPortValue(); + for (const auto &port : getOutputPorts>()) + port->propagateConstant(); + } + + void operator<<(std::function &&propagationFunction) { + if (m_propagationFunction) { + throw std::runtime_error("Propagation function reassignment prohibited"); } + m_propagationFunction = propagationFunction; + } - void operator<<(std::function&& propagationFunction) { - if (m_propagationFunction) { - throw std::runtime_error("Propagation function reassignment prohibited"); - } - m_propagationFunction = propagationFunction; - } - - // Value access operators - explicit operator VSRTL_VT_U() const { return m_value; } - explicit operator bool() const { return m_value & 0b1; } + // Value access operators + explicit operator VSRTL_VT_U() const { return m_value; } + explicit operator bool() const { return m_value & 0b1; } protected: - // Port values are initialized to 0xdeadbeef for error detection reasons. In reality (in a circuit), this would - // not be the case - the entire circuit is reset when the registers are reset (to 0), and the circuit state is - // then propagated. - VSRTL_VT_U m_value = 0xdeadbeef; + // Port values are initialized to 0xdeadbeef for error detection reasons. In + // reality (in a circuit), this would not be the case - the entire circuit is + // reset when the registers are reset (to 0), and the circuit state is then + // propagated. + VSRTL_VT_U m_value = 0xdeadbeef; - std::function m_propagationFunction = {}; + std::function m_propagationFunction = {}; }; template class EnumPort : public Port { public: - EnumPort(const std::string& name, Component* parent, vsrtl::SimPort::PortType type) : Port(name, parent, type) {} - - bool isEnumPort() const override { return true; } - std::string valueToEnumString() const override { return E_t::_from_integral(this->uValue())._to_string(); } - VSRTL_VT_U enumStringToValue(const char* str) const override { return E_t::_from_string(str); } + EnumPort(const std::string &name, Component *parent, + vsrtl::SimPort::PortType type) + : Port(name, parent, type) {} + + bool isEnumPort() const override { return true; } + std::string valueToEnumString() const override { + return E_t::_from_integral(this->uValue())._to_string(); + } + VSRTL_VT_U enumStringToValue(const char *str) const override { + return E_t::_from_string(str); + } }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_SIGNAL_H +#endif // VSRTL_SIGNAL_H diff --git a/core/vsrtl_register.h b/core/vsrtl_register.h index 7cef6ac..0315eac 100644 --- a/core/vsrtl_register.h +++ b/core/vsrtl_register.h @@ -10,250 +10,264 @@ #include /** Registered input - * Constructs an inputport, outputport and register component with a given name @p input and @p width. - * Useful for creatng shift registers or pipeline stage separating register banks. + * Constructs an inputport, outputport and register component with a given name + * @p input and @p width. Useful for creatng shift registers or pipeline stage + * separating register banks. */ -#define REGISTERED_INPUT(input, width) \ - INPUTPORT(input##_in, width); \ - SUBCOMPONENT(input##_reg, Register); \ - OUTPUTPORT(input##_out, width) +#define REGISTERED_INPUT(input, width) \ + INPUTPORT(input##_in, width); \ + SUBCOMPONENT(input##_reg, Register); \ + OUTPUTPORT(input##_out, width) -#define CONNECT_REGISTERED_INPUT(input) \ - input##_in >> input##_reg->in; \ - input##_reg->out >> input##_out +#define CONNECT_REGISTERED_INPUT(input) \ + input##_in >> input##_reg->in; \ + input##_reg->out >> input##_out /** Registeren Clear/Enable input * Similar to above, but instantiates clear/enable registers. */ -#define REGISTERED_CLEN_INPUT(input, width) \ - INPUTPORT(input##_in, width); \ - SUBCOMPONENT(input##_reg, RegisterClEn); \ - OUTPUTPORT(input##_out, width) +#define REGISTERED_CLEN_INPUT(input, width) \ + INPUTPORT(input##_in, width); \ + SUBCOMPONENT(input##_reg, RegisterClEn); \ + OUTPUTPORT(input##_out, width) -#define CONNECT_REGISTERED_CLEN_INPUT(input, cl, en) \ - input##_in >> input##_reg->in; \ - cl >> input##_reg->clear; \ - en >> input##_reg->enable; \ - input##_reg->out >> input##_out +#define CONNECT_REGISTERED_CLEN_INPUT(input, cl, en) \ + input##_in >> input##_reg->in; \ + cl >> input##_reg->clear; \ + en >> input##_reg->enable; \ + input##_reg->out >> input##_out namespace vsrtl { namespace core { class ClockedComponent : public Component, public SimSynchronous { - SetGraphicsType(ClockedComponent); + SetGraphicsType(ClockedComponent); public: - ClockedComponent(const std::string& name, SimComponent* parent) : Component(name, parent), SimSynchronous(this) {} - virtual void save() = 0; - - /** - * @brief Reverse stack management - * The following functions manages a static count of the current number of reversible cycles in the design. It is - * expected that modifications to the reverse stack counters are performed solely by the top-level managing design. - */ - static void setReverseStackSize(unsigned size) { - getReverseStack().max = size; - - if (getReverseStack().current > size) { - getReverseStack().current = size; - } + ClockedComponent(const std::string &name, SimComponent *parent) + : Component(name, parent), SimSynchronous(this) {} + virtual void save() = 0; + + /** + * @brief Reverse stack management + * The following functions manages a static count of the current number of + * reversible cycles in the design. It is expected that modifications to the + * reverse stack counters are performed solely by the top-level managing + * design. + */ + static void setReverseStackSize(unsigned size) { + getReverseStack().max = size; + + if (getReverseStack().current > size) { + getReverseStack().current = size; } - static unsigned reverseStackSize() { return getReverseStack().max; } - static unsigned reversibleCycles() { return getReverseStack().current; } - static void resetReverseStackCount() { getReverseStack().current = 0; } - static void pushReversibleCycle() { - // Increment reverse-stack count if possible - if (reversibleCycles() < reverseStackSize()) { - getReverseStack().current++; - } + } + static unsigned reverseStackSize() { return getReverseStack().max; } + static unsigned reversibleCycles() { return getReverseStack().current; } + static void resetReverseStackCount() { getReverseStack().current = 0; } + static void pushReversibleCycle() { + // Increment reverse-stack count if possible + if (reversibleCycles() < reverseStackSize()) { + getReverseStack().current++; } - static void popReversibleCycle() { - if (!canReverse()) { - throw std::runtime_error("Tried to reverse the design with empty reverse stacks"); - } - getReverseStack().current--; + } + static void popReversibleCycle() { + if (!canReverse()) { + throw std::runtime_error( + "Tried to reverse the design with empty reverse stacks"); } - static bool canReverse() { return getReverseStack().current != 0; } + getReverseStack().current--; + } + static bool canReverse() { return getReverseStack().current != 0; } - /** - * @brief reverseStackSizeChanged - * Whenever the reverse stack changes, all synchronous elements may check whether they need to delete cycles within - * their current reverse stack. - */ - virtual void reverseStackSizeChanged() = 0; + /** + * @brief reverseStackSizeChanged + * Whenever the reverse stack changes, all synchronous elements may check + * whether they need to delete cycles within their current reverse stack. + */ + virtual void reverseStackSizeChanged() = 0; private: - struct ReverseStackCounter { - unsigned max = 100; // Maximum number of cycles on clocked components reverse stacks - unsigned current = 0; // Current number of reversible cycles - }; - static ReverseStackCounter& getReverseStack() { - static ReverseStackCounter reverseStackCounter; - return reverseStackCounter; - } + struct ReverseStackCounter { + unsigned max = + 100; // Maximum number of cycles on clocked components reverse stacks + unsigned current = 0; // Current number of reversible cycles + }; + static ReverseStackCounter &getReverseStack() { + static ReverseStackCounter reverseStackCounter; + return reverseStackCounter; + } }; class RegisterBase : public ClockedComponent { public: - RegisterBase(const std::string& name, SimComponent* parent) : ClockedComponent(name, parent) {} + RegisterBase(const std::string &name, SimComponent *parent) + : ClockedComponent(name, parent) {} - virtual PortBase* getIn() = 0; - virtual PortBase* getOut() = 0; + virtual PortBase *getIn() = 0; + virtual PortBase *getOut() = 0; }; template class Register : public RegisterBase { public: - SetGraphicsType(Register); - - Register(const std::string& name, SimComponent* parent) : RegisterBase(name, parent) { - // Calling out.propagate() will clock the register the register - out << ([=] { return m_savedValue; }); - } - - void setInitValue(VSRTL_VT_U value) { m_initvalue = value; } - - void reset() override { - m_savedValue = m_initvalue; - m_reverseStack.clear(); + SetGraphicsType(Register); + + Register(const std::string &name, SimComponent *parent) + : RegisterBase(name, parent) { + // Calling out.propagate() will clock the register the register + out << ([=] { return m_savedValue; }); + } + + void setInitValue(VSRTL_VT_U value) { m_initvalue = value; } + + void reset() override { + m_savedValue = m_initvalue; + m_reverseStack.clear(); + } + + void save() override { + saveToStack(); + m_savedValue = in.uValue(); + } + + void forceValue(VSRTL_VT_U /* addr */, VSRTL_VT_U value) override { + // Sign-extension with unsigned type forces width truncation to m_width bits + m_savedValue = signextend(value); + // Forced values are a modification of the current state and thus not pushed + // onto the reverse stack + } + + void reverse() override { + if (m_reverseStack.size() > 0) { + m_savedValue = m_reverseStack.front(); + m_reverseStack.pop_front(); } + } - void save() override { - saveToStack(); - m_savedValue = in.uValue(); - } + PortBase *getIn() override { return ∈ } + PortBase *getOut() override { return &out; } - void forceValue(VSRTL_VT_U /* addr */, VSRTL_VT_U value) override { - // Sign-extension with unsigned type forces width truncation to m_width bits - m_savedValue = signextend(value); - // Forced values are a modification of the current state and thus not pushed onto the reverse stack - } + INPUTPORT(in, W); + OUTPUTPORT(out, W); - void reverse() override { - if (m_reverseStack.size() > 0) { - m_savedValue = m_reverseStack.front(); - m_reverseStack.pop_front(); - } - } - - PortBase* getIn() override { return ∈ } - PortBase* getOut() override { return &out; } - - INPUTPORT(in, W); - OUTPUTPORT(out, W); - - void reverseStackSizeChanged() override { - if (reverseStackSize() < m_reverseStack.size()) { - m_reverseStack.resize(m_reverseStack.size()); - } + void reverseStackSizeChanged() override { + if (reverseStackSize() < m_reverseStack.size()) { + m_reverseStack.resize(m_reverseStack.size()); } + } protected: - void saveToStack() { - m_reverseStack.push_front(m_savedValue); - if (m_reverseStack.size() > reverseStackSize()) { - m_reverseStack.pop_back(); - } + void saveToStack() { + m_reverseStack.push_front(m_savedValue); + if (m_reverseStack.size() > reverseStackSize()) { + m_reverseStack.pop_back(); } + } - VSRTL_VT_U m_savedValue = 0; - VSRTL_VT_U m_initvalue = 0; - std::deque m_reverseStack; + VSRTL_VT_U m_savedValue = 0; + VSRTL_VT_U m_initvalue = 0; + std::deque m_reverseStack; }; // Synchronous clear/enable register template class RegisterClEn : public Register { public: - RegisterClEn(const std::string& name, SimComponent* parent) : Register(name, parent) {} - - void save() override { - this->saveToStack(); - if (enable.uValue()) { - if (clear.uValue()) { - this->m_savedValue = 0; - } else { - this->m_savedValue = this->in.uValue(); - } - } + RegisterClEn(const std::string &name, SimComponent *parent) + : Register(name, parent) {} + + void save() override { + this->saveToStack(); + if (enable.uValue()) { + if (clear.uValue()) { + this->m_savedValue = 0; + } else { + this->m_savedValue = this->in.uValue(); + } } + } - INPUTPORT(enable, 1); - INPUTPORT(clear, 1); + INPUTPORT(enable, 1); + INPUTPORT(clear, 1); }; template class ShiftRegister : public RegisterBase { public: - SetGraphicsType(Register); + SetGraphicsType(Register); - ShiftRegister(const std::string& name, SimComponent* parent) : RegisterBase(name, parent) { - stages.setTooltip("Number of shift register stages"); - stages.setOptions({1, 100}); - stages.changed.Connect(this, &ShiftRegister::stagesChanged); - stagesChanged(); // Default initialize + ShiftRegister(const std::string &name, SimComponent *parent) + : RegisterBase(name, parent) { + stages.setTooltip("Number of shift register stages"); + stages.setOptions({1, 100}); + stages.changed.Connect(this, &ShiftRegister::stagesChanged); + stagesChanged(); // Default initialize - // Calling out.propagate() will clock the register the register. - // Output the value for the last register in the shift register array - out << ([=] { return m_savedValues.at(stages.getValue() - 1); }); - } + // Calling out.propagate() will clock the register the register. + // Output the value for the last register in the shift register array + out << ([=] { return m_savedValues.at(stages.getValue() - 1); }); + } - void setInitValue(VSRTL_VT_U value) { m_initvalue = value; } + void setInitValue(VSRTL_VT_U value) { m_initvalue = value; } - void reset() override { - for (unsigned i = 0; i < m_savedValues.size(); i++) { - m_savedValues[i] = m_initvalue; - } - m_reverseStack.clear(); + void reset() override { + for (unsigned i = 0; i < m_savedValues.size(); i++) { + m_savedValues[i] = m_initvalue; } + m_reverseStack.clear(); + } - void save() override { - m_reverseStack.push_front(m_savedValues.at(stages.getValue() - 1)); - if (m_reverseStack.size() > reverseStackSize()) { - m_reverseStack.pop_back(); - } - // Rotate to the right and store new value as first register - std::rotate(m_savedValues.rbegin(), m_savedValues.rbegin() + 1, m_savedValues.rend()); - m_savedValues.at(0) = in.uValue(); + void save() override { + m_reverseStack.push_front(m_savedValues.at(stages.getValue() - 1)); + if (m_reverseStack.size() > reverseStackSize()) { + m_reverseStack.pop_back(); } - - void forceValue(VSRTL_VT_U /* addr */, VSRTL_VT_U value) override { - // Sign-extension with unsigned type forces width truncation to m_width bits - m_savedValues[0] = signextend(value); - // Forced values are a modification of the current state and thus not pushed onto the reverse stack - } - - void reverse() override { - if (m_reverseStack.size() > 0) { - // Rotate to the left and store popped value as last register - std::rotate(m_savedValues.begin(), m_savedValues.begin() + 1, m_savedValues.end()); - m_savedValues.at(stages.getValue() - 1) = m_reverseStack.front(); - m_reverseStack.pop_front(); - } + // Rotate to the right and store new value as first register + std::rotate(m_savedValues.rbegin(), m_savedValues.rbegin() + 1, + m_savedValues.rend()); + m_savedValues.at(0) = in.uValue(); + } + + void forceValue(VSRTL_VT_U /* addr */, VSRTL_VT_U value) override { + // Sign-extension with unsigned type forces width truncation to m_width bits + m_savedValues[0] = signextend(value); + // Forced values are a modification of the current state and thus not pushed + // onto the reverse stack + } + + void reverse() override { + if (m_reverseStack.size() > 0) { + // Rotate to the left and store popped value as last register + std::rotate(m_savedValues.begin(), m_savedValues.begin() + 1, + m_savedValues.end()); + m_savedValues.at(stages.getValue() - 1) = m_reverseStack.front(); + m_reverseStack.pop_front(); } + } - PortBase* getIn() override { return ∈ } - PortBase* getOut() override { return &out; } + PortBase *getIn() override { return ∈ } + PortBase *getOut() override { return &out; } - INPUTPORT(in, W); - OUTPUTPORT(out, W); - PARAMETER(stages, int, 2); + INPUTPORT(in, W); + OUTPUTPORT(out, W); + PARAMETER(stages, int, 2); - void reverseStackSizeChanged() override { - if (reverseStackSize() < m_reverseStack.size()) { - m_reverseStack.resize(m_reverseStack.size()); - } + void reverseStackSizeChanged() override { + if (reverseStackSize() < m_reverseStack.size()) { + m_reverseStack.resize(m_reverseStack.size()); } + } protected: - void stagesChanged() { m_savedValues.resize(stages.getValue()); } + void stagesChanged() { m_savedValues.resize(stages.getValue()); } - std::vector m_savedValues; - VSRTL_VT_U m_initvalue = 0; - std::deque m_reverseStack; + std::vector m_savedValues; + VSRTL_VT_U m_initvalue = 0; + std::deque m_reverseStack; }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // VSRTL_REGISTER_H +#endif // VSRTL_REGISTER_H diff --git a/core/vsrtl_registerfile.h b/core/vsrtl_registerfile.h index 410d93e..23d03fe 100644 --- a/core/vsrtl_registerfile.h +++ b/core/vsrtl_registerfile.h @@ -17,38 +17,39 @@ namespace core { template class RegisterFile : public Component { public: - RegisterFile(const std::string& name, SimComponent* parent) : Component(name, parent) { - // Connect write (source) muxes - for (int i = 0; i < N; i++) { - src_muxes[i]->out >> regs[i]->in; - wr_data >> *src_muxes[i]->ins[1]; - regs[i]->out >> *src_muxes[i]->ins[0]; - wr_idx >> cmps[i]->op1; - i >> cmps[i]->op2; - cmps[i]->out >> src_muxes[i]->select; - } - - // Connect read (out) mux - for (int i = 0; i < N; i++) { - regs[i]->out >> *out_mux->ins[i]; - } - rd_idx >> out_mux->select; - out_mux->out >> rd_data; + RegisterFile(const std::string &name, SimComponent *parent) + : Component(name, parent) { + // Connect write (source) muxes + for (int i = 0; i < N; i++) { + src_muxes[i]->out >> regs[i]->in; + wr_data >> *src_muxes[i]->ins[1]; + regs[i]->out >> *src_muxes[i]->ins[0]; + wr_idx >> cmps[i]->op1; + i >> cmps[i]->op2; + cmps[i]->out >> src_muxes[i]->select; } - INPUTPORT(wr_idx, ceillog2(N)); - INPUTPORT(wr_data, W); - INPUTPORT(wr_en, 1); - INPUTPORT(rd_idx, ceillog2(N)); - OUTPUTPORT(rd_data, W); - - SUBCOMPONENTS(regs, Register, N); - SUBCOMPONENTS(src_muxes, TYPE(Multiplexer<2, W>), N); - SUBCOMPONENTS(cmps, Eq, N); - SUBCOMPONENT(out_mux, TYPE(Multiplexer)); + // Connect read (out) mux + for (int i = 0; i < N; i++) { + regs[i]->out >> *out_mux->ins[i]; + } + rd_idx >> out_mux->select; + out_mux->out >> rd_data; + } + + INPUTPORT(wr_idx, ceillog2(N)); + INPUTPORT(wr_data, W); + INPUTPORT(wr_en, 1); + INPUTPORT(rd_idx, ceillog2(N)); + OUTPUTPORT(rd_data, W); + + SUBCOMPONENTS(regs, Register, N); + SUBCOMPONENTS(src_muxes, TYPE(Multiplexer<2, W>), N); + SUBCOMPONENTS(cmps, Eq, N); + SUBCOMPONENT(out_mux, TYPE(Multiplexer)); }; -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl -#endif // REGISTERFILE_H +#endif // REGISTERFILE_H diff --git a/core/vsrtl_shift.h b/core/vsrtl_shift.h index 62a7acc..100e52e 100644 --- a/core/vsrtl_shift.h +++ b/core/vsrtl_shift.h @@ -11,24 +11,26 @@ enum class ShiftType { sl, sra, srl }; template class Shift : public Component { public: - Shift(const std::string& name, SimComponent* parent, ShiftType t, unsigned int shamt) : Component(name, parent) { - out << [=] { - if (t == ShiftType::sl) { - return in.uValue() << shamt; - } else if (t == ShiftType::sra) { - return VT_U(in.sValue() >> shamt); - } else if (t == ShiftType::srl) { - return in.uValue() >> shamt; - } else { - throw std::runtime_error("Unknown shift type"); - } - }; - } + Shift(const std::string &name, SimComponent *parent, ShiftType t, + unsigned int shamt) + : Component(name, parent) { + out << [=] { + if (t == ShiftType::sl) { + return in.uValue() << shamt; + } else if (t == ShiftType::sra) { + return VT_U(in.sValue() >> shamt); + } else if (t == ShiftType::srl) { + return in.uValue() >> shamt; + } else { + throw std::runtime_error("Unknown shift type"); + } + }; + } - OUTPUTPORT(out, W); - INPUTPORT(in, W); + OUTPUTPORT(out, W); + INPUTPORT(in, W); }; -} // namespace core -} // namespace vsrtl -#endif // VSRTL_SHIFT_H +} // namespace core +} // namespace vsrtl +#endif // VSRTL_SHIFT_H diff --git a/core/vsrtl_wire.h b/core/vsrtl_wire.h index f8809c7..214b098 100644 --- a/core/vsrtl_wire.h +++ b/core/vsrtl_wire.h @@ -4,32 +4,36 @@ namespace vsrtl { namespace core { /** @class Wire - * the wire class represents component-local assignments of values which are to be passed as an output or further on - * to subcomponents of a component. Similar to wires in VHDL, wires in Verilog. - * A wire will be instantiated as a component local to the instantiating component. - * wires are only defined to have an output, which will be assigned a propagation function by the utilizing component. - * To ensure that the wire is propagated correctly, >all< ports used in the wire's propagation function must be - * added to its sensitivity list. + * the wire class represents component-local assignments of values which are to + * be passed as an output or further on to subcomponents of a component. Similar + * to wires in VHDL, wires in Verilog. A wire will be instantiated as a + * component local to the instantiating component. wires are only defined to + * have an output, which will be assigned a propagation function by the + * utilizing component. To ensure that the wire is propagated correctly, >all< + * ports used in the wire's propagation function must be added to its + * sensitivity list. */ -#define WIRE(name, outWidth) \ - class wire_##name : public Component { \ - public: \ - SetGraphicsType(Wire); \ - wire_##name(const std::string& name, SimComponent* parent) : Component(name, parent) {} \ - \ - OUTPUTPORT(out, outWidth); \ - \ - protected: \ - void verifyComponent() const override { \ - if (m_sensitivityList.empty()) { \ - throw std::runtime_error("Wire: '" + getName() + \ - "' is not sensitive to anything, will never update'"); \ - } \ - Component::verifyComponent(); \ - } \ - }; \ - SUBCOMPONENT(name, wire_##name); +#define WIRE(name, outWidth) \ + class wire_##name : public Component { \ + public: \ + SetGraphicsType(Wire); \ + wire_##name(const std::string &name, SimComponent *parent) \ + : Component(name, parent) {} \ + \ + OUTPUTPORT(out, outWidth); \ + \ + protected: \ + void verifyComponent() const override { \ + if (m_sensitivityList.empty()) { \ + throw std::runtime_error( \ + "Wire: '" + getName() + \ + "' is not sensitive to anything, will never update'"); \ + } \ + Component::verifyComponent(); \ + } \ + }; \ + SUBCOMPONENT(name, wire_##name); -} // namespace core -} // namespace vsrtl +} // namespace core +} // namespace vsrtl diff --git a/graphics/gallantsignalwrapper.h b/graphics/gallantsignalwrapper.h index 9d58a39..ee2802c 100644 --- a/graphics/gallantsignalwrapper.h +++ b/graphics/gallantsignalwrapper.h @@ -1,32 +1,36 @@ #pragma once +#include "Signals/Signal.h" #include #include -#include "Signals/Signal.h" namespace vsrtl { /** * class CrossThreadSignalWrapper - * This class acts as a wrapper for Gallant signals. @p fun will be executed within the event loop context of @p obj, - * upon signal @p sig being triggered. This serves the following purposes: - * - Gallant signals only accepts raw pointers as callback functions; we'd like to be able to pass a lambda function. - * - Gallant signals are not thread safe. The wrapper will ensure that signals go through the signal/slot system of Qt, - * which checks for, and handles, cross-thread signals. + * This class acts as a wrapper for Gallant signals. @p fun will be executed + * within the event loop context of @p obj, upon signal @p sig being triggered. + * This serves the following purposes: + * - Gallant signals only accepts raw pointers as callback functions; we'd like + * to be able to pass a lambda function. + * - Gallant signals are not thread safe. The wrapper will ensure that signals + * go through the signal/slot system of Qt, which checks for, and handles, + * cross-thread signals. */ struct GallantSignalWrapperBase { - virtual ~GallantSignalWrapperBase(){}; + virtual ~GallantSignalWrapperBase(){}; }; template class GallantSignalWrapper : public GallantSignalWrapperBase { public: - GallantSignalWrapper(T* obj, F&& fun, D& sig, Qt::ConnectionType type = Qt::AutoConnection) { - wrapper = [=] { QMetaObject::invokeMethod(obj, fun, type); }; - sig.Connect(this, &GallantSignalWrapper::ftunnel); - } + GallantSignalWrapper(T *obj, F &&fun, D &sig, + Qt::ConnectionType type = Qt::AutoConnection) { + wrapper = [=] { QMetaObject::invokeMethod(obj, fun, type); }; + sig.Connect(this, &GallantSignalWrapper::ftunnel); + } private: - void ftunnel() { wrapper(); } - std::function wrapper; + void ftunnel() { wrapper(); } + std::function wrapper; }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_componentborder.h b/graphics/vsrtl_componentborder.h index 341b19f..3da59da 100644 --- a/graphics/vsrtl_componentborder.h +++ b/graphics/vsrtl_componentborder.h @@ -11,170 +11,173 @@ namespace vsrtl { enum class Side { Left, Right, Top, Bottom }; struct PortPos { - Side side; - int index; - bool validIndex() const { return index > 0; } + Side side; + int index; + bool validIndex() const { return index > 0; } - bool operator==(const PortPos& rhs) { return (this->index == rhs.index) && (this->side == rhs.side); } + bool operator==(const PortPos &rhs) { + return (this->index == rhs.index) && (this->side == rhs.side); + } }; class ComponentBorder { public: - using IdToPortMap = std::map; - using PortToIdMap = std::map; - using NameToPortMap = std::map; - - ComponentBorder(const SimComponent* c) { - std::set placedPorts; - - for (const auto& p : c->getPorts()) { - initPlacePortOnSide(Side::Left, p, placedPorts); - } - for (const auto& p : c->getPorts()) { - initPlacePortOnSide(Side::Right, p, placedPorts); - } - } + using IdToPortMap = std::map; + using PortToIdMap = std::map; + using NameToPortMap = std::map; - void initPlacePortOnSide(Side s, SimPort* p, std::set& placed) { - if (placed.count(p)) - return; + ComponentBorder(const SimComponent *c) { + std::set placedPorts; - m_portMap[p] = nullptr; - const auto& sideMap = sideToMap(s); - addPortToSide(PortPos{s, static_cast(-(sideMap.count() + 1))}, p); - m_namePortMap[p->getName()] = p; - placed.insert(p); + for (const auto &p : c->getPorts()) { + initPlacePortOnSide(Side::Left, p, placedPorts); } - - struct PortIdBiMap { - PortIdBiMap(Side _dir) : dir(_dir) {} - Side dir; - IdToPortMap idToPort; - PortToIdMap portToId; - - unsigned count() const { - assert(idToPort.size() == portToId.size()); - return idToPort.size(); - } - - // PortIdBiMap(const PortIdBiMap&) = delete; // no copying! - }; - - const SimPort* getPortAt(PortPos p) { - auto& map = sideToMap(p.side); - if (map.idToPort.count(p.index)) - return map.idToPort.at(p.index); - return nullptr; + for (const auto &p : c->getPorts()) { + initPlacePortOnSide(Side::Right, p, placedPorts); } - - /** - * @brief movePort - * @return list of ports moved in the operation - */ - std::vector movePort(const SimPort* port, PortPos pos) { - std::vector portsMoved; - const auto* portAtPos = getPortAt(pos); - if (portAtPos == port) - return {}; - - if (portAtPos != nullptr) { - portsMoved.push_back(portAtPos); - swapPorts(getPortAt(pos), port); - } else { - removePortFromSide(port); - addPortToSide(pos, port); - } - portsMoved.push_back(port); - return portsMoved; + } + + void initPlacePortOnSide(Side s, SimPort *p, std::set &placed) { + if (placed.count(p)) + return; + + m_portMap[p] = nullptr; + const auto &sideMap = sideToMap(s); + addPortToSide(PortPos{s, static_cast(-(sideMap.count() + 1))}, p); + m_namePortMap[p->getName()] = p; + placed.insert(p); + } + + struct PortIdBiMap { + PortIdBiMap(Side _dir) : dir(_dir) {} + Side dir; + IdToPortMap idToPort; + PortToIdMap portToId; + + unsigned count() const { + assert(idToPort.size() == portToId.size()); + return idToPort.size(); } - void swapPorts(const SimPort* port1, const SimPort* port2) { - auto pos1 = getPortPos(port1); - auto pos2 = getPortPos(port2); - - removePortFromSide(port1); - removePortFromSide(port2); - addPortToSide(pos1, port2); - addPortToSide(pos2, port1); + // PortIdBiMap(const PortIdBiMap&) = delete; // no copying! + }; + + const SimPort *getPortAt(PortPos p) { + auto &map = sideToMap(p.side); + if (map.idToPort.count(p.index)) + return map.idToPort.at(p.index); + return nullptr; + } + + /** + * @brief movePort + * @return list of ports moved in the operation + */ + std::vector movePort(const SimPort *port, PortPos pos) { + std::vector portsMoved; + const auto *portAtPos = getPortAt(pos); + if (portAtPos == port) + return {}; + + if (portAtPos != nullptr) { + portsMoved.push_back(portAtPos); + swapPorts(getPortAt(pos), port); + } else { + removePortFromSide(port); + addPortToSide(pos, port); } - - void addPortToSide(PortPos pos, const SimPort* port) { - assert(m_portMap.count(port) > 0); - auto& map = sideToMap(pos.side); - if (map.idToPort.count(pos.index)) { - assert(false && "Port already at index"); - } - map.idToPort[pos.index] = port; - map.portToId[port] = pos.index; - m_portMap[port] = ↦ + portsMoved.push_back(port); + return portsMoved; + } + + void swapPorts(const SimPort *port1, const SimPort *port2) { + auto pos1 = getPortPos(port1); + auto pos2 = getPortPos(port2); + + removePortFromSide(port1); + removePortFromSide(port2); + addPortToSide(pos1, port2); + addPortToSide(pos2, port1); + } + + void addPortToSide(PortPos pos, const SimPort *port) { + assert(m_portMap.count(port) > 0); + auto &map = sideToMap(pos.side); + if (map.idToPort.count(pos.index)) { + assert(false && "Port already at index"); } - - void removePortFromSide(const SimPort* port) { - auto* map = m_portMap.at(port); - const int id = map->portToId.at(port); - map->portToId.erase(port); - map->idToPort.erase(id); - m_portMap[port] = nullptr; + map.idToPort[pos.index] = port; + map.portToId[port] = pos.index; + m_portMap[port] = ↦ + } + + void removePortFromSide(const SimPort *port) { + auto *map = m_portMap.at(port); + const int id = map->portToId.at(port); + map->portToId.erase(port); + map->idToPort.erase(id); + m_portMap[port] = nullptr; + } + + PortPos getPortPos(const SimPort *p) { + auto *map = m_portMap.at(p); + return {map->dir, map->portToId[p]}; + } + + inline PortIdBiMap &sideToMap(Side d) { + switch (d) { + case Side::Left: + return m_left; + case Side::Right: + return m_right; + case Side::Top: + return m_top; + case Side::Bottom: + return m_bottom; } - - PortPos getPortPos(const SimPort* p) { - auto* map = m_portMap.at(p); - return {map->dir, map->portToId[p]}; + Q_UNREACHABLE(); + } + + template + void serialize(Archive &archive) { + std::map> portPosSerialMap; + // Create a mapping between port names and their positions + for (const auto &pm : {m_left, m_right, m_top, m_bottom}) { + for (const auto &p : pm.idToPort) { + portPosSerialMap[pm.dir][p.second->getName()] = p.first; + } } - inline PortIdBiMap& sideToMap(Side d) { - switch (d) { - case Side::Left: - return m_left; - case Side::Right: - return m_right; - case Side::Top: - return m_top; - case Side::Bottom: - return m_bottom; - } - Q_UNREACHABLE(); + try { + archive(cereal::make_nvp("Component border", portPosSerialMap)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } - template - void serialize(Archive& archive) { - std::map> portPosSerialMap; - // Create a mapping between port names and their positions - for (const auto& pm : {m_left, m_right, m_top, m_bottom}) { - for (const auto& p : pm.idToPort) { - portPosSerialMap[pm.dir][p.second->getName()] = p.first; - } - } - - try { - archive(cereal::make_nvp("Component border", portPosSerialMap)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - // Locate ports via. their names and set their positions as per the serialized archive. - for (const auto& pm : portPosSerialMap) { - for (const auto& p : pm.second) { - if (!m_namePortMap.count(p.first)) - continue; - - PortPos pos; - pos.side = pm.first; - pos.index = p.second; - - const SimPort* port = m_namePortMap.at(p.first); - movePort(port, pos); - } - } + // Locate ports via. their names and set their positions as per the + // serialized archive. + for (const auto &pm : portPosSerialMap) { + for (const auto &p : pm.second) { + if (!m_namePortMap.count(p.first)) + continue; + + PortPos pos; + pos.side = pm.first; + pos.index = p.second; + + const SimPort *port = m_namePortMap.at(p.first); + movePort(port, pos); + } } + } private: - NameToPortMap m_namePortMap; - std::map m_portMap; - PortIdBiMap m_left = PortIdBiMap(Side::Left); - PortIdBiMap m_right = PortIdBiMap(Side::Right); - PortIdBiMap m_top = PortIdBiMap(Side::Top); - PortIdBiMap m_bottom = PortIdBiMap(Side::Bottom); + NameToPortMap m_namePortMap; + std::map m_portMap; + PortIdBiMap m_left = PortIdBiMap(Side::Left); + PortIdBiMap m_right = PortIdBiMap(Side::Right); + PortIdBiMap m_top = PortIdBiMap(Side::Top); + PortIdBiMap m_bottom = PortIdBiMap(Side::Bottom); }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_componentbutton.h b/graphics/vsrtl_componentbutton.h index 378c361..5f288df 100644 --- a/graphics/vsrtl_componentbutton.h +++ b/graphics/vsrtl_componentbutton.h @@ -10,55 +10,63 @@ namespace vsrtl { class ComponentButton : public QGraphicsObject { - Q_OBJECT + Q_OBJECT public: - ComponentButton(QGraphicsItem* parentItem = nullptr) : QGraphicsObject(parentItem) { setFlags(ItemIsSelectable); } - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override { - if (boundingRect().contains(event->pos())) { - m_expanded = !m_expanded; - emit toggled(m_expanded); - update(); - } - QGraphicsItem::mouseReleaseEvent(event); + ComponentButton(QGraphicsItem *parentItem = nullptr) + : QGraphicsObject(parentItem) { + setFlags(ItemIsSelectable); + } + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override { + if (boundingRect().contains(event->pos())) { + m_expanded = !m_expanded; + emit toggled(m_expanded); + update(); } + QGraphicsItem::mouseReleaseEvent(event); + } - QRectF boundingRect() const override { return QRectF(0, 0, GRID_SIZE * 2, GRID_SIZE * 2); } - QPainterPath shape() const override { - QPainterPath p; - p.addRect(QRectF(GRID_SIZE / 4, GRID_SIZE / 4, GRID_SIZE * 1.75, GRID_SIZE * 1.75)); - return p; - } + QRectF boundingRect() const override { + return QRectF(0, 0, GRID_SIZE * 2, GRID_SIZE * 2); + } + QPainterPath shape() const override { + QPainterPath p; + p.addRect(QRectF(GRID_SIZE / 4, GRID_SIZE / 4, GRID_SIZE * 1.75, + GRID_SIZE * 1.75)); + return p; + } - void setChecked(bool state) { - m_expanded = state; - update(); - } + void setChecked(bool state) { + m_expanded = state; + update(); + } - void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override { - painter->save(); - QPen pen(m_expanded ? BUTTON_COLLAPSE_COLOR : BUTTON_EXPAND_COLOR); - pen.setWidth(4); - pen.setCapStyle(Qt::RoundCap); - painter->setPen(pen); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) override { + painter->save(); + QPen pen(m_expanded ? BUTTON_COLLAPSE_COLOR : BUTTON_EXPAND_COLOR); + pen.setWidth(4); + pen.setCapStyle(Qt::RoundCap); + painter->setPen(pen); - const auto rect = boundingRect(); + const auto rect = boundingRect(); - painter->drawLine(QPointF(GRID_SIZE / 2, rect.height() / 2), - QPointF(rect.right() - GRID_SIZE / 2, rect.height() / 2)); - if (!m_expanded) { - painter->drawLine(QPointF(rect.width() / 2, GRID_SIZE / 2), - QPointF(rect.width() / 2, rect.height() - GRID_SIZE / 2)); - } - painter->restore(); + painter->drawLine(QPointF(GRID_SIZE / 2, rect.height() / 2), + QPointF(rect.right() - GRID_SIZE / 2, rect.height() / 2)); + if (!m_expanded) { + painter->drawLine( + QPointF(rect.width() / 2, GRID_SIZE / 2), + QPointF(rect.width() / 2, rect.height() - GRID_SIZE / 2)); } + painter->restore(); + } signals: - void toggled(bool state); + void toggled(bool state); private: - bool m_expanded = false; + bool m_expanded = false; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_COMPONENTBUTTON_H +#endif // VSRTL_COMPONENTBUTTON_H diff --git a/graphics/vsrtl_componentgraphic.cpp b/graphics/vsrtl_componentgraphic.cpp index 4a1e9ec..aa26517 100644 --- a/graphics/vsrtl_componentgraphic.cpp +++ b/graphics/vsrtl_componentgraphic.cpp @@ -13,9 +13,9 @@ #include -#include #include #include +#include #include #include @@ -37,644 +37,705 @@ namespace vsrtl { static constexpr qreal c_resizeMargin = GRID_SIZE; -ComponentGraphic::ComponentGraphic(SimComponent* c, ComponentGraphic* parent) : GridComponent(c, parent) { - // Connect changes from simulator through our signal translation mechanism. - wrapSimSignal(c->changed); - c->registerGraphic(this); - verifySpecialSignals(); +ComponentGraphic::ComponentGraphic(SimComponent *c, ComponentGraphic *parent) + : GridComponent(c, parent) { + // Connect changes from simulator through our signal translation mechanism. + wrapSimSignal(c->changed); + c->registerGraphic(this); + verifySpecialSignals(); } void ComponentGraphic::verifySpecialSignals() const { - // Ensure that all special signals required by the graphics type of this component has been set by the simulator - // component - auto* type = m_component->getGraphicsType(); - if (!type) { - throw std::runtime_error("No graphics type registerred for component " + m_component->getHierName()); - } - for (const auto& typeID : type->specialPortIDs()) { - if (m_component->getSpecialPort(typeID) == nullptr) { - m_component->throwError( - "Special port: '" + std::string(typeID) + - "' not assigned. A special port of this ID should be registered through SimComponent::setSpecialPort"); - } - } + // Ensure that all special signals required by the graphics type of this + // component has been set by the simulator component + auto *type = m_component->getGraphicsType(); + if (!type) { + throw std::runtime_error("No graphics type registerred for component " + + m_component->getHierName()); + } + for (const auto &typeID : type->specialPortIDs()) { + if (m_component->getSpecialPort(typeID) == nullptr) { + m_component->throwError( + "Special port: '" + std::string(typeID) + + "' not assigned. A special port of this ID should be registered " + "through SimComponent::setSpecialPort"); + } + } } -GraphicsBaseItem* ComponentGraphic::moduleParent() { - auto* parent = dynamic_cast*>(parentItem()); - Q_ASSERT(parent); - return parent; +GraphicsBaseItem *ComponentGraphic::moduleParent() { + auto *parent = dynamic_cast *>(parentItem()); + Q_ASSERT(parent); + return parent; } void ComponentGraphic::initialize(bool doPlaceAndRoute) { - setToolTip(QString::fromStdString(m_component->getDescription())); - setFlags(ItemIsSelectable | flags()); - setAcceptHoverEvents(true); - setMoveable(); - - m_labelVisibilityAction = std::make_shared("Show label"); - m_labelVisibilityAction->setCheckable(true); - m_labelVisibilityAction->setChecked(true); - connect(m_labelVisibilityAction.get(), &QAction::toggled, [=](bool checked) { m_label->setVisible(checked); }); - - m_label = new Label(this, QString::fromStdString(m_component->getDisplayName()), m_labelVisibilityAction); - - // Create IO ports of Component - for (const auto& p_in : m_component->getInputPorts()) { - m_inputPorts[p_in] = new PortGraphic(p_in, vsrtl::SimPort::PortType::in, this); - } - for (const auto& p_out : m_component->getOutputPorts()) { - m_outputPorts[p_out] = new PortGraphic(p_out, vsrtl::SimPort::PortType::out, this); - } - - m_restrictSubcomponentPositioning = false; - if (hasSubcomponents()) { - // Setup expand button - m_expandButton = new ComponentButton(this); - connect(m_expandButton, &ComponentButton::toggled, [this](bool expanded) { setExpanded(expanded); }); - - createSubcomponents(doPlaceAndRoute); - if (doPlaceAndRoute) { - placeAndRouteSubcomponents(); - } - } - - connect(this, &GridComponent::gridRectChanged, this, &ComponentGraphic::updateGeometry); - connect(this, &GridComponent::portPosChanged, this, &ComponentGraphic::handlePortPosChanged); - connect(this, &GridComponent::gridPosChanged, this, &ComponentGraphic::handleGridPosChange); - - // By default, a component is collapsed. This has no effect if a component does not have any subcomponents - setExpanded(false); - m_restrictSubcomponentPositioning = true; - - updateGeometry(); - spreadPorts(); + setToolTip(QString::fromStdString(m_component->getDescription())); + setFlags(ItemIsSelectable | flags()); + setAcceptHoverEvents(true); + setMoveable(); + + m_labelVisibilityAction = std::make_shared("Show label"); + m_labelVisibilityAction->setCheckable(true); + m_labelVisibilityAction->setChecked(true); + connect(m_labelVisibilityAction.get(), &QAction::toggled, + [=](bool checked) { m_label->setVisible(checked); }); + + m_label = + new Label(this, QString::fromStdString(m_component->getDisplayName()), + m_labelVisibilityAction); + + // Create IO ports of Component + for (const auto &p_in : m_component->getInputPorts()) { + m_inputPorts[p_in] = + new PortGraphic(p_in, vsrtl::SimPort::PortType::in, this); + } + for (const auto &p_out : m_component->getOutputPorts()) { + m_outputPorts[p_out] = + new PortGraphic(p_out, vsrtl::SimPort::PortType::out, this); + } + + m_restrictSubcomponentPositioning = false; + if (hasSubcomponents()) { + // Setup expand button + m_expandButton = new ComponentButton(this); + connect(m_expandButton, &ComponentButton::toggled, + [this](bool expanded) { setExpanded(expanded); }); + + createSubcomponents(doPlaceAndRoute); + if (doPlaceAndRoute) { + placeAndRouteSubcomponents(); + } + } + + connect(this, &GridComponent::gridRectChanged, this, + &ComponentGraphic::updateGeometry); + connect(this, &GridComponent::portPosChanged, this, + &ComponentGraphic::handlePortPosChanged); + connect(this, &GridComponent::gridPosChanged, this, + &ComponentGraphic::handleGridPosChange); + + // By default, a component is collapsed. This has no effect if a component + // does not have any subcomponents + setExpanded(false); + m_restrictSubcomponentPositioning = true; + + updateGeometry(); + spreadPorts(); } /** * @brief ComponentGraphic::createSubcomponents - * In charge of hide()ing subcomponents if the parent component (this) is not expanded + * In charge of hide()ing subcomponents if the parent component (this) is not + * expanded */ void ComponentGraphic::createSubcomponents(bool doPlaceAndRoute) { - for (const auto& c : m_component->getSubComponents()) { - ComponentGraphic* nc; - auto type = c->getGraphicsType(); - if (type == GraphicsTypeFor(Multiplexer)) { - nc = new MultiplexerGraphic(c, this); - } else if (type == GraphicsTypeFor(Constant)) { - // Don't create a distinct ComponentGraphic for constants - these will be drawn next to the port connecting - // to it - continue; - } else { - nc = new ComponentGraphic(c, this); - } - nc->initialize(doPlaceAndRoute); - nc->setParentItem(this); - nc->setZValue(VSRTLScene::Z_Component); - m_subcomponents.push_back(nc); - if (!isExpanded()) { - nc->hide(); - } + for (const auto &c : m_component->getSubComponents()) { + ComponentGraphic *nc; + auto type = c->getGraphicsType(); + if (type == GraphicsTypeFor(Multiplexer)) { + nc = new MultiplexerGraphic(c, this); + } else if (type == GraphicsTypeFor(Constant)) { + // Don't create a distinct ComponentGraphic for constants - these will be + // drawn next to the port connecting to it + continue; + } else { + nc = new ComponentGraphic(c, this); } + nc->initialize(doPlaceAndRoute); + nc->setParentItem(this); + nc->setZValue(VSRTLScene::Z_Component); + m_subcomponents.push_back(nc); + if (!isExpanded()) { + nc->hide(); + } + } } void ComponentGraphic::resetWires() { - const QString text = - "Reset wires?\nThis will remove all interconnecting points for all wires within this subcomponent"; - - if (QMessageBox::Yes == QMessageBox::question(QApplication::activeWindow(), "Reset wires", text)) { - // Clear subcomponent wires - for (const auto& c : m_subcomponents) { - for (const auto& p : c->outputPorts()) { - p->getOutputWire()->clearWirePoints(); - } - } - // Clear wires from this components input ports - for (const auto& p : qAsConst(m_inputPorts)) { - p->getOutputWire()->clearWirePoints(); - } - } + const QString text = "Reset wires?\nThis will remove all interconnecting " + "points for all wires within this subcomponent"; + + if (QMessageBox::Yes == QMessageBox::question(QApplication::activeWindow(), + "Reset wires", text)) { + // Clear subcomponent wires + for (const auto &c : m_subcomponents) { + for (const auto &p : c->outputPorts()) { + p->getOutputWire()->clearWirePoints(); + } + } + // Clear wires from this components input ports + for (const auto &p : qAsConst(m_inputPorts)) { + p->getOutputWire()->clearWirePoints(); + } + } } -void ComponentGraphic::loadLayoutFile(const QString& fileName) { - std::ifstream file(fileName.toStdString()); - cereal::JSONInputArchive archive(file); - m_isTopLevelSerializedComponent = true; - - try { - archive(CEREAL_NVP(m_layoutVersion)); - } catch (const cereal::Exception& e) { - // No layout version - m_layoutVersion = 0; - } - - try { - archive(cereal::make_nvp("ComponentGraphic", *this)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - m_isTopLevelSerializedComponent = false; - file.close(); +void ComponentGraphic::loadLayoutFile(const QString &fileName) { + std::ifstream file(fileName.toStdString()); + cereal::JSONInputArchive archive(file); + m_isTopLevelSerializedComponent = true; + + try { + archive(CEREAL_NVP(m_layoutVersion)); + } catch (const cereal::Exception &e) { + // No layout version + m_layoutVersion = 0; + } + + try { + archive(cereal::make_nvp("ComponentGraphic", *this)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + m_isTopLevelSerializedComponent = false; + file.close(); } void ComponentGraphic::loadLayout() { - QString fileName = QFileDialog::getOpenFileName(QApplication::activeWindow(), - "Save Layout " + QString::fromStdString(m_component->getName()), - QString(), tr("JSON (*.json)")); + QString fileName = QFileDialog::getOpenFileName( + QApplication::activeWindow(), + "Save Layout " + QString::fromStdString(m_component->getName()), + QString(), tr("JSON (*.json)")); - if (fileName.isEmpty()) - return; + if (fileName.isEmpty()) + return; - loadLayoutFile(fileName); + loadLayoutFile(fileName); } void ComponentGraphic::saveLayout() { - QString fileName = QFileDialog::getSaveFileName(QApplication::activeWindow(), - "Save Layout " + QString::fromStdString(m_component->getName()), - QString(), tr("JSON (*.json)")); - - if (fileName.isEmpty()) - return; - if (!fileName.endsWith(".json")) - fileName += ".json"; - std::ofstream file(fileName.toStdString()); - cereal::JSONOutputArchive archive(file); - - /// @todo: Is it more applicable to do a typeid(getComponent()).name() ? this would not work accross separate - /// compilers, but would directly indicate the underlying type of which this layout is compatible with... - m_isTopLevelSerializedComponent = true; - try { - m_layoutVersion = LatestLayoutVersion - 1; - archive(CEREAL_NVP(m_layoutVersion)); - archive(cereal::make_nvp("ComponentGraphic", *this)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - m_isTopLevelSerializedComponent = false; -} + QString fileName = QFileDialog::getSaveFileName( + QApplication::activeWindow(), + "Save Layout " + QString::fromStdString(m_component->getName()), + QString(), tr("JSON (*.json)")); -void ComponentGraphic::parameterDialogTriggered() { - ParameterDialog dialog(m_component); - - if (dialog.exec()) { - } + if (fileName.isEmpty()) return; + if (!fileName.endsWith(".json")) + fileName += ".json"; + std::ofstream file(fileName.toStdString()); + cereal::JSONOutputArchive archive(file); + + /// @todo: Is it more applicable to do a typeid(getComponent()).name() ? this + /// would not work accross separate compilers, but would directly indicate the + /// underlying type of which this layout is compatible with... + m_isTopLevelSerializedComponent = true; + try { + m_layoutVersion = LatestLayoutVersion - 1; + archive(CEREAL_NVP(m_layoutVersion)); + archive(cereal::make_nvp("ComponentGraphic", *this)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + m_isTopLevelSerializedComponent = false; } -void ComponentGraphic::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - QMenu menu; - - if (!m_component->getParameters().empty()) { - auto* parameterAction = menu.addAction("Parameters"); - connect(parameterAction, &QAction::triggered, this, &ComponentGraphic::parameterDialogTriggered); - } - - if (hasSubcomponents() && !isLocked()) { - // ======================== Layout menu ============================ // - auto* layoutMenu = menu.addMenu("Layout"); - auto* loadAction = layoutMenu->addAction("Load layout"); - auto* saveAction = layoutMenu->addAction("Save layout"); - auto* resetWiresAction = layoutMenu->addAction("Reset wires"); +void ComponentGraphic::parameterDialogTriggered() { + ParameterDialog dialog(m_component); - connect(saveAction, &QAction::triggered, this, &ComponentGraphic::saveLayout); - connect(loadAction, &QAction::triggered, this, &ComponentGraphic::loadLayout); - connect(resetWiresAction, &QAction::triggered, this, &ComponentGraphic::resetWires); - } + if (dialog.exec()) { + } + return; +} - if (m_outputPorts.size() > 0) { - // ======================== Ports menu ============================ // - auto* portMenu = menu.addMenu("Ports"); - auto* showOutputsAction = portMenu->addAction("Show output values"); - auto* hideOutputsAction = portMenu->addAction("Hide output values"); - connect(showOutputsAction, &QAction::triggered, [=] { - for (auto& c : m_outputPorts) - c->setValueLabelVisible(true); - }); - connect(hideOutputsAction, &QAction::triggered, [=] { - for (auto& c : m_outputPorts) - c->setValueLabelVisible(false); - }); - - if (!isLocked()) { - auto* hiddenPortsMenu = portMenu->addMenu("Hidden ports"); - for (const auto& p : m_component->getAllPorts()) { - auto* gp = p->getGraphic(); - if (gp->userHidden()) { - auto* showPortAction = hiddenPortsMenu->addAction(QString::fromStdString(p->getName())); - connect(showPortAction, &QAction::triggered, [=] { gp->setUserVisible(true); }); - } - } - if (hiddenPortsMenu->actions().size() == 0) { - delete hiddenPortsMenu; - } - } - } +void ComponentGraphic::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + QMenu menu; + + if (!m_component->getParameters().empty()) { + auto *parameterAction = menu.addAction("Parameters"); + connect(parameterAction, &QAction::triggered, this, + &ComponentGraphic::parameterDialogTriggered); + } + + if (hasSubcomponents() && !isLocked()) { + // ======================== Layout menu ============================ // + auto *layoutMenu = menu.addMenu("Layout"); + auto *loadAction = layoutMenu->addAction("Load layout"); + auto *saveAction = layoutMenu->addAction("Save layout"); + auto *resetWiresAction = layoutMenu->addAction("Reset wires"); + + connect(saveAction, &QAction::triggered, this, + &ComponentGraphic::saveLayout); + connect(loadAction, &QAction::triggered, this, + &ComponentGraphic::loadLayout); + connect(resetWiresAction, &QAction::triggered, this, + &ComponentGraphic::resetWires); + } + + if (m_outputPorts.size() > 0) { + // ======================== Ports menu ============================ // + auto *portMenu = menu.addMenu("Ports"); + auto *showOutputsAction = portMenu->addAction("Show output values"); + auto *hideOutputsAction = portMenu->addAction("Hide output values"); + connect(showOutputsAction, &QAction::triggered, [=] { + for (auto &c : m_outputPorts) + c->setValueLabelVisible(true); + }); + connect(hideOutputsAction, &QAction::triggered, [=] { + for (auto &c : m_outputPorts) + c->setValueLabelVisible(false); + }); - // ======================== Indicators menu ============================ // - if ((m_inputPorts.size() > 0) && !isLocked()) { - auto* indicatorMenu = menu.addMenu("Indicators"); - for (const auto& portmap : {m_inputPorts, m_outputPorts}) { - for (const auto& p : portmap) { - // Value indicators for boolean signals. Boolean indicators will be visible even if the port - // responsible for the indicator gets hidden. - if (p->getPort()->getWidth() == 1) { - auto* indicatorAction = indicatorMenu->addAction(QString::fromStdString(p->getPort()->getName())); - indicatorAction->setCheckable(true); - indicatorAction->setChecked(m_indicators.count(p)); - connect(indicatorAction, &QAction::triggered, [=](bool checked) { setIndicatorState(p, checked); }); - } - } + if (!isLocked()) { + auto *hiddenPortsMenu = portMenu->addMenu("Hidden ports"); + for (const auto &p : m_component->getAllPorts()) { + auto *gp = p->getGraphic(); + if (gp->userHidden()) { + auto *showPortAction = + hiddenPortsMenu->addAction(QString::fromStdString(p->getName())); + connect(showPortAction, &QAction::triggered, + [=] { gp->setUserVisible(true); }); } - if (indicatorMenu->actions().size() == 0) { - delete indicatorMenu; + } + if (hiddenPortsMenu->actions().size() == 0) { + delete hiddenPortsMenu; + } + } + } + + // ======================== Indicators menu ============================ // + if ((m_inputPorts.size() > 0) && !isLocked()) { + auto *indicatorMenu = menu.addMenu("Indicators"); + for (const auto &portmap : {m_inputPorts, m_outputPorts}) { + for (const auto &p : portmap) { + // Value indicators for boolean signals. Boolean indicators will be + // visible even if the port responsible for the indicator gets hidden. + if (p->getPort()->getWidth() == 1) { + auto *indicatorAction = indicatorMenu->addAction( + QString::fromStdString(p->getPort()->getName())); + indicatorAction->setCheckable(true); + indicatorAction->setChecked(m_indicators.count(p)); + connect(indicatorAction, &QAction::triggered, + [=](bool checked) { setIndicatorState(p, checked); }); } - } - - // ======================== Rotation menu ============================ // - if ((m_component->getParent() != nullptr) && !isLocked()) { - auto* rotationMenu = menu.addMenu("Rotate"); - auto* rotateClockwiseAction = rotationMenu->addAction("+90º"); - auto* rotateCounterClockwiseAction = rotationMenu->addAction("-90º"); - connect(rotateClockwiseAction, &QAction::triggered, [=] { gridRotate(RotationDirection::RightHand); }); - connect(rotateCounterClockwiseAction, &QAction::triggered, [=] { gridRotate(RotationDirection::LeftHand); }); - } - - if ((m_component->getParent() != nullptr) && !isLocked()) { - auto* hideAction = menu.addAction("Hide component"); - connect(hideAction, &QAction::triggered, [=] { - m_userHidden = true; - this->hide(); - }); - } - - if (!isLocked()) { - menu.addAction(m_labelVisibilityAction.get()); - } - - menu.exec(event->screenPos()); + } + } + if (indicatorMenu->actions().size() == 0) { + delete indicatorMenu; + } + } + + // ======================== Rotation menu ============================ // + if ((m_component->getParent() != nullptr) && !isLocked()) { + auto *rotationMenu = menu.addMenu("Rotate"); + auto *rotateClockwiseAction = rotationMenu->addAction("+90º"); + auto *rotateCounterClockwiseAction = rotationMenu->addAction("-90º"); + connect(rotateClockwiseAction, &QAction::triggered, + [=] { gridRotate(RotationDirection::RightHand); }); + connect(rotateCounterClockwiseAction, &QAction::triggered, + [=] { gridRotate(RotationDirection::LeftHand); }); + } + + if ((m_component->getParent() != nullptr) && !isLocked()) { + auto *hideAction = menu.addAction("Hide component"); + connect(hideAction, &QAction::triggered, [=] { + m_userHidden = true; + this->hide(); + }); + } + + if (!isLocked()) { + menu.addAction(m_labelVisibilityAction.get()); + } + + menu.exec(event->screenPos()); } -void ComponentGraphic::setIndicatorState(PortGraphic* p, bool enabled) { - if (enabled) { - m_indicators.emplace(p); - } else { - m_indicators.erase(p); - } - update(); +void ComponentGraphic::setIndicatorState(PortGraphic *p, bool enabled) { + if (enabled) { + m_indicators.emplace(p); + } else { + m_indicators.erase(p); + } + update(); } -void ComponentGraphic::registerWire(WireGraphic* wire) { - m_wires.push_back(wire); +void ComponentGraphic::registerWire(WireGraphic *wire) { + m_wires.push_back(wire); } void ComponentGraphic::setExpanded(bool state) { - GridComponent::setExpanded(state); - bool areWeExpanded = isExpanded(); - if (m_expandButton != nullptr) { - m_expandButton->setChecked(areWeExpanded); - for (const auto& c : m_subcomponents) { - const bool visible = areWeExpanded && !c->userHidden(); - if (visible) { - c->setParentItem(this); - } else if (auto* scenep = scene()) { - scenep->removeItem(c); - } - c->setVisible(visible); - } - // We are not hiding the input ports of a component, because these should always be drawn. However, a input - // port of an expandable component has wires drawin inside the component, which must be hidden aswell, such - // that they do not accept mouse events nor are drawn. - for (const auto& w : m_wires) { - w->setVisible(areWeExpanded); - } - } + GridComponent::setExpanded(state); + bool areWeExpanded = isExpanded(); + if (m_expandButton != nullptr) { + m_expandButton->setChecked(areWeExpanded); + for (const auto &c : m_subcomponents) { + const bool visible = areWeExpanded && !c->userHidden(); + if (visible) { + c->setParentItem(this); + } else if (auto *scenep = scene()) { + scenep->removeItem(c); + } + c->setVisible(visible); + } + // We are not hiding the input ports of a component, because these should + // always be drawn. However, a input port of an expandable component has + // wires drawin inside the component, which must be hidden aswell, such that + // they do not accept mouse events nor are drawn. + for (const auto &w : m_wires) { + w->setVisible(areWeExpanded); + } + } } void ComponentGraphic::setUserVisible(bool visible) { - m_userHidden = !visible; - setVisible(visible); + m_userHidden = !visible; + setVisible(visible); } -ComponentGraphic* ComponentGraphic::getParent() const { - return dynamic_cast(parentItem()); +ComponentGraphic *ComponentGraphic::getParent() const { + return dynamic_cast(parentItem()); } void ComponentGraphic::updateGeometry() { - prepareGeometryChange(); - const QRectF sceneRect = sceneGridRect(); - const QPointF sceneRectCenter = {sceneRect.width() / 2.0, sceneRect.height() / 2.0}; - const QRect& currentGridRect = getCurrentComponentRect(); - - // Apply rotation around center of shape. All shape points are defined in grid [x,y] in [0:1], so rotate around - // [0.5, 0.5] - // Next, separately apply the scaling through a secondary matrix (The transformation gets a lot simpler like - // this, rather than composing translation + rotation +translation + scaling in a single matrix. - QTransform t; - QMatrix4x4 mat; - mat.scale(sceneRect.width(), sceneRect.height()); - t.translate(0.5, 0.5).rotate(gridRotation()).translate(-0.5, -0.5); - m_shape = mat.toTransform().map(ShapeRegister::getTypeShape(m_component->getGraphicsType(), t)); - - // Position the expand-button - if (hasSubcomponents()) { - if (isExpanded()) { - m_expandButton->setPos(QPointF(0, 0)); - } else { - // Center - const qreal x = sceneRectCenter.x() - m_expandButton->boundingRect().width() / 2; - const qreal y = sceneRectCenter.y() - m_expandButton->boundingRect().height() / 2; - m_expandButton->setPos(QPointF(x, y)); - } - } - - // Update the grid points within this component, if it has subcomponents - if (hasSubcomponents() && isExpanded()) { - // Grid should only be drawing inside the component, so remove 1 gridsize from each edge of the - // component rect - auto rect = m_shape.boundingRect(); - QPoint gridTopLeft = (rect.topLeft() / GRID_SIZE).toPoint() * GRID_SIZE; - gridTopLeft += QPoint(GRID_SIZE, GRID_SIZE); - QPoint gridBotRight = (rect.bottomRight() / GRID_SIZE).toPoint() * GRID_SIZE; - gridBotRight -= QPoint(GRID_SIZE, GRID_SIZE); - - m_gridPoints.clear(); - for (int x = gridTopLeft.x(); x <= gridBotRight.x(); x += GRID_SIZE) - for (int y = gridTopLeft.y(); y <= gridBotRight.y(); y += GRID_SIZE) - m_gridPoints << QPoint(x, y); - } - - if (!isSerializing()) { - // Adjust label position through scaling by the relative size change of the component. - const auto& lastComponentRect = getLastComponentRect(); - if (lastComponentRect != QRect()) { - const auto widthScaledChanged = static_cast(currentGridRect.width()) / lastComponentRect.width(); - const auto heightScaledChanged = static_cast(currentGridRect.height()) / lastComponentRect.height(); - - // Scale the positioning of the label, adjusting it accordingly to the component size change - auto labelPos = m_label->pos(); - labelPos.rx() *= widthScaledChanged; - labelPos.ry() *= heightScaledChanged; - m_label->setPos(labelPos); - } else { - // First time setting label position. Position label centered above component. - m_label->setPos((sceneRect.width() / 2) - m_label->boundingRect().width() / 2, - -m_label->boundingRect().height()); - } + prepareGeometryChange(); + const QRectF sceneRect = sceneGridRect(); + const QPointF sceneRectCenter = {sceneRect.width() / 2.0, + sceneRect.height() / 2.0}; + const QRect ¤tGridRect = getCurrentComponentRect(); + + // Apply rotation around center of shape. All shape points are defined in grid + // [x,y] in [0:1], so rotate around [0.5, 0.5] Next, separately apply the + // scaling through a secondary matrix (The transformation gets a lot simpler + // like this, rather than composing translation + rotation +translation + + // scaling in a single matrix. + QTransform t; + QMatrix4x4 mat; + mat.scale(sceneRect.width(), sceneRect.height()); + t.translate(0.5, 0.5).rotate(gridRotation()).translate(-0.5, -0.5); + m_shape = mat.toTransform().map( + ShapeRegister::getTypeShape(m_component->getGraphicsType(), t)); + + // Position the expand-button + if (hasSubcomponents()) { + if (isExpanded()) { + m_expandButton->setPos(QPointF(0, 0)); + } else { + // Center + const qreal x = + sceneRectCenter.x() - m_expandButton->boundingRect().width() / 2; + const qreal y = + sceneRectCenter.y() - m_expandButton->boundingRect().height() / 2; + m_expandButton->setPos(QPointF(x, y)); + } + } + + // Update the grid points within this component, if it has subcomponents + if (hasSubcomponents() && isExpanded()) { + // Grid should only be drawing inside the component, so remove 1 gridsize + // from each edge of the component rect + auto rect = m_shape.boundingRect(); + QPoint gridTopLeft = (rect.topLeft() / GRID_SIZE).toPoint() * GRID_SIZE; + gridTopLeft += QPoint(GRID_SIZE, GRID_SIZE); + QPoint gridBotRight = + (rect.bottomRight() / GRID_SIZE).toPoint() * GRID_SIZE; + gridBotRight -= QPoint(GRID_SIZE, GRID_SIZE); + + m_gridPoints.clear(); + for (int x = gridTopLeft.x(); x <= gridBotRight.x(); x += GRID_SIZE) + for (int y = gridTopLeft.y(); y <= gridBotRight.y(); y += GRID_SIZE) + m_gridPoints << QPoint(x, y); + } + + if (!isSerializing()) { + // Adjust label position through scaling by the relative size change of the + // component. + const auto &lastComponentRect = getLastComponentRect(); + if (lastComponentRect != QRect()) { + const auto widthScaledChanged = + static_cast(currentGridRect.width()) / + lastComponentRect.width(); + const auto heightScaledChanged = + static_cast(currentGridRect.height()) / + lastComponentRect.height(); + + // Scale the positioning of the label, adjusting it accordingly to the + // component size change + auto labelPos = m_label->pos(); + labelPos.rx() *= widthScaledChanged; + labelPos.ry() *= heightScaledChanged; + m_label->setPos(labelPos); + } else { + // First time setting label position. Position label centered above + // component. + m_label->setPos((sceneRect.width() / 2) - + m_label->boundingRect().width() / 2, + -m_label->boundingRect().height()); } + } } -bool ComponentGraphic::handlePortGraphicMoveAttempt(const PortGraphic* port, const QPointF& newBorderPos) { - // Port will report its new position in its portGraphic coordinates. Transfor to this (the port parent) - // coordinate system and attempt to adjust port position. +bool ComponentGraphic::handlePortGraphicMoveAttempt( + const PortGraphic *port, const QPointF &newBorderPos) { + // Port will report its new position in its portGraphic coordinates. Transfor + // to this (the port parent) coordinate system and attempt to adjust port + // position. - const QPoint adjustedPos = sceneToGrid(mapFromItem(port, newBorderPos).toPoint()); - return adjustPort(port->getPort(), adjustedPos); + const QPoint adjustedPos = + sceneToGrid(mapFromItem(port, newBorderPos).toPoint()); + return adjustPort(port->getPort(), adjustedPos); } void ComponentGraphic::handleGridPosChange(const QPoint p) { - setPos(gridToScene(p)); + setPos(gridToScene(p)); } -void ComponentGraphic::handlePortPosChanged(const SimPort* port) { - const auto pos = getPortPos(port); - auto* g = port->getGraphic(); - - g->setSide(pos.side); - switch (pos.side) { - case Side::Left: { - g->setPos(QPointF(sceneGridRect().left(), pos.index * GRID_SIZE)); - break; - } - case Side::Right: { - g->setPos(QPointF(sceneGridRect().right(), pos.index * GRID_SIZE)); - break; - } - case Side::Top: { - g->setPos(QPointF(pos.index * GRID_SIZE, sceneGridRect().top())); - break; - } - case Side::Bottom: { - g->setPos(QPointF(pos.index * GRID_SIZE, sceneGridRect().bottom())); - break; - } - } +void ComponentGraphic::handlePortPosChanged(const SimPort *port) { + const auto pos = getPortPos(port); + auto *g = port->getGraphic(); + + g->setSide(pos.side); + switch (pos.side) { + case Side::Left: { + g->setPos(QPointF(sceneGridRect().left(), pos.index * GRID_SIZE)); + break; + } + case Side::Right: { + g->setPos(QPointF(sceneGridRect().right(), pos.index * GRID_SIZE)); + break; + } + case Side::Top: { + g->setPos(QPointF(pos.index * GRID_SIZE, sceneGridRect().top())); + break; + } + case Side::Bottom: { + g->setPos(QPointF(pos.index * GRID_SIZE, sceneGridRect().bottom())); + break; + } + } } void ComponentGraphic::setLocked(bool locked) { - // No longer give the option of expand the component if the scene is locked - if (m_expandButton) - m_expandButton->setVisible(!locked); + // No longer give the option of expand the component if the scene is locked + if (m_expandButton) + m_expandButton->setVisible(!locked); - GraphicsBaseItem::setLocked(locked); + GraphicsBaseItem::setLocked(locked); } -QVariant ComponentGraphic::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) { - Q_ASSERT((flags() & QGraphicsItem::ItemSendsGeometryChanges) && - "Need ItemSendsGeometryChanges for ItemPositionHasChanged"); - - // @todo implement snapping inside parent component - if (change == ItemPositionChange && scene()) { - // Output port wires are implicitely redrawn given that the wire is a child of $this. We need to manually - // signal the wires going to the input ports of this component, to redraw - if (m_initialized) { - for (const auto& inputPort : qAsConst(m_inputPorts)) { - if (!inputPort->getPort()->isConstant()) { - if (auto* simInputPort = inputPort->getPort()->getInputPort()) { - if (auto* graphic = simInputPort->getGraphic()) { - graphic->updateWireGeometry(); - } - } - } - } - } - - if (parentIsPlacing()) { - // New position has been validated (and set) through the grid layer, and the grid component has - // >already< been moved to its new position. This slot is called through the move signal of the - // gridComponent, emitted during place and route. - return value; - } else { - /// Parent is not placing, @p value represents a QPointF in parent scene coordinates. Go through - /// GridComponent to validate and attempt to place the component. - const QPoint newGridPos = sceneToGrid(value.toPoint()); - - if (move(newGridPos)) { - return gridToScene(getGridPos()); - } else { - // Move was unsuccessfull, keep current positioning - return pos(); +QVariant ComponentGraphic::itemChange(QGraphicsItem::GraphicsItemChange change, + const QVariant &value) { + Q_ASSERT((flags() & QGraphicsItem::ItemSendsGeometryChanges) && + "Need ItemSendsGeometryChanges for ItemPositionHasChanged"); + + // @todo implement snapping inside parent component + if (change == ItemPositionChange && scene()) { + // Output port wires are implicitely redrawn given that the wire is a child + // of $this. We need to manually signal the wires going to the input ports + // of this component, to redraw + if (m_initialized) { + for (const auto &inputPort : qAsConst(m_inputPorts)) { + if (!inputPort->getPort()->isConstant()) { + if (auto *simInputPort = inputPort->getPort()->getInputPort()) { + if (auto *graphic = simInputPort->getGraphic()) { + graphic->updateWireGeometry(); } + } } + } } - if (change == ItemPositionHasChanged) { - // Notify ports that their position inside the module has changed - for (const auto& portmap : {m_inputPorts, m_outputPorts}) { - for (const auto& p : portmap) { - p->modulePositionHasChanged(); - } - } - } - - return GraphicsBaseItem::itemChange(change, value); -} - -void ComponentGraphic::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* w) { - painter->save(); - QColor color; - if (static_cast(scene())->darkmode()) { - color = hasSubcomponents() && isExpanded() ? QColorConstants::DarkGray.darker() : QColor{0x80, 0x84, 0x8a}; + if (parentIsPlacing()) { + // New position has been validated (and set) through the grid layer, and + // the grid component has >already< been moved to its new position. This + // slot is called through the move signal of the gridComponent, emitted + // during place and route. + return value; } else { - color = hasSubcomponents() && isExpanded() ? QColor{0xec, 0xf0, 0xf1} : QColorConstants::White; - } - - QColor fillColor = (option->state & QStyle::State_Selected) ? color.darker(150) : color; - if (option->state & QStyle::State_MouseOver) - fillColor = fillColor.lighter(125); - - const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); - - // Draw component outline - QPen oldPen = painter->pen(); - QPen pen = oldPen; - int width = COMPONENT_BORDER_WIDTH; - if (option->state & QStyle::State_Selected) - width += 1; - - pen.setWidth(width); - painter->setBrush(QBrush(fillColor.darker((option->state & QStyle::State_Sunken) ? 120 : 100))); - painter->setPen(pen); - painter->drawPath(m_shape); - - painter->setPen(oldPen); - - if (hasSubcomponents()) { - if (lod >= 0.35) { - // Determine whether expand button should be shown. If we are in locked state, do not interfere with the - // view state of the expand button - if (!isLocked()) { - m_expandButton->show(); - } else { - m_expandButton->hide(); - } - - if (isExpanded()) { - // Draw grid - painter->save(); - painter->setPen(QPen(Qt::lightGray, 1)); - painter->drawPoints(m_gridPoints); - painter->restore(); - } - } - } - - // Paint boolean indicators - for (const auto& p : m_indicators) { - paintIndicator(painter, p, p->getPort()->uValue() ? Qt::green : Qt::red); - } + /// Parent is not placing, @p value represents a QPointF in parent scene + /// coordinates. Go through GridComponent to validate and attempt to place + /// the component. + const QPoint newGridPos = sceneToGrid(value.toPoint()); + + if (move(newGridPos)) { + return gridToScene(getGridPos()); + } else { + // Move was unsuccessfull, keep current positioning + return pos(); + } + } + } + + if (change == ItemPositionHasChanged) { + // Notify ports that their position inside the module has changed + for (const auto &portmap : {m_inputPorts, m_outputPorts}) { + for (const auto &p : portmap) { + p->modulePositionHasChanged(); + } + } + } + + return GraphicsBaseItem::itemChange(change, value); +} - // Paint overlay - paintOverlay(painter, option, w); +void ComponentGraphic::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *w) { + painter->save(); + QColor color; + if (static_cast(scene())->darkmode()) { + color = hasSubcomponents() && isExpanded() + ? QColorConstants::DarkGray.darker() + : QColor{0x80, 0x84, 0x8a}; + } else { + color = hasSubcomponents() && isExpanded() ? QColor{0xec, 0xf0, 0xf1} + : QColorConstants::White; + } + + QColor fillColor = + (option->state & QStyle::State_Selected) ? color.darker(150) : color; + if (option->state & QStyle::State_MouseOver) + fillColor = fillColor.lighter(125); + + const qreal lod = + option->levelOfDetailFromTransform(painter->worldTransform()); + + // Draw component outline + QPen oldPen = painter->pen(); + QPen pen = oldPen; + int width = COMPONENT_BORDER_WIDTH; + if (option->state & QStyle::State_Selected) + width += 1; + + pen.setWidth(width); + painter->setBrush(QBrush( + fillColor.darker((option->state & QStyle::State_Sunken) ? 120 : 100))); + painter->setPen(pen); + painter->drawPath(m_shape); + + painter->setPen(oldPen); + + if (hasSubcomponents()) { + if (lod >= 0.35) { + // Determine whether expand button should be shown. If we are in locked + // state, do not interfere with the view state of the expand button + if (!isLocked()) { + m_expandButton->show(); + } else { + m_expandButton->hide(); + } + + if (isExpanded()) { + // Draw grid + painter->save(); + painter->setPen(QPen(Qt::lightGray, 1)); + painter->drawPoints(m_gridPoints); + painter->restore(); + } + } + } + + // Paint boolean indicators + for (const auto &p : m_indicators) { + paintIndicator(painter, p, p->getPort()->uValue() ? Qt::green : Qt::red); + } + + // Paint overlay + paintOverlay(painter, option, w); #ifdef VSRTL_DEBUG_DRAW - painter->save(); - painter->setPen(Qt::green); - painter->drawRect(sceneGridRect()); - painter->restore(); - DRAW_BOUNDING_RECT(painter) + painter->save(); + painter->setPen(Qt::green); + painter->drawRect(sceneGridRect()); + painter->restore(); + DRAW_BOUNDING_RECT(painter) #endif - painter->restore(); + painter->restore(); } -void ComponentGraphic::paintIndicator(QPainter* painter, PortGraphic* p, QColor color) { - painter->save(); - constexpr qreal dotSize = 12; - QPen pen = painter->pen(); - pen.setWidth(WIRE_WIDTH - 1); - painter->setBrush(color); - painter->setPen(pen); - - const bool inPort = p->getPortType() == vsrtl::SimPort::PortType::in; - QRectF chordRect(-dotSize / 2, -dotSize / 2, dotSize, dotSize); - chordRect.translate(mapFromItem(p, inPort ? p->getOutputPoint() : p->getInputPoint())); - - int startAngle = 0; - // clang-format off +void ComponentGraphic::paintIndicator(QPainter *painter, PortGraphic *p, + QColor color) { + painter->save(); + constexpr qreal dotSize = 12; + QPen pen = painter->pen(); + pen.setWidth(WIRE_WIDTH - 1); + painter->setBrush(color); + painter->setPen(pen); + + const bool inPort = p->getPortType() == vsrtl::SimPort::PortType::in; + QRectF chordRect(-dotSize / 2, -dotSize / 2, dotSize, dotSize); + chordRect.translate( + mapFromItem(p, inPort ? p->getOutputPoint() : p->getInputPoint())); + + int startAngle = 0; + // clang-format off switch(p->getSide()){ case Side::Top : startAngle = 0; break; case Side::Bottom : startAngle = -180; break; case Side::Left : startAngle = 90; break; case Side::Right : startAngle = -90; break; } - // clang-format on + // clang-format on - painter->drawChord(chordRect, startAngle * 16, -180 * 16); - painter->restore(); + painter->drawChord(chordRect, startAngle * 16, -180 * 16); + painter->restore(); } QRectF ComponentGraphic::sceneGridRect() const { - return gridToScene(getCurrentComponentRect()); + return gridToScene(getCurrentComponentRect()); } QPainterPath ComponentGraphic::shape() const { - QPainterPath s; - s.addRect(sceneGridRect()); - return s; + QPainterPath s; + s.addRect(sceneGridRect()); + return s; } QRectF ComponentGraphic::boundingRect() const { - QRectF boundingRect = sceneGridRect(); + QRectF boundingRect = sceneGridRect(); - // Adjust slightly for stuff such as shadows, pen sizes etc. - boundingRect.adjust(-SIDE_MARGIN, -SIDE_MARGIN, SIDE_MARGIN, SIDE_MARGIN); + // Adjust slightly for stuff such as shadows, pen sizes etc. + boundingRect.adjust(-SIDE_MARGIN, -SIDE_MARGIN, SIDE_MARGIN, SIDE_MARGIN); - return boundingRect; + return boundingRect; } -void ComponentGraphic::mousePressEvent(QGraphicsSceneMouseEvent* event) { - if (flags().testFlag(ItemIsMovable) && event->button() == Qt::LeftButton && m_inResizeDragZone) { - // start resize drag - setFlags(flags() & ~ItemIsMovable); - m_resizeDragging = true; - } +void ComponentGraphic::mousePressEvent(QGraphicsSceneMouseEvent *event) { + if (flags().testFlag(ItemIsMovable) && event->button() == Qt::LeftButton && + m_inResizeDragZone) { + // start resize drag + setFlags(flags() & ~ItemIsMovable); + m_resizeDragging = true; + } - QGraphicsItem::mousePressEvent(event); + QGraphicsItem::mousePressEvent(event); } -void ComponentGraphic::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - if (m_resizeDragging) { - QPoint gridPos = (event->pos() / GRID_SIZE).toPoint(); - const auto oldGridRect = getCurrentComponentRect(); - auto newGridRect = oldGridRect; - newGridRect.setBottomRight(gridPos); +void ComponentGraphic::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (m_resizeDragging) { + QPoint gridPos = (event->pos() / GRID_SIZE).toPoint(); + const auto oldGridRect = getCurrentComponentRect(); + auto newGridRect = oldGridRect; + newGridRect.setBottomRight(gridPos); - adjust(newGridRect); - } + adjust(newGridRect); + } - QGraphicsItem::mouseMoveEvent(event); + QGraphicsItem::mouseMoveEvent(event); } -void ComponentGraphic::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { - if (m_resizeDragging) { - setFlags(flags() | ItemIsMovable); - m_resizeDragging = false; - } +void ComponentGraphic::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + if (m_resizeDragging) { + setFlags(flags() | ItemIsMovable); + m_resizeDragging = false; + } - QGraphicsItem::mouseReleaseEvent(event); + QGraphicsItem::mouseReleaseEvent(event); } -void ComponentGraphic::hoverMoveEvent(QGraphicsSceneHoverEvent* event) { - if (!isLocked()) { - const auto& sceneRect = sceneGridRect(); - if (sceneRect.width() - event->pos().x() <= c_resizeMargin && - sceneRect.height() - event->pos().y() <= c_resizeMargin) { - this->setCursor(Qt::SizeFDiagCursor); - m_inResizeDragZone = true; - } else { - this->setCursor(Qt::ArrowCursor); - m_inResizeDragZone = false; - } +void ComponentGraphic::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { + if (!isLocked()) { + const auto &sceneRect = sceneGridRect(); + if (sceneRect.width() - event->pos().x() <= c_resizeMargin && + sceneRect.height() - event->pos().y() <= c_resizeMargin) { + this->setCursor(Qt::SizeFDiagCursor); + m_inResizeDragZone = true; + } else { + this->setCursor(Qt::ArrowCursor); + m_inResizeDragZone = false; } + } } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_componentgraphic.h b/graphics/vsrtl_componentgraphic.h index 7b76494..f699392 100644 --- a/graphics/vsrtl_componentgraphic.h +++ b/graphics/vsrtl_componentgraphic.h @@ -23,300 +23,320 @@ namespace vsrtl { static inline qreal snapToGrid(qreal v) { - return round(v / GRID_SIZE) * GRID_SIZE; + return round(v / GRID_SIZE) * GRID_SIZE; } static inline QRectF gridToScene(QRect gridRect) { - // Scales a rectangle in grid coordinates to scene coordinates - QRectF sceneGridRect; - sceneGridRect.setWidth(gridRect.width() * GRID_SIZE); - sceneGridRect.setHeight(gridRect.height() * GRID_SIZE); - return sceneGridRect; + // Scales a rectangle in grid coordinates to scene coordinates + QRectF sceneGridRect; + sceneGridRect.setWidth(gridRect.width() * GRID_SIZE); + sceneGridRect.setHeight(gridRect.height() * GRID_SIZE); + return sceneGridRect; } static inline QPoint sceneToGrid(QPointF p) { - return (p / GRID_SIZE).toPoint(); + return (p / GRID_SIZE).toPoint(); } -static inline QPointF gridToScene(QPoint p) { - return p * GRID_SIZE; -} +static inline QPointF gridToScene(QPoint p) { return p * GRID_SIZE; } class PortGraphic; class ComponentButton; class ComponentGraphic : public GridComponent { - Q_OBJECT + Q_OBJECT public: - ComponentGraphic(SimComponent* c, ComponentGraphic* parent); - - QRectF boundingRect() const override; - QPainterPath shape() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget*) override; - - /** - * @brief paintOverlay - * May be implemented by derived classes. - * Called after ComponentGraphic::paint (painting of the basic shape/outline of the component), wherein derived - * class specific painting is painted on top - */ - virtual void paintOverlay(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) {} - - void initialize(bool placeAndRoute = false); - bool restrictSubcomponentPositioning() const { return m_restrictSubcomponentPositioning; } - std::vector& getGraphicSubcomponents() { return m_subcomponents; } - ComponentGraphic* getParent() const; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - void setLocked(bool locked) override; - bool handlePortGraphicMoveAttempt(const PortGraphic* port, const QPointF& newBorderPos); - - void setExpanded(bool isExpanded); - void registerWire(WireGraphic* wire); - - GraphicsBaseItem* moduleParent() override; - - /** - * @brief setUserVisible - * Called whenever the user enables the visibility of a component. - */ - void setUserVisible(bool state); - const auto& outputPorts() const { return m_outputPorts; } + ComponentGraphic(SimComponent *c, ComponentGraphic *parent); + + QRectF boundingRect() const override; + QPainterPath shape() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *) override; + + /** + * @brief paintOverlay + * May be implemented by derived classes. + * Called after ComponentGraphic::paint (painting of the basic shape/outline + * of the component), wherein derived class specific painting is painted on + * top + */ + virtual void paintOverlay(QPainter *, const QStyleOptionGraphicsItem *, + QWidget *) {} + + void initialize(bool placeAndRoute = false); + bool restrictSubcomponentPositioning() const { + return m_restrictSubcomponentPositioning; + } + std::vector &getGraphicSubcomponents() { + return m_subcomponents; + } + ComponentGraphic *getParent() const; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + void setLocked(bool locked) override; + bool handlePortGraphicMoveAttempt(const PortGraphic *port, + const QPointF &newBorderPos); + + void setExpanded(bool isExpanded); + void registerWire(WireGraphic *wire); + + GraphicsBaseItem *moduleParent() override; + + /** + * @brief setUserVisible + * Called whenever the user enables the visibility of a component. + */ + void setUserVisible(bool state); + const auto &outputPorts() const { return m_outputPorts; } private slots: - /** - * @brief handleGridPosChange - * Slot called when position of grid component changed through the grid-layer (ie. through place and route). - */ - void handleGridPosChange(const QPoint pos); - void handlePortPosChanged(const vsrtl::SimPort* port); - void updateGeometry(); - void setIndicatorState(vsrtl::PortGraphic* p, bool enabled); + /** + * @brief handleGridPosChange + * Slot called when position of grid component changed through the grid-layer + * (ie. through place and route). + */ + void handleGridPosChange(const QPoint pos); + void handlePortPosChanged(const vsrtl::SimPort *port); + void updateGeometry(); + void setIndicatorState(vsrtl::PortGraphic *p, bool enabled); private: - void verifySpecialSignals() const; + void verifySpecialSignals() const; protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event) override; - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; - void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override; - QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) override; - void paintIndicator(QPainter* painter, PortGraphic* port, QColor color); - - enum class GeometryChange { - None, - Resize, - Expand, - Collapse, - ChildJustExpanded, - ChildJustCollapsed, - }; - void createSubcomponents(bool doPlaceAndRoute); - QRectF sceneGridRect() const; - - bool m_restrictSubcomponentPositioning = false; - bool m_inResizeDragZone = false; - bool m_resizeDragging = false; - bool m_isTopLevelSerializedComponent = false; - /** - * @brief m_userHidden - * True if the user has asked to hide this component. Maintains logical hide-state even - * if the parent component is collaposed, rendering this component as non-visible in the scene. - */ - bool m_userHidden = false; - bool userHidden() const { return m_userHidden; } - - std::set m_indicators; - std::vector m_subcomponents; - - /** - * @brief m_wires - * WireGraphics always lie within some parent ComponentGraphic. When ports create their wires, they will register - * their wire with a corresponding ComponentGraphic. These registrations may in turn be used to toggle the - * visibility of wires inside a ComponentGraphic, based on the expansion state of the ComponentGraphic. - */ - std::vector m_wires; - - QMap m_inputPorts; - QMap m_outputPorts; - - Label* m_label; - std::shared_ptr m_labelVisibilityAction; - - // Rectangles - QPainterPath m_shape; - - QPolygon m_gridPoints; - - QFont m_font; - - QPointF m_expandButtonPos; // Draw position of expand/collapse button in scene coordinates - ComponentButton* m_expandButton = nullptr; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; + QVariant itemChange(QGraphicsItem::GraphicsItemChange change, + const QVariant &value) override; + void paintIndicator(QPainter *painter, PortGraphic *port, QColor color); + + enum class GeometryChange { + None, + Resize, + Expand, + Collapse, + ChildJustExpanded, + ChildJustCollapsed, + }; + void createSubcomponents(bool doPlaceAndRoute); + QRectF sceneGridRect() const; + + bool m_restrictSubcomponentPositioning = false; + bool m_inResizeDragZone = false; + bool m_resizeDragging = false; + bool m_isTopLevelSerializedComponent = false; + /** + * @brief m_userHidden + * True if the user has asked to hide this component. Maintains logical + * hide-state even if the parent component is collaposed, rendering this + * component as non-visible in the scene. + */ + bool m_userHidden = false; + bool userHidden() const { return m_userHidden; } + + std::set m_indicators; + std::vector m_subcomponents; + + /** + * @brief m_wires + * WireGraphics always lie within some parent ComponentGraphic. When ports + * create their wires, they will register their wire with a corresponding + * ComponentGraphic. These registrations may in turn be used to toggle the + * visibility of wires inside a ComponentGraphic, based on the expansion state + * of the ComponentGraphic. + */ + std::vector m_wires; + + QMap m_inputPorts; + QMap m_outputPorts; + + Label *m_label; + std::shared_ptr m_labelVisibilityAction; + + // Rectangles + QPainterPath m_shape; + + QPolygon m_gridPoints; + + QFont m_font; + + QPointF m_expandButtonPos; // Draw position of expand/collapse button in scene + // coordinates + ComponentButton *m_expandButton = nullptr; public slots: - void loadLayoutFile(const QString& file); - void loadLayout(); - void saveLayout(); - void resetWires(); - void parameterDialogTriggered(); + void loadLayoutFile(const QString &file); + void loadLayout(); + void saveLayout(); + void resetWires(); + void parameterDialogTriggered(); public: - // Bump this when making logic-changing modifications to the serialization logic - enum LayoutVersions { NoLayoutVersion, v1, LatestLayoutVersion }; - uint32_t m_layoutVersion = 0; - - uint32_t layoutVersion() const override { - if (m_isTopLevelSerializedComponent) { - return m_layoutVersion; - } else { - return GraphicsBaseItem::layoutVersion(); - } + // Bump this when making logic-changing modifications to the serialization + // logic + enum LayoutVersions { NoLayoutVersion, v1, LatestLayoutVersion }; + uint32_t m_layoutVersion = 0; + + uint32_t layoutVersion() const override { + if (m_isTopLevelSerializedComponent) { + return m_layoutVersion; + } else { + return GraphicsBaseItem::layoutVersion(); + } + } + + template + void serialize(Archive &archive) { + setSerializing(true); + // Serialize the original component name. Wires within the component will + // reference this when describing parent components, but this component may + // have different names based on the design which instantiated it. Thus, we + // need to replace the stored name with the actual name of the component. + try { + std::string storedName = getComponent()->getName(); + archive(cereal::make_nvp("Top name", storedName)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } - template - void serialize(Archive& archive) { - setSerializing(true); - // Serialize the original component name. Wires within the component will reference this when describing parent - // components, but this component may have different names based on the design which instantiated it. - // Thus, we need to replace the stored name with the actual name of the component. - try { - std::string storedName = getComponent()->getName(); - archive(cereal::make_nvp("Top name", storedName)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - // Serialize expansion state - if (hasSubcomponents()) { - try { - bool expanded = isExpanded(); - archive(cereal::make_nvp("Expanded", expanded)); - if (expanded != isExpanded()) { - setExpanded(expanded); - } - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } + // Serialize expansion state + if (hasSubcomponents()) { + try { + bool expanded = isExpanded(); + archive(cereal::make_nvp("Expanded", expanded)); + if (expanded != isExpanded()) { + setExpanded(expanded); } + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + } - // Serialize size - try { - QRect r = getCurrentComponentRect(); - archive(cereal::make_nvp("Rect", r)); - adjust(r); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } + // Serialize size + try { + QRect r = getCurrentComponentRect(); + archive(cereal::make_nvp("Rect", r)); + adjust(r); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } - // Serialize rotation - try { - archive(cereal::make_nvp("rot", m_gridRotation)); - } catch (const cereal::Exception& e) { - } + // Serialize rotation + try { + archive(cereal::make_nvp("rot", m_gridRotation)); + } catch (const cereal::Exception &e) { + } - // If this is not a top level component, we should serialize its position within its parent component - if (!m_isTopLevelSerializedComponent) { - // Serealize position within parent component - try { - QPoint p = pos().toPoint(); - archive(cereal::make_nvp("Pos", p)); - setPos(p); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - // Serialize visibility state - try { - bool v = isVisible(); - archive(cereal::make_nvp("Visible", v)); - archive(cereal::make_nvp("User hidden", m_userHidden)); - setVisible(v && !m_userHidden); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - } + // If this is not a top level component, we should serialize its position + // within its parent component + if (!m_isTopLevelSerializedComponent) { + // Serealize position within parent component + try { + QPoint p = pos().toPoint(); + archive(cereal::make_nvp("Pos", p)); + setPos(p); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + // Serialize visibility state + try { + bool v = isVisible(); + archive(cereal::make_nvp("Visible", v)); + archive(cereal::make_nvp("User hidden", m_userHidden)); + setVisible(v && !m_userHidden); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + } - /** Serialize port positions - * @todo this is right now done through GridComponent. In reality, all serialization of grid-component logic - * (size, positions etc.) should be performed in the grid component. - */ - serializeBorder(archive); - - // Serialize ports - for (const auto& pm : {m_inputPorts, m_outputPorts}) { - for (const auto& p : pm) { - try { - archive(cereal::make_nvp(p->getPort()->getName(), *p)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - } - } + /** Serialize port positions + * @todo this is right now done through GridComponent. In reality, all + * serialization of grid-component logic (size, positions etc.) should be + * performed in the grid component. + */ + serializeBorder(archive); - if (hasSubcomponents()) { - // Serialize wires from input ports to subcomponents - // @todo: should this be in port serialization? - for (auto& p : m_inputPorts) { - try { - archive(cereal::make_nvp(p->getPort()->getName() + "_in_wire", *p->getOutputWire())); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - } - - // Serealize subcomponents - for (const auto& c : m_subcomponents) { - try { - archive(cereal::make_nvp(c->getComponent()->getName(), *c)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - } + // Serialize ports + for (const auto &pm : {m_inputPorts, m_outputPorts}) { + for (const auto &p : pm) { + try { + archive(cereal::make_nvp(p->getPort()->getName(), *p)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + } + } - // If this is a top-level component, we should _not_ serialize the output wires. Layouts should be compatible - // between designs, and the output wire of a top-level component with subcomponents may connect to components - // which are present in óne design but not another. - if (!m_isTopLevelSerializedComponent) { - // Serialize output wire - for (auto& p : m_outputPorts) { - try { - archive(cereal::make_nvp(p->getPort()->getName() + "_out_wire", *p->getOutputWire())); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - } + if (hasSubcomponents()) { + // Serialize wires from input ports to subcomponents + // @todo: should this be in port serialization? + for (auto &p : m_inputPorts) { + try { + archive(cereal::make_nvp(p->getPort()->getName() + "_in_wire", + *p->getOutputWire())); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + } - // Serialize component name label + // Serealize subcomponents + for (const auto &c : m_subcomponents) { try { - archive(cereal::make_nvp("Name label", *m_label)); - } catch (const cereal::Exception& e) { - /// @todo: build an error report + archive(cereal::make_nvp(c->getComponent()->getName(), *c)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + } + } - // Serialize indicators + // If this is a top-level component, we should _not_ serialize the output + // wires. Layouts should be compatible between designs, and the output wire + // of a top-level component with subcomponents may connect to components + // which are present in óne design but not another. + if (!m_isTopLevelSerializedComponent) { + // Serialize output wire + for (auto &p : m_outputPorts) { try { - std::set indicators; - for (const auto& i : m_indicators) { - indicators.emplace(i->getPort()->getName()); - } - archive(cereal::make_nvp("Indicators", indicators)); - for (const auto& pm : {m_inputPorts, m_outputPorts}) { - for (const auto& ip : pm) { - if (std::find(indicators.begin(), indicators.end(), ip->getPort()->getName()) != indicators.end()) { - m_indicators.emplace(ip); - } - } - } - } catch (const cereal::Exception& e) { - /// @todo: build an error report + archive(cereal::make_nvp(p->getPort()->getName() + "_out_wire", + *p->getOutputWire())); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + } + } - updateGeometry(); - setSerializing(false); + // Serialize component name label + try { + archive(cereal::make_nvp("Name label", *m_label)); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + + // Serialize indicators + try { + std::set indicators; + for (const auto &i : m_indicators) { + indicators.emplace(i->getPort()->getName()); + } + archive(cereal::make_nvp("Indicators", indicators)); + for (const auto &pm : {m_inputPorts, m_outputPorts}) { + for (const auto &ip : pm) { + if (std::find(indicators.begin(), indicators.end(), + ip->getPort()->getName()) != indicators.end()) { + m_indicators.emplace(ip); + } + } + } + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + updateGeometry(); + setSerializing(false); + } }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_COMPONENTGRAPHIC_H +#endif // VSRTL_COMPONENTGRAPHIC_H diff --git a/graphics/vsrtl_graphics_defines.h b/graphics/vsrtl_graphics_defines.h index b8cc73c..0b7f17d 100644 --- a/graphics/vsrtl_graphics_defines.h +++ b/graphics/vsrtl_graphics_defines.h @@ -6,12 +6,12 @@ #include "../interface/vsrtl_defines.h" -#define DRAW_BOUNDING_RECT(painter) \ - painter->save(); \ - painter->setPen(QPen(Qt::red, 1)); \ - painter->setBrush(Qt::transparent); \ - painter->drawRect(boundingRect()); \ - painter->restore(); +#define DRAW_BOUNDING_RECT(painter) \ + painter->save(); \ + painter->setPen(QPen(Qt::red, 1)); \ + painter->setBrush(Qt::transparent); \ + painter->drawRect(boundingRect()); \ + painter->restore(); // Allow vsrtl base value types to be used as a QVariant Q_DECLARE_METATYPE(vsrtl::VSRTL_VT_S) @@ -19,7 +19,12 @@ Q_DECLARE_METATYPE(vsrtl::VSRTL_VT_U) namespace vsrtl { -enum class ValueDisplayFormat { binary = 2, baseTen = 10, hex = 16, unicode = 99 }; +enum class ValueDisplayFormat { + binary = 2, + baseTen = 10, + hex = 16, + unicode = 99 +}; constexpr QColor WIRE_DEFAULT_COLOR = {0x63, 0x63, 0x63}; constexpr QColor WIRE_SELECTED_COLOR = {0xFE, 0xF1, 0x60}; @@ -50,5 +55,5 @@ constexpr QColor BUTTON_EXPAND_COLOR = {0x26, 0xa6, 0x5b}; #define PORT_INNER_MARGIN 5 -} // namespace vsrtl -#endif // VSRTL_GRAPHICS_DEFINES_H +} // namespace vsrtl +#endif // VSRTL_GRAPHICS_DEFINES_H diff --git a/graphics/vsrtl_graphics_util.cpp b/graphics/vsrtl_graphics_util.cpp index e45141b..956ba7f 100644 --- a/graphics/vsrtl_graphics_util.cpp +++ b/graphics/vsrtl_graphics_util.cpp @@ -1,5 +1,3 @@ #include "vsrtl_graphics_util.h" -namespace vsrtl { - -} +namespace vsrtl {} diff --git a/graphics/vsrtl_graphics_util.h b/graphics/vsrtl_graphics_util.h index a5efef9..6718cb1 100644 --- a/graphics/vsrtl_graphics_util.h +++ b/graphics/vsrtl_graphics_util.h @@ -11,126 +11,124 @@ namespace vsrtl { // Round up v to nearest multiple of m inline int roundUp(int v, int m) { - int remainder = v % m; - if (remainder == 0) - return v; - return v + m - remainder; + int remainder = v % m; + if (remainder == 0) + return v; + return v + m - remainder; } template -inline void scaleToGrid(RectType& r, int gridsize) { - r.setWidth(r.width() * gridsize); - r.setHeight(r.height() * gridsize); +inline void scaleToGrid(RectType &r, int gridsize) { + r.setWidth(r.width() * gridsize); + r.setHeight(r.height() * gridsize); } // Round v to nearest multiple of m (tie: round up) -inline int roundNear(int v, int m) { - return ((v + m / 2) / m) * m; -} +inline int roundNear(int v, int m) { return ((v + m / 2) / m) * m; } -inline void roundNear(QPointF& p, int m) { - p.setX(roundNear(p.x(), m)); - p.setY(roundNear(p.y(), m)); +inline void roundNear(QPointF &p, int m) { + p.setX(roundNear(p.x(), m)); + p.setY(roundNear(p.y(), m)); } template std::vector collect(T list, F func) { - std::vector r; - for (const auto& i : list) { - r.push_back((i->*func)()); - } - return r; + std::vector r; + for (const auto &i : list) { + r.push_back((i->*func)()); + } + return r; } template -auto trueRight(const RectType& r) { - return r.left() + r.width(); +auto trueRight(const RectType &r) { + return r.left() + r.width(); } template -auto trueBottom(const RectType& r) { - return r.top() + r.height(); +auto trueBottom(const RectType &r) { + return r.top() + r.height(); } template -RectType boundingRectOfRects(const RectType& r1, const RectType& r2) { - qreal top, bottom, right, left; - left = r1.left() < r2.left() ? r1.left() : r2.left(); - right = trueRight(r1) > trueRight(r2) ? trueRight(r1) : trueRight(r2); - top = r1.top() < r2.top() ? r1.top() : r2.top(); - bottom = trueBottom(r1) > trueBottom(r2) ? trueBottom(r1) : trueBottom(r2); - - return RectType(left, top, right - left, bottom - top); +RectType boundingRectOfRects(const RectType &r1, const RectType &r2) { + qreal top, bottom, right, left; + left = r1.left() < r2.left() ? r1.left() : r2.left(); + right = trueRight(r1) > trueRight(r2) ? trueRight(r1) : trueRight(r2); + top = r1.top() < r2.top() ? r1.top() : r2.top(); + bottom = trueBottom(r1) > trueBottom(r2) ? trueBottom(r1) : trueBottom(r2); + + return RectType(left, top, right - left, bottom - top); } template -bool snapRectToInnerRect(const RectType& inner, RectType& snapping) { - bool snap_r, snap_b; - snap_r = false; - snap_b = false; - - if (snapping.right() < inner.right()) { - snapping.setRight(inner.right()); - snap_r = true; - } - if (snapping.bottom() < inner.bottom()) { - snapping.setBottom(inner.bottom()); - snap_b = true; - } - - return !(snap_r & snap_b); +bool snapRectToInnerRect(const RectType &inner, RectType &snapping) { + bool snap_r, snap_b; + snap_r = false; + snap_b = false; + + if (snapping.right() < inner.right()) { + snapping.setRight(inner.right()); + snap_r = true; + } + if (snapping.bottom() < inner.bottom()) { + snapping.setBottom(inner.bottom()); + snap_b = true; + } + + return !(snap_r & snap_b); } template -bool snapRectToOuterRect(const RectType& outer, RectType& snapping) { - bool snap_r, snap_b; - snap_r = false; - snap_b = false; - - if (snapping.right() > outer.right()) { - snapping.setRight(outer.right()); - snap_r = true; - } - if (snapping.bottom() > outer.bottom()) { - snapping.setBottom(outer.bottom()); - snap_b = true; - } - - return !(snap_r & snap_b); +bool snapRectToOuterRect(const RectType &outer, RectType &snapping) { + bool snap_r, snap_b; + snap_r = false; + snap_b = false; + + if (snapping.right() > outer.right()) { + snapping.setRight(outer.right()); + snap_r = true; + } + if (snapping.bottom() > outer.bottom()) { + snapping.setBottom(outer.bottom()); + snap_b = true; + } + + return !(snap_r & snap_b); } template -RectType boundingRectOfRects(const std::vector& rects) { - if (rects.size() == 0) { - return RectType(); - } - RectType boundingRect = rects.at(0); - for (unsigned i = 1; i < rects.size(); i++) { - boundingRect = boundingRectOfRects(boundingRect, rects.at(i)); - } - return boundingRect; +RectType boundingRectOfRects(const std::vector &rects) { + if (rects.size() == 0) { + return RectType(); + } + RectType boundingRect = rects.at(0); + for (unsigned i = 1; i < rects.size(); i++) { + boundingRect = boundingRectOfRects(boundingRect, rects.at(i)); + } + return boundingRect; } template -RectType normalizeRect(const RectType& r1) { - auto r = r1; - r.setTopLeft(QPointF(0, 0)); - return r; +RectType normalizeRect(const RectType &r1) { + auto r = r1; + r.setTopLeft(QPointF(0, 0)); + return r; } -inline void getAllChildren(QGraphicsItem* p, QList& acc) { - if (p->childItems().size() == 0) { - // Leaf - acc.push_back(p); - } else { - const auto children = p->childItems(); - for (const auto& c : qAsConst(children)) { - getAllChildren(c, acc); - } - acc.push_back(p); +inline void getAllChildren(QGraphicsItem *p, QList &acc) { + if (p->childItems().size() == 0) { + // Leaf + acc.push_back(p); + } else { + const auto children = p->childItems(); + for (const auto &c : qAsConst(children)) { + getAllChildren(c, acc); } + acc.push_back(p); + } } -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_GRAPHICS_UTIL_H +#endif // VSRTL_GRAPHICS_UTIL_H diff --git a/graphics/vsrtl_graphicsbase.h b/graphics/vsrtl_graphicsbase.h index 7926d4e..a143630 100644 --- a/graphics/vsrtl_graphicsbase.h +++ b/graphics/vsrtl_graphicsbase.h @@ -1,71 +1,76 @@ #ifndef VSRTL_GRAPHICSBASE_H #define VSRTL_GRAPHICSBASE_H -#include #include "gallantsignalwrapper.h" +#include namespace vsrtl { class GraphicsBase { public: - /** - * @brief The VirtualChildLink struct - * Defines which changes to mirror in the virtual child - */ - enum VirtualChildLink { Position = 0b1, Visibility = 0b10 }; - Q_DECLARE_FLAGS(VirtualChildLinks, VirtualChildLink); + /** + * @brief The VirtualChildLink struct + * Defines which changes to mirror in the virtual child + */ + enum VirtualChildLink { Position = 0b1, Visibility = 0b10 }; + Q_DECLARE_FLAGS(VirtualChildLinks, VirtualChildLink); - GraphicsBase() {} - virtual ~GraphicsBase() {} + GraphicsBase() {} + virtual ~GraphicsBase() {} - /** - * @brief postSceneConstructionInitialize# - * Some graphic components may need extra initialization steps after all graphics have been added to the scene (such - * as wires). When overriding, overriding function must call GraphicsBase::postSceneConstructionInitialize#() - * - * Multiple passes may be used, allowing for staged initialization - */ - virtual void postSceneConstructionInitialize1() = 0; - virtual void postSceneConstructionInitialize2() = 0; + /** + * @brief postSceneConstructionInitialize# + * Some graphic components may need extra initialization steps after all + * graphics have been added to the scene (such as wires). When overriding, + * overriding function must call + * GraphicsBase::postSceneConstructionInitialize#() + * + * Multiple passes may be used, allowing for staged initialization + */ + virtual void postSceneConstructionInitialize1() = 0; + virtual void postSceneConstructionInitialize2() = 0; - /** - * @brief setLocked - * Toggles any interaction with the object in the scene. Components may specialize, if further modifications to the - * components behaviour is required - */ - virtual void setLocked(bool locked) = 0; + /** + * @brief setLocked + * Toggles any interaction with the object in the scene. Components may + * specialize, if further modifications to the components behaviour is + * required + */ + virtual void setLocked(bool locked) = 0; - void setMoveable(bool moveable = true) { - m_isMoveable = moveable; - setLocked(!moveable); - } - virtual bool isLocked() const = 0; + void setMoveable(bool moveable = true) { + m_isMoveable = moveable; + setLocked(!moveable); + } + virtual bool isLocked() const = 0; - virtual void setSerializing(bool state) = 0; - bool isSerializing() const { return m_isSerializing; } + virtual void setSerializing(bool state) = 0; + bool isSerializing() const { return m_isSerializing; } - void addVirtualChild(const VirtualChildLinks& link, GraphicsBase* child) { - Q_ASSERT(m_virtualChildren.count(child) == 0); - m_virtualChildren[child] = link; - child->m_virtualParents[this] = link; - } + void addVirtualChild(const VirtualChildLinks &link, GraphicsBase *child) { + Q_ASSERT(m_virtualChildren.count(child) == 0); + m_virtualChildren[child] = link; + child->m_virtualParents[this] = link; + } - /** - * @brief m_virtualChildren - * Virtual children are items which have no QGraphicsItem child/parent relationship to this item, but who should - * mirror position and visibility changes made to this object. - */ - std::map m_virtualChildren; - std::map m_virtualParents; + /** + * @brief m_virtualChildren + * Virtual children are items which have no QGraphicsItem child/parent + * relationship to this item, but who should mirror position and visibility + * changes made to this object. + */ + std::map m_virtualChildren; + std::map m_virtualParents; - bool m_initialized = false; - bool m_isMoveable = false; + bool m_initialized = false; + bool m_isMoveable = false; - /** Flag for indicating when serializing this component. When active, GraphicsBase derived objects may - * temporarily disable various runtime-enabled checks which will could deserialization. */ - bool m_isSerializing = false; + /** Flag for indicating when serializing this component. When active, + * GraphicsBase derived objects may temporarily disable various + * runtime-enabled checks which will could deserialization. */ + bool m_isSerializing = false; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_GRAPHICSBASE_H +#endif // VSRTL_GRAPHICSBASE_H diff --git a/graphics/vsrtl_graphicsbaseitem.h b/graphics/vsrtl_graphicsbaseitem.h index ea4011c..857059a 100644 --- a/graphics/vsrtl_graphicsbaseitem.h +++ b/graphics/vsrtl_graphicsbaseitem.h @@ -1,8 +1,8 @@ #pragma once -#include #include "vsrtl_graphicsbase.h" #include "vsrtl_scene.h" +#include namespace vsrtl { @@ -10,162 +10,178 @@ template class GraphicsBaseItem; template -void recurseToChildren(GraphicsBaseItem* parent, const F& func) { - // Propagate post scene construction initialization call to child items - for (const auto& child : parent->childItems()) { - GraphicsBase* g_child = dynamic_cast(child); - if (g_child) { - func(g_child); - } +void recurseToChildren(GraphicsBaseItem *parent, const F &func) { + // Propagate post scene construction initialization call to child items + for (const auto &child : parent->childItems()) { + GraphicsBase *g_child = dynamic_cast(child); + if (g_child) { + func(g_child); } + } } /** - * Base type can be any QGraphicsItem derived type. This is useful if leveraging already available items such as - * QGraphicsTextItem etc.. + * Base type can be any QGraphicsItem derived type. This is useful if leveraging + * already available items such as QGraphicsTextItem etc.. */ template class GraphicsBaseItem : public GraphicsBase, public T { - static_assert(std::is_base_of::value, "GraphicsBaseItem must derive from QGraphicsItem"); + static_assert(std::is_base_of::value, + "GraphicsBaseItem must derive from QGraphicsItem"); public: - GraphicsBaseItem(QGraphicsItem* parent) : T(parent) { - T::setFlag(QGraphicsItem::ItemSendsGeometryChanges); - // Always ensure that this is off - scene position changes absolutely kills performance when we have many nested - // components. - T::setFlag(QGraphicsItem::ItemSendsScenePositionChanges, false); + GraphicsBaseItem(QGraphicsItem *parent) : T(parent) { + T::setFlag(QGraphicsItem::ItemSendsGeometryChanges); + // Always ensure that this is off - scene position changes absolutely kills + // performance when we have many nested components. + T::setFlag(QGraphicsItem::ItemSendsScenePositionChanges, false); + } + + void postSceneConstructionInitialize1() override { + recurseToChildren(this, [](GraphicsBase *child) { + child->postSceneConstructionInitialize1(); + }); + } + void postSceneConstructionInitialize2() override { + recurseToChildren(this, [](GraphicsBase *child) { + child->postSceneConstructionInitialize2(); + }); + + m_initialized = true; + } + + /** + * @brief moduleParent + * @return the first SimComponent which encloses this GraphicsBaseItem + */ + virtual GraphicsBaseItem *moduleParent() { + auto *parent = + dynamic_cast *>(T::parentItem()); + Q_ASSERT(parent); + return parent->moduleParent(); + } + + template + T_C *createModuleChild(const VirtualChildLinks &link, Args... args) { + auto *parent = moduleParent(); + Q_ASSERT(parent); + auto ptr = new T_C(parent, args...); + // Set initial position to (0,0) in this components coordinate system + const auto p = T::mapToItem(parent, QPointF(0, 0)); + ptr->setPos(p); + addVirtualChild(link, ptr); + return ptr; + } + + void setLocked(bool locked) override { + if (!m_isMoveable) + return; + + if (locked) + T::setFlags(T::flags() & ~QGraphicsItem::ItemIsMovable); + else + T::setFlag(QGraphicsItem::ItemIsMovable); + } + + bool isLocked() const override { + if (auto *p = dynamic_cast(T::scene())) { + return p->isLocked(); + } else { + return false; } - - void postSceneConstructionInitialize1() override { - recurseToChildren(this, [](GraphicsBase* child) { child->postSceneConstructionInitialize1(); }); - } - void postSceneConstructionInitialize2() override { - recurseToChildren(this, [](GraphicsBase* child) { child->postSceneConstructionInitialize2(); }); - - m_initialized = true; + } + + void setSerializing(bool state) override { + m_isSerializing = state; + // Recursively propagate serialize state to children + for (const auto &c : T::childItems()) { + if (auto *gb = dynamic_cast(c)) { + gb->setSerializing(state); + } } - - /** - * @brief moduleParent - * @return the first SimComponent which encloses this GraphicsBaseItem - */ - virtual GraphicsBaseItem* moduleParent() { - auto* parent = dynamic_cast*>(T::parentItem()); - Q_ASSERT(parent); - return parent->moduleParent(); - } - - template - T_C* createModuleChild(const VirtualChildLinks& link, Args... args) { - auto* parent = moduleParent(); - Q_ASSERT(parent); - auto ptr = new T_C(parent, args...); - // Set initial position to (0,0) in this components coordinate system - const auto p = T::mapToItem(parent, QPointF(0, 0)); - ptr->setPos(p); - addVirtualChild(link, ptr); - return ptr; - } - - void setLocked(bool locked) override { - if (!m_isMoveable) - return; - - if (locked) - T::setFlags(T::flags() & ~QGraphicsItem::ItemIsMovable); - else - T::setFlag(QGraphicsItem::ItemIsMovable); - } - - bool isLocked() const override { - if (auto* p = dynamic_cast(T::scene())) { - return p->isLocked(); - } else { - return false; + } + + virtual uint32_t layoutVersion() const { + auto *parent = + dynamic_cast *>(T::parentItem()); + Q_ASSERT(parent); + return parent->layoutVersion(); + } + + QVariant itemChange(QGraphicsItem::GraphicsItemChange change, + const QVariant &value) override { + Q_ASSERT((T::flags() & QGraphicsItem::ItemSendsScenePositionChanges) == 0 && + "ItemSendsScenePositionChanges should never be enabled - kills " + "performance"); + const auto curPos = T::pos(); + const auto dp = curPos - m_prePos; + + // Propagate completed state changes to virtual children + if ((change == QGraphicsItem::ItemPositionHasChanged) || + (change == QGraphicsItem::ItemVisibleHasChanged)) { + // Propagate geometry state changes to virtual children + if (m_virtualChildren.size() != 0) { + const bool vis = T::isVisible(); + for (auto &vt : m_virtualChildren) { + if (change == QGraphicsItem::ItemPositionHasChanged && + vt.second.testFlag(Position)) { + dynamic_cast(vt.first)->moveBy(dp.x(), dp.y()); + } else if (change == QGraphicsItem::ItemVisibleHasChanged && + vt.second.testFlag(Visibility)) { + dynamic_cast(vt.first)->setVisible(vis); + } } + } } - - void setSerializing(bool state) override { - m_isSerializing = state; - // Recursively propagate serialize state to children - for (const auto& c : T::childItems()) { - if (auto* gb = dynamic_cast(c)) { - gb->setSerializing(state); - } + m_prePos = curPos; + + // Decide state change based on virtual parent status + if (!isSerializing() && (change == QGraphicsItem::ItemVisibleChange)) { + bool virtualParentsVisible = true; + for (const auto &vp : m_virtualParents) { + if (vp.second.testFlag(Visibility)) { + virtualParentsVisible &= + dynamic_cast(vp.first)->isVisible(); } + } + if (!virtualParentsVisible) { + // reject visibility change event + return QVariant(); + } } - virtual uint32_t layoutVersion() const { - auto* parent = dynamic_cast*>(T::parentItem()); - Q_ASSERT(parent); - return parent->layoutVersion(); + // Handle Z level based on selection state + if (change == QGraphicsItem::ItemSelectedChange) { + if (value.toBool()) { + m_preZLayer = T::zValue(); + T::setZValue(VSRTLScene::Z_Selected); + } else { + T::setZValue(m_preZLayer); + } } - QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) override { - Q_ASSERT((T::flags() & QGraphicsItem::ItemSendsScenePositionChanges) == 0 && - "ItemSendsScenePositionChanges should never be enabled - kills performance"); - const auto curPos = T::pos(); - const auto dp = curPos - m_prePos; - - // Propagate completed state changes to virtual children - if ((change == QGraphicsItem::ItemPositionHasChanged) || (change == QGraphicsItem::ItemVisibleHasChanged)) { - // Propagate geometry state changes to virtual children - if (m_virtualChildren.size() != 0) { - const bool vis = T::isVisible(); - for (auto& vt : m_virtualChildren) { - if (change == QGraphicsItem::ItemPositionHasChanged && vt.second.testFlag(Position)) { - dynamic_cast(vt.first)->moveBy(dp.x(), dp.y()); - } else if (change == QGraphicsItem::ItemVisibleHasChanged && vt.second.testFlag(Visibility)) { - dynamic_cast(vt.first)->setVisible(vis); - } - } - } - } - m_prePos = curPos; - - // Decide state change based on virtual parent status - if (!isSerializing() && (change == QGraphicsItem::ItemVisibleChange)) { - bool virtualParentsVisible = true; - for (const auto& vp : m_virtualParents) { - if (vp.second.testFlag(Visibility)) { - virtualParentsVisible &= dynamic_cast(vp.first)->isVisible(); - } - } - if (!virtualParentsVisible) { - // reject visibility change event - return QVariant(); - } - } - - // Handle Z level based on selection state - if (change == QGraphicsItem::ItemSelectedChange) { - if (value.toBool()) { - m_preZLayer = T::zValue(); - T::setZValue(VSRTLScene::Z_Selected); - } else { - T::setZValue(m_preZLayer); - } - } - - return QGraphicsItem::itemChange(change, value); - } + return QGraphicsItem::itemChange(change, value); + } - /** - * @brief modulePositionChanged - * This function adds the capabilities of QGraphicsItem::ItemScenePositionHasChanged restricted to within the scope - * of a module and its direct children, drawn inside the module. By avoiding to use the itemScenePos change - * infrastructure, we gain a massive speedup, while still being able to notify the correct entities that module - * position has changed. - */ - virtual void modulePositionHasChanged() {} + /** + * @brief modulePositionChanged + * This function adds the capabilities of + * QGraphicsItem::ItemScenePositionHasChanged restricted to within the scope + * of a module and its direct children, drawn inside the module. By avoiding + * to use the itemScenePos change infrastructure, we gain a massive speedup, + * while still being able to notify the correct entities that module position + * has changed. + */ + virtual void modulePositionHasChanged() {} protected: - // State-change preservation needed for Item#HasChanged value difference calculations - QPointF m_prePos; + // State-change preservation needed for Item#HasChanged value difference + // calculations + QPointF m_prePos; private: - // Buffer Z-layer value in between selection state changes - qreal m_preZLayer; + // Buffer Z-layer value in between selection state changes + qreal m_preZLayer; }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_gridcomponent.cpp b/graphics/vsrtl_gridcomponent.cpp index 3b8cc7b..5af1742 100644 --- a/graphics/vsrtl_gridcomponent.cpp +++ b/graphics/vsrtl_gridcomponent.cpp @@ -8,381 +8,408 @@ namespace vsrtl { -GridComponent::GridComponent(SimComponent* c, GridComponent* parent) - : GraphicsBaseItem(parent), m_component(c), m_border(std::make_unique(c)) { - setInitialRect(); - m_currentExpandedRect = m_currentSubcomponentBoundingRect; +GridComponent::GridComponent(SimComponent *c, GridComponent *parent) + : GraphicsBaseItem(parent), m_component(c), + m_border(std::make_unique(c)) { + setInitialRect(); + m_currentExpandedRect = m_currentSubcomponentBoundingRect; } void GridComponent::setExpanded(bool state) { - if (!hasSubcomponents()) - return; + if (!hasSubcomponents()) + return; - m_lastComponentRect = getCurrentComponentRect(); - m_expanded = state; - spreadPortsOrdered(); + m_lastComponentRect = getCurrentComponentRect(); + m_expanded = state; + spreadPortsOrdered(); - // This component just modified its geometry - this might require the parent component to expand its current - // bounding rect - auto* parent = dynamic_cast(parentItem()); - if (parent && hasSubcomponents()) - parent->childGeometryChanged(); + // This component just modified its geometry - this might require the parent + // component to expand its current bounding rect + auto *parent = dynamic_cast(parentItem()); + if (parent && hasSubcomponents()) + parent->childGeometryChanged(); - emit gridRectChanged(); + emit gridRectChanged(); } -bool GridComponent::adjust(const QPoint& p) { - if (p == QPoint(0, 0)) - return false; - - const auto& minRect = getCurrentMinRect(); - auto newRect = getCurrentComponentRect(); - newRect.adjust(0, 0, p.x(), p.y()); - - if (!parentIsPlacing() && !isSerializing()) { - // Parent is not placing components, snap subcomponents inside rect inside parent - snapRectToInnerRect(minRect, newRect); - auto* parent = dynamic_cast(parentItem()); - if (parent) { - // Snap new rect to stay within parent - newRect.translate(m_relPos); - snapRectToOuterRect(parent->getCurrentComponentRect(), newRect); - newRect.translate(-m_relPos); - } +bool GridComponent::adjust(const QPoint &p) { + if (p == QPoint(0, 0)) + return false; + + const auto &minRect = getCurrentMinRect(); + auto newRect = getCurrentComponentRect(); + newRect.adjust(0, 0, p.x(), p.y()); + + if (!parentIsPlacing() && !isSerializing()) { + // Parent is not placing components, snap subcomponents inside rect inside + // parent + snapRectToInnerRect(minRect, newRect); + auto *parent = dynamic_cast(parentItem()); + if (parent) { + // Snap new rect to stay within parent + newRect.translate(m_relPos); + snapRectToOuterRect(parent->getCurrentComponentRect(), newRect); + newRect.translate(-m_relPos); } + } - // Rect is now snapped, calculate difference between current and new rect - const QPoint diff = newRect.bottomRight() - getCurrentComponentRect().bottomRight(); + // Rect is now snapped, calculate difference between current and new rect + const QPoint diff = + newRect.bottomRight() - getCurrentComponentRect().bottomRight(); - updateCurrentComponentRect(diff.x(), diff.y()); + updateCurrentComponentRect(diff.x(), diff.y()); - auto* parent = dynamic_cast(parentItem()); - if (parent && (p.x() > 0 || p.y() > 0)) { - parent->childGeometryChanged(); - } + auto *parent = dynamic_cast(parentItem()); + if (parent && (p.x() > 0 || p.y() > 0)) { + parent->childGeometryChanged(); + } - return true; + return true; } -bool GridComponent::adjust(const QRect& newRect) { - auto r1 = getCurrentComponentRect(); - auto r2 = newRect; - r2.setTopLeft({0, 0}); - r1.setTopLeft({0, 0}); - const QPoint diff = r2.bottomRight() - r1.bottomRight(); - return adjust(diff); +bool GridComponent::adjust(const QRect &newRect) { + auto r1 = getCurrentComponentRect(); + auto r2 = newRect; + r2.setTopLeft({0, 0}); + r1.setTopLeft({0, 0}); + const QPoint diff = r2.bottomRight() - r1.bottomRight(); + return adjust(diff); } void GridComponent::childGeometryChanged() { - if (!isSerializing()) { - updateSubcomponentBoundingRect(); - } + if (!isSerializing()) { + updateSubcomponentBoundingRect(); + } } bool GridComponent::hasSubcomponents() const { - return m_component->hasSubcomponents(); + return m_component->hasSubcomponents(); } -bool GridComponent::move(const QPoint& pos) { - if (parentIsPlacing()) { - // Parent is placing components, do not try to snap inside parent - m_relPos = pos; - emit gridPosChanged(m_relPos); - return true; - } - - // Restrict positioning to inside parent rect - QPoint newPos = pos; - auto* parent = dynamic_cast(parentItem()); - if (parent) { - newPos.setX(qMin(parent->getCurrentComponentRect().right() + 1 - getCurrentComponentRect().width(), - qMax(newPos.x(), 0))); - newPos.setY(qMin(parent->getCurrentComponentRect().bottom() + 1 - getCurrentComponentRect().height(), - qMax(newPos.y(), 0))); - } - - auto translatedRectInParentCS = getCurrentComponentRect().translated(newPos); - if (!parentIsPlacing() && !parentContainsRect(translatedRectInParentCS)) - return false; +bool GridComponent::move(const QPoint &pos) { + if (parentIsPlacing()) { + // Parent is placing components, do not try to snap inside parent + m_relPos = pos; + emit gridPosChanged(m_relPos); + return true; + } + + // Restrict positioning to inside parent rect + QPoint newPos = pos; + auto *parent = dynamic_cast(parentItem()); + if (parent) { + newPos.setX(qMin(parent->getCurrentComponentRect().right() + 1 - + getCurrentComponentRect().width(), + qMax(newPos.x(), 0))); + newPos.setY(qMin(parent->getCurrentComponentRect().bottom() + 1 - + getCurrentComponentRect().height(), + qMax(newPos.y(), 0))); + } + + auto translatedRectInParentCS = getCurrentComponentRect().translated(newPos); + if (!parentIsPlacing() && !parentContainsRect(translatedRectInParentCS)) + return false; - m_relPos = newPos; - if (parent) { - parent->childGeometryChanged(); - } + m_relPos = newPos; + if (parent) { + parent->childGeometryChanged(); + } - return true; + return true; } void GridComponent::placeAndRouteSubcomponents() { - m_isPlacing = true; - const auto& placements = PlaceRoute::get()->placeAndRoute(getGridSubcomponents()); - for (const auto& p : placements) { - p.first->move(p.second); - } - m_isPlacing = false; - updateSubcomponentBoundingRect(); + m_isPlacing = true; + const auto &placements = + PlaceRoute::get()->placeAndRoute(getGridSubcomponents()); + for (const auto &p : placements) { + p.first->move(p.second); + } + m_isPlacing = false; + updateSubcomponentBoundingRect(); } bool GridComponent::parentIsPlacing() const { - auto* p = dynamic_cast(parentItem()); - if (p) - return p->m_isPlacing; - return false; + auto *p = dynamic_cast(parentItem()); + if (p) + return p->m_isPlacing; + return false; } -bool GridComponent::parentContainsRect(const QRect& r) const { - auto* gc_parent = dynamic_cast(parentItem()); - if (gc_parent == nullptr) - return true; +bool GridComponent::parentContainsRect(const QRect &r) const { + auto *gc_parent = dynamic_cast(parentItem()); + if (gc_parent == nullptr) + return true; - return gc_parent->getCurrentComponentRect().contains(r); + return gc_parent->getCurrentComponentRect().contains(r); } -std::vector GridComponent::getGridSubcomponents() const { - std::vector c; - const auto children = childItems(); - for (const auto& subc : qAsConst(children)) { - auto* ptr = dynamic_cast(subc); - if (ptr) - c.push_back(ptr); - } - return c; +std::vector GridComponent::getGridSubcomponents() const { + std::vector c; + const auto children = childItems(); + for (const auto &subc : qAsConst(children)) { + auto *ptr = dynamic_cast(subc); + if (ptr) + c.push_back(ptr); + } + return c; } -QRect& GridComponent::getCurrentComponentRectRef() { - return m_expanded ? m_currentExpandedRect : m_currentContractedRect; +QRect &GridComponent::getCurrentComponentRectRef() { + return m_expanded ? m_currentExpandedRect : m_currentContractedRect; } -const QRect& GridComponent::getCurrentComponentRect() const { - return m_expanded ? m_currentExpandedRect : m_currentContractedRect; +const QRect &GridComponent::getCurrentComponentRect() const { + return m_expanded ? m_currentExpandedRect : m_currentContractedRect; } QRect GridComponent::getCurrentMinRect() const { - return m_expanded ? m_currentSubcomponentBoundingRect : getContractedMinimumGridRect(); + return m_expanded ? m_currentSubcomponentBoundingRect + : getContractedMinimumGridRect(); } bool GridComponent::updateSubcomponentBoundingRect() { - if (hasSubcomponents()) { - std::vector rects; - for (const auto& c : getGridSubcomponents()) { - rects.push_back(c->getCurrentComponentRect().translated(c->getGridPos())); - } - const auto br = boundingRectOfRects(rects); - m_currentSubcomponentBoundingRect = br; - // Update current expanded rect if it does not contain the subcomponent bounding rect - if (!m_currentExpandedRect.contains(m_currentSubcomponentBoundingRect)) { - m_currentExpandedRect = br; - m_currentExpandedRect.setTopLeft({0, 0}); - m_currentExpandedRect.adjust(0, 0, SUBCOMPONENT_INDENT, SUBCOMPONENT_INDENT); - emit gridRectChanged(); - } - return true; + if (hasSubcomponents()) { + std::vector rects; + for (const auto &c : getGridSubcomponents()) { + rects.push_back(c->getCurrentComponentRect().translated(c->getGridPos())); } - return false; + const auto br = boundingRectOfRects(rects); + m_currentSubcomponentBoundingRect = br; + // Update current expanded rect if it does not contain the subcomponent + // bounding rect + if (!m_currentExpandedRect.contains(m_currentSubcomponentBoundingRect)) { + m_currentExpandedRect = br; + m_currentExpandedRect.setTopLeft({0, 0}); + m_currentExpandedRect.adjust(0, 0, SUBCOMPONENT_INDENT, + SUBCOMPONENT_INDENT); + emit gridRectChanged(); + } + return true; + } + return false; } void GridComponent::setInitialRect() { - const auto preferredRect = ShapeRegister::getTypePreferredRect(m_component->getGraphicsType()); - - auto initialRect = getContractedMinimumGridRect(); - if (preferredRect == QRect()) { - // No preferred size, adjust width heuristically based on height of component - const auto widthToAdd = static_cast(std::floor(std::log2(initialRect.height()))); - initialRect.adjust(0, 0, widthToAdd, 0); - } else { - // Attempt to adjust according to the preferred dimensions of the component - auto heightToAdd = preferredRect.height() - initialRect.height(); - heightToAdd = heightToAdd < 0 ? 0 : heightToAdd; - auto widthToAdd = preferredRect.width() - initialRect.width(); - widthToAdd = widthToAdd < 0 ? 0 : widthToAdd; - initialRect.adjust(0, 0, widthToAdd, heightToAdd); - } - - m_currentContractedRect = initialRect; + const auto preferredRect = + ShapeRegister::getTypePreferredRect(m_component->getGraphicsType()); + + auto initialRect = getContractedMinimumGridRect(); + if (preferredRect == QRect()) { + // No preferred size, adjust width heuristically based on height of + // component + const auto widthToAdd = + static_cast(std::floor(std::log2(initialRect.height()))); + initialRect.adjust(0, 0, widthToAdd, 0); + } else { + // Attempt to adjust according to the preferred dimensions of the component + auto heightToAdd = preferredRect.height() - initialRect.height(); + heightToAdd = heightToAdd < 0 ? 0 : heightToAdd; + auto widthToAdd = preferredRect.width() - initialRect.width(); + widthToAdd = widthToAdd < 0 ? 0 : widthToAdd; + initialRect.adjust(0, 0, widthToAdd, heightToAdd); + } + + m_currentContractedRect = initialRect; } QRect GridComponent::getContractedMinimumGridRect() const { - // The contracted minimum grid rect is defined as a 1x1 rectangle, with each side being elongated by the number of - // ports on that side - QRect shapeMinRect = QRect(0, 0, 1, 1); + // The contracted minimum grid rect is defined as a 1x1 rectangle, with each + // side being elongated by the number of ports on that side + QRect shapeMinRect = QRect(0, 0, 1, 1); - const unsigned maxVerticalPorts = m_border->sideToMap(Side::Left).count() > m_border->sideToMap(Side::Right).count() - ? m_border->sideToMap(Side::Left).count() - : m_border->sideToMap(Side::Right).count(); + const unsigned maxVerticalPorts = + m_border->sideToMap(Side::Left).count() > + m_border->sideToMap(Side::Right).count() + ? m_border->sideToMap(Side::Left).count() + : m_border->sideToMap(Side::Right).count(); - const unsigned maxHorizontalPorts = - m_border->sideToMap(Side::Top).count() > m_border->sideToMap(Side::Bottom).count() - ? m_border->sideToMap(Side::Top).count() - : m_border->sideToMap(Side::Bottom).count(); + const unsigned maxHorizontalPorts = + m_border->sideToMap(Side::Top).count() > + m_border->sideToMap(Side::Bottom).count() + ? m_border->sideToMap(Side::Top).count() + : m_border->sideToMap(Side::Bottom).count(); - shapeMinRect.adjust(0, 0, maxHorizontalPorts, maxVerticalPorts); + shapeMinRect.adjust(0, 0, maxHorizontalPorts, maxVerticalPorts); - return shapeMinRect; + return shapeMinRect; } -void GridComponent::gridRotate(const RotationDirection& dir) { - m_lastComponentRect = getCurrentComponentRect(); - m_gridRotation += (dir == RotationDirection::RightHand ? 90 : -90); - getCurrentComponentRectRef().setHeight(m_lastComponentRect.width()); - getCurrentComponentRectRef().setWidth(m_lastComponentRect.height()); +void GridComponent::gridRotate(const RotationDirection &dir) { + m_lastComponentRect = getCurrentComponentRect(); + m_gridRotation += (dir == RotationDirection::RightHand ? 90 : -90); + getCurrentComponentRectRef().setHeight(m_lastComponentRect.width()); + getCurrentComponentRectRef().setWidth(m_lastComponentRect.height()); - rotatePorts(dir); - emit gridRectChanged(); + rotatePorts(dir); + emit gridRectChanged(); } void GridComponent::updateCurrentComponentRect(int dx, int dy) { - m_lastComponentRect = getCurrentComponentRect(); - getCurrentComponentRectRef().adjust(0, 0, dx, dy); - - spreadPortsOrdered(); - - // Port spreading only emits port positioning update signals when a port changes position logically on a given side. - // If dx !^ dy, the component is adjusted in only a single direction. As such, ports on the side in the given change - // direction will not move logically, but must be adjusted in terms of where they are drawn. - if ((dx == 0) ^ (dy == 0)) { - auto axisMovedPorts = dx == 0 ? m_border->sideToMap(Side::Bottom) : m_border->sideToMap(Side::Right); - for (const auto& p : axisMovedPorts.portToId) { - emit portPosChanged(p.first); - } + m_lastComponentRect = getCurrentComponentRect(); + getCurrentComponentRectRef().adjust(0, 0, dx, dy); + + spreadPortsOrdered(); + + // Port spreading only emits port positioning update signals when a port + // changes position logically on a given side. If dx !^ dy, the component is + // adjusted in only a single direction. As such, ports on the side in the + // given change direction will not move logically, but must be adjusted in + // terms of where they are drawn. + if ((dx == 0) ^ (dy == 0)) { + auto axisMovedPorts = dx == 0 ? m_border->sideToMap(Side::Bottom) + : m_border->sideToMap(Side::Right); + for (const auto &p : axisMovedPorts.portToId) { + emit portPosChanged(p.first); } - emit gridRectChanged(); + } + emit gridRectChanged(); } -PortPos GridComponent::getPortPos(const SimPort* port) const { - return m_border->getPortPos(port); +PortPos GridComponent::getPortPos(const SimPort *port) const { + return m_border->getPortPos(port); } std::vector GridComponent::getFreePortPositions(Side s) { - std::vector freePos; - const auto& usedIndexes = m_border->sideToMap(s).idToPort; - for (int i = 0; i < getCurrentComponentRect().height(); i++) { - if (usedIndexes.count(i) == 0) { - freePos.push_back(i); - } + std::vector freePos; + const auto &usedIndexes = m_border->sideToMap(s).idToPort; + for (int i = 0; i < getCurrentComponentRect().height(); i++) { + if (usedIndexes.count(i) == 0) { + freePos.push_back(i); } - return freePos; + } + return freePos; } -bool GridComponent::adjustPort(SimPort* port, QPoint newPos) { - const auto ccr = getCurrentComponentRect(); - if ((newPos.x() <= 0 && newPos.y() <= 0) || (newPos.x() >= ccr.width() && newPos.y() >= ccr.height()) || - (newPos.x() <= 0 && newPos.y() >= ccr.height()) || (newPos.x() >= ccr.width() && newPos.y() <= 0)) { - // Out of bounds - return false; - } - - if ((newPos.x() > 0 && newPos.x() < ccr.width()) && (newPos.y() > 0 && newPos.y() < ccr.height())) { - // newPos is inside this component - return false; - } - - PortPos newPortPos; - - // Snap port position to the border of the component - newPos.rx() = newPos.x() < 0 ? 0 : newPos.x(); - newPos.rx() = newPos.x() > ccr.width() ? ccr.width() : newPos.x(); - - newPos.ry() = newPos.y() < 0 ? 0 : newPos.y(); - newPos.ry() = newPos.y() > ccr.height() ? ccr.height() : newPos.y(); - - if (newPos.x() == 0) { - newPortPos.side = Side::Left; - newPortPos.index = newPos.y(); - } else if (newPos.x() == ccr.width()) { - newPortPos.side = Side::Right; - newPortPos.index = newPos.y(); - } else if (newPos.y() == 0) { - newPortPos.side = Side::Top; - newPortPos.index = newPos.x(); - } else if (newPos.y() == ccr.height()) { - newPortPos.side = Side::Bottom; - newPortPos.index = newPos.x(); - } else { - Q_ASSERT(false && "Error in snapping or out-of-bounds handling"); - } +bool GridComponent::adjustPort(SimPort *port, QPoint newPos) { + const auto ccr = getCurrentComponentRect(); + if ((newPos.x() <= 0 && newPos.y() <= 0) || + (newPos.x() >= ccr.width() && newPos.y() >= ccr.height()) || + (newPos.x() <= 0 && newPos.y() >= ccr.height()) || + (newPos.x() >= ccr.width() && newPos.y() <= 0)) { + // Out of bounds + return false; + } - if (newPortPos == m_border->getPortPos(port)) { - // No change, same index - return false; - } + if ((newPos.x() > 0 && newPos.x() < ccr.width()) && + (newPos.y() > 0 && newPos.y() < ccr.height())) { + // newPos is inside this component + return false; + } + + PortPos newPortPos; + + // Snap port position to the border of the component + newPos.rx() = newPos.x() < 0 ? 0 : newPos.x(); + newPos.rx() = newPos.x() > ccr.width() ? ccr.width() : newPos.x(); + + newPos.ry() = newPos.y() < 0 ? 0 : newPos.y(); + newPos.ry() = newPos.y() > ccr.height() ? ccr.height() : newPos.y(); + + if (newPos.x() == 0) { + newPortPos.side = Side::Left; + newPortPos.index = newPos.y(); + } else if (newPos.x() == ccr.width()) { + newPortPos.side = Side::Right; + newPortPos.index = newPos.y(); + } else if (newPos.y() == 0) { + newPortPos.side = Side::Top; + newPortPos.index = newPos.x(); + } else if (newPos.y() == ccr.height()) { + newPortPos.side = Side::Bottom; + newPortPos.index = newPos.x(); + } else { + Q_ASSERT(false && "Error in snapping or out-of-bounds handling"); + } + + if (newPortPos == m_border->getPortPos(port)) { + // No change, same index + return false; + } - const auto movedPorts = m_border->movePort(port, newPortPos); - for (const auto& p : movedPorts) { - emit portPosChanged(p); - } - return movedPorts.size() > 0; + const auto movedPorts = m_border->movePort(port, newPortPos); + for (const auto &p : movedPorts) { + emit portPosChanged(p); + } + return movedPorts.size() > 0; } -void GridComponent::rotatePorts(const RotationDirection& dir) { - std::vector> oldPorts; - for (const auto& side : {Side::Left, Side::Right, Side::Top, Side::Bottom}) { - oldPorts.push_back({side, m_border->sideToMap(side)}); - } +void GridComponent::rotatePorts(const RotationDirection &dir) { + std::vector> oldPorts; + for (const auto &side : {Side::Left, Side::Right, Side::Top, Side::Bottom}) { + oldPorts.push_back({side, m_border->sideToMap(side)}); + } - for (const auto& sidePorts : oldPorts) { - for (const auto& port : sidePorts.second.portToId) { - Side newSide = Side(); - // clang-format off + for (const auto &sidePorts : oldPorts) { + for (const auto &port : sidePorts.second.portToId) { + Side newSide = Side(); + // clang-format off switch(sidePorts.first) { case Side::Top: newSide = dir == RotationDirection::RightHand ? Side::Right : Side::Left; break; case Side::Left: newSide = dir == RotationDirection::RightHand ? Side::Top : Side::Bottom; break; case Side::Right: newSide = dir == RotationDirection::RightHand ? Side::Bottom : Side::Top; break; case Side::Bottom: newSide = dir == RotationDirection::RightHand ? Side::Left : Side::Right; break; } - // clang-format on + // clang-format on - m_border->movePort(port.first, PortPos{newSide, port.second}); - emit portPosChanged(port.first); - } + m_border->movePort(port.first, PortPos{newSide, port.second}); + emit portPosChanged(port.first); } + } } -void GridComponent::spreadPortsOnSide(const Side& side) { - auto biMapCopy = m_border->sideToMap(side); - const auto n_ports = biMapCopy.count(); - if (n_ports > 0) { - int i = 0; - auto h = getCurrentComponentRect().height(); - const double diff = h / n_ports; - for (const auto& portId : biMapCopy.portToId) { - const int gridIndex = static_cast(std::ceil((i * diff + diff / 2))); - const auto* port = portId.first; // Store port pointer here; p reference may change during port moving - const auto movedPorts = m_border->movePort(port, PortPos{side, gridIndex}); - for (const auto& p : movedPorts) { - emit portPosChanged(p); - } - i++; - } +void GridComponent::spreadPortsOnSide(const Side &side) { + auto biMapCopy = m_border->sideToMap(side); + const auto n_ports = biMapCopy.count(); + if (n_ports > 0) { + int i = 0; + auto h = getCurrentComponentRect().height(); + const double diff = h / n_ports; + for (const auto &portId : biMapCopy.portToId) { + const int gridIndex = static_cast(std::ceil((i * diff + diff / 2))); + const auto *port = portId.first; // Store port pointer here; p reference + // may change during port moving + const auto movedPorts = + m_border->movePort(port, PortPos{side, gridIndex}); + for (const auto &p : movedPorts) { + emit portPosChanged(p); + } + i++; } + } } void GridComponent::spreadPortsOrdered() { - for (const auto& side : {Side::Left, Side::Right, Side::Top, Side::Bottom}) { - auto biMapCopy = m_border->sideToMap(side); - const auto n_ports = biMapCopy.count(); - if (n_ports > 0) { - int i = 0; - const double diff = ((side == Side::Left || side == Side::Right) ? getCurrentComponentRect().height() - : getCurrentComponentRect().width()) / - n_ports; - for (const auto& idp : biMapCopy.idToPort) { - const int gridIndex = static_cast(std::ceil((i * diff + diff / 2))); - const auto* port = idp.second; // Store port pointer here; p reference may change during port moving - const auto movedPorts = m_border->movePort(port, PortPos{side, gridIndex}); - for (const auto& p : movedPorts) { - emit portPosChanged(p); - } - i++; - } + for (const auto &side : {Side::Left, Side::Right, Side::Top, Side::Bottom}) { + auto biMapCopy = m_border->sideToMap(side); + const auto n_ports = biMapCopy.count(); + if (n_ports > 0) { + int i = 0; + const double diff = ((side == Side::Left || side == Side::Right) + ? getCurrentComponentRect().height() + : getCurrentComponentRect().width()) / + n_ports; + for (const auto &idp : biMapCopy.idToPort) { + const int gridIndex = + static_cast(std::ceil((i * diff + diff / 2))); + const auto *port = idp.second; // Store port pointer here; p reference + // may change during port moving + const auto movedPorts = + m_border->movePort(port, PortPos{side, gridIndex}); + for (const auto &p : movedPorts) { + emit portPosChanged(p); } + i++; + } } + } } void GridComponent::spreadPorts() { - spreadPortsOnSide(Side::Left); - spreadPortsOnSide(Side::Right); - spreadPortsOnSide(Side::Top); - spreadPortsOnSide(Side::Bottom); + spreadPortsOnSide(Side::Left); + spreadPortsOnSide(Side::Right); + spreadPortsOnSide(Side::Top); + spreadPortsOnSide(Side::Bottom); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_gridcomponent.h b/graphics/vsrtl_gridcomponent.h index 4a5e5e2..aa3a689 100644 --- a/graphics/vsrtl_gridcomponent.h +++ b/graphics/vsrtl_gridcomponent.h @@ -12,159 +12,169 @@ namespace vsrtl { -class GridComponent : public SimQObject, public GraphicsBaseItem { - Q_OBJECT +class GridComponent : public SimQObject, + public GraphicsBaseItem { + Q_OBJECT public: - GridComponent(SimComponent* c, GridComponent* parent); - - enum class RotationDirection { RightHand, LeftHand }; - /** - * @brief rotate - * Attempt to rotate the component in the direction of @p dir - */ - void gridRotate(const RotationDirection& dir); - int gridRotation() const { return m_gridRotation; } - - /** - * @brief adjust - * Attempt to expand the currently visible grid rect. Bounded by current minimum rect size. - * @return true if any change to the currently visible grid rect was performed - */ - bool adjust(const QPoint&); - bool adjust(const QRect&); - - /** - * @brief move - * Attempts to move this gridcomponent to the desired pos in the given coordinate basis. - * @return true if move was successfull (ie. if move was within bounds) - */ - bool move(const QPoint& pos); - - /** - * @brief setExpanded - * Change the current expansion state to @p state - */ - void setExpanded(bool state); - - bool isExpanded() const { return m_expanded; } - - /** - * @brief adjustPort - * Attempt to move the position of @p port to @p pos of its current side - * @return whether the requested pos has been set for the port - */ - bool adjustPort(SimPort* port, QPoint pos); - - PortPos getPortPos(const SimPort* port) const; - std::vector getFreePortPositions(Side s); - - bool parentIsPlacing() const; - - const ComponentBorder& getBorder() const { return *m_border; } - const QRect& getCurrentComponentRect() const; - QRect getCurrentMinRect() const; - const QRect& getLastComponentRect() const { return m_lastComponentRect; } - - QPoint getGridPos() const { return m_relPos; } - - SimComponent* getComponent() const { return m_component; } - bool hasSubcomponents() const; - - void placeAndRouteSubcomponents(); - - template - void serializeBorder(Archive& archive) { - m_border->serialize(archive); - // Invalidate all port positions - - for (const auto& p : m_component->getAllPorts()) { - emit portPosChanged(p); - } + GridComponent(SimComponent *c, GridComponent *parent); + + enum class RotationDirection { RightHand, LeftHand }; + /** + * @brief rotate + * Attempt to rotate the component in the direction of @p dir + */ + void gridRotate(const RotationDirection &dir); + int gridRotation() const { return m_gridRotation; } + + /** + * @brief adjust + * Attempt to expand the currently visible grid rect. Bounded by current + * minimum rect size. + * @return true if any change to the currently visible grid rect was performed + */ + bool adjust(const QPoint &); + bool adjust(const QRect &); + + /** + * @brief move + * Attempts to move this gridcomponent to the desired pos in the given + * coordinate basis. + * @return true if move was successfull (ie. if move was within bounds) + */ + bool move(const QPoint &pos); + + /** + * @brief setExpanded + * Change the current expansion state to @p state + */ + void setExpanded(bool state); + + bool isExpanded() const { return m_expanded; } + + /** + * @brief adjustPort + * Attempt to move the position of @p port to @p pos of its current side + * @return whether the requested pos has been set for the port + */ + bool adjustPort(SimPort *port, QPoint pos); + + PortPos getPortPos(const SimPort *port) const; + std::vector getFreePortPositions(Side s); + + bool parentIsPlacing() const; + + const ComponentBorder &getBorder() const { return *m_border; } + const QRect &getCurrentComponentRect() const; + QRect getCurrentMinRect() const; + const QRect &getLastComponentRect() const { return m_lastComponentRect; } + + QPoint getGridPos() const { return m_relPos; } + + SimComponent *getComponent() const { return m_component; } + bool hasSubcomponents() const; + + void placeAndRouteSubcomponents(); + + template + void serializeBorder(Archive &archive) { + m_border->serialize(archive); + // Invalidate all port positions + + for (const auto &p : m_component->getAllPorts()) { + emit portPosChanged(p); } + } signals: - void gridRectChanged(); - void gridPosChanged(QPoint); - void portPosChanged(const vsrtl::SimPort* p); + void gridRectChanged(); + void gridPosChanged(QPoint); + void portPosChanged(const vsrtl::SimPort *p); protected: - int m_gridRotation = 0; // Rotation angle, in the range [0, 360[ in intervals of 90 deg. - - SimComponent* m_component = nullptr; - /** - * @brief spreadPorts - * Adjust all ports of the component such that they are evenly spread on a given face of the gridcomponent. Ports' - * direction will not be changed - */ - void spreadPorts(); - - /** - * @brief spreadPortsOrdered - * Adjust all ports of the component such that their position is evenly spread on the faces of the gridcomponent. - * Ports are spread in the order of which they currently appear on the given side. - */ - void spreadPortsOrdered(); + int m_gridRotation = + 0; // Rotation angle, in the range [0, 360[ in intervals of 90 deg. + + SimComponent *m_component = nullptr; + /** + * @brief spreadPorts + * Adjust all ports of the component such that they are evenly spread on a + * given face of the gridcomponent. Ports' direction will not be changed + */ + void spreadPorts(); + + /** + * @brief spreadPortsOrdered + * Adjust all ports of the component such that their position is evenly spread + * on the faces of the gridcomponent. Ports are spread in the order of which + * they currently appear on the given side. + */ + void spreadPortsOrdered(); private: - /** - * @brief childGeometryChanged - * Called by child components, signalling that their geometry or position was changed, which (may) require a rezing - * of the current minimum component rect. - */ - void childGeometryChanged(); + /** + * @brief childGeometryChanged + * Called by child components, signalling that their geometry or position was + * changed, which (may) require a rezing of the current minimum component + * rect. + */ + void childGeometryChanged(); private: - /** - * @brief spreadPortsOnSide - * Spread all ports currently located on @p side - */ - void spreadPortsOnSide(const Side& side); - - /** - * @brief rotatePorts - * Rotates all ports in the provided @param dir - */ - void rotatePorts(const RotationDirection& dir); - - /** - * @brief Rect update functions - */ - void updateCurrentComponentRect(int dx, int dy); - QRect getContractedMinimumGridRect() const; - bool updateSubcomponentBoundingRect(); - void setInitialRect(); - - QRect& getCurrentComponentRectRef(); - - /** - * @brief moveInsideParent - * Attempts to move this gridcomponent to the desired @p pos inside the parent. - * @return true if move was successfull (ie. if move was within the parent bounds) - */ - bool moveInsideParent(QPoint pos); - - bool parentContainsRect(const QRect& r) const; - std::vector getGridSubcomponents() const; - - std::unique_ptr m_border; - - /** current expansion state */ - bool m_expanded = false; - - /** Flag for indicating when placement/routing. If so, do not restrict subcomponent - * positioning inside the current minimum subcomponent bounding rect */ - bool m_isPlacing = false; - - /// Managed rectangles - QRect m_currentExpandedRect; - QRect m_currentContractedRect; - QRect m_currentSubcomponentBoundingRect; - QRect m_lastComponentRect = - QRect(); // whenever the current component rect changes, objects such as component labels etc. - // may need to be adjusted based on the scaled difference between the new and old - // visible grid rects. For this, we cache the last updated grid rect. - - QPoint m_relPos; // Position in parent coordinates + /** + * @brief spreadPortsOnSide + * Spread all ports currently located on @p side + */ + void spreadPortsOnSide(const Side &side); + + /** + * @brief rotatePorts + * Rotates all ports in the provided @param dir + */ + void rotatePorts(const RotationDirection &dir); + + /** + * @brief Rect update functions + */ + void updateCurrentComponentRect(int dx, int dy); + QRect getContractedMinimumGridRect() const; + bool updateSubcomponentBoundingRect(); + void setInitialRect(); + + QRect &getCurrentComponentRectRef(); + + /** + * @brief moveInsideParent + * Attempts to move this gridcomponent to the desired @p pos inside the + * parent. + * @return true if move was successfull (ie. if move was within the parent + * bounds) + */ + bool moveInsideParent(QPoint pos); + + bool parentContainsRect(const QRect &r) const; + std::vector getGridSubcomponents() const; + + std::unique_ptr m_border; + + /** current expansion state */ + bool m_expanded = false; + + /** Flag for indicating when placement/routing. If so, do not restrict + * subcomponent positioning inside the current minimum subcomponent bounding + * rect */ + bool m_isPlacing = false; + + /// Managed rectangles + QRect m_currentExpandedRect; + QRect m_currentContractedRect; + QRect m_currentSubcomponentBoundingRect; + QRect m_lastComponentRect = + QRect(); // whenever the current component rect changes, objects such as + // component labels etc. may need to be adjusted based on the + // scaled difference between the new and old visible grid rects. + // For this, we cache the last updated grid rect. + + QPoint m_relPos; // Position in parent coordinates }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_label.cpp b/graphics/vsrtl_label.cpp index 111ac0a..3406967 100644 --- a/graphics/vsrtl_label.cpp +++ b/graphics/vsrtl_label.cpp @@ -15,134 +15,142 @@ namespace vsrtl { -Label::Label(QGraphicsItem* parent, const QString& text, std::shared_ptr visibilityAction, int fontSize) +Label::Label(QGraphicsItem *parent, const QString &text, + std::shared_ptr visibilityAction, int fontSize) : GraphicsBaseItem(parent), m_font(QFont("Roboto", fontSize)) { - if (!visibilityAction) { - m_visibilityAction = std::make_shared("Show label"); - m_visibilityAction->setCheckable(true); - m_visibilityAction->setChecked(false); - connect(m_visibilityAction.get(), &QAction::toggled, [=](bool checked) { setVisible(checked); }); - } else { - m_visibilityAction = visibilityAction; - } - - setMoveable(); - setText(text); + if (!visibilityAction) { + m_visibilityAction = std::make_shared("Show label"); + m_visibilityAction->setCheckable(true); + m_visibilityAction->setChecked(false); + connect(m_visibilityAction.get(), &QAction::toggled, + [=](bool checked) { setVisible(checked); }); + } else { + m_visibilityAction = visibilityAction; + } + + setMoveable(); + setText(text); } -void Label::forceDefaultTextColor(const QColor& color) { - m_defaultColorOverridden = true; - QGraphicsTextItem::setDefaultTextColor(color); +void Label::forceDefaultTextColor(const QColor &color) { + m_defaultColorOverridden = true; + QGraphicsTextItem::setDefaultTextColor(color); } -void Label::setText(const QString& text) { - setPlainText(text); - applyFormatChanges(); +void Label::setText(const QString &text) { + setPlainText(text); + applyFormatChanges(); } void Label::updateText() {} void Label::setPointSize(int size) { - m_font.setPointSize(size); - applyFormatChanges(); + m_font.setPointSize(size); + applyFormatChanges(); } void Label::setLocked(bool locked) { - setFlag(ItemIsSelectable, !locked); - GraphicsBaseItem::setLocked(locked); + setFlag(ItemIsSelectable, !locked); + GraphicsBaseItem::setLocked(locked); } void Label::setHoverable(bool enabled) { - m_hoverable = enabled; - prepareGeometryChange(); + m_hoverable = enabled; + prepareGeometryChange(); } QPainterPath Label::shape() const { - // A non-hoverable/non-selectable item has a null shape - if (m_hoverable) { - return QGraphicsTextItem::shape(); - } else { - return QPainterPath(); - } + // A non-hoverable/non-selectable item has a null shape + if (m_hoverable) { + return QGraphicsTextItem::shape(); + } else { + return QPainterPath(); + } } -QVariant Label::itemChange(GraphicsItemChange change, const QVariant& value) { - if (change == GraphicsItemChange::ItemVisibleChange) { - if (m_visibilityAction && !m_visibilityAction->isChecked()) { - // Reject visibility change - the value label has deliberatly been turned off - return QVariant(); - } +QVariant Label::itemChange(GraphicsItemChange change, const QVariant &value) { + if (change == GraphicsItemChange::ItemVisibleChange) { + if (m_visibilityAction && !m_visibilityAction->isChecked()) { + // Reject visibility change - the value label has deliberatly been turned + // off + return QVariant(); } - return GraphicsBaseItem::itemChange(change, value); + } + return GraphicsBaseItem::itemChange(change, value); } -void Label::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - QMenu menu; - if (!isLocked()) { - auto* editAction = menu.addAction("Edit label"); - connect(editAction, &QAction::triggered, this, &Label::editTriggered); - menu.addAction(m_visibilityAction.get()); - } +void Label::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + QMenu menu; + if (!isLocked()) { + auto *editAction = menu.addAction("Edit label"); + connect(editAction, &QAction::triggered, this, &Label::editTriggered); + menu.addAction(m_visibilityAction.get()); + } - menu.exec(event->screenPos()); + menu.exec(event->screenPos()); }; -void Label::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* w) { - // There exists a bug within the drawing of QGraphicsTextItem wherein the painter pen does not return to its initial - // state wrt. the draw style (the pen draw style is set to Qt::DashLine after finishing painting whilst the - // QGraphicsTextItem is selected). - painter->save(); - if (!m_defaultColorOverridden) { - setDefaultTextColor(static_cast(scene())->darkmode() ? Qt::white : Qt::black); - } - - QGraphicsTextItem::paint(painter, option, w); - painter->restore(); +void Label::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *w) { + // There exists a bug within the drawing of QGraphicsTextItem wherein the + // painter pen does not return to its initial state wrt. the draw style (the + // pen draw style is set to Qt::DashLine after finishing painting whilst the + // QGraphicsTextItem is selected). + painter->save(); + if (!m_defaultColorOverridden) { + setDefaultTextColor( + static_cast(scene())->darkmode() ? Qt::white : Qt::black); + } + + QGraphicsTextItem::paint(painter, option, w); + painter->restore(); } -void Label::mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) { - if (isLocked()) - return; +void Label::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { + if (isLocked()) + return; - editTriggered(); + editTriggered(); } void Label::editTriggered() { - LabelEditDialog diag; - diag.m_ui->bold->setChecked(m_font.bold()); - diag.m_ui->italic->setChecked(m_font.italic()); - diag.m_ui->size->setValue(m_font.pointSize()); - diag.setAlignment(document()->defaultTextOption().alignment()); - diag.m_ui->text->setText(toPlainText()); - - if (diag.exec()) { - prepareGeometryChange(); - m_font.setBold(diag.m_ui->bold->isChecked()); - m_font.setItalic(diag.m_ui->italic->isChecked()); - m_font.setPointSize(diag.m_ui->size->value()); - setFont(m_font); - setPlainText(diag.m_ui->text->toPlainText()); - setAlignment(diag.getAlignment()); - } + LabelEditDialog diag; + diag.m_ui->bold->setChecked(m_font.bold()); + diag.m_ui->italic->setChecked(m_font.italic()); + diag.m_ui->size->setValue(m_font.pointSize()); + diag.setAlignment(document()->defaultTextOption().alignment()); + diag.m_ui->text->setText(toPlainText()); + + if (diag.exec()) { + prepareGeometryChange(); + m_font.setBold(diag.m_ui->bold->isChecked()); + m_font.setItalic(diag.m_ui->italic->isChecked()); + m_font.setPointSize(diag.m_ui->size->value()); + setFont(m_font); + setPlainText(diag.m_ui->text->toPlainText()); + setAlignment(diag.getAlignment()); + } } void Label::setAlignment(Qt::Alignment alignment) { - auto textOption = document()->defaultTextOption(); - textOption.setAlignment(alignment); - document()->setDefaultTextOption(textOption); - m_alignment = alignment; - applyFormatChanges(); + auto textOption = document()->defaultTextOption(); + textOption.setAlignment(alignment); + document()->setDefaultTextOption(textOption); + m_alignment = alignment; + applyFormatChanges(); } void Label::applyFormatChanges() { - setFont(m_font); - setPlainText(toPlainText()); - // Setting text width to -1 will remove any textOption alignments. As such, any inferred linebreaks from having a - // fixed text width will be removed. Given this, the bounding rect width of the item will reflect the required width - // for representing the text without inferred linebreaks. - setTextWidth(-1); - // A non-negative text width is set, enabling alignment within the text document - setTextWidth(boundingRect().width()); + setFont(m_font); + setPlainText(toPlainText()); + // Setting text width to -1 will remove any textOption alignments. As such, + // any inferred linebreaks from having a fixed text width will be removed. + // Given this, the bounding rect width of the item will reflect the required + // width for representing the text without inferred linebreaks. + setTextWidth(-1); + // A non-negative text width is set, enabling alignment within the text + // document + setTextWidth(boundingRect().width()); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_label.h b/graphics/vsrtl_label.h index 8b4dfde..c96c656 100644 --- a/graphics/vsrtl_label.h +++ b/graphics/vsrtl_label.h @@ -10,104 +10,107 @@ namespace vsrtl { class Label : public GraphicsBaseItem { - Q_OBJECT + Q_OBJECT public: - Label(QGraphicsItem* parent, const QString& text, std::shared_ptr visibilityAction = {}, - int fontSize = 12); - - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget*) override; - QPainterPath shape() const override; - QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - - void setHoverable(bool enabled); - void setText(const QString& text); - void setAlignment(Qt::Alignment alignment); - void setPointSize(int size); - void setLocked(bool locked) override; - void forceDefaultTextColor(const QColor& color); - void clearForcedDefaultTextColor() { m_defaultColorOverridden = false; } - - /** - * @brief updateText - * Triggers an update of the current text. Relevant if the Label gathers its text from some dynamic structure. - */ - virtual void updateText(); - - template - void serialize(Archive& archive) { - prepareGeometryChange(); - try { - bool v = m_visibilityAction->isChecked(); - archive(cereal::make_nvp("Visible", v)); - m_visibilityAction->setChecked(v); - - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - bool bold = m_font.bold(); - archive(cereal::make_nvp("Bold", bold)); - m_font.setBold(bold); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - bool italic = m_font.italic(); - archive(cereal::make_nvp("Italic", italic)); - m_font.setItalic(italic); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - int ptSize = m_font.pointSize(); - archive(cereal::make_nvp("PtSize", ptSize)); - m_font.setPointSize(ptSize); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - QString text = toPlainText(); - archive(cereal::make_nvp("Text", text)); - setPlainText(text); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - QPointF p = pos(); - archive(cereal::make_nvp("Pos", p)); - setPos(p); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - unsigned alignment = m_alignment; - archive(cereal::make_nvp("Alignment", alignment)); - m_alignment = static_cast(alignment); - setAlignment(m_alignment); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - applyFormatChanges(); + Label(QGraphicsItem *parent, const QString &text, + std::shared_ptr visibilityAction = {}, int fontSize = 12); + + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *) override; + QPainterPath shape() const override; + QVariant itemChange(GraphicsItemChange change, + const QVariant &value) override; + + void setHoverable(bool enabled); + void setText(const QString &text); + void setAlignment(Qt::Alignment alignment); + void setPointSize(int size); + void setLocked(bool locked) override; + void forceDefaultTextColor(const QColor &color); + void clearForcedDefaultTextColor() { m_defaultColorOverridden = false; } + + /** + * @brief updateText + * Triggers an update of the current text. Relevant if the Label gathers its + * text from some dynamic structure. + */ + virtual void updateText(); + + template + void serialize(Archive &archive) { + prepareGeometryChange(); + try { + bool v = m_visibilityAction->isChecked(); + archive(cereal::make_nvp("Visible", v)); + m_visibilityAction->setChecked(v); + + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + try { + bool bold = m_font.bold(); + archive(cereal::make_nvp("Bold", bold)); + m_font.setBold(bold); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + try { + bool italic = m_font.italic(); + archive(cereal::make_nvp("Italic", italic)); + m_font.setItalic(italic); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + try { + int ptSize = m_font.pointSize(); + archive(cereal::make_nvp("PtSize", ptSize)); + m_font.setPointSize(ptSize); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + try { + QString text = toPlainText(); + archive(cereal::make_nvp("Text", text)); + setPlainText(text); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + try { + QPointF p = pos(); + archive(cereal::make_nvp("Pos", p)); + setPos(p); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + try { + unsigned alignment = m_alignment; + archive(cereal::make_nvp("Alignment", alignment)); + m_alignment = static_cast(alignment); + setAlignment(m_alignment); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + applyFormatChanges(); + } + protected: - void applyFormatChanges(); - void editTriggered(); - - bool m_hoverable = true; - QFont m_font; - bool m_defaultColorOverridden = false; - Qt::Alignment m_alignment = Qt::AlignCenter; - std::shared_ptr m_visibilityAction; + void applyFormatChanges(); + void editTriggered(); + + bool m_hoverable = true; + QFont m_font; + bool m_defaultColorOverridden = false; + Qt::Alignment m_alignment = Qt::AlignCenter; + std::shared_ptr m_visibilityAction; }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_labeleditdialog.h b/graphics/vsrtl_labeleditdialog.h index 59ab19d..24fbe23 100644 --- a/graphics/vsrtl_labeleditdialog.h +++ b/graphics/vsrtl_labeleditdialog.h @@ -14,63 +14,65 @@ class LabelEditDialog; } class LabelEditDialog : public QDialog { - Q_OBJECT - friend class Label; + Q_OBJECT + friend class Label; private: - Ui::LabelEditDialog* m_ui = nullptr; + Ui::LabelEditDialog *m_ui = nullptr; public: - explicit LabelEditDialog(QWidget* parent = nullptr) : QDialog(parent), m_ui(new Ui::LabelEditDialog) { - m_ui->setupUi(this); - setWindowTitle("Edit label"); + explicit LabelEditDialog(QWidget *parent = nullptr) + : QDialog(parent), m_ui(new Ui::LabelEditDialog) { + m_ui->setupUi(this); + setWindowTitle("Edit label"); - m_ui->alignLeft->setIcon(QIcon(":/vsrtl_icons/alignleft.svg")); - m_ui->alignCenter->setIcon(QIcon(":/vsrtl_icons/aligncenter.svg")); - m_ui->alignRight->setIcon(QIcon(":/vsrtl_icons/alignright.svg")); + m_ui->alignLeft->setIcon(QIcon(":/vsrtl_icons/alignleft.svg")); + m_ui->alignCenter->setIcon(QIcon(":/vsrtl_icons/aligncenter.svg")); + m_ui->alignRight->setIcon(QIcon(":/vsrtl_icons/alignright.svg")); - QButtonGroup* alignment = new QButtonGroup(this); - alignment->addButton(m_ui->alignLeft); - alignment->addButton(m_ui->alignCenter); - alignment->addButton(m_ui->alignRight); - alignment->setExclusive(true); - m_ui->alignCenter->setChecked(true); + QButtonGroup *alignment = new QButtonGroup(this); + alignment->addButton(m_ui->alignLeft); + alignment->addButton(m_ui->alignCenter); + alignment->addButton(m_ui->alignRight); + alignment->setExclusive(true); + m_ui->alignCenter->setChecked(true); - connect(m_ui->text, &QTextEdit::textChanged, [=]() { - const auto text = m_ui->text->toPlainText(); - m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); - }); - } + connect(m_ui->text, &QTextEdit::textChanged, [=]() { + const auto text = m_ui->text->toPlainText(); + m_ui->buttonBox->button(QDialogButtonBox::Ok) + ->setEnabled(!text.isEmpty()); + }); + } - void setAlignment(Qt::Alignment alignment) { - switch (alignment) { - case Qt::AlignLeft: { - m_ui->alignLeft->setChecked(true); - break; - } - case Qt::AlignCenter: { - m_ui->alignCenter->setChecked(true); - break; - } - case Qt::AlignRight: { - m_ui->alignRight->setChecked(true); - break; - } - } + void setAlignment(Qt::Alignment alignment) { + switch (alignment) { + case Qt::AlignLeft: { + m_ui->alignLeft->setChecked(true); + break; + } + case Qt::AlignCenter: { + m_ui->alignCenter->setChecked(true); + break; + } + case Qt::AlignRight: { + m_ui->alignRight->setChecked(true); + break; + } } + } - Qt::Alignment getAlignment() const { - if (m_ui->alignLeft->isChecked()) { - return Qt::AlignLeft; - } else if (m_ui->alignCenter->isChecked()) { - return Qt::AlignCenter; - } else if (m_ui->alignRight->isChecked()) { - return Qt::AlignRight; - } - Q_UNREACHABLE(); + Qt::Alignment getAlignment() const { + if (m_ui->alignLeft->isChecked()) { + return Qt::AlignLeft; + } else if (m_ui->alignCenter->isChecked()) { + return Qt::AlignCenter; + } else if (m_ui->alignRight->isChecked()) { + return Qt::AlignRight; } + Q_UNREACHABLE(); + } - ~LabelEditDialog() { delete m_ui; } + ~LabelEditDialog() { delete m_ui; } }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_mainwindow.cpp b/graphics/vsrtl_mainwindow.cpp index f536efd..56a6a7c 100644 --- a/graphics/vsrtl_mainwindow.cpp +++ b/graphics/vsrtl_mainwindow.cpp @@ -19,126 +19,133 @@ namespace vsrtl { -MainWindow::MainWindow(SimDesign& arch, QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - ui->setupUi(this); - setWindowState(Qt::WindowMaximized); - m_vsrtlWidget = new VSRTLWidget(this); - m_vsrtlWidget->setDesign(&arch, true); +MainWindow::MainWindow(SimDesign &arch, QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow) { + ui->setupUi(this); + setWindowState(Qt::WindowMaximized); + m_vsrtlWidget = new VSRTLWidget(this); + m_vsrtlWidget->setDesign(&arch, true); - m_netlist = new Netlist(arch, this); + m_netlist = new Netlist(arch, this); - QSplitter* splitter = new QSplitter(this); + QSplitter *splitter = new QSplitter(this); - splitter->addWidget(m_netlist); - splitter->addWidget(m_vsrtlWidget); + splitter->addWidget(m_netlist); + splitter->addWidget(m_vsrtlWidget); - connect(m_netlist, &Netlist::selectionChanged, m_vsrtlWidget, &VSRTLWidget::handleSelectionChanged); - connect(m_vsrtlWidget, &VSRTLWidget::componentSelectionChanged, m_netlist, &Netlist::updateSelection); + connect(m_netlist, &Netlist::selectionChanged, m_vsrtlWidget, + &VSRTLWidget::handleSelectionChanged); + connect(m_vsrtlWidget, &VSRTLWidget::componentSelectionChanged, m_netlist, + &Netlist::updateSelection); - setCentralWidget(splitter); + setCentralWidget(splitter); - createToolbar(); + createToolbar(); - setWindowTitle("VSRTL - Visual Simulation of Register Transfer Logic"); + setWindowTitle("VSRTL - Visual Simulation of Register Transfer Logic"); } MainWindow::~MainWindow() { - delete ui; - delete m_vsrtlWidget; + delete ui; + delete m_vsrtlWidget; } void MainWindow::createToolbar() { - QToolBar* simulatorToolBar = addToolBar("Simulator"); - - const QIcon resetIcon = QIcon(":/vsrtl_icons/reset.svg"); - QAction* resetAct = new QAction(resetIcon, "Reset", this); - connect(resetAct, &QAction::triggered, [this] { - m_vsrtlWidget->reset(); - m_netlist->reloadNetlist(); - }); - resetAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); - simulatorToolBar->addAction(resetAct); - - const QIcon reverseIcon = QIcon(":/vsrtl_icons/reverse.svg"); - QAction* reverseAct = new QAction(reverseIcon, "Reverse", this); - connect(reverseAct, &QAction::triggered, [this] { - m_vsrtlWidget->reverse(); - m_netlist->reloadNetlist(); - }); - reverseAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z)); - simulatorToolBar->addAction(reverseAct); - reverseAct->setEnabled(false); - connect(m_vsrtlWidget, &VSRTLWidget::canReverse, reverseAct, &QAction::setEnabled); - - const QIcon clockIcon = QIcon(":/vsrtl_icons/step.svg"); - QAction* clockAct = new QAction(clockIcon, "Clock", this); - connect(clockAct, &QAction::triggered, [this] { - m_vsrtlWidget->clock(); - m_netlist->reloadNetlist(); - }); - clockAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); - simulatorToolBar->addAction(clockAct); - - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, clockAct, &QAction::trigger); - - const QIcon startTimerIcon = QIcon(":/vsrtl_icons/step-clock.svg"); - const QIcon stopTimerIcon = QIcon(":/vsrtl_icons/stop-clock.svg"); - QAction* clockTimerAct = new QAction(startTimerIcon, "Auto Clock", this); - clockTimerAct->setCheckable(true); - clockTimerAct->setChecked(false); - connect(clockTimerAct, &QAction::triggered, this, [=] { - if (timer->isActive()) { - timer->stop(); - clockTimerAct->setIcon(startTimerIcon); - } else { - timer->start(); - clockTimerAct->setIcon(stopTimerIcon); - } - }); - - simulatorToolBar->addAction(clockTimerAct); - - QSpinBox* stepSpinBox = new QSpinBox(); - stepSpinBox->setRange(1, 10000); - stepSpinBox->setSuffix(" ms"); - stepSpinBox->setToolTip("Auto clock interval"); - connect(stepSpinBox, qOverload(&QSpinBox::valueChanged), [timer](int msec) { timer->setInterval(msec); }); - stepSpinBox->setValue(100); - - simulatorToolBar->addWidget(stepSpinBox); - - QAction* runAct = new QAction(clockIcon, "Run", this); - runAct->setCheckable(true); - runAct->setChecked(false); - connect(runAct, &QAction::triggered, [this](bool state) { - if (state) { - auto thread = QThread::create([=] { this->m_vsrtlWidget->run(); }); - thread->start(); - } else { - this->m_vsrtlWidget->stop(); - } - }); - simulatorToolBar->addAction(runAct); - - simulatorToolBar->addSeparator(); - - const QIcon showNetlistIcon = QIcon(":/vsrtl_icons/list.svg"); - QAction* showNetlist = new QAction(showNetlistIcon, "Show Netlist", this); - connect(showNetlist, &QAction::triggered, [this] { - if (m_netlist->isVisible()) { - m_netlist->hide(); - } else { - m_netlist->show(); - } - }); - simulatorToolBar->addAction(showNetlist); - - const QIcon expandAllComponentsIcon = QIcon(":/vsrtl_icons/expandSquare.svg"); - QAction* expandAllComponents = new QAction(expandAllComponentsIcon, "Expand all components", this); - connect(expandAllComponents, &QAction::triggered, [this] { m_vsrtlWidget->expandAllComponents(); }); - simulatorToolBar->addAction(expandAllComponents); - -} // namespace vsrtl - -} // namespace vsrtl + QToolBar *simulatorToolBar = addToolBar("Simulator"); + + const QIcon resetIcon = QIcon(":/vsrtl_icons/reset.svg"); + QAction *resetAct = new QAction(resetIcon, "Reset", this); + connect(resetAct, &QAction::triggered, [this] { + m_vsrtlWidget->reset(); + m_netlist->reloadNetlist(); + }); + resetAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); + simulatorToolBar->addAction(resetAct); + + const QIcon reverseIcon = QIcon(":/vsrtl_icons/reverse.svg"); + QAction *reverseAct = new QAction(reverseIcon, "Reverse", this); + connect(reverseAct, &QAction::triggered, [this] { + m_vsrtlWidget->reverse(); + m_netlist->reloadNetlist(); + }); + reverseAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z)); + simulatorToolBar->addAction(reverseAct); + reverseAct->setEnabled(false); + connect(m_vsrtlWidget, &VSRTLWidget::canReverse, reverseAct, + &QAction::setEnabled); + + const QIcon clockIcon = QIcon(":/vsrtl_icons/step.svg"); + QAction *clockAct = new QAction(clockIcon, "Clock", this); + connect(clockAct, &QAction::triggered, [this] { + m_vsrtlWidget->clock(); + m_netlist->reloadNetlist(); + }); + clockAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); + simulatorToolBar->addAction(clockAct); + + QTimer *timer = new QTimer(); + connect(timer, &QTimer::timeout, clockAct, &QAction::trigger); + + const QIcon startTimerIcon = QIcon(":/vsrtl_icons/step-clock.svg"); + const QIcon stopTimerIcon = QIcon(":/vsrtl_icons/stop-clock.svg"); + QAction *clockTimerAct = new QAction(startTimerIcon, "Auto Clock", this); + clockTimerAct->setCheckable(true); + clockTimerAct->setChecked(false); + connect(clockTimerAct, &QAction::triggered, this, [=] { + if (timer->isActive()) { + timer->stop(); + clockTimerAct->setIcon(startTimerIcon); + } else { + timer->start(); + clockTimerAct->setIcon(stopTimerIcon); + } + }); + + simulatorToolBar->addAction(clockTimerAct); + + QSpinBox *stepSpinBox = new QSpinBox(); + stepSpinBox->setRange(1, 10000); + stepSpinBox->setSuffix(" ms"); + stepSpinBox->setToolTip("Auto clock interval"); + connect(stepSpinBox, qOverload(&QSpinBox::valueChanged), + [timer](int msec) { timer->setInterval(msec); }); + stepSpinBox->setValue(100); + + simulatorToolBar->addWidget(stepSpinBox); + + QAction *runAct = new QAction(clockIcon, "Run", this); + runAct->setCheckable(true); + runAct->setChecked(false); + connect(runAct, &QAction::triggered, [this](bool state) { + if (state) { + auto thread = QThread::create([=] { this->m_vsrtlWidget->run(); }); + thread->start(); + } else { + this->m_vsrtlWidget->stop(); + } + }); + simulatorToolBar->addAction(runAct); + + simulatorToolBar->addSeparator(); + + const QIcon showNetlistIcon = QIcon(":/vsrtl_icons/list.svg"); + QAction *showNetlist = new QAction(showNetlistIcon, "Show Netlist", this); + connect(showNetlist, &QAction::triggered, [this] { + if (m_netlist->isVisible()) { + m_netlist->hide(); + } else { + m_netlist->show(); + } + }); + simulatorToolBar->addAction(showNetlist); + + const QIcon expandAllComponentsIcon = QIcon(":/vsrtl_icons/expandSquare.svg"); + QAction *expandAllComponents = + new QAction(expandAllComponentsIcon, "Expand all components", this); + connect(expandAllComponents, &QAction::triggered, + [this] { m_vsrtlWidget->expandAllComponents(); }); + simulatorToolBar->addAction(expandAllComponents); + +} // namespace vsrtl + +} // namespace vsrtl diff --git a/graphics/vsrtl_mainwindow.h b/graphics/vsrtl_mainwindow.h index a5f58fb..950b807 100644 --- a/graphics/vsrtl_mainwindow.h +++ b/graphics/vsrtl_mainwindow.h @@ -19,21 +19,21 @@ class MainWindow; } class MainWindow : public QMainWindow { - Q_OBJECT + Q_OBJECT public: - explicit MainWindow(SimDesign& arch, QWidget* parent = nullptr); - ~MainWindow(); + explicit MainWindow(SimDesign &arch, QWidget *parent = nullptr); + ~MainWindow(); private: - Ui::MainWindow* ui; + Ui::MainWindow *ui; - VSRTLWidget* m_vsrtlWidget; - Netlist* m_netlist; + VSRTLWidget *m_vsrtlWidget; + Netlist *m_netlist; - void createToolbar(); + void createToolbar(); }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_MAINWINDOW_H +#endif // VSRTL_MAINWINDOW_H diff --git a/graphics/vsrtl_multiplexergraphic.cpp b/graphics/vsrtl_multiplexergraphic.cpp index 8bbf6dc..0a26ec6 100644 --- a/graphics/vsrtl_multiplexergraphic.cpp +++ b/graphics/vsrtl_multiplexergraphic.cpp @@ -5,38 +5,44 @@ namespace vsrtl { -MultiplexerGraphic::MultiplexerGraphic(SimComponent* c, ComponentGraphic* parent) : ComponentGraphic(c, parent) { - // Make changes in the select signal trigger a redraw of the multiplexer (and its input signal markings) - wrapSimSignal(getSelect()->changed); +MultiplexerGraphic::MultiplexerGraphic(SimComponent *c, + ComponentGraphic *parent) + : ComponentGraphic(c, parent) { + // Make changes in the select signal trigger a redraw of the multiplexer (and + // its input signal markings) + wrapSimSignal(getSelect()->changed); } -SimPort* MultiplexerGraphic::getSelect() { - // Simulator component must have set the select port to special port 0 - return m_component->getSpecialPort(GFX_MUX_SELECT); +SimPort *MultiplexerGraphic::getSelect() { + // Simulator component must have set the select port to special port 0 + return m_component->getSpecialPort(GFX_MUX_SELECT); } -void MultiplexerGraphic::paintOverlay(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { - // Mark input IO with circles, highlight selected input with green - painter->save(); - - const auto inputPorts = m_component->getPorts(); - const auto* select = getSelect(); - const unsigned int index = select->uValue(); - Q_ASSERT(static_cast(index) < m_inputPorts.size()); - - for (const auto& ip : qAsConst(m_inputPorts)) { - const auto* p_base = ip->getPort(); - if (p_base != select) { - if (p_base == inputPorts[index]) { - painter->setBrush(Qt::green); - } else { - painter->setBrush(Qt::white); - } - - paintIndicator(painter, ip, p_base == inputPorts[index] ? Qt::green : Qt::white); - } +void MultiplexerGraphic::paintOverlay(QPainter *painter, + const QStyleOptionGraphicsItem *, + QWidget *) { + // Mark input IO with circles, highlight selected input with green + painter->save(); + + const auto inputPorts = m_component->getPorts(); + const auto *select = getSelect(); + const unsigned int index = select->uValue(); + Q_ASSERT(static_cast(index) < m_inputPorts.size()); + + for (const auto &ip : qAsConst(m_inputPorts)) { + const auto *p_base = ip->getPort(); + if (p_base != select) { + if (p_base == inputPorts[index]) { + painter->setBrush(Qt::green); + } else { + painter->setBrush(Qt::white); + } + + paintIndicator(painter, ip, + p_base == inputPorts[index] ? Qt::green : Qt::white); } - painter->restore(); + } + painter->restore(); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_multiplexergraphic.h b/graphics/vsrtl_multiplexergraphic.h index d6d0a28..eeaf2ba 100644 --- a/graphics/vsrtl_multiplexergraphic.h +++ b/graphics/vsrtl_multiplexergraphic.h @@ -9,13 +9,14 @@ namespace vsrtl { class MultiplexerGraphic : public ComponentGraphic { public: - MultiplexerGraphic(SimComponent* c, ComponentGraphic* parent); - void paintOverlay(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget* w) override; + MultiplexerGraphic(SimComponent *c, ComponentGraphic *parent); + void paintOverlay(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *w) override; private: - SimPort* getSelect(); + SimPort *getSelect(); }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_MULTIPLEXERGRAPHIC_H +#endif // VSRTL_MULTIPLEXERGRAPHIC_H diff --git a/graphics/vsrtl_netlist.cpp b/graphics/vsrtl_netlist.cpp index cfb278b..28f1aec 100644 --- a/graphics/vsrtl_netlist.cpp +++ b/graphics/vsrtl_netlist.cpp @@ -11,110 +11,119 @@ namespace vsrtl { -Netlist::Netlist(SimDesign& design, QWidget* parent) : QWidget(parent), ui(new Ui::Netlist) { - ui->setupUi(this); - - m_netlistView = new NetlistView(this); - m_netlistModel = new NetlistModel(&design, this); - m_netlistView->setModel(m_netlistModel); - ui->netlistViews->addTab(m_netlistView, "Netlist"); - m_netlistModel->invalidate(); - - m_registerModel = new RegisterModel(&design, this); - m_registerView = new NetlistView(this); - m_registerView->setModel(m_registerModel); - ui->netlistViews->addTab(m_registerView, "Registers"); - m_registerModel->invalidate(); - - m_selectionModel = new QItemSelectionModel(m_netlistModel); - m_netlistView->setSelectionModel(m_selectionModel); - connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &Netlist::handleViewSelectionChanged); - - m_netlistView->header()->setSectionResizeMode(NetlistModel::ComponentColumn, QHeaderView::ResizeToContents); - m_netlistView->header()->setSectionResizeMode(NetlistModel::IOColumn, QHeaderView::ResizeToContents); - - m_registerView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - - m_netlistView->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_netlistView->setSelectionBehavior(QAbstractItemView::SelectRows); - - m_registerView->expandAll(); - m_netlistView->expandAll(); - - m_registerView->setItemDelegate(new NetlistDelegate(this)); - - const QIcon expandIcon = QIcon(":/vsrtl_icons/expand.svg"); - QAction* expandAct = new QAction(expandIcon, "Expand All", this); - connect(expandAct, &QAction::triggered, [=] { this->setCurrentViewExpandState(true); }); - ui->expand->setIcon(expandIcon); - connect(ui->expand, &QPushButton::clicked, expandAct, &QAction::trigger); - - const QIcon collapseIcon = QIcon(":/vsrtl_icons/collapse.svg"); - QAction* collapseAct = new QAction(collapseIcon, "Collapse All", this); - connect(collapseAct, &QAction::triggered, [=] { this->setCurrentViewExpandState(false); }); - ui->collapse->setIcon(collapseIcon); - connect(ui->collapse, &QPushButton::clicked, collapseAct, &QAction::trigger); +Netlist::Netlist(SimDesign &design, QWidget *parent) + : QWidget(parent), ui(new Ui::Netlist) { + ui->setupUi(this); + + m_netlistView = new NetlistView(this); + m_netlistModel = new NetlistModel(&design, this); + m_netlistView->setModel(m_netlistModel); + ui->netlistViews->addTab(m_netlistView, "Netlist"); + m_netlistModel->invalidate(); + + m_registerModel = new RegisterModel(&design, this); + m_registerView = new NetlistView(this); + m_registerView->setModel(m_registerModel); + ui->netlistViews->addTab(m_registerView, "Registers"); + m_registerModel->invalidate(); + + m_selectionModel = new QItemSelectionModel(m_netlistModel); + m_netlistView->setSelectionModel(m_selectionModel); + connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, + &Netlist::handleViewSelectionChanged); + + m_netlistView->header()->setSectionResizeMode(NetlistModel::ComponentColumn, + QHeaderView::ResizeToContents); + m_netlistView->header()->setSectionResizeMode(NetlistModel::IOColumn, + QHeaderView::ResizeToContents); + + m_registerView->header()->setSectionResizeMode(0, + QHeaderView::ResizeToContents); + + m_netlistView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_netlistView->setSelectionBehavior(QAbstractItemView::SelectRows); + + m_registerView->expandAll(); + m_netlistView->expandAll(); + + m_registerView->setItemDelegate(new NetlistDelegate(this)); + + const QIcon expandIcon = QIcon(":/vsrtl_icons/expand.svg"); + QAction *expandAct = new QAction(expandIcon, "Expand All", this); + connect(expandAct, &QAction::triggered, + [=] { this->setCurrentViewExpandState(true); }); + ui->expand->setIcon(expandIcon); + connect(ui->expand, &QPushButton::clicked, expandAct, &QAction::trigger); + + const QIcon collapseIcon = QIcon(":/vsrtl_icons/collapse.svg"); + QAction *collapseAct = new QAction(collapseIcon, "Collapse All", this); + connect(collapseAct, &QAction::triggered, + [=] { this->setCurrentViewExpandState(false); }); + ui->collapse->setIcon(collapseIcon); + connect(ui->collapse, &QPushButton::clicked, collapseAct, &QAction::trigger); } void Netlist::setCurrentViewExpandState(bool state) { - QTreeView* view = nullptr; - switch (ui->netlistViews->currentIndex()) { - case 0: { - view = m_netlistView; - break; - } - case 1: { - view = m_registerView; - break; - } - default: - Q_ASSERT(false); - } - - if (view) { - if (state) { - view->expandAll(); - } else { - view->collapseAll(); - } + QTreeView *view = nullptr; + switch (ui->netlistViews->currentIndex()) { + case 0: { + view = m_netlistView; + break; + } + case 1: { + view = m_registerView; + break; + } + default: + Q_ASSERT(false); + } + + if (view) { + if (state) { + view->expandAll(); + } else { + view->collapseAll(); } + } } -void Netlist::updateSelection(const std::vector& selected) { - m_selectionModel->clearSelection(); - for (const auto& c : selected) { - m_selectionModel->select(m_netlistModel->lookupIndexForComponent(c), - QItemSelectionModel::SelectionFlag::Select | QItemSelectionModel::SelectionFlag::Rows); - } +void Netlist::updateSelection(const std::vector &selected) { + m_selectionModel->clearSelection(); + for (const auto &c : selected) { + m_selectionModel->select(m_netlistModel->lookupIndexForComponent(c), + QItemSelectionModel::SelectionFlag::Select | + QItemSelectionModel::SelectionFlag::Rows); + } } namespace { -void getIndexComponentPtr(const QItemSelection& selected, std::vector& c_v) { - const auto indexes = selected.indexes(); - for (const auto& sel : qAsConst(indexes)) { - auto* c = static_cast(sel.internalPointer())->m_component; - if (c) { - c_v.push_back(c); - } +void getIndexComponentPtr(const QItemSelection &selected, + std::vector &c_v) { + const auto indexes = selected.indexes(); + for (const auto &sel : qAsConst(indexes)) { + auto *c = + static_cast(sel.internalPointer())->m_component; + if (c) { + c_v.push_back(c); } + } } -} // namespace +} // namespace -void Netlist::handleViewSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { - std::vector sel_components, desel_components; +void Netlist::handleViewSelectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) { + std::vector sel_components, desel_components; - getIndexComponentPtr(selected, sel_components); - getIndexComponentPtr(deselected, desel_components); + getIndexComponentPtr(selected, sel_components); + getIndexComponentPtr(deselected, desel_components); - emit selectionChanged(sel_components, desel_components); + emit selectionChanged(sel_components, desel_components); } -Netlist::~Netlist() { - delete ui; -} +Netlist::~Netlist() { delete ui; } void Netlist::reloadNetlist() { - m_netlistModel->invalidate(); - m_registerModel->invalidate(); + m_netlistModel->invalidate(); + m_registerModel->invalidate(); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_netlist.h b/graphics/vsrtl_netlist.h index ab978ca..ea8f991 100644 --- a/graphics/vsrtl_netlist.h +++ b/graphics/vsrtl_netlist.h @@ -19,33 +19,35 @@ class RegisterTreeItem; class NetlistTreeItem; class Netlist : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit Netlist(SimDesign& design, QWidget* parent = 0); - ~Netlist(); + explicit Netlist(SimDesign &design, QWidget *parent = 0); + ~Netlist(); signals: - void selectionChanged(const std::vector& selected, std::vector& deselected); + void selectionChanged(const std::vector &selected, + std::vector &deselected); public slots: - void reloadNetlist(); - void updateSelection(const std::vector&); + void reloadNetlist(); + void updateSelection(const std::vector &); private slots: - void handleViewSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void handleViewSelectionChanged(const QItemSelection &selected, + const QItemSelection &deselected); private: - void setCurrentViewExpandState(bool state); + void setCurrentViewExpandState(bool state); - Ui::Netlist* ui; - QItemSelectionModel* m_selectionModel; - NetlistModel* m_netlistModel; - RegisterModel* m_registerModel; + Ui::Netlist *ui; + QItemSelectionModel *m_selectionModel; + NetlistModel *m_netlistModel; + RegisterModel *m_registerModel; - NetlistView* m_registerView; - NetlistView* m_netlistView; + NetlistView *m_registerView; + NetlistView *m_netlistView; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_NETLIST_H +#endif // VSRTL_NETLIST_H diff --git a/graphics/vsrtl_netlistdelegate.cpp b/graphics/vsrtl_netlistdelegate.cpp index fd22128..0bd864b 100644 --- a/graphics/vsrtl_netlistdelegate.cpp +++ b/graphics/vsrtl_netlistdelegate.cpp @@ -10,60 +10,66 @@ namespace vsrtl { -NetlistDelegate::NetlistDelegate(QObject* parent) : QStyledItemDelegate(parent) { - // The validator does not concern itself whether the value is actually writeable to the register. Any hex input is - // accepted. When a register is forced to a value, the value is truncated to the bit width of the register. - m_validator = new QRegularExpressionValidator(this); +NetlistDelegate::NetlistDelegate(QObject *parent) + : QStyledItemDelegate(parent) { + // The validator does not concern itself whether the value is actually + // writeable to the register. Any hex input is accepted. When a register is + // forced to a value, the value is truncated to the bit width of the register. + m_validator = new QRegularExpressionValidator(this); } -QWidget* NetlistDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { - QLineEdit* editor = new QLineEdit(parent); - editor->setFont(QFont("monospace")); +QWidget *NetlistDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &, + const QModelIndex &) const { + QLineEdit *editor = new QLineEdit(parent); + editor->setFont(QFont("monospace")); - QPalette palette = QApplication::palette(); - palette.setBrush(QPalette::Text, Qt::blue); - editor->setPalette(palette); - editor->setValidator(m_validator); + QPalette palette = QApplication::palette(); + palette.setBrush(QPalette::Text, Qt::blue); + editor->setPalette(palette); + editor->setValidator(m_validator); - return editor; + return editor; } -void NetlistDelegate::setEditorData(QWidget* e, const QModelIndex& index) const { - auto* treeItem = static_cast(index.internalPointer()); +void NetlistDelegate::setEditorData(QWidget *e, + const QModelIndex &index) const { + auto *treeItem = static_cast(index.internalPointer()); - switch (treeItem->m_radix) { - case Radix::Binary: { - m_validator->setRegularExpression(binRegex); - break; - } - case Radix::Hex: { - m_validator->setRegularExpression(hexRegex); - break; - } - case Radix::Unsigned: { - m_validator->setRegularExpression(unsignedRegex); - break; - } - case Radix::Signed: { - m_validator->setRegularExpression(signedRegex); - break; - } - case Radix::Enum: { - return; - } - } + switch (treeItem->m_radix) { + case Radix::Binary: { + m_validator->setRegularExpression(binRegex); + break; + } + case Radix::Hex: { + m_validator->setRegularExpression(hexRegex); + break; + } + case Radix::Unsigned: { + m_validator->setRegularExpression(unsignedRegex); + break; + } + case Radix::Signed: { + m_validator->setRegularExpression(signedRegex); + break; + } + case Radix::Enum: { + return; + } + } - QLineEdit* editor = dynamic_cast(e); - if (editor) { - editor->setText(index.data().toString()); - } + QLineEdit *editor = dynamic_cast(e); + if (editor) { + editor->setText(index.data().toString()); + } } -void NetlistDelegate::setModelData(QWidget* e, QAbstractItemModel* model, const QModelIndex& index) const { - QLineEdit* editor = dynamic_cast(e); - if (editor) { - model->setData(index, editor->text(), Qt::EditRole); - } +void NetlistDelegate::setModelData(QWidget *e, QAbstractItemModel *model, + const QModelIndex &index) const { + QLineEdit *editor = dynamic_cast(e); + if (editor) { + model->setData(index, editor->text(), Qt::EditRole); + } } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_netlistdelegate.h b/graphics/vsrtl_netlistdelegate.h index d338e78..f309499 100644 --- a/graphics/vsrtl_netlistdelegate.h +++ b/graphics/vsrtl_netlistdelegate.h @@ -8,14 +8,16 @@ namespace vsrtl { class NetlistDelegate : public QStyledItemDelegate { public: - NetlistDelegate(QObject* parent); - QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void setEditorData(QWidget* editor, const QModelIndex& index) const override; - void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; + NetlistDelegate(QObject *parent); + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const override; private: - QRegularExpressionValidator* m_validator; + QRegularExpressionValidator *m_validator; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_NETLISTDELEGATE_H +#endif // VSRTL_NETLISTDELEGATE_H diff --git a/graphics/vsrtl_netlistmodel.cpp b/graphics/vsrtl_netlistmodel.cpp index 2e216c1..b59edb6 100644 --- a/graphics/vsrtl_netlistmodel.cpp +++ b/graphics/vsrtl_netlistmodel.cpp @@ -11,129 +11,136 @@ namespace vsrtl { -NetlistModel::NetlistModel(SimDesign* arch, QObject* parent) +NetlistModel::NetlistModel(SimDesign *arch, QObject *parent) : NetlistModelBase({"Component", "I/O", "Value", "Width"}, arch, parent) { - rootItem = new NetlistTreeItem(nullptr); + rootItem = new NetlistTreeItem(nullptr); - loadDesign(rootItem, m_arch); + loadDesign(rootItem, m_arch); } -QVariant NetlistModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) - return QVariant(); +QVariant NetlistModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); - NetlistTreeItem* item = getTreeItem(index); - return item->data(index.column(), role); + NetlistTreeItem *item = getTreeItem(index); + return item->data(index.column(), role); } -Qt::ItemFlags NetlistModel::flags(const QModelIndex& index) const { - if (!index.isValid()) - return Qt::ItemFlags(); - Qt::ItemFlags flags = QAbstractItemModel::flags(index); +Qt::ItemFlags NetlistModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::ItemFlags(); + Qt::ItemFlags flags = QAbstractItemModel::flags(index); - // Output ports of register components are editable. - // Check if parent component is a Register, and if the current port is an output port. If so, the port is editable - if (indexIsRegisterOutputPortValue(index)) { - flags |= Qt::ItemIsEditable; - } + // Output ports of register components are editable. + // Check if parent component is a Register, and if the current port is an + // output port. If so, the port is editable + if (indexIsRegisterOutputPortValue(index)) { + flags |= Qt::ItemIsEditable; + } - return flags; + return flags; } -bool NetlistModel::setData(const QModelIndex& index, const QVariant& value, int) { - if (indexIsRegisterOutputPortValue(index)) { - SimSynchronous* reg = dynamic_cast(getParentComponent(index)); - if (reg) { - reg->forceValue(0, value.toInt()); - assert(false && "Todo: No way to propagate design through interface"); - // m_arch.propagateDesign(); - return true; - } +bool NetlistModel::setData(const QModelIndex &index, const QVariant &value, + int) { + if (indexIsRegisterOutputPortValue(index)) { + SimSynchronous *reg = + dynamic_cast(getParentComponent(index)); + if (reg) { + reg->forceValue(0, value.toInt()); + assert(false && "Todo: No way to propagate design through interface"); + // m_arch.propagateDesign(); + return true; } - return false; + } + return false; } void NetlistModel::invalidate() { - // Data changed within Design, invalidate value column - emit dataChanged(index(0, ValueColumn), index(rowCount(), ValueColumn), {Qt::DisplayRole}); + // Data changed within Design, invalidate value column + emit dataChanged(index(0, ValueColumn), index(rowCount(), ValueColumn), + {Qt::DisplayRole}); } -QModelIndex NetlistModel::lookupIndexForComponent(SimComponent* c) const { - if (m_componentIndicies.find(c) != m_componentIndicies.end()) { - NetlistTreeItem* item = m_componentIndicies.at(c); - if (item->index.isValid()) { - return item->index; - } +QModelIndex NetlistModel::lookupIndexForComponent(SimComponent *c) const { + if (m_componentIndicies.find(c) != m_componentIndicies.end()) { + NetlistTreeItem *item = m_componentIndicies.at(c); + if (item->index.isValid()) { + return item->index; } - return QModelIndex(); + } + return QModelIndex(); } -void NetlistModel::addPortToComponent(SimPort* port, NetlistTreeItem* parent, PortDirection dir) { - auto* child = new NetlistTreeItem(parent); - child->setPort(port); - parent->insertChild(parent->childCount(), child); - child->m_direction = dir; +void NetlistModel::addPortToComponent(SimPort *port, NetlistTreeItem *parent, + PortDirection dir) { + auto *child = new NetlistTreeItem(parent); + child->setPort(port); + parent->insertChild(parent->childCount(), child); + child->m_direction = dir; } -bool NetlistModel::indexIsRegisterOutputPortValue(const QModelIndex& index) const { - if (index.column() == ValueColumn) { - auto* item = getTreeItem(index); - auto* parentItem = static_cast(item->parent()); - auto* parentComponent = parentItem->m_component; - if (parentComponent) { - if (dynamic_cast(parentComponent)) { - // Parent is register, check if current index is an output port - if (item->m_port && item->m_direction == PortDirection::Output) { - return true; - } - } +bool NetlistModel::indexIsRegisterOutputPortValue( + const QModelIndex &index) const { + if (index.column() == ValueColumn) { + auto *item = getTreeItem(index); + auto *parentItem = static_cast(item->parent()); + auto *parentComponent = parentItem->m_component; + if (parentComponent) { + if (dynamic_cast(parentComponent)) { + // Parent is register, check if current index is an output port + if (item->m_port && item->m_direction == PortDirection::Output) { + return true; } + } } - return false; + } + return false; } -void NetlistModel::loadDesignRecursive(NetlistTreeItem* parent, SimComponent* component) { - // Subcomponents - for (const auto& subcomponent : component->getSubComponents()) { - if (subcomponent->getGraphicsType() == GraphicsTypeFor(Constant)) { - // Do not display constants in the netlist - continue; - } +void NetlistModel::loadDesignRecursive(NetlistTreeItem *parent, + SimComponent *component) { + // Subcomponents + for (const auto &subcomponent : component->getSubComponents()) { + if (subcomponent->getGraphicsType() == GraphicsTypeFor(Constant)) { + // Do not display constants in the netlist + continue; + } - auto* child = new NetlistTreeItem(parent); - parent->insertChild(parent->childCount(), child); + auto *child = new NetlistTreeItem(parent); + parent->insertChild(parent->childCount(), child); - // Set component data (component pointer and name) - m_componentIndicies[subcomponent] = child; + // Set component data (component pointer and name) + m_componentIndicies[subcomponent] = child; - child->m_component = subcomponent; - child->m_name = QString::fromStdString(subcomponent->getName()); + child->m_component = subcomponent; + child->m_name = QString::fromStdString(subcomponent->getName()); - // Recurse into the child - loadDesignRecursive(child, subcomponent); - } + // Recurse into the child + loadDesignRecursive(child, subcomponent); + } - // I/O ports of component - for (const auto& input : component->getPorts()) { - addPortToComponent(input, parent, PortDirection::Input); - } - for (const auto& output : component->getPorts()) { - addPortToComponent(output, parent, PortDirection::Output); - } + // I/O ports of component + for (const auto &input : component->getPorts()) { + addPortToComponent(input, parent, PortDirection::Input); + } + for (const auto &output : component->getPorts()) { + addPortToComponent(output, parent, PortDirection::Output); + } } -SimComponent* NetlistModel::getParentComponent(const QModelIndex& index) const { - auto* item = getTreeItem(index); +SimComponent *NetlistModel::getParentComponent(const QModelIndex &index) const { + auto *item = getTreeItem(index); + if (item) { + item = static_cast(item->parent()); if (item) { - item = static_cast(item->parent()); - if (item) { - return item->m_component; - } + return item->m_component; } - return nullptr; + } + return nullptr; } -void NetlistModel::loadDesign(NetlistTreeItem* parent, SimDesign* design) { - loadDesignRecursive(parent, design); +void NetlistModel::loadDesign(NetlistTreeItem *parent, SimDesign *design) { + loadDesignRecursive(parent, design); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_netlistmodel.h b/graphics/vsrtl_netlistmodel.h index 55be788..970d505 100644 --- a/graphics/vsrtl_netlistmodel.h +++ b/graphics/vsrtl_netlistmodel.h @@ -13,30 +13,38 @@ namespace vsrtl { class NetlistModel : public NetlistModelBase { - Q_OBJECT + Q_OBJECT public: - enum columns { ComponentColumn, IOColumn, ValueColumn, WidthColumn, NUM_COLUMNS }; - NetlistModel(SimDesign* arch, QObject* parent = nullptr); - - QVariant data(const QModelIndex& index, int role) const override; - Qt::ItemFlags flags(const QModelIndex& index) const override; - bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; - - QModelIndex lookupIndexForComponent(SimComponent* c) const; + enum columns { + ComponentColumn, + IOColumn, + ValueColumn, + WidthColumn, + NUM_COLUMNS + }; + NetlistModel(SimDesign *arch, QObject *parent = nullptr); + + QVariant data(const QModelIndex &index, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + + QModelIndex lookupIndexForComponent(SimComponent *c) const; public slots: - void invalidate() override; + void invalidate() override; private: - void addPortToComponent(SimPort* port, NetlistTreeItem* parent, PortDirection); - void loadDesign(NetlistTreeItem* parent, SimDesign* design); - void loadDesignRecursive(NetlistTreeItem* parent, SimComponent* component); - SimComponent* getParentComponent(const QModelIndex& index) const; - std::map m_componentIndicies; - bool indexIsRegisterOutputPortValue(const QModelIndex& index) const; + void addPortToComponent(SimPort *port, NetlistTreeItem *parent, + PortDirection); + void loadDesign(NetlistTreeItem *parent, SimDesign *design); + void loadDesignRecursive(NetlistTreeItem *parent, SimComponent *component); + SimComponent *getParentComponent(const QModelIndex &index) const; + std::map m_componentIndicies; + bool indexIsRegisterOutputPortValue(const QModelIndex &index) const; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_NETLISTMODEL_H +#endif // VSRTL_NETLISTMODEL_H diff --git a/graphics/vsrtl_netlistmodelbase.cpp b/graphics/vsrtl_netlistmodelbase.cpp index 41e90da..d162fe0 100644 --- a/graphics/vsrtl_netlistmodelbase.cpp +++ b/graphics/vsrtl_netlistmodelbase.cpp @@ -3,23 +3,23 @@ namespace vsrtl { int getRootIndex(QModelIndex index) { - if (index.isValid()) { - while (index.parent().isValid()) { - index = index.parent(); - } - return index.row(); - } else { - return -1; + if (index.isValid()) { + while (index.parent().isValid()) { + index = index.parent(); } + return index.row(); + } else { + return -1; + } } -int getRootSelectedIndex(QItemSelectionModel* model) { - auto indexes = model->selectedIndexes(); - if (!indexes.isEmpty()) { - return getRootIndex(indexes.first()); - } else { - return -1; - } +int getRootSelectedIndex(QItemSelectionModel *model) { + auto indexes = model->selectedIndexes(); + if (!indexes.isEmpty()) { + return getRootIndex(indexes.first()); + } else { + return -1; + } } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_netlistmodelbase.h b/graphics/vsrtl_netlistmodelbase.h index a82c2c1..088c77a 100644 --- a/graphics/vsrtl_netlistmodelbase.h +++ b/graphics/vsrtl_netlistmodelbase.h @@ -11,87 +11,94 @@ namespace vsrtl { int getRootIndex(QModelIndex index); -int getRootSelectedIndex(QItemSelectionModel* model); +int getRootSelectedIndex(QItemSelectionModel *model); template class NetlistModelBase : public QAbstractItemModel { public: - NetlistModelBase(QStringList headers, SimDesign* arch, QObject* parent = nullptr) - : QAbstractItemModel(parent), m_headers(headers), m_arch(arch) {} - - ~NetlistModelBase() override { delete rootItem; } - - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_headers.size() > section) - return m_headers[section]; - - return QVariant(); - } - - QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override { - if (parent.isValid() && parent.column() != 0) - return QModelIndex(); - - T* parentItem = getTreeItem(parent); - if (!parentItem) - parentItem = rootItem; - - Q_ASSERT(rootItem); - T* childItem = static_cast(parentItem->child(row)); - if (childItem) { - auto i = createIndex(row, column, childItem); - childItem->index = i; - return i; - } else - return QModelIndex(); - } - - QModelIndex parent(const QModelIndex& index) const override { - if (!index.isValid()) - return QModelIndex(); - - auto* childItem = getTreeItem(index); - auto* parentItem = childItem->parent(); - - Q_ASSERT(rootItem); - if (parentItem == rootItem) - return QModelIndex(); - - return createIndex(parentItem->childNumber(), 0, parentItem); - } - - int rowCount(const QModelIndex& parent = QModelIndex()) const override { - auto* parentItem = getTreeItem(parent); - return parentItem->childCount(); - } - - int columnCount(const QModelIndex& = QModelIndex()) const override { return m_headers.size(); } - - bool setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, - int role = Qt::EditRole) override { - if (role != Qt::EditRole || orientation != Qt::Horizontal || section > m_headers.size()) - return false; - m_headers[section] = value.toString(); - - emit headerDataChanged(orientation, section, section); - return true; - } + NetlistModelBase(QStringList headers, SimDesign *arch, + QObject *parent = nullptr) + : QAbstractItemModel(parent), m_headers(headers), m_arch(arch) {} + + ~NetlistModelBase() override { delete rootItem; } + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole && + m_headers.size() > section) + return m_headers[section]; + + return QVariant(); + } + + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override { + if (parent.isValid() && parent.column() != 0) + return QModelIndex(); + + T *parentItem = getTreeItem(parent); + if (!parentItem) + parentItem = rootItem; + + Q_ASSERT(rootItem); + T *childItem = static_cast(parentItem->child(row)); + if (childItem) { + auto i = createIndex(row, column, childItem); + childItem->index = i; + return i; + } else + return QModelIndex(); + } + + QModelIndex parent(const QModelIndex &index) const override { + if (!index.isValid()) + return QModelIndex(); + + auto *childItem = getTreeItem(index); + auto *parentItem = childItem->parent(); + + Q_ASSERT(rootItem); + if (parentItem == rootItem) + return QModelIndex(); + + return createIndex(parentItem->childNumber(), 0, parentItem); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const override { + auto *parentItem = getTreeItem(parent); + return parentItem->childCount(); + } + + int columnCount(const QModelIndex & = QModelIndex()) const override { + return m_headers.size(); + } + + bool setHeaderData(int section, Qt::Orientation orientation, + const QVariant &value, int role = Qt::EditRole) override { + if (role != Qt::EditRole || orientation != Qt::Horizontal || + section > m_headers.size()) + return false; + m_headers[section] = value.toString(); + + emit headerDataChanged(orientation, section, section); + return true; + } public slots: - virtual void invalidate() = 0; + virtual void invalidate() = 0; protected: - T* getTreeItem(const QModelIndex& index) const { - if (index.isValid()) { - return static_cast(index.internalPointer()); - } - return rootItem; + T *getTreeItem(const QModelIndex &index) const { + if (index.isValid()) { + return static_cast(index.internalPointer()); } + return rootItem; + } - T* rootItem = nullptr; - QStringList m_headers; - SimDesign* m_arch = nullptr; + T *rootItem = nullptr; + QStringList m_headers; + SimDesign *m_arch = nullptr; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_NETLISTMODELBASE_H +#endif // VSRTL_NETLISTMODELBASE_H diff --git a/graphics/vsrtl_netlistview.h b/graphics/vsrtl_netlistview.h index e905ba7..fadfc9e 100644 --- a/graphics/vsrtl_netlistview.h +++ b/graphics/vsrtl_netlistview.h @@ -12,33 +12,35 @@ namespace vsrtl { template class NetlistView : public QTreeView { public: - NetlistView(QWidget* parent) : QTreeView(parent) { - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &QTreeView::customContextMenuRequested, this, &NetlistView::contextMenuRequest); - } + NetlistView(QWidget *parent) : QTreeView(parent) { + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &QTreeView::customContextMenuRequested, this, + &NetlistView::contextMenuRequest); + } private slots: - void contextMenuRequest(const QPoint& pos) { - QModelIndex index = indexAt(pos); - if (index.isValid()) { - /* The TreeItem at the given index will be queried for its available actions (both TreeItem actions and - * derived class actions). These actions are then presented in the context menu */ - T* item = static_cast(index.internalPointer()); + void contextMenuRequest(const QPoint &pos) { + QModelIndex index = indexAt(pos); + if (index.isValid()) { + /* The TreeItem at the given index will be queried for its available + * actions (both TreeItem actions and derived class actions). These + * actions are then presented in the context menu */ + T *item = static_cast(index.internalPointer()); - QMenu menu; - const auto& actions = item->getActions(); - if (actions.size() != 0) { - for (const auto& actionMenu : actions) { - menu.addSection(actionMenu->title()); - for (const auto& action : actionMenu->actions()) { - menu.addAction(action); - } - } - menu.exec(viewport()->mapToGlobal(pos)); - } + QMenu menu; + const auto &actions = item->getActions(); + if (actions.size() != 0) { + for (const auto &actionMenu : actions) { + menu.addSection(actionMenu->title()); + for (const auto &action : actionMenu->actions()) { + menu.addAction(action); + } } + menu.exec(viewport()->mapToGlobal(pos)); + } } + } }; -} // namespace vsrtl -#endif // VSRTL_NETLISTVIEW_H +} // namespace vsrtl +#endif // VSRTL_NETLISTVIEW_H diff --git a/graphics/vsrtl_parameterdialog.cpp b/graphics/vsrtl_parameterdialog.cpp index 633d619..8a6221c 100644 --- a/graphics/vsrtl_parameterdialog.cpp +++ b/graphics/vsrtl_parameterdialog.cpp @@ -9,84 +9,89 @@ namespace vsrtl { -ParameterDialog::ParameterDialog(SimComponent* component, QWidget* parent) +ParameterDialog::ParameterDialog(SimComponent *component, QWidget *parent) : QDialog(parent), m_ui(new Ui::ParameterDialog) { - m_ui->setupUi(this); - setWindowTitle("Parameters for: \"" + QString::fromStdString(component->getDisplayName()) + "\""); + m_ui->setupUi(this); + setWindowTitle("Parameters for: \"" + + QString::fromStdString(component->getDisplayName()) + "\""); - for (const auto& p : component->getParameters()) { - QLabel* label = new QLabel(QString::fromStdString(p->getName())); - QWidget* editWidget = nullptr; - if (auto* pt = dynamic_cast*>(p)) { - auto* intWidget = new QSpinBox(); - intWidget->setValue(pt->getValue()); - editWidget = intWidget; + for (const auto &p : component->getParameters()) { + QLabel *label = new QLabel(QString::fromStdString(p->getName())); + QWidget *editWidget = nullptr; + if (auto *pt = dynamic_cast *>(p)) { + auto *intWidget = new QSpinBox(); + intWidget->setValue(pt->getValue()); + editWidget = intWidget; - const auto& options = pt->getOptions(); - if (options.size() == 2) { - intWidget->setRange(options[0], options[1]); - } + const auto &options = pt->getOptions(); + if (options.size() == 2) { + intWidget->setRange(options[0], options[1]); + } - connect(intWidget, QOverload::of(&QSpinBox::valueChanged), [=](int value) { + connect(intWidget, QOverload::of(&QSpinBox::valueChanged), + [=](int value) { if (value != pt->getValue()) { - m_parameterChangeApplierFunctions[p] = [=] { pt->setValue(value); }; + m_parameterChangeApplierFunctions[p] = [=] { + pt->setValue(value); + }; } else { - m_parameterChangeApplierFunctions.erase(pt); + m_parameterChangeApplierFunctions.erase(pt); } - }); - } else if (auto* pt = dynamic_cast*>(p)) { - auto* textWidget = new QLineEdit(); - textWidget->setText(QString::fromStdString(pt->getValue())); - editWidget = textWidget; - connect(textWidget, &QLineEdit::textChanged, [=](const QString& text) { - if (text != QString::fromStdString(pt->getValue())) { - m_parameterChangeApplierFunctions[p] = [=] { pt->setValue(text.toStdString()); }; - } else { - m_parameterChangeApplierFunctions.erase(pt); - } - }); - } else if (auto* pt = dynamic_cast*>(p)) { - auto* intWidget = new QCheckBox(); - intWidget->setChecked(pt->getValue()); - editWidget = intWidget; - connect(intWidget, &QCheckBox::toggled, [=](bool state) { - if (state != pt->getValue()) { - m_parameterChangeApplierFunctions[p] = [=] { pt->setValue(state); }; - } else { - m_parameterChangeApplierFunctions.erase(pt); - } - }); - } else if (auto* pt = dynamic_cast>*>(p)) { - Q_UNUSED(pt); - Q_ASSERT(false && "todo"); - } else if (auto* pt = dynamic_cast>*>(p)) { - Q_UNUSED(pt); - Q_ASSERT(false && "todo"); + }); + } else if (auto *pt = dynamic_cast *>(p)) { + auto *textWidget = new QLineEdit(); + textWidget->setText(QString::fromStdString(pt->getValue())); + editWidget = textWidget; + connect(textWidget, &QLineEdit::textChanged, [=](const QString &text) { + if (text != QString::fromStdString(pt->getValue())) { + m_parameterChangeApplierFunctions[p] = [=] { + pt->setValue(text.toStdString()); + }; + } else { + m_parameterChangeApplierFunctions.erase(pt); + } + }); + } else if (auto *pt = dynamic_cast *>(p)) { + auto *intWidget = new QCheckBox(); + intWidget->setChecked(pt->getValue()); + editWidget = intWidget; + connect(intWidget, &QCheckBox::toggled, [=](bool state) { + if (state != pt->getValue()) { + m_parameterChangeApplierFunctions[p] = [=] { pt->setValue(state); }; } else { - Q_ASSERT(false && "Unknown parameter type"); + m_parameterChangeApplierFunctions.erase(pt); } + }); + } else if (auto *pt = dynamic_cast> *>(p)) { + Q_UNUSED(pt); + Q_ASSERT(false && "todo"); + } else if (auto *pt = + dynamic_cast> *>(p)) { + Q_UNUSED(pt); + Q_ASSERT(false && "todo"); + } else { + Q_ASSERT(false && "Unknown parameter type"); + } - label->setToolTip(QString::fromStdString(p->getTooltip())); - editWidget->setToolTip(QString::fromStdString(p->getTooltip())); + label->setToolTip(QString::fromStdString(p->getTooltip())); + editWidget->setToolTip(QString::fromStdString(p->getTooltip())); - // Add widgets to layout - const int row = m_ui->parameterLayout->rowCount(); - m_ui->parameterLayout->addWidget(label, row, 0, Qt::AlignLeft); - m_ui->parameterLayout->addWidget(editWidget, row, 1, Qt::AlignRight); - } + // Add widgets to layout + const int row = m_ui->parameterLayout->rowCount(); + m_ui->parameterLayout->addWidget(label, row, 0, Qt::AlignLeft); + m_ui->parameterLayout->addWidget(editWidget, row, 1, Qt::AlignRight); + } - resize(width(), height()); + resize(width(), height()); } void ParameterDialog::accept() { - for (const auto& f : m_parameterChangeApplierFunctions) { - f.second(); - } - QDialog::accept(); + for (const auto &f : m_parameterChangeApplierFunctions) { + f.second(); + } + QDialog::accept(); } -ParameterDialog::~ParameterDialog() { - delete m_ui; -} +ParameterDialog::~ParameterDialog() { delete m_ui; } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_parameterdialog.h b/graphics/vsrtl_parameterdialog.h index d559990..c522dc7 100644 --- a/graphics/vsrtl_parameterdialog.h +++ b/graphics/vsrtl_parameterdialog.h @@ -16,18 +16,19 @@ class ParameterDialog; } class ParameterDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - ParameterDialog(SimComponent* component, QWidget* parent = nullptr); - ~ParameterDialog(); + ParameterDialog(SimComponent *component, QWidget *parent = nullptr); + ~ParameterDialog(); - void accept() override; + void accept() override; private: - Ui::ParameterDialog* m_ui; + Ui::ParameterDialog *m_ui; - std::map> m_parameterChangeApplierFunctions; + std::map> + m_parameterChangeApplierFunctions; }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_placeroute.cpp b/graphics/vsrtl_placeroute.cpp index b8f118b..56a6f72 100644 --- a/graphics/vsrtl_placeroute.cpp +++ b/graphics/vsrtl_placeroute.cpp @@ -11,124 +11,138 @@ namespace vsrtl { /** * @brief topologicalSortUtil * Subcomponent ordering is based on a topological sort algorithm. - * This algorithm is applicable for DAG's (Directed Acyclical Graph's). Naturally, digital circuits does not fulfull the - * requirement for being a DAG. However, if Registers are seen as having either their input or output disregarded as an - * edge in a DAG, a DAG can be created from a digital circuit, if only outputs are considered. - * Having a topological sorting of the components, rows- and columns can - * be generated for each depth in the topological sort, wherein the components are finally layed out. + * This algorithm is applicable for DAG's (Directed Acyclical Graph's). + * Naturally, digital circuits does not fulfull the requirement for being a DAG. + * However, if Registers are seen as having either their input or output + * disregarded as an edge in a DAG, a DAG can be created from a digital circuit, + * if only outputs are considered. Having a topological sorting of the + * components, rows- and columns can be generated for each depth in the + * topological sort, wherein the components are finally layed out. * @param parent */ -void topologicalSortUtil(SimComponent* c, std::map& visited, std::deque& stack) { - visited[c] = true; - - for (const auto& cc : c->getOutputComponents()) { - // The find call ensures that the output components are inside this subcomponent. OutputComponents are "parent" - // agnostic, and has no notion of enclosing components. - if (visited.find(cc) != visited.end() && !visited[cc]) { - topologicalSortUtil(cc, visited, stack); - } +void topologicalSortUtil(SimComponent *c, + std::map &visited, + std::deque &stack) { + visited[c] = true; + + for (const auto &cc : c->getOutputComponents()) { + // The find call ensures that the output components are inside this + // subcomponent. OutputComponents are "parent" agnostic, and has no notion + // of enclosing components. + if (visited.find(cc) != visited.end() && !visited[cc]) { + topologicalSortUtil(cc, visited, stack); } - stack.push_front(c); + } + stack.push_front(c); } -std::deque topologicalSort(const std::vector& components) { - std::map visited; - std::deque stack; +std::deque +topologicalSort(const std::vector &components) { + std::map visited; + std::deque stack; - for (const auto& cpt : components) - visited[cpt->getComponent()] = false; + for (const auto &cpt : components) + visited[cpt->getComponent()] = false; - for (const auto& c : visited) { - if (!c.second) { - topologicalSortUtil(c.first, visited, stack); - } + for (const auto &c : visited) { + if (!c.second) { + topologicalSortUtil(c.first, visited, stack); } - return stack; + } + return stack; } -std::map> ASAPSchedule(const std::vector& components) { - std::deque sortedComponents = topologicalSort(components); - std::map> schedule; - std::map componentToDepth; - - // 1. assign a depth to the components based on their depth in the topological sort, disregarding output edges - for (const auto& c : sortedComponents) { - int lastPredLayer = -1; - for (const auto& inC : c->getInputComponents()) { - if (componentToDepth.count(inC) != 0) { - int predDepth = componentToDepth.at(inC); - lastPredLayer = lastPredLayer < predDepth ? predDepth : lastPredLayer; - } - } - componentToDepth[c] = lastPredLayer + 1; +std::map> +ASAPSchedule(const std::vector &components) { + std::deque sortedComponents = topologicalSort(components); + std::map> schedule; + std::map componentToDepth; + + // 1. assign a depth to the components based on their depth in the topological + // sort, disregarding output edges + for (const auto &c : sortedComponents) { + int lastPredLayer = -1; + for (const auto &inC : c->getInputComponents()) { + if (componentToDepth.count(inC) != 0) { + int predDepth = componentToDepth.at(inC); + lastPredLayer = lastPredLayer < predDepth ? predDepth : lastPredLayer; + } } + componentToDepth[c] = lastPredLayer + 1; + } - // 2. Create a map between depths and components - for (const auto& c : componentToDepth) { - schedule[c.second].insert(c.first); - } + // 2. Create a map between depths and components + for (const auto &c : componentToDepth) { + schedule[c.second].insert(c.first); + } - return schedule; + return schedule; } -std::map ASAPPlacement(const std::vector& components) { - std::map placements; - const auto asapSchedule = ASAPSchedule(components); - - // 1. create a width of each column - std::map columnWidths; - for (const auto& iter : asapSchedule) { - int maxWidth = 0; - for (const auto& c : iter.second) { - int width = c->getGraphic()->getCurrentComponentRect().width(); - maxWidth = maxWidth < width ? width : maxWidth; - } - columnWidths[iter.first] = maxWidth; +std::map +ASAPPlacement(const std::vector &components) { + std::map placements; + const auto asapSchedule = ASAPSchedule(components); + + // 1. create a width of each column + std::map columnWidths; + for (const auto &iter : asapSchedule) { + int maxWidth = 0; + for (const auto &c : iter.second) { + int width = + c->getGraphic()->getCurrentComponentRect().width(); + maxWidth = maxWidth < width ? width : maxWidth; } - - // Position components - // Start a bit offset from the parent borders - const QPoint start{SUBCOMPONENT_INDENT, SUBCOMPONENT_INDENT}; - int x = start.x(); - int y = start.y(); - for (const auto& iter : asapSchedule) { - for (const auto& c : iter.second) { - auto* g = c->getGraphic(); - placements[g] = QPoint(x, y); - y += g->getCurrentComponentRect().height() + COMPONENT_COLUMN_MARGIN; - } - x += columnWidths[iter.first] + 2 * COMPONENT_COLUMN_MARGIN; - y = start.y(); + columnWidths[iter.first] = maxWidth; + } + + // Position components + // Start a bit offset from the parent borders + const QPoint start{SUBCOMPONENT_INDENT, SUBCOMPONENT_INDENT}; + int x = start.x(); + int y = start.y(); + for (const auto &iter : asapSchedule) { + for (const auto &c : iter.second) { + auto *g = c->getGraphic(); + placements[g] = QPoint(x, y); + y += g->getCurrentComponentRect().height() + COMPONENT_COLUMN_MARGIN; } + x += columnWidths[iter.first] + 2 * COMPONENT_COLUMN_MARGIN; + y = start.y(); + } - return placements; + return placements; } -std::map topologicalSortPlacement(const std::vector& components) { - std::map placements; - std::deque sortedComponents = topologicalSort(components); - - // Position components - QPoint pos = QPoint(SUBCOMPONENT_INDENT, SUBCOMPONENT_INDENT); // Start a bit offset from the parent borders - for (const auto& c : sortedComponents) { - auto* g = c->getGraphic(); - placements[g] = pos; - pos.rx() += g->getCurrentComponentRect().width() + COMPONENT_COLUMN_MARGIN; - } - - return placements; +std::map +topologicalSortPlacement(const std::vector &components) { + std::map placements; + std::deque sortedComponents = topologicalSort(components); + + // Position components + QPoint pos = + QPoint(SUBCOMPONENT_INDENT, + SUBCOMPONENT_INDENT); // Start a bit offset from the parent borders + for (const auto &c : sortedComponents) { + auto *g = c->getGraphic(); + placements[g] = pos; + pos.rx() += g->getCurrentComponentRect().width() + COMPONENT_COLUMN_MARGIN; + } + + return placements; } -std::map PlaceRoute::placeAndRoute(const std::vector& components) const { - switch (m_placementAlgorithm) { - case PlaceAlg::TopologicalSort: { - return topologicalSortPlacement(components); - } - case PlaceAlg::ASAP: { - return ASAPPlacement(components); - } - } - Q_UNREACHABLE(); +std::map PlaceRoute::placeAndRoute( + const std::vector &components) const { + switch (m_placementAlgorithm) { + case PlaceAlg::TopologicalSort: { + return topologicalSortPlacement(components); + } + case PlaceAlg::ASAP: { + return ASAPPlacement(components); + } + } + Q_UNREACHABLE(); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_placeroute.h b/graphics/vsrtl_placeroute.h index 71c5ad9..710b625 100644 --- a/graphics/vsrtl_placeroute.h +++ b/graphics/vsrtl_placeroute.h @@ -14,29 +14,32 @@ enum class RouteAlg { Direct }; /** * @brief The PlaceRoute class * Singleton class for containing the various place & route algorithms. - * Contains state information regarding the current place & route algorithms, as well as the parameters for these. - * GridComponent's may acces the singleton and query it to perform place & route on the provided subcomponents + * Contains state information regarding the current place & route algorithms, as + * well as the parameters for these. GridComponent's may acces the singleton and + * query it to perform place & route on the provided subcomponents */ class PlaceRoute { public: - static const PlaceRoute* get() { - static PlaceRoute* instance = new PlaceRoute(); - return instance; - } + static const PlaceRoute *get() { + static PlaceRoute *instance = new PlaceRoute(); + return instance; + } - void setPlacementAlgorithm(PlaceAlg alg) { m_placementAlgorithm = alg; } - void setRoutingAlgorithm(RouteAlg alg) { m_routingAlgorithm = alg; } + void setPlacementAlgorithm(PlaceAlg alg) { m_placementAlgorithm = alg; } + void setRoutingAlgorithm(RouteAlg alg) { m_routingAlgorithm = alg; } - /** @todo: Return a data structure which may be interpreted by the calling GridComponent to place its - * subcomponents and draw the signal paths. For now, just return a structure suitable for placement*/ - std::map placeAndRoute(const std::vector& components) const; + /** @todo: Return a data structure which may be interpreted by the calling + * GridComponent to place its subcomponents and draw the signal paths. For + * now, just return a structure suitable for placement*/ + std::map + placeAndRoute(const std::vector &components) const; private: - PlaceRoute() {} + PlaceRoute() {} - PlaceAlg m_placementAlgorithm = PlaceAlg::ASAP; - RouteAlg m_routingAlgorithm = RouteAlg::Direct; + PlaceAlg m_placementAlgorithm = PlaceAlg::ASAP; + RouteAlg m_routingAlgorithm = RouteAlg::Direct; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_PLACEROUTE_H +#endif // VSRTL_PLACEROUTE_H diff --git a/graphics/vsrtl_portgraphic.cpp b/graphics/vsrtl_portgraphic.cpp index d813169..7ba36df 100644 --- a/graphics/vsrtl_portgraphic.cpp +++ b/graphics/vsrtl_portgraphic.cpp @@ -16,487 +16,545 @@ namespace vsrtl { constexpr int s_portGridWidth = 1; -PortGraphic::PortGraphic(SimPort* port, vsrtl::SimPort::PortType type, QGraphicsItem* parent) +PortGraphic::PortGraphic(SimPort *port, vsrtl::SimPort::PortType type, + QGraphicsItem *parent) : GraphicsBaseItem(parent), m_type(type), m_port(port) { - port->registerGraphic(this); - m_font = QFont("Monospace", 8); - m_pen.setWidth(WIRE_WIDTH); - m_pen.setCapStyle(Qt::RoundCap); - setAcceptHoverEvents(true); - ComponentGraphic* directParent = static_cast(parentItem()); - - m_radix = std::make_shared(Radix::Hex); - if (m_port->isEnumPort()) { - // By default, display Enum value if underlying port is enum - *m_radix = Radix::Enum; - } - - // Connect changes from simulator through our signal translation mechanism - wrapSimSignal(port->changed); - - m_colorAnimation = std::make_unique(this, "penColor"); - m_colorAnimation->setDuration(100); - m_colorAnimation->setStartValue(m_port->getWidth() == 1 ? WIRE_BOOLHIGH_COLOR : WIRE_HIGH_COLOR); - - m_colorAnimation->setEndValue(WIRE_DEFAULT_COLOR); - m_colorAnimation->setEasingCurve(QEasingCurve::Linear); - connect(m_colorAnimation.get(), &QPropertyAnimation::valueChanged, this, &PortGraphic::updatePenColor); - - setFlag(ItemIsSelectable); - - m_inputPortPoint = new PortPoint(this); - m_inputPortPoint->setPos(getInputPoint()); - m_outputPortPoint = new PortPoint(this); - m_outputPortPoint->setPos(getOutputPoint()); - - ComponentGraphic* scopeParent = nullptr; - - if (m_type == vsrtl::SimPort::PortType::in) { - scopeParent = directParent; - m_outputWire = - new WireGraphic(scopeParent, this, m_port->getOutputPorts(), WireGraphic::WireType::BorderOutput); - } else { - // Output wires exists in the domain in between different components in the PARENT of the parent of this port. - scopeParent = dynamic_cast(moduleParent()); - m_outputWire = - new WireGraphic(scopeParent, this, m_port->getOutputPorts(), WireGraphic::WireType::ComponentOutput); - } - m_outputWire->setZValue(VSRTLScene::Z_Wires); - - // Setup actions - m_showValueAction = std::make_shared("Show value"); - m_showValueAction->setCheckable(true); - m_showValueAction->setChecked(false); - connect(m_showValueAction.get(), &QAction::toggled, this, &PortGraphic::setValueLabelVisible); - - m_showWidthAction = std::make_shared("Show width"); - m_showWidthAction->setCheckable(true); - m_showWidthAction->setChecked(true); - connect(m_showWidthAction.get(), &QAction::toggled, this, &PortGraphic::setPortWidthVisible); - - m_showLabelAction = std::make_shared("Show label"); - m_showLabelAction->setCheckable(true); - m_showLabelAction->setChecked(true); - connect(m_showLabelAction.get(), &QAction::toggled, [this](bool checked) { - m_label->setVisible(checked); - m_label->setLocked(false); - }); - - m_showAction = std::make_shared("Show port"); - m_showAction->setCheckable(true); - connect(m_showAction.get(), &QAction::toggled, this, &PortGraphic::setUserVisible); - - // Setup labels - m_label = new Label(directParent, QString::fromStdString(m_port->getDisplayName()), m_showLabelAction, 8); - addVirtualChild({VirtualChildLink::Visibility, VirtualChildLink::Position}, m_label); - m_label->setVisible(false); - m_label->setZValue(VSRTLScene::Z_PortLabel); - - m_valueLabel = createModuleChild({VirtualChildLink::Visibility, VirtualChildLink::Position}, m_radix, - this, m_showValueAction); - m_valueLabel->setVisible(false); - directParent->addVirtualChild(VirtualChildLink::Position, m_valueLabel); // Also move when direct parent moves - m_valueLabel->setZValue(VSRTLScene::Z_ValueLabel); - - m_portWidthLabel = new Label(directParent, QString::number(port->getWidth() - 1) + ":0", m_showWidthAction, 7); - addVirtualChild({VirtualChildLink::Visibility, VirtualChildLink::Position}, m_portWidthLabel); - m_portWidthLabel->setMoveable(false); - m_portWidthLabel->setHoverable(false); - m_portWidthLabel->setFlags(m_portWidthLabel->flags() & - ~(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable)); - m_portWidthLabel->setZValue(VSRTLScene::Z_PortWidth); - - updateGeometry(); + port->registerGraphic(this); + m_font = QFont("Monospace", 8); + m_pen.setWidth(WIRE_WIDTH); + m_pen.setCapStyle(Qt::RoundCap); + setAcceptHoverEvents(true); + ComponentGraphic *directParent = + static_cast(parentItem()); + + m_radix = std::make_shared(Radix::Hex); + if (m_port->isEnumPort()) { + // By default, display Enum value if underlying port is enum + *m_radix = Radix::Enum; + } + + // Connect changes from simulator through our signal translation mechanism + wrapSimSignal(port->changed); + + m_colorAnimation = std::make_unique(this, "penColor"); + m_colorAnimation->setDuration(100); + m_colorAnimation->setStartValue(m_port->getWidth() == 1 ? WIRE_BOOLHIGH_COLOR + : WIRE_HIGH_COLOR); + + m_colorAnimation->setEndValue(WIRE_DEFAULT_COLOR); + m_colorAnimation->setEasingCurve(QEasingCurve::Linear); + connect(m_colorAnimation.get(), &QPropertyAnimation::valueChanged, this, + &PortGraphic::updatePenColor); + + setFlag(ItemIsSelectable); + + m_inputPortPoint = new PortPoint(this); + m_inputPortPoint->setPos(getInputPoint()); + m_outputPortPoint = new PortPoint(this); + m_outputPortPoint->setPos(getOutputPoint()); + + ComponentGraphic *scopeParent = nullptr; + + if (m_type == vsrtl::SimPort::PortType::in) { + scopeParent = directParent; + m_outputWire = new WireGraphic(scopeParent, this, m_port->getOutputPorts(), + WireGraphic::WireType::BorderOutput); + } else { + // Output wires exists in the domain in between different components in the + // PARENT of the parent of this port. + scopeParent = dynamic_cast(moduleParent()); + m_outputWire = new WireGraphic(scopeParent, this, m_port->getOutputPorts(), + WireGraphic::WireType::ComponentOutput); + } + m_outputWire->setZValue(VSRTLScene::Z_Wires); + + // Setup actions + m_showValueAction = std::make_shared("Show value"); + m_showValueAction->setCheckable(true); + m_showValueAction->setChecked(false); + connect(m_showValueAction.get(), &QAction::toggled, this, + &PortGraphic::setValueLabelVisible); + + m_showWidthAction = std::make_shared("Show width"); + m_showWidthAction->setCheckable(true); + m_showWidthAction->setChecked(true); + connect(m_showWidthAction.get(), &QAction::toggled, this, + &PortGraphic::setPortWidthVisible); + + m_showLabelAction = std::make_shared("Show label"); + m_showLabelAction->setCheckable(true); + m_showLabelAction->setChecked(true); + connect(m_showLabelAction.get(), &QAction::toggled, [this](bool checked) { + m_label->setVisible(checked); + m_label->setLocked(false); + }); + + m_showAction = std::make_shared("Show port"); + m_showAction->setCheckable(true); + connect(m_showAction.get(), &QAction::toggled, this, + &PortGraphic::setUserVisible); + + // Setup labels + m_label = + new Label(directParent, QString::fromStdString(m_port->getDisplayName()), + m_showLabelAction, 8); + addVirtualChild({VirtualChildLink::Visibility, VirtualChildLink::Position}, + m_label); + m_label->setVisible(false); + m_label->setZValue(VSRTLScene::Z_PortLabel); + + m_valueLabel = createModuleChild( + {VirtualChildLink::Visibility, VirtualChildLink::Position}, m_radix, this, + m_showValueAction); + m_valueLabel->setVisible(false); + directParent->addVirtualChild( + VirtualChildLink::Position, + m_valueLabel); // Also move when direct parent moves + m_valueLabel->setZValue(VSRTLScene::Z_ValueLabel); + + m_portWidthLabel = + new Label(directParent, QString::number(port->getWidth() - 1) + ":0", + m_showWidthAction, 7); + addVirtualChild({VirtualChildLink::Visibility, VirtualChildLink::Position}, + m_portWidthLabel); + m_portWidthLabel->setMoveable(false); + m_portWidthLabel->setHoverable(false); + m_portWidthLabel->setFlags( + m_portWidthLabel->flags() & + ~(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable)); + m_portWidthLabel->setZValue(VSRTLScene::Z_PortWidth); + + updateGeometry(); } void PortGraphic::simUpdateSlot() { - Q_ASSERT(m_valueLabel); - updatePen(); - update(); + Q_ASSERT(m_valueLabel); + updatePen(); + update(); - // Propagate any changes to current port value to this label, and all other connected ports which may have their - // labels visible - m_valueLabel->updateText(); + // Propagate any changes to current port value to this label, and all other + // connected ports which may have their labels visible + m_valueLabel->updateText(); } void PortGraphic::setValueLabelVisible(bool visible) { - Q_ASSERT(m_valueLabel); - if (!userHidden() || m_valueLabel->isVisible()) { - m_showValueAction->setChecked(visible); - m_valueLabel->setVisible(visible); - simUpdateSlot(); - } + Q_ASSERT(m_valueLabel); + if (!userHidden() || m_valueLabel->isVisible()) { + m_showValueAction->setChecked(visible); + m_valueLabel->setVisible(visible); + simUpdateSlot(); + } } void PortGraphic::setPortWidthVisible(bool visible) { - Q_ASSERT(m_portWidthLabel); - m_portWidthLabel->setVisible(visible); - update(); + Q_ASSERT(m_portWidthLabel); + m_portWidthLabel->setVisible(visible); + update(); } void PortGraphic::updateWireGeometry() { - m_outputWire->prepareGeometryChange(); + m_outputWire->prepareGeometryChange(); } void PortGraphic::redraw() { - // Schedules redrawing of the current port and its output wire - update(); - if (m_outputWire) { - m_outputWire->update(); - } + // Schedules redrawing of the current port and its output wire + update(); + if (m_outputWire) { + m_outputWire->update(); + } } void PortGraphic::setSide(Side side) { - m_side = side; - updateGeometry(); + m_side = side; + updateGeometry(); } void PortGraphic::propagateRedraw() { - m_port->traverseToSinks([=](SimPort* port) { - if (auto* portGraphic = port->getGraphic()) { - portGraphic->redraw(); - } - }); + m_port->traverseToSinks([=](SimPort *port) { + if (auto *portGraphic = port->getGraphic()) { + portGraphic->redraw(); + } + }); } /** * @brief PortGraphic::postSceneConstructionInitialize2 - * With all wires created, the port may now issue an updatePen() call. This must be executed after wires have - * initialized, given that updatePen() will trigger a redraw of wires + * With all wires created, the port may now issue an updatePen() call. This must + * be executed after wires have initialized, given that updatePen() will trigger + * a redraw of wires */ void PortGraphic::postSceneConstructionInitialize2() { - if (!m_inputWire) { - updatePen(); - } - - if (m_port->isConstant()) { - // For constant ports, we by default display the value of the port - m_valueLabel->show(); - *m_radix = m_port->getWidth() == 1 ? Radix::Unsigned : Radix::Signed; - simUpdateSlot(); // propagate radix change to value label - - const auto br = m_valueLabel->boundingRect(); - m_valueLabel->setPos( - mapToItem(m_valueLabel->parentItem(), {-br.width() - boundingRect().width(), -br.height() / 2})); - - // Initial port color is implicitely set by triggering the wire animation - m_colorAnimation->start(QPropertyAnimation::KeepWhenStopped); - } + if (!m_inputWire) { + updatePen(); + } + + if (m_port->isConstant()) { + // For constant ports, we by default display the value of the port + m_valueLabel->show(); + *m_radix = m_port->getWidth() == 1 ? Radix::Unsigned : Radix::Signed; + simUpdateSlot(); // propagate radix change to value label + + const auto br = m_valueLabel->boundingRect(); + m_valueLabel->setPos( + mapToItem(m_valueLabel->parentItem(), + {-br.width() - boundingRect().width(), -br.height() / 2})); + + // Initial port color is implicitely set by triggering the wire animation + m_colorAnimation->start(QPropertyAnimation::KeepWhenStopped); + } } -void PortGraphic::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - QMenu menu; +void PortGraphic::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + QMenu menu; - if (!userHidden()) { - menu.addAction(m_showValueAction.get()); - } + if (!userHidden()) { + menu.addAction(m_showValueAction.get()); + } - if (!isLocked()) { - menu.addAction(m_showWidthAction.get()); - menu.addAction(m_showLabelAction.get()); - m_showAction->setChecked(!userHidden()); - menu.addAction(m_showAction.get()); - } + if (!isLocked()) { + menu.addAction(m_showWidthAction.get()); + menu.addAction(m_showLabelAction.get()); + m_showAction->setChecked(!userHidden()); + menu.addAction(m_showAction.get()); + } - menu.exec(event->screenPos()); - m_valueLabel->updateText(); + menu.exec(event->screenPos()); + m_valueLabel->updateText(); } void PortGraphic::modulePositionHasChanged() { - // Notify in- and output points that they have been moved within the module - m_inputPortPoint->modulePositionHasChanged(); - m_outputPortPoint->modulePositionHasChanged(); + // Notify in- and output points that they have been moved within the module + m_inputPortPoint->modulePositionHasChanged(); + m_outputPortPoint->modulePositionHasChanged(); } -void PortGraphic::setInputWire(WireGraphic* wire) { - // Set wire is called during post scene construction initialization, wherein WireGraphic's will register with its - // destination ports (inputs) - Q_ASSERT(m_inputWire == nullptr); - m_inputWire = wire; +void PortGraphic::setInputWire(WireGraphic *wire) { + // Set wire is called during post scene construction initialization, wherein + // WireGraphic's will register with its destination ports (inputs) + Q_ASSERT(m_inputWire == nullptr); + m_inputWire = wire; } -QRectF PortGraphic::boundingRect() const { - return m_boundingRect; -} +QRectF PortGraphic::boundingRect() const { return m_boundingRect; } QPointF PortGraphic::getInputPoint() const { - switch (m_side) { - case Side::Right: { - return QPointF(m_type == vsrtl::SimPort::PortType::in ? s_portGridWidth * GRID_SIZE : 0, 0); - } - case Side::Left: { - return QPointF(m_type == vsrtl::SimPort::PortType::in ? -s_portGridWidth * GRID_SIZE : 0, 0); - } - case Side::Top: { - return QPointF(0, m_type == vsrtl::SimPort::PortType::in ? -s_portGridWidth * GRID_SIZE : 0); - } - case Side::Bottom: { - return QPointF(0, m_type == vsrtl::SimPort::PortType::in ? s_portGridWidth * GRID_SIZE : 0); - } - } - Q_UNREACHABLE(); + switch (m_side) { + case Side::Right: { + return QPointF(m_type == vsrtl::SimPort::PortType::in + ? s_portGridWidth * GRID_SIZE + : 0, + 0); + } + case Side::Left: { + return QPointF(m_type == vsrtl::SimPort::PortType::in + ? -s_portGridWidth * GRID_SIZE + : 0, + 0); + } + case Side::Top: { + return QPointF(0, m_type == vsrtl::SimPort::PortType::in + ? -s_portGridWidth * GRID_SIZE + : 0); + } + case Side::Bottom: { + return QPointF(0, m_type == vsrtl::SimPort::PortType::in + ? s_portGridWidth * GRID_SIZE + : 0); + } + } + Q_UNREACHABLE(); } QPointF PortGraphic::getOutputPoint() const { - switch (m_side) { - case Side::Right: { - return QPointF(m_type == vsrtl::SimPort::PortType::out ? s_portGridWidth * GRID_SIZE : 0, 0); - } - case Side::Left: { - return QPointF(m_type == vsrtl::SimPort::PortType::out ? -s_portGridWidth * GRID_SIZE : 0, 0); - } - case Side::Top: { - return QPointF(0, m_type == vsrtl::SimPort::PortType::out ? -s_portGridWidth * GRID_SIZE : 0); - } - case Side::Bottom: { - return QPointF(0, m_type == vsrtl::SimPort::PortType::out ? s_portGridWidth * GRID_SIZE : 0); - } - } - Q_UNREACHABLE(); + switch (m_side) { + case Side::Right: { + return QPointF(m_type == vsrtl::SimPort::PortType::out + ? s_portGridWidth * GRID_SIZE + : 0, + 0); + } + case Side::Left: { + return QPointF(m_type == vsrtl::SimPort::PortType::out + ? -s_portGridWidth * GRID_SIZE + : 0, + 0); + } + case Side::Top: { + return QPointF(0, m_type == vsrtl::SimPort::PortType::out + ? -s_portGridWidth * GRID_SIZE + : 0); + } + case Side::Bottom: { + return QPointF(0, m_type == vsrtl::SimPort::PortType::out + ? s_portGridWidth * GRID_SIZE + : 0); + } + } + Q_UNREACHABLE(); } void PortGraphic::updatePenColor() { - // This is a source port. Update pen based on current state - // Selection check is based on whether item is currently selected or about to be selected (via itemChange()) - if (m_signalSelected) { - m_pen.setColor(WIRE_SELECTED_COLOR); - m_pen.setWidth(static_cast(WIRE_WIDTH * 1.5)); + // This is a source port. Update pen based on current state + // Selection check is based on whether item is currently selected or about to + // be selected (via itemChange()) + if (m_signalSelected) { + m_pen.setColor(WIRE_SELECTED_COLOR); + m_pen.setWidth(static_cast(WIRE_WIDTH * 1.5)); + } else { + m_pen.setWidth(WIRE_WIDTH); + if (m_port->getWidth() == 1) { + if (static_cast(m_port->uValue())) { + m_pen.setColor(WIRE_BOOLHIGH_COLOR); + } else { + m_pen.setColor(WIRE_DEFAULT_COLOR); + } } else { - m_pen.setWidth(WIRE_WIDTH); - if (m_port->getWidth() == 1) { - if (static_cast(m_port->uValue())) { - m_pen.setColor(WIRE_BOOLHIGH_COLOR); - } else { - m_pen.setColor(WIRE_DEFAULT_COLOR); - } - } else { - m_pen.setColor(m_penColor); - } + m_pen.setColor(m_penColor); } - propagateRedraw(); + } + propagateRedraw(); } void PortGraphic::updatePen(bool aboutToBeSelected, bool aboutToBeDeselected) { - m_port->traverseToRoot([=](SimPort* node) { - if (node->isConstant()) { - return; - } - - // Traverse to root, and only execute when no input wire is present. This signifies that the root source - // port has been reached - auto* portGraphic = node->getGraphic(); - if (portGraphic && !portGraphic->m_inputWire) { - if (aboutToBeDeselected || aboutToBeSelected) { - portGraphic->m_signalSelected = aboutToBeSelected; - } - - /* If the port is anything other than a boolean port, a change in the signal is represented by starting - * the color change animation. If it is a boolean signal, just update the pen color */ - if (m_port->getWidth() != 1) { - portGraphic->m_colorAnimation->start(QPropertyAnimation::KeepWhenStopped); - } else { - portGraphic->updatePenColor(); - } - - // Make output port cascade an update call to all ports and wires which originate from this source - portGraphic->propagateRedraw(); - } - }); + m_port->traverseToRoot([=](SimPort *node) { + if (node->isConstant()) { + return; + } + + // Traverse to root, and only execute when no input wire is present. This + // signifies that the root source port has been reached + auto *portGraphic = node->getGraphic(); + if (portGraphic && !portGraphic->m_inputWire) { + if (aboutToBeDeselected || aboutToBeSelected) { + portGraphic->m_signalSelected = aboutToBeSelected; + } + + /* If the port is anything other than a boolean port, a change in the + * signal is represented by starting + * the color change animation. If it is a boolean signal, just update the + * pen color */ + if (m_port->getWidth() != 1) { + portGraphic->m_colorAnimation->start( + QPropertyAnimation::KeepWhenStopped); + } else { + portGraphic->updatePenColor(); + } + + // Make output port cascade an update call to all ports and wires which + // originate from this source + portGraphic->propagateRedraw(); + } + }); } QString PortGraphic::getTooltipString() const { - return QString::fromStdString(m_port->getDisplayName() + ":\n") + encodePortRadixValue(m_port, *m_radix); + return QString::fromStdString(m_port->getDisplayName() + ":\n") + + encodePortRadixValue(m_port, *m_radix); } -QVariant PortGraphic::itemChange(GraphicsItemChange change, const QVariant& value) { - if (change == QGraphicsItem::ItemSelectedChange) { - updatePen(value.toBool(), !value.toBool()); - } +QVariant PortGraphic::itemChange(GraphicsItemChange change, + const QVariant &value) { + if (change == QGraphicsItem::ItemSelectedChange) { + updatePen(value.toBool(), !value.toBool()); + } - if (change == QGraphicsItem::ItemVisibleChange) { - setPortVisible(value.toBool()); - } + if (change == QGraphicsItem::ItemVisibleChange) { + setPortVisible(value.toBool()); + } - if (change == QGraphicsItem::ItemPositionHasChanged) { - modulePositionHasChanged(); - } + if (change == QGraphicsItem::ItemPositionHasChanged) { + modulePositionHasChanged(); + } - return GraphicsBaseItem::itemChange(change, value); + return GraphicsBaseItem::itemChange(change, value); } void PortGraphic::setPortVisible(bool visible) { - m_inputPortPoint->setVisible(visible); - m_outputPortPoint->setVisible(visible); - if (m_inputWire && m_type == vsrtl::SimPort::PortType::in) { - // Inform wires terminating in this port to set their visibility based on this ports visibility - m_inputWire->setWiresVisibleToPort(m_inputPortPoint, visible && m_sourceVisible && !m_userHidden); - } - - else if (m_type == vsrtl::SimPort::PortType::out) { - // hide all input ports of other components which this port is the source of. - for (const auto& p_conn : m_port->getOutputPorts()) { - auto* portParent = p_conn->getParent(); - auto* portGraphic = p_conn->getGraphic(); - if (!portGraphic) { - continue; - } - - const bool isNestedComponent = portParent == m_port->getParent()->getParent(); - - if (!isNestedComponent && portParent && portGraphic) { - portGraphic->setSourceVisible(visible && !m_userHidden); - } - } + m_inputPortPoint->setVisible(visible); + m_outputPortPoint->setVisible(visible); + if (m_inputWire && m_type == vsrtl::SimPort::PortType::in) { + // Inform wires terminating in this port to set their visibility based on + // this ports visibility + m_inputWire->setWiresVisibleToPort( + m_inputPortPoint, visible && m_sourceVisible && !m_userHidden); + } + + else if (m_type == vsrtl::SimPort::PortType::out) { + // hide all input ports of other components which this port is the source + // of. + for (const auto &p_conn : m_port->getOutputPorts()) { + auto *portParent = p_conn->getParent(); + auto *portGraphic = p_conn->getGraphic(); + if (!portGraphic) { + continue; + } + + const bool isNestedComponent = + portParent == + m_port->getParent()->getParent(); + + if (!isNestedComponent && portParent && portGraphic) { + portGraphic->setSourceVisible(visible && !m_userHidden); + } } + } } void PortGraphic::setSourceVisible(bool visible) { - m_sourceVisible = visible; - setPortVisible(visible); - update(); + m_sourceVisible = visible; + setPortVisible(visible); + update(); } void PortGraphic::setUserVisible(bool visible) { - // User visibility only affects port draw state, >not< its scene visibility. - m_userHidden = !visible; - setPortVisible(visible); + // User visibility only affects port draw state, >not< its scene visibility. + m_userHidden = !visible; + setPortVisible(visible); - update(); + update(); } -void PortGraphic::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - // Move port position - if (isSelected() && !isLocked()) { - auto* parent = static_cast(parentItem()); - if (parent->handlePortGraphicMoveAttempt(this, event->pos())) { - update(); - } +void PortGraphic::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + // Move port position + if (isSelected() && !isLocked()) { + auto *parent = static_cast(parentItem()); + if (parent->handlePortGraphicMoveAttempt(this, event->pos())) { + update(); } + } } -void PortGraphic::hoverEnterEvent(QGraphicsSceneHoverEvent*) { - m_hoverActive = true; - update(); +void PortGraphic::hoverEnterEvent(QGraphicsSceneHoverEvent *) { + m_hoverActive = true; + update(); } -void PortGraphic::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { - m_hoverActive = false; - update(); +void PortGraphic::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { + m_hoverActive = false; + update(); } -void PortGraphic::hoverMoveEvent(QGraphicsSceneHoverEvent*) { - setToolTip(getTooltipString()); +void PortGraphic::hoverMoveEvent(QGraphicsSceneHoverEvent *) { + setToolTip(getTooltipString()); } void PortGraphic::updateGeometry() { - prepareGeometryChange(); + prepareGeometryChange(); + + m_boundingRect.setTopLeft({0, 0}); + + // Note: When changing m_portWidthLabel positions, this is done with respect + // to its parent (m_portWidthLabel is only a virtual child of this port). + QPointF pDiff; + switch (m_side) { + case Side::Left: + case Side::Right: { + m_boundingRect.setY(-GRID_SIZE / 2); + m_boundingRect.setHeight(GRID_SIZE); + m_boundingRect.setX(m_side == Side::Left ? -GRID_SIZE * s_portGridWidth + : 0); + m_boundingRect.setWidth(GRID_SIZE * s_portGridWidth); + + const qreal vDiff = + std::abs(GRID_SIZE / 2 - m_portWidthLabel->boundingRect().height() / 2); + pDiff = m_side == Side::Left + ? QPointF{-m_portWidthLabel->boundingRect().width(), -vDiff} + : QPointF{0, -vDiff}; + break; + } + case Side::Top: + case Side::Bottom: { + m_boundingRect.setX(-GRID_SIZE / 2); + m_boundingRect.setWidth(GRID_SIZE); + m_boundingRect.setY(m_side == Side::Top ? -GRID_SIZE * s_portGridWidth : 0); + m_boundingRect.setHeight(GRID_SIZE * s_portGridWidth); + + const qreal vDiff = + std::abs(GRID_SIZE / 2 - m_portWidthLabel->boundingRect().height() / 2); + pDiff = + (m_side == Side::Top + ? QPointF{0, -m_portWidthLabel->boundingRect().height() + vDiff} + : QPointF{0, -vDiff}); + break; + } + } + m_portWidthLabel->setPos(mapToItem(m_portWidthLabel->parentItem(), pDiff)); + + // The shape of the component is defined as the above created rectangle + m_shape = QPainterPath(); + m_shape.addPolygon(m_boundingRect); + + // Exapnd the rectangle to adjust for pen sizes etc. + m_boundingRect.adjust(-WIRE_WIDTH, -WIRE_WIDTH, WIRE_WIDTH, WIRE_WIDTH); +} + +QPainterPath PortGraphic::shape() const { return m_shape; } + +const QPen &PortGraphic::getPen() { + // Only source ports (ports with no input wires) can provide a pen. + // Sink ports request their pens from their endpoint source port. This call + // might go through multiple port in/out combinations and component boundaries + // before reaching the pen. + if (m_inputWire) { + return m_inputWire->getFromPort()->getPen(); + } else { + return m_pen; + } +} - m_boundingRect.setTopLeft({0, 0}); +void PortGraphic::paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) { + // Only draw the port if the source of the port is visible, or if the user is + // currently hovering over the port. + if (!((m_sourceVisible && !m_userHidden) || m_hoverActive)) + return; + + painter->save(); + painter->setPen(getPen()); + const QLineF portLine = QLineF(getInputPoint(), getOutputPoint()); + painter->drawLine(portLine); + + if (m_hoverActive) { + // Draw an arrow indicating the direction of the port + const auto d = + std::sqrt(std::pow(GRID_SIZE / 2, 2) + std::pow(GRID_SIZE / 2, 2)) / 2; + QPointF start = portLine.center(); + QPointF p1, p2; - // Note: When changing m_portWidthLabel positions, this is done with respect to its parent (m_portWidthLabel is only - // a virtual child of this port). - QPointF pDiff; switch (m_side) { - case Side::Left: - case Side::Right: { - m_boundingRect.setY(-GRID_SIZE / 2); - m_boundingRect.setHeight(GRID_SIZE); - m_boundingRect.setX(m_side == Side::Left ? -GRID_SIZE * s_portGridWidth : 0); - m_boundingRect.setWidth(GRID_SIZE * s_portGridWidth); - - const qreal vDiff = std::abs(GRID_SIZE / 2 - m_portWidthLabel->boundingRect().height() / 2); - pDiff = - m_side == Side::Left ? QPointF{-m_portWidthLabel->boundingRect().width(), -vDiff} : QPointF{0, -vDiff}; - break; - } - case Side::Top: - case Side::Bottom: { - m_boundingRect.setX(-GRID_SIZE / 2); - m_boundingRect.setWidth(GRID_SIZE); - m_boundingRect.setY(m_side == Side::Top ? -GRID_SIZE * s_portGridWidth : 0); - m_boundingRect.setHeight(GRID_SIZE * s_portGridWidth); - - const qreal vDiff = std::abs(GRID_SIZE / 2 - m_portWidthLabel->boundingRect().height() / 2); - pDiff = (m_side == Side::Top ? QPointF{0, -m_portWidthLabel->boundingRect().height() + vDiff} - : QPointF{0, -vDiff}); - break; - } - } - m_portWidthLabel->setPos(mapToItem(m_portWidthLabel->parentItem(), pDiff)); + case Side::Left: + case Side::Right: { + int dir = m_type == vsrtl::SimPort::PortType::out ? -1 : 1; + dir *= m_side == Side::Right ? -1 : 1; - // The shape of the component is defined as the above created rectangle - m_shape = QPainterPath(); - m_shape.addPolygon(m_boundingRect); + start.rx() = start.x() + dir * d / 2; - // Exapnd the rectangle to adjust for pen sizes etc. - m_boundingRect.adjust(-WIRE_WIDTH, -WIRE_WIDTH, WIRE_WIDTH, WIRE_WIDTH); -} + p1 = start - QPointF(dir * d, d); + p2 = start - QPointF(dir * d, -d); + break; + } -QPainterPath PortGraphic::shape() const { - return m_shape; -} + case Side::Bottom: + case Side::Top: { + int dir = m_type == vsrtl::SimPort::PortType::in ? -1 : 1; + dir *= m_side == Side::Top ? -1 : 1; -const QPen& PortGraphic::getPen() { - // Only source ports (ports with no input wires) can provide a pen. - // Sink ports request their pens from their endpoint source port. This call might go through multiple port - // in/out combinations and component boundaries before reaching the pen. - if (m_inputWire) { - return m_inputWire->getFromPort()->getPen(); - } else { - return m_pen; - } -} + start.ry() = start.y() + dir * d / 2; -void PortGraphic::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { - // Only draw the port if the source of the port is visible, or if the user is currently hovering over the port. - if (!((m_sourceVisible && !m_userHidden) || m_hoverActive)) - return; - - painter->save(); - painter->setPen(getPen()); - const QLineF portLine = QLineF(getInputPoint(), getOutputPoint()); - painter->drawLine(portLine); - - if (m_hoverActive) { - // Draw an arrow indicating the direction of the port - const auto d = std::sqrt(std::pow(GRID_SIZE / 2, 2) + std::pow(GRID_SIZE / 2, 2)) / 2; - QPointF start = portLine.center(); - QPointF p1, p2; - - switch (m_side) { - case Side::Left: - case Side::Right: { - int dir = m_type == vsrtl::SimPort::PortType::out ? -1 : 1; - dir *= m_side == Side::Right ? -1 : 1; - - start.rx() = start.x() + dir * d / 2; - - p1 = start - QPointF(dir * d, d); - p2 = start - QPointF(dir * d, -d); - break; - } - - case Side::Bottom: - case Side::Top: { - int dir = m_type == vsrtl::SimPort::PortType::in ? -1 : 1; - dir *= m_side == Side::Top ? -1 : 1; - - start.ry() = start.y() + dir * d / 2; - - p1 = start - QPointF(d, dir * d); - p2 = start - QPointF(-d, dir * d); - break; - } - } - painter->drawLine(start, p1); - painter->drawLine(start, p2); + p1 = start - QPointF(d, dir * d); + p2 = start - QPointF(-d, dir * d); + break; + } } + painter->drawLine(start, p1); + painter->drawLine(start, p2); + } - painter->restore(); + painter->restore(); #ifdef VSRTL_DEBUG_DRAW - DRAW_BOUNDING_RECT(painter) + DRAW_BOUNDING_RECT(painter) #endif } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_portgraphic.h b/graphics/vsrtl_portgraphic.h index 3863a2d..8437780 100644 --- a/graphics/vsrtl_portgraphic.h +++ b/graphics/vsrtl_portgraphic.h @@ -25,163 +25,173 @@ class WireGraphic; class PortPoint; class PortGraphic : public SimQObject, public GraphicsBaseItem { - Q_OBJECT - Q_PROPERTY(QColor penColor MEMBER m_penColor) - friend class ValueLabel; - friend class VSRTLScene; + Q_OBJECT + Q_PROPERTY(QColor penColor MEMBER m_penColor) + friend class ValueLabel; + friend class VSRTLScene; public: - PortGraphic(SimPort* port, vsrtl::SimPort::PortType type, QGraphicsItem* parent = nullptr); - - QRectF boundingRect() const override; - QPainterPath shape() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget*) override; - QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; - - void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override; - void hoverEnterEvent(QGraphicsSceneHoverEvent* event) override; - void hoverLeaveEvent(QGraphicsSceneHoverEvent* event) override; - - void postSceneConstructionInitialize2() override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - - /** - * @brief setSourceVisible - * Whenever a component is hidden, all output ports of said component will set the connecting ports as having their - * source ports non-visible. - */ - void setSourceVisible(bool visible); - - /** - * @brief setPortVisible - * Routine called whenever this port has triggered its visibility, either through scene- or user visibility. - */ - void setPortVisible(bool visible); - - void updateGeometry(); - SimPort* getPort() const { return m_port; } - void setInputWire(WireGraphic* wire); - WireGraphic* getOutputWire() { return m_outputWire; } - void updateInputWire(); - void updateWireGeometry(); - PortPoint* getPortPoint(vsrtl::SimPort::PortType t) { - return t == vsrtl::SimPort::PortType::in ? m_inputPortPoint : m_outputPortPoint; - } - QString getTooltipString() const; - - bool userHidden() const { return m_userHidden; } - void setUserVisible(bool visible); - - QPointF getInputPoint() const; - QPointF getOutputPoint() const; - - vsrtl::SimPort::PortType getPortType() const { return m_type; } - void setValueLabelVisible(bool visible); - void setPortWidthVisible(bool visible); - - const QPen& getPen(); - - void setSide(Side side); - Side getSide() const { return m_side; } - - void modulePositionHasChanged() override; + PortGraphic(SimPort *port, vsrtl::SimPort::PortType type, + QGraphicsItem *parent = nullptr); + + QRectF boundingRect() const override; + QPainterPath shape() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *) override; + QVariant itemChange(GraphicsItemChange change, + const QVariant &value) override; + + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; + + void postSceneConstructionInitialize2() override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + + /** + * @brief setSourceVisible + * Whenever a component is hidden, all output ports of said component will set + * the connecting ports as having their source ports non-visible. + */ + void setSourceVisible(bool visible); + + /** + * @brief setPortVisible + * Routine called whenever this port has triggered its visibility, either + * through scene- or user visibility. + */ + void setPortVisible(bool visible); + + void updateGeometry(); + SimPort *getPort() const { return m_port; } + void setInputWire(WireGraphic *wire); + WireGraphic *getOutputWire() { return m_outputWire; } + void updateInputWire(); + void updateWireGeometry(); + PortPoint *getPortPoint(vsrtl::SimPort::PortType t) { + return t == vsrtl::SimPort::PortType::in ? m_inputPortPoint + : m_outputPortPoint; + } + QString getTooltipString() const; + + bool userHidden() const { return m_userHidden; } + void setUserVisible(bool visible); + + QPointF getInputPoint() const; + QPointF getOutputPoint() const; + + vsrtl::SimPort::PortType getPortType() const { return m_type; } + void setValueLabelVisible(bool visible); + void setPortWidthVisible(bool visible); + + const QPen &getPen(); + + void setSide(Side side); + Side getSide() const { return m_side; } + + void modulePositionHasChanged() override; protected: - void simUpdateSlot() override; + void simUpdateSlot() override; private slots: - void updatePenColor(); + void updatePenColor(); private: - void redraw(); - void propagateRedraw(); - void updatePen(bool aboutToBeSelected = false, bool aboutToBeDeselected = false); - - /** - * @brief m_userHidden - * True if the user has asked to hide this component. Maintains logical hide-state even - * if the parent component is collaposed, rendering this component as non-visible in the scene. - */ - bool m_userHidden = false; - - // m_signalSelected: does not indicate visual selection (ie. isSelected()), but rather whether any port in the - // port/wire connection of this port has been selected. - bool m_signalSelected = false; - bool m_hoverActive = false; - - /** - * @brief m_sourceVisible (for input ports) - * true if the outport which this inputport connects to, is visible. If not, the port shall not be drawn. However, - * the port is still scene-visible to allow for user interaction. - */ - bool m_sourceVisible = true; - ValueDisplayFormat m_valueBase = ValueDisplayFormat::baseTen; - - QRectF m_boundingRect; - QPainterPath m_shape; - QRectF m_textRect; - - vsrtl::SimPort::PortType m_type; - SimPort* m_port = nullptr; - - // Used for allowing WireSegments to join up with a port - PortPoint* m_inputPortPoint = nullptr; - PortPoint* m_outputPortPoint = nullptr; - - WireGraphic* m_outputWire = nullptr; - WireGraphic* m_inputWire = nullptr; - - ValueLabel* m_valueLabel = nullptr; - - /** - * @brief The radix is shared between the port and the value label. - */ - std::shared_ptr m_radix; - - std::unique_ptr m_colorAnimation; - - Side m_side = Side::Right; - Label* m_label = nullptr; - Label* m_portWidthLabel = nullptr; - QString m_widthText; - QFont m_font; - QPen m_pen; - QColor m_penColor; - QPen m_oldPen; // Pen which was previously used for paint(). If a change between m_oldPen and m_pen is seen, this - // triggers redrawing of the connected wires - - std::shared_ptr m_showValueAction = nullptr; - std::shared_ptr m_showWidthAction = nullptr; - std::shared_ptr m_showLabelAction = nullptr; - std::shared_ptr m_showAction = nullptr; + void redraw(); + void propagateRedraw(); + void updatePen(bool aboutToBeSelected = false, + bool aboutToBeDeselected = false); + + /** + * @brief m_userHidden + * True if the user has asked to hide this component. Maintains logical + * hide-state even if the parent component is collaposed, rendering this + * component as non-visible in the scene. + */ + bool m_userHidden = false; + + // m_signalSelected: does not indicate visual selection (ie. isSelected()), + // but rather whether any port in the port/wire connection of this port has + // been selected. + bool m_signalSelected = false; + bool m_hoverActive = false; + + /** + * @brief m_sourceVisible (for input ports) + * true if the outport which this inputport connects to, is visible. If not, + * the port shall not be drawn. However, the port is still scene-visible to + * allow for user interaction. + */ + bool m_sourceVisible = true; + ValueDisplayFormat m_valueBase = ValueDisplayFormat::baseTen; + + QRectF m_boundingRect; + QPainterPath m_shape; + QRectF m_textRect; + + vsrtl::SimPort::PortType m_type; + SimPort *m_port = nullptr; + + // Used for allowing WireSegments to join up with a port + PortPoint *m_inputPortPoint = nullptr; + PortPoint *m_outputPortPoint = nullptr; + + WireGraphic *m_outputWire = nullptr; + WireGraphic *m_inputWire = nullptr; + + ValueLabel *m_valueLabel = nullptr; + + /** + * @brief The radix is shared between the port and the value label. + */ + std::shared_ptr m_radix; + + std::unique_ptr m_colorAnimation; + + Side m_side = Side::Right; + Label *m_label = nullptr; + Label *m_portWidthLabel = nullptr; + QString m_widthText; + QFont m_font; + QPen m_pen; + QColor m_penColor; + QPen m_oldPen; // Pen which was previously used for paint(). If a change + // between m_oldPen and m_pen is seen, this triggers redrawing + // of the connected wires + + std::shared_ptr m_showValueAction = nullptr; + std::shared_ptr m_showWidthAction = nullptr; + std::shared_ptr m_showLabelAction = nullptr; + std::shared_ptr m_showAction = nullptr; public: - template - void serialize(Archive& archive) { - // Serialize labels - try { - archive(cereal::make_nvp("Label", *m_label)); - archive(cereal::make_nvp("ValueLabel", *m_valueLabel)); - archive(cereal::make_nvp("UserHidden", m_userHidden)); - setUserVisible(!userHidden()); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - try { - bool visible = m_showWidthAction->isChecked(); - archive(cereal::make_nvp("PortWidthVisible", visible)); - m_showWidthAction->setChecked(visible); - } catch (const cereal::Exception& e) { - /// @todo: build an error report - } - - update(); + template + void serialize(Archive &archive) { + // Serialize labels + try { + archive(cereal::make_nvp("Label", *m_label)); + archive(cereal::make_nvp("ValueLabel", *m_valueLabel)); + archive(cereal::make_nvp("UserHidden", m_userHidden)); + setUserVisible(!userHidden()); + } catch (const cereal::Exception &e) { + /// @todo: build an error report } + + try { + bool visible = m_showWidthAction->isChecked(); + archive(cereal::make_nvp("PortWidthVisible", visible)); + m_showWidthAction->setChecked(visible); + } catch (const cereal::Exception &e) { + /// @todo: build an error report + } + + update(); + } }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_PORTGRAPHIC_H +#endif // VSRTL_PORTGRAPHIC_H diff --git a/graphics/vsrtl_qt_serializers.h b/graphics/vsrtl_qt_serializers.h index ae069ab..e56614f 100644 --- a/graphics/vsrtl_qt_serializers.h +++ b/graphics/vsrtl_qt_serializers.h @@ -8,38 +8,38 @@ // QPoint serializer template -void serialize(Archive& archive, QPoint& m) { - archive(cereal::make_nvp("x", m.rx())); - archive(cereal::make_nvp("y", m.ry())); +void serialize(Archive &archive, QPoint &m) { + archive(cereal::make_nvp("x", m.rx())); + archive(cereal::make_nvp("y", m.ry())); } template -void serialize(Archive& archive, QPointF& m) { - archive(cereal::make_nvp("x", m.rx())); - archive(cereal::make_nvp("y", m.ry())); +void serialize(Archive &archive, QPointF &m) { + archive(cereal::make_nvp("x", m.rx())); + archive(cereal::make_nvp("y", m.ry())); } // QRect serializer template -void serialize(Archive& archive, QRect& m) { - int x = m.x(); - int y = m.y(); - int w = m.width(); - int h = m.height(); - archive(cereal::make_nvp("x", x)); - archive(cereal::make_nvp("y", y)); - archive(cereal::make_nvp("w", w)); - archive(cereal::make_nvp("h", h)); - m.setX(x); - m.setY(y); - m.setHeight(h); - m.setWidth(w); +void serialize(Archive &archive, QRect &m) { + int x = m.x(); + int y = m.y(); + int w = m.width(); + int h = m.height(); + archive(cereal::make_nvp("x", x)); + archive(cereal::make_nvp("y", y)); + archive(cereal::make_nvp("w", w)); + archive(cereal::make_nvp("h", h)); + m.setX(x); + m.setY(y); + m.setHeight(h); + m.setWidth(w); } // QString serializer template -void serialize(Archive& archive, QString& m) { - std::string str = m.toStdString(); - archive(cereal::make_nvp("str", str)); - m = QString::fromStdString(str); +void serialize(Archive &archive, QString &m) { + std::string str = m.toStdString(); + archive(cereal::make_nvp("str", str)); + m = QString::fromStdString(str); } diff --git a/graphics/vsrtl_radix.cpp b/graphics/vsrtl_radix.cpp index e6b9ff6..4e1aa4f 100644 --- a/graphics/vsrtl_radix.cpp +++ b/graphics/vsrtl_radix.cpp @@ -13,150 +13,153 @@ namespace vsrtl { -VSRTL_VT_U decodePortRadixValue(const SimPort& port, const Radix type, const QString& valueString) { - bool ok = false; - VSRTL_VT_U value = 0; - switch (type) { - case Radix::Hex: { - value = valueString.toULong(&ok, 16); - break; - } - case Radix::Binary: { - QString trimmed = valueString; - trimmed.remove(0, 2); // Remove "0b" to allow decoding - value = trimmed.toULong(&ok, 2); - break; - } - case Radix::Unsigned: { - value = valueString.toULong(&ok, 10); - break; - } - case Radix::Signed: { - // Zero extend the value, truncated at $port.getWidth() - long signedValue = valueString.toLong(&ok, 10); - // set zero as sign bit at $port.getWidth() - signedValue &= ~(0x1 << port.getWidth()); - // Sign extend from $port.getWidth() - value = signextend(signedValue, port.getWidth()); - break; - } - case Radix::Enum: { - if (!port.isEnumPort()) { - throw std::runtime_error("Port is not an Enum port"); - } - value = port.enumStringToValue(valueString.toStdString().c_str()); - break; - } +VSRTL_VT_U decodePortRadixValue(const SimPort &port, const Radix type, + const QString &valueString) { + bool ok = false; + VSRTL_VT_U value = 0; + switch (type) { + case Radix::Hex: { + value = valueString.toULong(&ok, 16); + break; + } + case Radix::Binary: { + QString trimmed = valueString; + trimmed.remove(0, 2); // Remove "0b" to allow decoding + value = trimmed.toULong(&ok, 2); + break; + } + case Radix::Unsigned: { + value = valueString.toULong(&ok, 10); + break; + } + case Radix::Signed: { + // Zero extend the value, truncated at $port.getWidth() + long signedValue = valueString.toLong(&ok, 10); + // set zero as sign bit at $port.getWidth() + signedValue &= ~(0x1 << port.getWidth()); + // Sign extend from $port.getWidth() + value = signextend(signedValue, port.getWidth()); + break; + } + case Radix::Enum: { + if (!port.isEnumPort()) { + throw std::runtime_error("Port is not an Enum port"); } - Q_ASSERT(ok); - return value; + value = port.enumStringToValue(valueString.toStdString().c_str()); + break; + } + } + Q_ASSERT(ok); + return value; } -QString encodePortRadixValue(const SimPort* port, const Radix type) { - VSRTL_VT_U value = port->uValue(); - switch (type) { - case Radix::Hex: { - const unsigned maxChars = (port->getWidth() / 4) + (port->getWidth() % 4 != 0 ? 1 : 0); - return "0x" + QString::number(value, 16).rightJustified(maxChars, '0'); - } - case Radix::Binary: { - return "0b" + QString::number(value, 2).rightJustified(port->getWidth(), '0'); - } - case Radix::Unsigned: { - return QString::number(value, 10); - } - case Radix::Signed: { - return QString::number(signextend(value, port->getWidth()), 10); - } - case Radix::Enum: { - if (!port->isEnumPort()) { - throw std::runtime_error("Port is not an Enum port"); - } - - return QString::fromStdString(port->valueToEnumString()); - } +QString encodePortRadixValue(const SimPort *port, const Radix type) { + VSRTL_VT_U value = port->uValue(); + switch (type) { + case Radix::Hex: { + const unsigned maxChars = + (port->getWidth() / 4) + (port->getWidth() % 4 != 0 ? 1 : 0); + return "0x" + QString::number(value, 16).rightJustified(maxChars, '0'); + } + case Radix::Binary: { + return "0b" + + QString::number(value, 2).rightJustified(port->getWidth(), '0'); + } + case Radix::Unsigned: { + return QString::number(value, 10); + } + case Radix::Signed: { + return QString::number(signextend(value, port->getWidth()), 10); + } + case Radix::Enum: { + if (!port->isEnumPort()) { + throw std::runtime_error("Port is not an Enum port"); } - Q_UNREACHABLE(); -} - -QMenu* createPortRadixMenu(const SimPort* port, Radix& type) { - QMenu* menu = new QMenu("Radix"); - QActionGroup* RadixActionGroup = new QActionGroup(menu); - - QAction* enumTypeAction = nullptr; - if (port->isEnumPort()) { - enumTypeAction = RadixActionGroup->addAction("Enum"); - enumTypeAction->setCheckable(true); - QObject::connect(enumTypeAction, &QAction::triggered, [&](bool checked) { - if (checked) - type = Radix::Enum; - }); - menu->addAction(enumTypeAction); - } - - QAction* hexTypeAction = RadixActionGroup->addAction("Hex"); - hexTypeAction->setCheckable(true); - QObject::connect(hexTypeAction, &QAction::triggered, [&](bool checked) { - if (checked) - type = Radix::Hex; - }); - menu->addAction(hexTypeAction); - QAction* binTypeAction = RadixActionGroup->addAction("Binary"); - binTypeAction->setCheckable(true); - QObject::connect(binTypeAction, &QAction::triggered, [&](bool checked) { - if (checked) - type = Radix::Binary; - }); - menu->addAction(binTypeAction); - - QAction* unsignedTypeAction = RadixActionGroup->addAction("Unsigned"); - unsignedTypeAction->setCheckable(true); - QObject::connect(unsignedTypeAction, &QAction::triggered, [&](bool checked) { - if (checked) - type = Radix::Unsigned; - }); - menu->addAction(unsignedTypeAction); + return QString::fromStdString(port->valueToEnumString()); + } + } + Q_UNREACHABLE(); +} - QAction* signedTypeAction = RadixActionGroup->addAction("Signed"); - signedTypeAction->setCheckable(true); - QObject::connect(signedTypeAction, &QAction::triggered, [&](bool checked) { - if (checked) - type = Radix::Signed; +QMenu *createPortRadixMenu(const SimPort *port, Radix &type) { + QMenu *menu = new QMenu("Radix"); + QActionGroup *RadixActionGroup = new QActionGroup(menu); + + QAction *enumTypeAction = nullptr; + if (port->isEnumPort()) { + enumTypeAction = RadixActionGroup->addAction("Enum"); + enumTypeAction->setCheckable(true); + QObject::connect(enumTypeAction, &QAction::triggered, [&](bool checked) { + if (checked) + type = Radix::Enum; }); - menu->addAction(signedTypeAction); - - RadixActionGroup->setExclusive(true); - - // Set the currently selected display type as checked - switch (type) { - case Radix::Hex: { - hexTypeAction->setChecked(true); - break; - } - case Radix::Binary: { - binTypeAction->setChecked(true); - break; - } - case Radix::Signed: { - signedTypeAction->setChecked(true); - break; - } - case Radix::Unsigned: { - unsignedTypeAction->setChecked(true); - break; - } - case Radix::Enum: { - if (!port->isEnumPort()) { - throw std::runtime_error("Port is not an Enum port"); - } - Q_ASSERT(enumTypeAction); - enumTypeAction->setChecked(true); - break; - } + menu->addAction(enumTypeAction); + } + + QAction *hexTypeAction = RadixActionGroup->addAction("Hex"); + hexTypeAction->setCheckable(true); + QObject::connect(hexTypeAction, &QAction::triggered, [&](bool checked) { + if (checked) + type = Radix::Hex; + }); + menu->addAction(hexTypeAction); + + QAction *binTypeAction = RadixActionGroup->addAction("Binary"); + binTypeAction->setCheckable(true); + QObject::connect(binTypeAction, &QAction::triggered, [&](bool checked) { + if (checked) + type = Radix::Binary; + }); + menu->addAction(binTypeAction); + + QAction *unsignedTypeAction = RadixActionGroup->addAction("Unsigned"); + unsignedTypeAction->setCheckable(true); + QObject::connect(unsignedTypeAction, &QAction::triggered, [&](bool checked) { + if (checked) + type = Radix::Unsigned; + }); + menu->addAction(unsignedTypeAction); + + QAction *signedTypeAction = RadixActionGroup->addAction("Signed"); + signedTypeAction->setCheckable(true); + QObject::connect(signedTypeAction, &QAction::triggered, [&](bool checked) { + if (checked) + type = Radix::Signed; + }); + menu->addAction(signedTypeAction); + + RadixActionGroup->setExclusive(true); + + // Set the currently selected display type as checked + switch (type) { + case Radix::Hex: { + hexTypeAction->setChecked(true); + break; + } + case Radix::Binary: { + binTypeAction->setChecked(true); + break; + } + case Radix::Signed: { + signedTypeAction->setChecked(true); + break; + } + case Radix::Unsigned: { + unsignedTypeAction->setChecked(true); + break; + } + case Radix::Enum: { + if (!port->isEnumPort()) { + throw std::runtime_error("Port is not an Enum port"); } + Q_ASSERT(enumTypeAction); + enumTypeAction->setChecked(true); + break; + } + } - return menu; + return menu; } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_radix.h b/graphics/vsrtl_radix.h index cd93c1e..2695a17 100644 --- a/graphics/vsrtl_radix.h +++ b/graphics/vsrtl_radix.h @@ -1,8 +1,8 @@ #ifndef VSRTL_Radix_H #define VSRTL_Radix_H -#include #include "../interface/vsrtl_defines.h" +#include QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QMenu) @@ -16,10 +16,11 @@ static const auto binRegex = QRegularExpression("0[bB][0-1]+"); static const auto unsignedRegex = QRegularExpression("[0-9]+"); static const auto signedRegex = QRegularExpression("[-]*[0-9]+"); -VSRTL_VT_U decodePortRadixValue(const SimPort& port, const Radix type, const QString& valueString); -QString encodePortRadixValue(const SimPort* port, const Radix type); -QMenu* createPortRadixMenu(const SimPort* port, Radix& type); +VSRTL_VT_U decodePortRadixValue(const SimPort &port, const Radix type, + const QString &valueString); +QString encodePortRadixValue(const SimPort *port, const Radix type); +QMenu *createPortRadixMenu(const SimPort *port, Radix &type); -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_Radix_H +#endif // VSRTL_Radix_H diff --git a/graphics/vsrtl_registermodel.cpp b/graphics/vsrtl_registermodel.cpp index acbc115..f3bda3b 100644 --- a/graphics/vsrtl_registermodel.cpp +++ b/graphics/vsrtl_registermodel.cpp @@ -9,148 +9,158 @@ namespace vsrtl { -QList RegisterTreeItem::getActions() const { - // Only return actions for items which have a port (default actions from TreeItem displays display type actions, - // which are not applicable for Component items) - return m_register != nullptr ? NetlistTreeItem::getActions() : QList(); +QList RegisterTreeItem::getActions() const { + // Only return actions for items which have a port (default actions from + // TreeItem displays display type actions, which are not applicable for + // Component items) + return m_register != nullptr ? NetlistTreeItem::getActions() + : QList(); } -void RegisterTreeItem::setRegister(SimComponent* reg) { - m_register = reg; - setPort(m_register->getPorts()[0]); - m_name = QString::fromStdString(reg->getName()); +void RegisterTreeItem::setRegister(SimComponent *reg) { + m_register = reg; + setPort(m_register->getPorts()[0]); + m_name = QString::fromStdString(reg->getName()); } QVariant RegisterTreeItem::data(int column, int role) const { - if (column == 1 && m_register != nullptr) { - switch (role) { - case Qt::FontRole: { - return QFont("monospace"); - } - case Qt::ForegroundRole: { - return QBrush(Qt::blue); - } - case Qt::DisplayRole: { - return encodePortRadixValue(m_port, m_radix); - } - } + if (column == 1 && m_register != nullptr) { + switch (role) { + case Qt::FontRole: { + return QFont("monospace"); } + case Qt::ForegroundRole: { + return QBrush(Qt::blue); + } + case Qt::DisplayRole: { + return encodePortRadixValue(m_port, m_radix); + } + } + } - if (role == Qt::DisplayRole || role == Qt::EditRole) { - switch (column) { - case RegisterModel::ComponentColumn: { - return m_name; - } - case RegisterModel::WidthColumn: { - if (m_register) { - return m_port->getWidth(); - } - break; - } - } + if (role == Qt::DisplayRole || role == Qt::EditRole) { + switch (column) { + case RegisterModel::ComponentColumn: { + return m_name; } + case RegisterModel::WidthColumn: { + if (m_register) { + return m_port->getWidth(); + } + break; + } + } + } - return QVariant(); + return QVariant(); } -bool RegisterTreeItem::setData(int, const QVariant& value, int) { - if (index.column() == RegisterModel::ValueColumn) { - if (m_register) { - m_design->setSynchronousValue(m_register->getSynchronous(), 0, value.value()); - return true; - } +bool RegisterTreeItem::setData(int, const QVariant &value, int) { + if (index.column() == RegisterModel::ValueColumn) { + if (m_register) { + m_design->setSynchronousValue(m_register->getSynchronous(), 0, + value.value()); + return true; } - return false; + } + return false; } void RegisterModel::invalidate() { - // Data changed within Design, invalidate value column - emit dataChanged(index(0, ValueColumn), index(rowCount(), ValueColumn), {Qt::DisplayRole}); + // Data changed within Design, invalidate value column + emit dataChanged(index(0, ValueColumn), index(rowCount(), ValueColumn), + {Qt::DisplayRole}); } -RegisterModel::RegisterModel(SimDesign* arch, QObject* parent) +RegisterModel::RegisterModel(SimDesign *arch, QObject *parent) : NetlistModelBase({"Component", "Value", "Width"}, arch, parent) { - rootItem = new RegisterTreeItem(nullptr, arch); - loadDesign(rootItem, m_arch); + rootItem = new RegisterTreeItem(nullptr, arch); + loadDesign(rootItem, m_arch); } -QVariant RegisterModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) - return QVariant(); +QVariant RegisterModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); - auto* item = getTreeItem(index); - return item->data(index.column(), role); + auto *item = getTreeItem(index); + return item->data(index.column(), role); } -Qt::ItemFlags RegisterModel::flags(const QModelIndex& index) const { - if (!index.isValid()) - return Qt::NoItemFlags; - Qt::ItemFlags flags = QAbstractItemModel::flags(index); +Qt::ItemFlags RegisterModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::NoItemFlags; + Qt::ItemFlags flags = QAbstractItemModel::flags(index); - // Register values are editable - if (index.column() == 1 && getTreeItem(index)->m_register != nullptr) { - flags |= Qt::ItemIsEditable; - } + // Register values are editable + if (index.column() == 1 && getTreeItem(index)->m_register != nullptr) { + flags |= Qt::ItemIsEditable; + } - return flags; + return flags; } -bool RegisterModel::setData(const QModelIndex& index, const QVariant& var, int role) { - auto* item = getTreeItem(index); - if (item) { - VSRTL_VT_U value = decodePortRadixValue(*item->m_port, item->m_radix, var.toString()); - return item->setData(index.column(), QVariant::fromValue(value), role); - } +bool RegisterModel::setData(const QModelIndex &index, const QVariant &var, + int role) { + auto *item = getTreeItem(index); + if (item) { + VSRTL_VT_U value = + decodePortRadixValue(*item->m_port, item->m_radix, var.toString()); + return item->setData(index.column(), QVariant::fromValue(value), role); + } - return false; + return false; } -void RegisterModel::loadDesign(RegisterTreeItem* parent, SimDesign* design) { - m_design = design; - const auto& registers = design->getRegisters(); - - std::map parentMap; - - const auto* rootComponent = dynamic_cast(design); - parentMap[rootComponent] = parent; - - // Build a tree representing the hierarchy of components and subcomponents containing registers - for (const auto& reg : registers) { - const auto* regParent = reg->getParent(); - RegisterTreeItem* regParentNetlistItem = nullptr; - - if (parentMap.count(regParent) == 0) { - // Create new parents in the tree until either the root component is detected, or a parent of a parent - // is already in the tree - std::vector newParentsInTree; - while (regParent != rootComponent && parentMap.count(regParent) == 0) { - newParentsInTree.insert(newParentsInTree.begin(), regParent); - regParent = regParent->getParent(); - } - // At this point, the first value in newParentsInTree has its parent present in the tree. Extend the - // tree from this index - regParentNetlistItem = parentMap[regParent]; - for (const auto& p : newParentsInTree) { - auto* newParent = new RegisterTreeItem(regParentNetlistItem, m_design); - regParentNetlistItem->insertChild(regParentNetlistItem->childCount(), newParent); - regParentNetlistItem = newParent; - regParentNetlistItem->m_name = QString::fromStdString(p->getName()); - Q_ASSERT(parentMap.count(p) == 0); - parentMap[p] = regParentNetlistItem; - } - // After the newParentsInTree stack has been iterated through, 'regParentNetlistItem' will point to the - // parent tree item of the current 'reg' in the outer foor loop - } else { - regParentNetlistItem = parentMap[regParent]; - } - - // Add register to its parent tree item - - auto* child = new RegisterTreeItem(regParentNetlistItem, m_design); - child->setRegister(reg); - regParentNetlistItem->insertChild(regParentNetlistItem->childCount(), child); - - // Set component data (component name and signal value) +void RegisterModel::loadDesign(RegisterTreeItem *parent, SimDesign *design) { + m_design = design; + const auto ®isters = design->getRegisters(); + + std::map parentMap; + + const auto *rootComponent = dynamic_cast(design); + parentMap[rootComponent] = parent; + + // Build a tree representing the hierarchy of components and subcomponents + // containing registers + for (const auto ® : registers) { + const auto *regParent = reg->getParent(); + RegisterTreeItem *regParentNetlistItem = nullptr; + + if (parentMap.count(regParent) == 0) { + // Create new parents in the tree until either the root component is + // detected, or a parent of a parent is already in the tree + std::vector newParentsInTree; + while (regParent != rootComponent && parentMap.count(regParent) == 0) { + newParentsInTree.insert(newParentsInTree.begin(), regParent); + regParent = regParent->getParent(); + } + // At this point, the first value in newParentsInTree has its parent + // present in the tree. Extend the tree from this index + regParentNetlistItem = parentMap[regParent]; + for (const auto &p : newParentsInTree) { + auto *newParent = new RegisterTreeItem(regParentNetlistItem, m_design); + regParentNetlistItem->insertChild(regParentNetlistItem->childCount(), + newParent); + regParentNetlistItem = newParent; + regParentNetlistItem->m_name = QString::fromStdString(p->getName()); + Q_ASSERT(parentMap.count(p) == 0); + parentMap[p] = regParentNetlistItem; + } + // After the newParentsInTree stack has been iterated through, + // 'regParentNetlistItem' will point to the parent tree item of the + // current 'reg' in the outer foor loop + } else { + regParentNetlistItem = parentMap[regParent]; } + + // Add register to its parent tree item + + auto *child = new RegisterTreeItem(regParentNetlistItem, m_design); + child->setRegister(reg); + regParentNetlistItem->insertChild(regParentNetlistItem->childCount(), + child); + + // Set component data (component name and signal value) + } } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_registermodel.h b/graphics/vsrtl_registermodel.h index a11dfcc..83e2ca0 100644 --- a/graphics/vsrtl_registermodel.h +++ b/graphics/vsrtl_registermodel.h @@ -13,38 +13,41 @@ namespace vsrtl { class RegisterTreeItem : public NetlistTreeItem { public: - RegisterTreeItem(TreeItem* parent, SimDesign* design) : NetlistTreeItem(parent), m_design(design) {} - - enum class PortDirection { Input, Output }; - QVariant data(int column, int role = Qt::EditRole) const override; - bool setData(int column, const QVariant& value, int role = Qt::EditRole) override; - QList getActions() const override; - void setRegister(SimComponent* reg); - - SimComponent* m_register = nullptr; - SimDesign* m_design = nullptr; + RegisterTreeItem(TreeItem *parent, SimDesign *design) + : NetlistTreeItem(parent), m_design(design) {} + + enum class PortDirection { Input, Output }; + QVariant data(int column, int role = Qt::EditRole) const override; + bool setData(int column, const QVariant &value, + int role = Qt::EditRole) override; + QList getActions() const override; + void setRegister(SimComponent *reg); + + SimComponent *m_register = nullptr; + SimDesign *m_design = nullptr; }; class RegisterModel : public NetlistModelBase { - Q_OBJECT + Q_OBJECT public: - enum columns { ComponentColumn, ValueColumn, WidthColumn, NUM_COLUMNS }; - RegisterModel(SimDesign* arch, QObject* parent = nullptr); + enum columns { ComponentColumn, ValueColumn, WidthColumn, NUM_COLUMNS }; + RegisterModel(SimDesign *arch, QObject *parent = nullptr); - QVariant data(const QModelIndex& index, int role) const override; - Qt::ItemFlags flags(const QModelIndex& index) const override; - bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + QVariant data(const QModelIndex &index, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; public slots: - void invalidate() override; + void invalidate() override; private: - void loadDesign(RegisterTreeItem* parent, SimDesign* component); + void loadDesign(RegisterTreeItem *parent, SimDesign *component); - SimDesign* m_design = nullptr; + SimDesign *m_design = nullptr; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_REGISTERMODEL_H +#endif // VSRTL_REGISTERMODEL_H diff --git a/graphics/vsrtl_scene.cpp b/graphics/vsrtl_scene.cpp index 6be8b25..dea1411 100644 --- a/graphics/vsrtl_scene.cpp +++ b/graphics/vsrtl_scene.cpp @@ -15,225 +15,244 @@ namespace vsrtl { template -T* getSingleSelectedItem(const QGraphicsScene& scene) { - const auto selectedItems = scene.selectedItems(); - if (selectedItems.size() != 1) { - return nullptr; - } - return dynamic_cast(selectedItems.at(0)); +T *getSingleSelectedItem(const QGraphicsScene &scene) { + const auto selectedItems = scene.selectedItems(); + if (selectedItems.size() != 1) { + return nullptr; + } + return dynamic_cast(selectedItems.at(0)); } -VSRTLScene::VSRTLScene(QObject* parent) : QGraphicsScene(parent) { - connect(this, &QGraphicsScene::selectionChanged, this, &VSRTLScene::handleSelectionChanged); +VSRTLScene::VSRTLScene(QObject *parent) : QGraphicsScene(parent) { + connect(this, &QGraphicsScene::selectionChanged, this, + &VSRTLScene::handleSelectionChanged); - m_darkmodeAction = new QAction("Darkmode", this); - m_darkmodeAction->setCheckable(true); - m_darkmodeAction->setChecked(m_darkmode); - connect(m_darkmodeAction, &QAction::toggled, [=](bool checked) { - m_darkmode = checked; + m_darkmodeAction = new QAction("Darkmode", this); + m_darkmodeAction->setCheckable(true); + m_darkmodeAction->setChecked(m_darkmode); + connect(m_darkmodeAction, &QAction::toggled, [=](bool checked) { + m_darkmode = checked; - // Background - this->setBackgroundBrush(m_darkmode ? QBrush(QColorConstants::DarkGray.darker(300)) : Qt::NoBrush); + // Background + this->setBackgroundBrush(m_darkmode + ? QBrush(QColorConstants::DarkGray.darker(300)) + : Qt::NoBrush); - this->update(); - }); + this->update(); + }); } /** * @brief VSRTLScene::handleWirePointMove - * For supporting drag/drop of points simultaneously with allowing point movement, we cannot use QDrag. Instead, the - * scene will manage whether a single point is currently being dragged - if so, it will keep track of the WirePoint's - * underneath the cursor, and notify them. - * From this,we are then able to highlight the WirePoint's which are eligible for merging. + * For supporting drag/drop of points simultaneously with allowing point + * movement, we cannot use QDrag. Instead, the scene will manage whether a + * single point is currently being dragged - if so, it will keep track of the + * WirePoint's underneath the cursor, and notify them. From this,we are then + * able to highlight the WirePoint's which are eligible for merging. */ -void VSRTLScene::handleWirePointMove(QGraphicsSceneMouseEvent* event) { - if (m_selectedPoint != nullptr && event->buttons() == Qt::LeftButton) { - std::set pointsUnderCursor; - const auto itemsAtPoint = items(event->scenePos()); - for (const auto& item : qAsConst(itemsAtPoint)) { - if (auto* point = dynamic_cast(item)) { - if (m_selectedPoint->canMergeWith(point)) { - pointsUnderCursor.insert(point); - } - } +void VSRTLScene::handleWirePointMove(QGraphicsSceneMouseEvent *event) { + if (m_selectedPoint != nullptr && event->buttons() == Qt::LeftButton) { + std::set pointsUnderCursor; + const auto itemsAtPoint = items(event->scenePos()); + for (const auto &item : qAsConst(itemsAtPoint)) { + if (auto *point = dynamic_cast(item)) { + if (m_selectedPoint->canMergeWith(point)) { + pointsUnderCursor.insert(point); } + } + } - pointsUnderCursor.erase(m_selectedPoint); + pointsUnderCursor.erase(m_selectedPoint); - // notify any new points that they are now potential drop targets - std::set diff; - std::set_difference(pointsUnderCursor.begin(), pointsUnderCursor.end(), m_currentDropTargets.begin(), - m_currentDropTargets.end(), std::inserter(diff, diff.begin())); + // notify any new points that they are now potential drop targets + std::set diff; + std::set_difference(pointsUnderCursor.begin(), pointsUnderCursor.end(), + m_currentDropTargets.begin(), + m_currentDropTargets.end(), + std::inserter(diff, diff.begin())); - for (const auto& point : diff) - point->pointDragEnter(m_selectedPoint); + for (const auto &point : diff) + point->pointDragEnter(m_selectedPoint); - // Clear any old points which are no longer under the curser - std::set oldPoints; - std::set_difference(m_currentDropTargets.begin(), m_currentDropTargets.end(), pointsUnderCursor.begin(), - pointsUnderCursor.end(), std::inserter(oldPoints, oldPoints.begin())); + // Clear any old points which are no longer under the curser + std::set oldPoints; + std::set_difference(m_currentDropTargets.begin(), + m_currentDropTargets.end(), pointsUnderCursor.begin(), + pointsUnderCursor.end(), + std::inserter(oldPoints, oldPoints.begin())); - for (const auto& oldPoint : oldPoints) { - oldPoint->pointDragLeave(m_selectedPoint); - m_currentDropTargets.erase(oldPoint); - } + for (const auto &oldPoint : oldPoints) { + oldPoint->pointDragLeave(m_selectedPoint); + m_currentDropTargets.erase(oldPoint); + } - // Add new points to the currently tracked drop targets - m_currentDropTargets.insert(diff.begin(), diff.end()); + // Add new points to the currently tracked drop targets + m_currentDropTargets.insert(diff.begin(), diff.end()); - } else { - for (const auto& oldTarget : m_currentDropTargets) { - oldTarget->pointDragLeave(m_selectedPoint); - } - m_currentDropTargets.clear(); + } else { + for (const auto &oldTarget : m_currentDropTargets) { + oldTarget->pointDragLeave(m_selectedPoint); } + m_currentDropTargets.clear(); + } } -void VSRTLScene::drawBackground(QPainter* painter, const QRectF& rect) { - if (backgroundBrush() != Qt::NoBrush) { - QGraphicsScene::drawBackground(painter, rect); - } +void VSRTLScene::drawBackground(QPainter *painter, const QRectF &rect) { + if (backgroundBrush() != Qt::NoBrush) { + QGraphicsScene::drawBackground(painter, rect); + } - if (!m_showGrid) - return; + if (!m_showGrid) + return; - painter->save(); - painter->setPen(QPen(Qt::lightGray, 1)); + painter->save(); + painter->setPen(QPen(Qt::lightGray, 1)); - // Align rect with grid - const QPoint gridTopLeft = (rect.topLeft() / GRID_SIZE).toPoint() * GRID_SIZE; - const QPoint gridBotRight = (rect.bottomRight() / GRID_SIZE).toPoint() * GRID_SIZE; + // Align rect with grid + const QPoint gridTopLeft = (rect.topLeft() / GRID_SIZE).toPoint() * GRID_SIZE; + const QPoint gridBotRight = + (rect.bottomRight() / GRID_SIZE).toPoint() * GRID_SIZE; - for (int x = gridTopLeft.x(); x <= gridBotRight.x(); x += GRID_SIZE) - for (int y = gridTopLeft.y(); y <= gridBotRight.y(); y += GRID_SIZE) - painter->drawPoint(x, y); + for (int x = gridTopLeft.x(); x <= gridBotRight.x(); x += GRID_SIZE) + for (int y = gridTopLeft.y(); y <= gridBotRight.y(); y += GRID_SIZE) + painter->drawPoint(x, y); - painter->restore(); + painter->restore(); } void VSRTLScene::setLocked(bool lock) { - m_isLocked = lock; + m_isLocked = lock; - for (auto& i : items()) { - if (auto* gb = dynamic_cast(i)) - gb->setLocked(lock); - } + for (auto &i : items()) { + if (auto *gb = dynamic_cast(i)) + gb->setLocked(lock); + } } -void VSRTLScene::setPortValuesVisibleForType(vsrtl::SimPort::PortType t, bool visible) { - predicatedExecOnItems([t](const PortGraphic* p) { return p->getPortType() == t; }, - &PortGraphic::setValueLabelVisible, visible); +void VSRTLScene::setPortValuesVisibleForType(vsrtl::SimPort::PortType t, + bool visible) { + predicatedExecOnItems( + [t](const PortGraphic *p) { return p->getPortType() == t; }, + &PortGraphic::setValueLabelVisible, visible); } -void VSRTLScene::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - // If there are any items at the click position, forward the context event to it - if (items(event->scenePos()).size() != 0) - return QGraphicsScene::contextMenuEvent(event); - - QMenu menu; - // Component positioning locking - auto lockAction = menu.addAction(m_isLocked ? "Unlock" : "Lock"); - lockAction->setCheckable(true); - lockAction->setChecked(m_isLocked); - connect(lockAction, &QAction::triggered, this, &VSRTLScene::setLocked); - +void VSRTLScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + // If there are any items at the click position, forward the context event to + // it + if (items(event->scenePos()).size() != 0) + return QGraphicsScene::contextMenuEvent(event); + + QMenu menu; + // Component positioning locking + auto lockAction = menu.addAction(m_isLocked ? "Unlock" : "Lock"); + lockAction->setCheckable(true); + lockAction->setChecked(m_isLocked); + connect(lockAction, &QAction::triggered, this, &VSRTLScene::setLocked); + + menu.addSeparator(); + + // ==================== Port modifying actions ==================== + auto *portMenu = menu.addMenu("Ports"); + auto *showValuesAction = portMenu->addAction("Show all values"); + connect(showValuesAction, &QAction::triggered, [=] { + this->setPortValuesVisibleForType(vsrtl::SimPort::PortType::out, true); + }); + + auto *hideValuesAction = portMenu->addAction("Hide all values"); + connect(hideValuesAction, &QAction::triggered, [=] { + this->setPortValuesVisibleForType(vsrtl::SimPort::PortType::out, false); + }); + + auto *showWidthsAction = portMenu->addAction("Show all widths"); + connect(showWidthsAction, &QAction::triggered, + [=] { this->setPortWidthsVisible(true); }); + + auto *hideWidthsAction = portMenu->addAction("Hide all widths"); + connect(hideWidthsAction, &QAction::triggered, + [=] { this->setPortWidthsVisible(false); }); + + // ==================== Scene modifying actions ==================== + if (!m_isLocked) { menu.addSeparator(); - // ==================== Port modifying actions ==================== - auto* portMenu = menu.addMenu("Ports"); - auto* showValuesAction = portMenu->addAction("Show all values"); - connect(showValuesAction, &QAction::triggered, - [=] { this->setPortValuesVisibleForType(vsrtl::SimPort::PortType::out, true); }); - - auto* hideValuesAction = portMenu->addAction("Hide all values"); - connect(hideValuesAction, &QAction::triggered, - [=] { this->setPortValuesVisibleForType(vsrtl::SimPort::PortType::out, false); }); - - auto* showWidthsAction = portMenu->addAction("Show all widths"); - connect(showWidthsAction, &QAction::triggered, [=] { this->setPortWidthsVisible(true); }); - - auto* hideWidthsAction = portMenu->addAction("Hide all widths"); - connect(hideWidthsAction, &QAction::triggered, [=] { this->setPortWidthsVisible(false); }); - - // ==================== Scene modifying actions ==================== - if (!m_isLocked) { - menu.addSeparator(); - - // Hidden components submenu - auto* hiddenMenu = menu.addMenu("Hidden components"); - std::vector showActions; - - const auto sceneItems = items(); - for (const auto& i : qAsConst(sceneItems)) { - if (!i->isVisible()) { - if (auto* c = dynamic_cast(i)) { - // If a components parent is expanded but it itself is not visible, then it may be set to being - // currently visible - if (c->getParent()->isExpanded()) { - auto* action = hiddenMenu->addAction(QString::fromStdString(c->getComponent()->getName())); - connect(action, &QAction::triggered, [c] { c->setUserVisible(true); }); - showActions.push_back(action); - } - } - } + // Hidden components submenu + auto *hiddenMenu = menu.addMenu("Hidden components"); + std::vector showActions; + + const auto sceneItems = items(); + for (const auto &i : qAsConst(sceneItems)) { + if (!i->isVisible()) { + if (auto *c = dynamic_cast(i)) { + // If a components parent is expanded but it itself is not visible, + // then it may be set to being currently visible + if (c->getParent()->isExpanded()) { + auto *action = hiddenMenu->addAction( + QString::fromStdString(c->getComponent()->getName())); + connect(action, &QAction::triggered, + [c] { c->setUserVisible(true); }); + showActions.push_back(action); + } } + } + } - if (hiddenMenu->actions().size() != 0) { - hiddenMenu->addSeparator(); - auto* showAllAction = hiddenMenu->addAction("Show all"); - connect(showAllAction, &QAction::triggered, [showActions] { - for (const auto& a : showActions) - a->trigger(); - }); - } else { - // Disable the hidden components menu if there are no hidden components to be re-enabled - hiddenMenu->setDisabled(hiddenMenu->actions().size() == 0); - } + if (hiddenMenu->actions().size() != 0) { + hiddenMenu->addSeparator(); + auto *showAllAction = hiddenMenu->addAction("Show all"); + connect(showAllAction, &QAction::triggered, [showActions] { + for (const auto &a : showActions) + a->trigger(); + }); + } else { + // Disable the hidden components menu if there are no hidden components to + // be re-enabled + hiddenMenu->setDisabled(hiddenMenu->actions().size() == 0); + } - menu.addSeparator(); + menu.addSeparator(); - // ============== Drawing options ============================= - auto* drawMenu = menu.addMenu("Drawing"); - auto* showGridAction = drawMenu->addAction("Show grid"); - showGridAction->setCheckable(true); - showGridAction->setChecked(m_showGrid); - connect(showGridAction, &QAction::toggled, [=](bool checked) { - m_showGrid = checked; - this->update(); - }); + // ============== Drawing options ============================= + auto *drawMenu = menu.addMenu("Drawing"); + auto *showGridAction = drawMenu->addAction("Show grid"); + showGridAction->setCheckable(true); + showGridAction->setChecked(m_showGrid); + connect(showGridAction, &QAction::toggled, [=](bool checked) { + m_showGrid = checked; + this->update(); + }); - drawMenu->addAction(m_darkmodeAction); - } + drawMenu->addAction(m_darkmodeAction); + } - menu.exec(event->screenPos()); + menu.exec(event->screenPos()); } void VSRTLScene::setDarkmode(bool enabled) { - m_darkmodeAction->setChecked(enabled); + m_darkmodeAction->setChecked(enabled); } void VSRTLScene::setPortWidthsVisible(bool visible) { - execOnItems(&PortGraphic::setPortWidthVisible, visible); + execOnItems(&PortGraphic::setPortWidthVisible, visible); } -void VSRTLScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - handleWirePointMove(event); +void VSRTLScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + handleWirePointMove(event); - return QGraphicsScene::mouseMoveEvent(event); + return QGraphicsScene::mouseMoveEvent(event); } -void VSRTLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { - if (m_selectedPoint && m_currentDropTargets.size() != 0) { - auto* mergepoint = *m_currentDropTargets.begin(); - mergepoint->pointDrop(m_selectedPoint); - mergepoint->pointDragLeave(m_selectedPoint); - m_currentDropTargets.clear(); - } - QGraphicsScene::mouseReleaseEvent(event); +void VSRTLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + if (m_selectedPoint && m_currentDropTargets.size() != 0) { + auto *mergepoint = *m_currentDropTargets.begin(); + mergepoint->pointDrop(m_selectedPoint); + mergepoint->pointDragLeave(m_selectedPoint); + m_currentDropTargets.clear(); + } + QGraphicsScene::mouseReleaseEvent(event); } void VSRTLScene::handleSelectionChanged() { - m_selectedPoint = getSingleSelectedItem(*this); + m_selectedPoint = getSingleSelectedItem(*this); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_scene.h b/graphics/vsrtl_scene.h index 4b272ee..9f0e5bb 100644 --- a/graphics/vsrtl_scene.h +++ b/graphics/vsrtl_scene.h @@ -16,70 +16,80 @@ class WirePoint; class VSRTLScene : public QGraphicsScene { public: - /** - * @brief The ZLayer enum - * Defines the ordering of different items in the scene. Items with higher Z values will be drawn above items with - * lower Z values. - */ - enum ZLayer { Z_Wires, Z_Component, Z_PortWidth, Z_PortLabel, Z_ValueLabelHoverLine, Z_ValueLabel, Z_Selected }; - VSRTLScene(QObject* parent = nullptr); + /** + * @brief The ZLayer enum + * Defines the ordering of different items in the scene. Items with higher Z + * values will be drawn above items with lower Z values. + */ + enum ZLayer { + Z_Wires, + Z_Component, + Z_PortWidth, + Z_PortLabel, + Z_ValueLabelHoverLine, + Z_ValueLabel, + Z_Selected + }; + VSRTLScene(QObject *parent = nullptr); - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - void drawBackground(QPainter* painter, const QRectF& rect) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + void drawBackground(QPainter *painter, const QRectF &rect) override; - void setPortValuesVisibleForType(vsrtl::SimPort::PortType t, bool visible); - void setPortWidthsVisible(bool visible); - void setLocked(bool locked); + void setPortValuesVisibleForType(vsrtl::SimPort::PortType t, bool visible); + void setPortWidthsVisible(bool visible); + void setLocked(bool locked); - bool isLocked() const { return m_isLocked; } - bool darkmode() const { return m_darkmode; } - void setDarkmode(bool enabled); + bool isLocked() const { return m_isLocked; } + bool darkmode() const { return m_darkmode; } + void setDarkmode(bool enabled); private: - void handleSelectionChanged(); - void handleWirePointMove(QGraphicsSceneMouseEvent* event); + void handleSelectionChanged(); + void handleWirePointMove(QGraphicsSceneMouseEvent *event); - bool m_darkmode = false; - bool m_showGrid = true; - std::set m_currentDropTargets; - WirePoint* m_selectedPoint = nullptr; - QAction* m_darkmodeAction = nullptr; + bool m_darkmode = false; + bool m_showGrid = true; + std::set m_currentDropTargets; + WirePoint *m_selectedPoint = nullptr; + QAction *m_darkmodeAction = nullptr; - /** - * @brief m_isLocked - * When set, components all interaction with objects in the scene beyond changing the view style of signal values - * is disabled. - */ - bool m_isLocked = false; + /** + * @brief m_isLocked + * When set, components all interaction with objects in the scene beyond + * changing the view style of signal values is disabled. + */ + bool m_isLocked = false; - /* Applies T::F to all items in the scene of type F, using the supplied arguments */ - template - void execOnItems(F f, Args... args) { - const auto sceneItems = items(); - for (auto* c : qAsConst(sceneItems)) { - if (auto* t_c = dynamic_cast(c)) { - (t_c->*f)(args...); - } - } + /* Applies T::F to all items in the scene of type F, using the supplied + * arguments */ + template + void execOnItems(F f, Args... args) { + const auto sceneItems = items(); + for (auto *c : qAsConst(sceneItems)) { + if (auto *t_c = dynamic_cast(c)) { + (t_c->*f)(args...); + } } + } - /* Applies T::F to all items in the scene of type F, using the supplied arguments, if predicate returns - * true */ - template - void predicatedExecOnItems(std::function pred, F&& f, Args... args) { - const auto sceneItems = items(); - for (auto* c : qAsConst(sceneItems)) { - if (auto* t_c = dynamic_cast(c)) { - if (pred(t_c)) { - (t_c->*f)(args...); - } - } + /* Applies T::F to all items in the scene of type F, using the supplied + * arguments, if predicate returns true */ + template + void predicatedExecOnItems(std::function pred, F &&f, + Args... args) { + const auto sceneItems = items(); + for (auto *c : qAsConst(sceneItems)) { + if (auto *t_c = dynamic_cast(c)) { + if (pred(t_c)) { + (t_c->*f)(args...); } + } } + } }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_SCENE_H +#endif // VSRTL_SCENE_H diff --git a/graphics/vsrtl_shape.cpp b/graphics/vsrtl_shape.cpp index 370d57a..83582b0 100644 --- a/graphics/vsrtl_shape.cpp +++ b/graphics/vsrtl_shape.cpp @@ -2,139 +2,164 @@ namespace vsrtl { ShapeRegister::ShapeRegister() { - // Base component - ShapeRegister::registerTypeShape(GraphicsTypeFor(Component), {[](QTransform t) { - QPainterPath shape; - shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); - return shape; - }}); + // Base component + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Component), {[](QTransform t) { + QPainterPath shape; + shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); + return shape; + }}); - // Signals - ShapeRegister::registerTypeShape(GraphicsTypeFor(Wire), {[](QTransform t) { - QPainterPath shape; - shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); - return shape; - }}); + // Signals + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Wire), {[](QTransform t) { + QPainterPath shape; + shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); + return shape; + }}); - // Register - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Register), - {[](QTransform t) { - QPainterPath shape; - shape.addPolygon(t.map(QPolygonF({QPointF(0.3, 1), QPointF(0.5, 0.8), QPointF(0.7, 1), QPointF(0.3, 1)}))); - shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); - shape.setFillRule(Qt::WindingFill); - return shape; - }, - QRect(0, 0, 3, 4)}); + // Register + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Register), + {[](QTransform t) { + QPainterPath shape; + shape.addPolygon(t.map(QPolygonF({QPointF(0.3, 1), QPointF(0.5, 0.8), + QPointF(0.7, 1), QPointF(0.3, 1)}))); + shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); + shape.setFillRule(Qt::WindingFill); + return shape; + }, + QRect(0, 0, 3, 4)}); - ShapeRegister::registerTypeShape(GraphicsTypeFor(ClockedComponent), - {[](QTransform t) { - QPainterPath shape; - shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); - shape.setFillRule(Qt::WindingFill); - return shape; - }, - QRect(0, 0, 3, 4)}); + ShapeRegister::registerTypeShape( + GraphicsTypeFor(ClockedComponent), + {[](QTransform t) { + QPainterPath shape; + shape.addRect(t.mapRect(QRectF(QPointF(0, 0), QPointF(1, 1)))); + shape.setFillRule(Qt::WindingFill); + return shape; + }, + QRect(0, 0, 3, 4)}); - // Logic gates - ShapeRegister::registerTypeShape( - GraphicsTypeFor(And), {[](QTransform t) { - constexpr double linearEnd = 0.3; - QPainterPath shape; - shape.moveTo(t.map(QPointF(0, 0))); - shape.lineTo(t.map(QPointF(linearEnd, 0))); - shape.cubicTo(t.map(QPointF(linearEnd, 0)), t.map(QPointF(1, 0)), t.map(QPointF(1, 0.5))); - shape.cubicTo(t.map(QPointF(1, 0.5)), t.map(QPointF(1, 1)), t.map(QPointF(linearEnd, 1))); - shape.lineTo(t.map(QPointF(0, 1))); - shape.lineTo(t.map(QPointF(0, 0))); - return shape; - }}); + // Logic gates + ShapeRegister::registerTypeShape( + GraphicsTypeFor(And), {[](QTransform t) { + constexpr double linearEnd = 0.3; + QPainterPath shape; + shape.moveTo(t.map(QPointF(0, 0))); + shape.lineTo(t.map(QPointF(linearEnd, 0))); + shape.cubicTo(t.map(QPointF(linearEnd, 0)), t.map(QPointF(1, 0)), + t.map(QPointF(1, 0.5))); + shape.cubicTo(t.map(QPointF(1, 0.5)), t.map(QPointF(1, 1)), + t.map(QPointF(linearEnd, 1))); + shape.lineTo(t.map(QPointF(0, 1))); + shape.lineTo(t.map(QPointF(0, 0))); + return shape; + }}); - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Nand), {[](QTransform t) { - constexpr double dotRadius = 0.1; - constexpr double gateRhs = 1.0 - dotRadius * 2; - constexpr double linearEnd = 0.3; - QPainterPath shape; - shape.moveTo(t.map(QPointF(0, 0))); - shape.lineTo(t.map(QPointF(linearEnd, 0))); - shape.cubicTo(t.map(QPointF(linearEnd, 0)), t.map(QPointF(gateRhs, 0)), t.map(QPointF(gateRhs, 0.5))); - shape.cubicTo(t.map(QPointF(gateRhs, 0.5)), t.map(QPointF(gateRhs, 1)), t.map(QPointF(linearEnd, 1))); - shape.lineTo(t.map(QPointF(0, 1))); - shape.lineTo(t.map(QPointF(0, 0))); - QRectF circle = t.mapRect(QRectF(QPointF(0, 0), QPointF(dotRadius, dotRadius))); - shape.addEllipse(t.map(QPointF(gateRhs + dotRadius, 0.5)), circle.width(), circle.height()); - shape.setFillRule(Qt::WindingFill); - return shape; - }}); + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Nand), {[](QTransform t) { + constexpr double dotRadius = 0.1; + constexpr double gateRhs = 1.0 - dotRadius * 2; + constexpr double linearEnd = 0.3; + QPainterPath shape; + shape.moveTo(t.map(QPointF(0, 0))); + shape.lineTo(t.map(QPointF(linearEnd, 0))); + shape.cubicTo(t.map(QPointF(linearEnd, 0)), t.map(QPointF(gateRhs, 0)), + t.map(QPointF(gateRhs, 0.5))); + shape.cubicTo(t.map(QPointF(gateRhs, 0.5)), t.map(QPointF(gateRhs, 1)), + t.map(QPointF(linearEnd, 1))); + shape.lineTo(t.map(QPointF(0, 1))); + shape.lineTo(t.map(QPointF(0, 0))); + QRectF circle = + t.mapRect(QRectF(QPointF(0, 0), QPointF(dotRadius, dotRadius))); + shape.addEllipse(t.map(QPointF(gateRhs + dotRadius, 0.5)), + circle.width(), circle.height()); + shape.setFillRule(Qt::WindingFill); + return shape; + }}); - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Xor), {[](QTransform t) { - QPainterPath shape; - shape.moveTo(t.map(QPointF(0, 0))); - shape.lineTo(t.map(QPointF(0.1, 0))); - shape.cubicTo(t.map(QPointF(0.1, 0)), t.map(QPointF(1, 0)), t.map(QPointF(1, 0.5))); - shape.cubicTo(t.map(QPointF(1, 0.5)), t.map(QPointF(1, 1)), t.map(QPointF(0.1, 1))); - shape.cubicTo(t.map(QPointF(0.1, 1)), t.map(QPointF(0.5, 0.5)), t.map(QPointF(0.1, 0))); - shape.moveTo(t.map(QPointF(0, 0))); - shape.cubicTo(t.map(QPointF(0, 0)), t.map(QPointF(0.4, 0.5)), t.map(QPointF(0, 1))); - shape.cubicTo(t.map(QPointF(0, 1)), t.map(QPointF(0.4, 0.5)), t.map(QPointF(0, 0))); - shape.setFillRule(Qt::WindingFill); - return shape; - }}); + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Xor), {[](QTransform t) { + QPainterPath shape; + shape.moveTo(t.map(QPointF(0, 0))); + shape.lineTo(t.map(QPointF(0.1, 0))); + shape.cubicTo(t.map(QPointF(0.1, 0)), t.map(QPointF(1, 0)), + t.map(QPointF(1, 0.5))); + shape.cubicTo(t.map(QPointF(1, 0.5)), t.map(QPointF(1, 1)), + t.map(QPointF(0.1, 1))); + shape.cubicTo(t.map(QPointF(0.1, 1)), t.map(QPointF(0.5, 0.5)), + t.map(QPointF(0.1, 0))); + shape.moveTo(t.map(QPointF(0, 0))); + shape.cubicTo(t.map(QPointF(0, 0)), t.map(QPointF(0.4, 0.5)), + t.map(QPointF(0, 1))); + shape.cubicTo(t.map(QPointF(0, 1)), t.map(QPointF(0.4, 0.5)), + t.map(QPointF(0, 0))); + shape.setFillRule(Qt::WindingFill); + return shape; + }}); - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Or), {[](QTransform t) { - QPainterPath shape; - constexpr double linearEnd = 0.3; - constexpr double cornerIndent = 0.09; - shape.moveTo(t.map(QPointF(0, 0))); - shape.lineTo(t.map(QPointF(linearEnd, 0))); - shape.cubicTo(t.map(QPointF(linearEnd, 0)), t.map(QPointF(1 - cornerIndent, cornerIndent)), - t.map(QPointF(1, 0.5))); - shape.cubicTo(t.map(QPointF(1, 0.5)), t.map(QPointF(1 - cornerIndent, 1 - cornerIndent)), - t.map(QPointF(linearEnd, 1))); - shape.lineTo(t.map(QPointF(0, 1))); - shape.cubicTo(t.map(QPointF(0, 1)), t.map(QPointF(linearEnd, 0.5)), t.map(QPointF(0, 0))); - return shape; - }}); + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Or), {[](QTransform t) { + QPainterPath shape; + constexpr double linearEnd = 0.3; + constexpr double cornerIndent = 0.09; + shape.moveTo(t.map(QPointF(0, 0))); + shape.lineTo(t.map(QPointF(linearEnd, 0))); + shape.cubicTo(t.map(QPointF(linearEnd, 0)), + t.map(QPointF(1 - cornerIndent, cornerIndent)), + t.map(QPointF(1, 0.5))); + shape.cubicTo(t.map(QPointF(1, 0.5)), + t.map(QPointF(1 - cornerIndent, 1 - cornerIndent)), + t.map(QPointF(linearEnd, 1))); + shape.lineTo(t.map(QPointF(0, 1))); + shape.cubicTo(t.map(QPointF(0, 1)), t.map(QPointF(linearEnd, 0.5)), + t.map(QPointF(0, 0))); + return shape; + }}); - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Not), {[](QTransform t) { - QPainterPath shape; - QRectF circle = t.mapRect(QRectF(QPointF(0, 0), QPointF(0.05, 0.05))); - shape.addEllipse(t.map(QPointF(0.9, 0.5)), circle.width(), circle.height()); - shape.addPolygon(t.map(QPolygonF({QPointF(0, 0), QPointF(0.8, 0.5), QPointF(0, 1), QPointF(0, 0)}))); - shape.setFillRule(Qt::WindingFill); - return shape; - }}); + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Not), {[](QTransform t) { + QPainterPath shape; + QRectF circle = t.mapRect(QRectF(QPointF(0, 0), QPointF(0.05, 0.05))); + shape.addEllipse(t.map(QPointF(0.9, 0.5)), circle.width(), + circle.height()); + shape.addPolygon(t.map(QPolygonF( + {QPointF(0, 0), QPointF(0.8, 0.5), QPointF(0, 1), QPointF(0, 0)}))); + shape.setFillRule(Qt::WindingFill); + return shape; + }}); - // Multiplexer - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Multiplexer), {[](QTransform t) { - QPainterPath shape; - shape.addPolygon( - t.map(QPolygonF({QPointF(0, 0), QPointF(1, 0.2), QPointF(1, 0.8), QPointF(0, 1), QPointF(0, 0)}))); - return shape; - }}); + // Multiplexer + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Multiplexer), {[](QTransform t) { + QPainterPath shape; + shape.addPolygon( + t.map(QPolygonF({QPointF(0, 0), QPointF(1, 0.2), QPointF(1, 0.8), + QPointF(0, 1), QPointF(0, 0)}))); + return shape; + }}); - // ALU - ShapeRegister::registerTypeShape( - GraphicsTypeFor(ALU), {[](QTransform t) { - QPainterPath shape; - shape.addPolygon(t.map(QPolygonF({QPointF(0, 0), QPointF(1, 0.2), QPointF(1, 0.8), QPointF(0, 1), - QPointF(0, 0.65), QPointF(0.2, 0.5), QPointF(0, 0.35), QPointF(0, 0)}))); - return shape; - }}); + // ALU + ShapeRegister::registerTypeShape( + GraphicsTypeFor(ALU), {[](QTransform t) { + QPainterPath shape; + shape.addPolygon( + t.map(QPolygonF({QPointF(0, 0), QPointF(1, 0.2), QPointF(1, 0.8), + QPointF(0, 1), QPointF(0, 0.65), QPointF(0.2, 0.5), + QPointF(0, 0.35), QPointF(0, 0)}))); + return shape; + }}); - // Adder - ShapeRegister::registerTypeShape( - GraphicsTypeFor(Adder), {[](QTransform t) { - QPainterPath shape; - shape.addPolygon(t.map(QPolygonF({QPointF(0, 0), QPointF(1, 0.2), QPointF(1, 0.8), QPointF(0, 1), - QPointF(0, 0.65), QPointF(0.2, 0.5), QPointF(0, 0.35), QPointF(0, 0)}))); - return shape; - }}); + // Adder + ShapeRegister::registerTypeShape( + GraphicsTypeFor(Adder), {[](QTransform t) { + QPainterPath shape; + shape.addPolygon( + t.map(QPolygonF({QPointF(0, 0), QPointF(1, 0.2), QPointF(1, 0.8), + QPointF(0, 1), QPointF(0, 0.65), QPointF(0.2, 0.5), + QPointF(0, 0.35), QPointF(0, 0)}))); + return shape; + }}); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_shape.h b/graphics/vsrtl_shape.h index 49f7c38..46f9a64 100644 --- a/graphics/vsrtl_shape.h +++ b/graphics/vsrtl_shape.h @@ -12,61 +12,67 @@ namespace vsrtl { /** Shape - * Class for storing the draw shape associated with a Component from the VSRTL library + * Class for storing the draw shape associated with a Component from the VSRTL + * library */ class ShapeRegister { private: - /** - * @brief ShapeRegister - * Constructor shall initialize all componentShapes - */ - ShapeRegister(); + /** + * @brief ShapeRegister + * Constructor shall initialize all componentShapes + */ + ShapeRegister(); public: - /** - * @brief The Shape struct - * Component shapes should be scalable in x- and y direction, but may contain complex shapes such as circles. - * The Shape struct, and shape generation, thus provides an interface for generating (scaling) a QPainterPath, - * without using QPainte's scale functionality. - * We avoid using QPainter::scale, given that this also scales the pen, yielding invalid drawings. - */ - struct Shape { - std::function shapeFunc; - QRect min_rect = QRect(); - }; + /** + * @brief The Shape struct + * Component shapes should be scalable in x- and y direction, but may contain + * complex shapes such as circles. The Shape struct, and shape generation, + * thus provides an interface for generating (scaling) a QPainterPath, without + * using QPainte's scale functionality. We avoid using QPainter::scale, given + * that this also scales the pen, yielding invalid drawings. + */ + struct Shape { + std::function shapeFunc; + QRect min_rect = QRect(); + }; - static QPainterPath getTypeShape(const GraphicsType* type, const QTransform& transform) { - // If no shape has been registered for the base component type, revert to displaying as a "SimComponent" - if (!get().m_typeShapes.count(type)) { - return get().m_typeShapes[GraphicsTypeFor(Component)].shapeFunc(transform); - } - return get().m_typeShapes[type].shapeFunc(transform); + static QPainterPath getTypeShape(const GraphicsType *type, + const QTransform &transform) { + // If no shape has been registered for the base component type, revert to + // displaying as a "SimComponent" + if (!get().m_typeShapes.count(type)) { + return get().m_typeShapes[GraphicsTypeFor(Component)].shapeFunc( + transform); } + return get().m_typeShapes[type].shapeFunc(transform); + } - static QRect getTypePreferredRect(const GraphicsType* type) { - // If no shape has been registered for the base component type, revert to displaying as a "SimComponent" - if (!get().m_typeShapes.count(type)) { - return get().m_typeShapes[GraphicsTypeFor(Component)].min_rect; - } - return get().m_typeShapes[type].min_rect; + static QRect getTypePreferredRect(const GraphicsType *type) { + // If no shape has been registered for the base component type, revert to + // displaying as a "SimComponent" + if (!get().m_typeShapes.count(type)) { + return get().m_typeShapes[GraphicsTypeFor(Component)].min_rect; } + return get().m_typeShapes[type].min_rect; + } private: - static ShapeRegister& get() { - static ShapeRegister sr; - return sr; - } + static ShapeRegister &get() { + static ShapeRegister sr; + return sr; + } - void registerTypeShape(const GraphicsType* type, const Shape& shape) { - Q_ASSERT(!m_typeShapes.count(type)); - Q_ASSERT(shape.min_rect.topLeft() == QPoint(0, 0)); + void registerTypeShape(const GraphicsType *type, const Shape &shape) { + Q_ASSERT(!m_typeShapes.count(type)); + Q_ASSERT(shape.min_rect.topLeft() == QPoint(0, 0)); - // Ensure that minimum rectangle is snapping to the grid - m_typeShapes[type] = shape; - } + // Ensure that minimum rectangle is snapping to the grid + m_typeShapes[type] = shape; + } - std::map m_typeShapes; + std::map m_typeShapes; }; -} // namespace vsrtl -#endif // VSRTL_SHAPE_H +} // namespace vsrtl +#endif // VSRTL_SHAPE_H diff --git a/graphics/vsrtl_simqobject.h b/graphics/vsrtl_simqobject.h index 91e5989..4805221 100644 --- a/graphics/vsrtl_simqobject.h +++ b/graphics/vsrtl_simqobject.h @@ -7,26 +7,29 @@ #include "gallantsignalwrapper.h" -/// The SimQObject class acts as a base class for graphics components which need the gallant-to-Qt signal translation -/// mechanism, for using simulator signals in a Qt signal/slot like way. +/// The SimQObject class acts as a base class for graphics components which need +/// the gallant-to-Qt signal translation mechanism, for using simulator signals +/// in a Qt signal/slot like way. namespace vsrtl { class SimQObject : public QObject { public: - /// This function translated a simulator signal object into Qt signals. simUpdateSlot will be called whenever the - /// simulator signal is emitted. - template - void wrapSimSignal(D& sig, Qt::ConnectionType type = Qt::AutoConnection) { - m_simSignalWrappers.push_back(std::unique_ptr(new GallantSignalWrapper( + /// This function translated a simulator signal object into Qt signals. + /// simUpdateSlot will be called whenever the simulator signal is emitted. + template + void wrapSimSignal(D &sig, Qt::ConnectionType type = Qt::AutoConnection) { + m_simSignalWrappers.push_back( + std::unique_ptr(new GallantSignalWrapper( this, [this]() { this->simUpdateSlot(); }, sig, type))); - } + } - /// Connect wrappers for making model signal emissions threadsafe Qt signals. - std::vector> m_simSignalWrappers; + /// Connect wrappers for making model signal emissions threadsafe Qt signals. + std::vector> m_simSignalWrappers; - /// The slot called whenever the associated simulator object announced that its state has changed. - virtual void simUpdateSlot() {} + /// The slot called whenever the associated simulator object announced that + /// its state has changed. + virtual void simUpdateSlot() {} }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_treeitem.cpp b/graphics/vsrtl_treeitem.cpp index ff518fa..146daa1 100644 --- a/graphics/vsrtl_treeitem.cpp +++ b/graphics/vsrtl_treeitem.cpp @@ -5,101 +5,92 @@ namespace vsrtl { -TreeItem::TreeItem(TreeItem* parent) { - parentItem = parent; -} +TreeItem::TreeItem(TreeItem *parent) { parentItem = parent; } -TreeItem::~TreeItem() { - qDeleteAll(childItems); -} +TreeItem::~TreeItem() { qDeleteAll(childItems); } -TreeItem* TreeItem::child(int number) { - return childItems.value(number); -} +TreeItem *TreeItem::child(int number) { return childItems.value(number); } -int TreeItem::childCount() const { - return childItems.count(); -} +int TreeItem::childCount() const { return childItems.count(); } int TreeItem::childNumber() const { - if (parentItem) - return parentItem->childItems.indexOf(const_cast(this)); + if (parentItem) + return parentItem->childItems.indexOf(const_cast(this)); - return 0; + return 0; } -bool TreeItem::insertChild(int position, TreeItem* item) { - if (position < 0 || position > childItems.size()) - return false; +bool TreeItem::insertChild(int position, TreeItem *item) { + if (position < 0 || position > childItems.size()) + return false; - childItems.insert(position, item); + childItems.insert(position, item); - return true; + return true; } -TreeItem* TreeItem::parent() { - return parentItem; -} +TreeItem *TreeItem::parent() { return parentItem; } bool TreeItem::removeChildren(int position, int count) { - if (position < 0 || position + count > childItems.size()) - return false; + if (position < 0 || position + count > childItems.size()) + return false; - for (int row = 0; row < count; ++row) - delete childItems.takeAt(position); + for (int row = 0; row < count; ++row) + delete childItems.takeAt(position); - return true; + return true; } -NetlistTreeItem::NetlistTreeItem(TreeItem* parent) : TreeItem(parent) {} +NetlistTreeItem::NetlistTreeItem(TreeItem *parent) : TreeItem(parent) {} -void NetlistTreeItem::setPort(SimPort* port) { - Q_ASSERT(port); - m_port = port; - m_name = QString::fromStdString(m_port->getName()); - m_radixMenu = createPortRadixMenu(m_port, m_radix); +void NetlistTreeItem::setPort(SimPort *port) { + Q_ASSERT(port); + m_port = port; + m_name = QString::fromStdString(m_port->getName()); + m_radixMenu = createPortRadixMenu(m_port, m_radix); - if (m_port->isEnumPort()) { - m_radix = Radix::Enum; - } + if (m_port->isEnumPort()) { + m_radix = Radix::Enum; + } } -QList NetlistTreeItem::getActions() const { - // Only return actions for items which have a port (default actions from TreeItem displays display type actions, - // which are not applicable for Component items) - if (m_radixMenu) { - return {m_radixMenu}; - } - return QList(); +QList NetlistTreeItem::getActions() const { + // Only return actions for items which have a port (default actions from + // TreeItem displays display type actions, which are not applicable for + // Component items) + if (m_radixMenu) { + return {m_radixMenu}; + } + return QList(); } QVariant NetlistTreeItem::data(int column, int role) const { - if (column == NetlistModel::IOColumn && role == Qt::DecorationRole && m_port != nullptr) { - return m_direction == PortDirection::Input ? QIcon(":/vsrtl_icons/input.svg") - : QIcon(":/vsrtl_icons/output.svg"); - } else if (role == Qt::DisplayRole || role == Qt::EditRole) { - switch (column) { - case NetlistModel::ComponentColumn: { - return m_name; - } - case NetlistModel::ValueColumn: { - if (m_port) { - return encodePortRadixValue(m_port, m_radix); - } - break; - } - case NetlistModel::WidthColumn: { - if (m_port) { - return m_port->getWidth(); - } - break; - } - } + if (column == NetlistModel::IOColumn && role == Qt::DecorationRole && + m_port != nullptr) { + return m_direction == PortDirection::Input + ? QIcon(":/vsrtl_icons/input.svg") + : QIcon(":/vsrtl_icons/output.svg"); + } else if (role == Qt::DisplayRole || role == Qt::EditRole) { + switch (column) { + case NetlistModel::ComponentColumn: { + return m_name; } - return QVariant(); -} -bool NetlistTreeItem::setData(int, const QVariant&, int) { - return false; + case NetlistModel::ValueColumn: { + if (m_port) { + return encodePortRadixValue(m_port, m_radix); + } + break; + } + case NetlistModel::WidthColumn: { + if (m_port) { + return m_port->getWidth(); + } + break; + } + } + } + return QVariant(); } +bool NetlistTreeItem::setData(int, const QVariant &, int) { return false; } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_treeitem.h b/graphics/vsrtl_treeitem.h index bd045d1..a94e7f7 100644 --- a/graphics/vsrtl_treeitem.h +++ b/graphics/vsrtl_treeitem.h @@ -16,28 +16,30 @@ class SimComponent; /** Generic tree structure node class */ class TreeItem : public QObject { - Q_OBJECT + Q_OBJECT public: - explicit TreeItem(TreeItem* parent); - virtual ~TreeItem(); + explicit TreeItem(TreeItem *parent); + virtual ~TreeItem(); - virtual QVariant data(int column, int role = Qt::EditRole) const = 0; - virtual bool setData(int column, const QVariant& value, int role = Qt::EditRole) = 0; + virtual QVariant data(int column, int role = Qt::EditRole) const = 0; + virtual bool setData(int column, const QVariant &value, + int role = Qt::EditRole) = 0; - TreeItem* child(int number); - int childCount() const; - bool insertChild(int position, TreeItem* item); - TreeItem* parent(); - bool removeChildren(int position, int count); - int childNumber() const; + TreeItem *child(int number); + int childCount() const; + bool insertChild(int position, TreeItem *item); + TreeItem *parent(); + bool removeChildren(int position, int count); + int childNumber() const; - // Store an index to the QModelIndex corresponding to this item in the tree. This is required for facilitating - // selection behaviour via. selections made in the graphics component. - QModelIndex index; + // Store an index to the QModelIndex corresponding to this item in the tree. + // This is required for facilitating selection behaviour via. selections made + // in the graphics component. + QModelIndex index; - QString m_name; - QList childItems; - TreeItem* parentItem; + QString m_name; + QList childItems; + TreeItem *parentItem; private: }; @@ -46,19 +48,20 @@ enum class PortDirection { Input, Output }; class NetlistTreeItem : public TreeItem { public: - NetlistTreeItem(TreeItem* parent); - QVariant data(int column, int role = Qt::EditRole) const override; - bool setData(int column, const QVariant& value, int role = Qt::EditRole) override; - virtual QList getActions() const; - void setPort(SimPort* port); + NetlistTreeItem(TreeItem *parent); + QVariant data(int column, int role = Qt::EditRole) const override; + bool setData(int column, const QVariant &value, + int role = Qt::EditRole) override; + virtual QList getActions() const; + void setPort(SimPort *port); - SimComponent* m_component = nullptr; - SimPort* m_port = nullptr; - PortDirection m_direction = PortDirection::Input; - QMenu* m_radixMenu = nullptr; - Radix m_radix = Radix::Hex; + SimComponent *m_component = nullptr; + SimPort *m_port = nullptr; + PortDirection m_direction = PortDirection::Input; + QMenu *m_radixMenu = nullptr; + Radix m_radix = Radix::Hex; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_TREEITEM_H +#endif // VSRTL_TREEITEM_H diff --git a/graphics/vsrtl_valuelabel.cpp b/graphics/vsrtl_valuelabel.cpp index bacdbb9..e52425e 100644 --- a/graphics/vsrtl_valuelabel.cpp +++ b/graphics/vsrtl_valuelabel.cpp @@ -9,114 +9,121 @@ namespace vsrtl { -ValueLabel::ValueLabel(QGraphicsItem* parent, const std::shared_ptr& radix, const PortGraphic* port, +ValueLabel::ValueLabel(QGraphicsItem *parent, + const std::shared_ptr &radix, + const PortGraphic *port, std::shared_ptr visibilityAction) : Label(parent, "", visibilityAction, 10), m_radix(radix), m_port(port) { - setFlag(ItemIsSelectable, true); - setAcceptHoverEvents(true); - updateText(); - - m_showLineToPortAction = std::make_unique("Always show line to port"); - m_showLineToPortAction->setCheckable(true); - m_showLineToPortAction->setChecked(false); - QObject::connect(m_showLineToPortAction.get(), &QAction::triggered, [this] { createLineToPort(); }); + setFlag(ItemIsSelectable, true); + setAcceptHoverEvents(true); + updateText(); + + m_showLineToPortAction = + std::make_unique("Always show line to port"); + m_showLineToPortAction->setCheckable(true); + m_showLineToPortAction->setChecked(false); + QObject::connect(m_showLineToPortAction.get(), &QAction::triggered, + [this] { createLineToPort(); }); } -void ValueLabel::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* w) { - // Paint a label box behind the text - painter->save(); - if (!m_port->getPort()->isConstant()) { - const bool darkmode = static_cast(scene())->darkmode(); - - QRectF textRect = shape().boundingRect(); - painter->fillRect(textRect, darkmode ? QColor{0x45, 0x45, 0x45} : QColorConstants::White); - painter->setBrush(Qt::NoBrush); - painter->setPen(QPen(Qt::black, 1)); - painter->drawRect(textRect); - } - painter->restore(); - - Label::paint(painter, option, w); +void ValueLabel::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *w) { + // Paint a label box behind the text + painter->save(); + if (!m_port->getPort()->isConstant()) { + const bool darkmode = static_cast(scene())->darkmode(); + + QRectF textRect = shape().boundingRect(); + painter->fillRect(textRect, darkmode ? QColor{0x45, 0x45, 0x45} + : QColorConstants::White); + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(Qt::black, 1)); + painter->drawRect(textRect); + } + painter->restore(); + + Label::paint(painter, option, w); } void ValueLabel::updateLine() { - if (m_lineToPort) { - m_lineToPort->setLine(lineToPort()); - } + if (m_lineToPort) { + m_lineToPort->setLine(lineToPort()); + } } QLineF ValueLabel::lineToPort() const { - const QRectF textRect = shape().boundingRect(); - QLineF shortestLine; - qreal minLength = qInf(); - for (const auto& corner : - {textRect.topLeft(), textRect.topRight(), textRect.bottomLeft(), textRect.bottomRight()}) { - const auto line = QLineF(corner, mapFromScene(m_port->scenePos())); - if (line.length() < minLength) { - shortestLine = line; - minLength = line.length(); - } + const QRectF textRect = shape().boundingRect(); + QLineF shortestLine; + qreal minLength = qInf(); + for (const auto &corner : {textRect.topLeft(), textRect.topRight(), + textRect.bottomLeft(), textRect.bottomRight()}) { + const auto line = QLineF(corner, mapFromScene(m_port->scenePos())); + if (line.length() < minLength) { + shortestLine = line; + minLength = line.length(); } + } - return shortestLine; + return shortestLine; } -void ValueLabel::mouseMoveEvent(QGraphicsSceneMouseEvent* e) { - updateLine(); - Label::mouseMoveEvent(e); +void ValueLabel::mouseMoveEvent(QGraphicsSceneMouseEvent *e) { + updateLine(); + Label::mouseMoveEvent(e); } void ValueLabel::createLineToPort() { - if (!m_lineToPort) { - m_lineToPort = new QGraphicsLineItem(lineToPort(), this); - m_lineToPort->setZValue(VSRTLScene::Z_ValueLabelHoverLine); - QPen pen = m_lineToPort->pen(); - pen.setStyle(Qt::DashLine); - m_lineToPort->setPen(pen); - } + if (!m_lineToPort) { + m_lineToPort = new QGraphicsLineItem(lineToPort(), this); + m_lineToPort->setZValue(VSRTLScene::Z_ValueLabelHoverLine); + QPen pen = m_lineToPort->pen(); + pen.setStyle(Qt::DashLine); + m_lineToPort->setPen(pen); + } } -void ValueLabel::hoverEnterEvent(QGraphicsSceneHoverEvent* e) { - setToolTip(m_port->getTooltipString()); - createLineToPort(); - Label::hoverEnterEvent(e); +void ValueLabel::hoverEnterEvent(QGraphicsSceneHoverEvent *e) { + setToolTip(m_port->getTooltipString()); + createLineToPort(); + Label::hoverEnterEvent(e); } -void ValueLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent* e) { - if (!m_showLineToPortAction->isChecked()) { - delete m_lineToPort; - m_lineToPort = nullptr; - } - Label::hoverLeaveEvent(e); +void ValueLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent *e) { + if (!m_showLineToPortAction->isChecked()) { + delete m_lineToPort; + m_lineToPort = nullptr; + } + Label::hoverLeaveEvent(e); } -QVariant ValueLabel::itemChange(GraphicsItemChange change, const QVariant& value) { - if (change == QGraphicsItem::ItemPositionHasChanged) { - updateLine(); - } - return Label::itemChange(change, value); +QVariant ValueLabel::itemChange(GraphicsItemChange change, + const QVariant &value) { + if (change == QGraphicsItem::ItemPositionHasChanged) { + updateLine(); + } + return Label::itemChange(change, value); } void ValueLabel::setLocked(bool) { - // ValueLabels should always be movable, even when the scene is locked - return; + // ValueLabels should always be movable, even when the scene is locked + return; } -void ValueLabel::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - QMenu menu; - menu.addMenu(createPortRadixMenu(m_port->getPort(), *m_radix)); - menu.addAction(m_visibilityAction.get()); - menu.addAction(m_showLineToPortAction.get()); +void ValueLabel::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + QMenu menu; + menu.addMenu(createPortRadixMenu(m_port->getPort(), *m_radix)); + menu.addAction(m_visibilityAction.get()); + menu.addAction(m_showLineToPortAction.get()); - menu.exec(event->screenPos()); + menu.exec(event->screenPos()); - // Schedule an update of the label to register any change in the display type - updateText(); + // Schedule an update of the label to register any change in the display type + updateText(); } void ValueLabel::updateText() { - setPlainText(encodePortRadixValue(m_port->getPort(), *m_radix)); - applyFormatChanges(); + setPlainText(encodePortRadixValue(m_port->getPort(), *m_radix)); + applyFormatChanges(); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_valuelabel.h b/graphics/vsrtl_valuelabel.h index 1d544d5..4ac63dc 100644 --- a/graphics/vsrtl_valuelabel.h +++ b/graphics/vsrtl_valuelabel.h @@ -13,28 +13,31 @@ class PortGraphic; class ValueLabel : public Label { public: - ValueLabel(QGraphicsItem* parent, const std::shared_ptr& radix, const PortGraphic* port, - std::shared_ptr visibilityAction = {}); - - void paint(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget*) override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; - void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; - void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; - QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - void updateText() override; - - void setLocked(bool locked) override; + ValueLabel(QGraphicsItem *parent, const std::shared_ptr &radix, + const PortGraphic *port, + std::shared_ptr visibilityAction = {}); + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent *) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override; + QVariant itemChange(GraphicsItemChange change, + const QVariant &value) override; + void updateText() override; + + void setLocked(bool locked) override; private: - void createLineToPort(); - QLineF lineToPort() const; - void updateLine(); - - std::shared_ptr m_radix; - const PortGraphic* m_port = nullptr; - QGraphicsLineItem* m_lineToPort = nullptr; - std::unique_ptr m_showLineToPortAction; + void createLineToPort(); + QLineF lineToPort() const; + void updateLine(); + + std::shared_ptr m_radix; + const PortGraphic *m_port = nullptr; + QGraphicsLineItem *m_lineToPort = nullptr; + std::unique_ptr m_showLineToPortAction; }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_view.cpp b/graphics/vsrtl_view.cpp index 8e4d013..3319fda 100644 --- a/graphics/vsrtl_view.cpp +++ b/graphics/vsrtl_view.cpp @@ -7,80 +7,83 @@ namespace vsrtl { -VSRTLView::VSRTLView(QWidget* parent) : QGraphicsView(parent) { - setDragMode(QGraphicsView::RubberBandDrag); - setOptimizationFlag(QGraphicsView::DontSavePainterState); - setViewportUpdateMode(QGraphicsView::FullViewportUpdate); - setTransformationAnchor(QGraphicsView::AnchorUnderMouse); - setRenderHint(QPainter::Antialiasing, false); - setInteractive(true); - setupMatrix(); +VSRTLView::VSRTLView(QWidget *parent) : QGraphicsView(parent) { + setDragMode(QGraphicsView::RubberBandDrag); + setOptimizationFlag(QGraphicsView::DontSavePainterState); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setRenderHint(QPainter::Antialiasing, false); + setInteractive(true); + setupMatrix(); } -ComponentGraphic* VSRTLView::lookupGraphicForComponent(const SimComponent* c) { - const auto sceneItems = items(); - for (auto* i : qAsConst(sceneItems)) { - auto d = dynamic_cast(i); - if (d) { - // Equality is based on pointer equality - if (d->getComponent() == c) - return d; - } +ComponentGraphic *VSRTLView::lookupGraphicForComponent(const SimComponent *c) { + const auto sceneItems = items(); + for (auto *i : qAsConst(sceneItems)) { + auto d = dynamic_cast(i); + if (d) { + // Equality is based on pointer equality + if (d->getComponent() == c) + return d; } - return nullptr; + } + return nullptr; } -void VSRTLView::zoomToFit(const QGraphicsItem* tlc) { - if (tlc == nullptr) { - return; - } - - fitInView(tlc->boundingRect(), Qt::KeepAspectRatio); - const auto m = transform(); - - // When zooming to fit, we want to adjust the current view via. fitInView, and then derive the zoom factor - // corresponding to the scale factor which was set by fitInView. - - if (m.m11() < 0.1) { - // There is some strange bug in the QGraphicsScene/QGraphicsView logic which does not calculate the bounding - // matrix of the top level component correctly, if the qgraphicsview is not currently visible. In this case, - // revert to the deafult zoom amount if some too small zoom is detected when trying to fit into view. - m_zoom = s_zoomDefault; - } else { - m_zoom = std::log2(transform().m11()) * s_zoomScale + s_zoomDefault; - } - - setupMatrix(); +void VSRTLView::zoomToFit(const QGraphicsItem *tlc) { + if (tlc == nullptr) { + return; + } + + fitInView(tlc->boundingRect(), Qt::KeepAspectRatio); + const auto m = transform(); + + // When zooming to fit, we want to adjust the current view via. fitInView, and + // then derive the zoom factor corresponding to the scale factor which was set + // by fitInView. + + if (m.m11() < 0.1) { + // There is some strange bug in the QGraphicsScene/QGraphicsView logic which + // does not calculate the bounding matrix of the top level component + // correctly, if the qgraphicsview is not currently visible. In this case, + // revert to the deafult zoom amount if some too small zoom is detected when + // trying to fit into view. + m_zoom = s_zoomDefault; + } else { + m_zoom = std::log2(transform().m11()) * s_zoomScale + s_zoomDefault; + } + + setupMatrix(); } -void VSRTLView::wheelEvent(QWheelEvent* e) { - if (e->modifiers() & Qt::ControlModifier) { - if (e->angleDelta().y() > 0) - zoomIn(s_zoomInterval); - else - zoomOut(s_zoomInterval); - e->accept(); - } else { - QGraphicsView::wheelEvent(e); - } +void VSRTLView::wheelEvent(QWheelEvent *e) { + if (e->modifiers() & Qt::ControlModifier) { + if (e->angleDelta().y() > 0) + zoomIn(s_zoomInterval); + else + zoomOut(s_zoomInterval); + e->accept(); + } else { + QGraphicsView::wheelEvent(e); + } } void VSRTLView::zoomIn(double level) { - m_zoom += level; - setupMatrix(); + m_zoom += level; + setupMatrix(); } void VSRTLView::zoomOut(double level) { - m_zoom -= level; - setupMatrix(); + m_zoom -= level; + setupMatrix(); } void VSRTLView::setupMatrix() { - const double scale = std::pow(2.0, (m_zoom - s_zoomDefault) / s_zoomScale); + const double scale = std::pow(2.0, (m_zoom - s_zoomDefault) / s_zoomScale); - QTransform matrix; - matrix.scale(scale, scale); + QTransform matrix; + matrix.scale(scale, scale); - setTransform(matrix); + setTransform(matrix); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_view.h b/graphics/vsrtl_view.h index 60df819..e318c7d 100644 --- a/graphics/vsrtl_view.h +++ b/graphics/vsrtl_view.h @@ -1,41 +1,41 @@ #ifndef VSRTL_VSRTLVIEW_H #define VSRTL_VSRTLVIEW_H +#include "vsrtl_componentgraphic.h" #include #include -#include "vsrtl_componentgraphic.h" namespace vsrtl { class VSRTLView : public QGraphicsView { - Q_OBJECT + Q_OBJECT - static constexpr double s_zoomDefault = 250.0; - static constexpr double s_zoomScale = 50.0; - static constexpr double s_zoomInterval = 6.0; + static constexpr double s_zoomDefault = 250.0; + static constexpr double s_zoomScale = 50.0; + static constexpr double s_zoomInterval = 6.0; public: - VSRTLView(QWidget* parent); - ComponentGraphic* lookupGraphicForComponent(const SimComponent* c); + VSRTLView(QWidget *parent); + ComponentGraphic *lookupGraphicForComponent(const SimComponent *c); - /** - * @brief zoomToFit - * Adjusts level of zoom to ensure that @p item is visible - */ - void zoomToFit(const QGraphicsItem* item); + /** + * @brief zoomToFit + * Adjusts level of zoom to ensure that @p item is visible + */ + void zoomToFit(const QGraphicsItem *item); protected: - void wheelEvent(QWheelEvent*) override; + void wheelEvent(QWheelEvent *) override; private slots: - void setupMatrix(); - void zoomIn(double level = s_zoomInterval); - void zoomOut(double level = s_zoomInterval); + void setupMatrix(); + void zoomIn(double level = s_zoomInterval); + void zoomOut(double level = s_zoomInterval); private: - QOpenGLWidget* m_renderer; - double m_zoom = s_zoomDefault; + QOpenGLWidget *m_renderer; + double m_zoom = s_zoomDefault; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_VSRTLVIEW_H +#endif // VSRTL_VSRTLVIEW_H diff --git a/graphics/vsrtl_widget.cpp b/graphics/vsrtl_widget.cpp index d32decd..27d0663 100644 --- a/graphics/vsrtl_widget.cpp +++ b/graphics/vsrtl_widget.cpp @@ -13,225 +13,229 @@ #include void initVsrtlResources() { - Q_INIT_RESOURCE(vsrtl_icons); - Q_INIT_RESOURCE(vsrtl_fonts); - - // Initialize fonts - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Thin.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-ThinItalic.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Regular.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-MediumItalic.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Medium.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-LightItalic.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Light.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Italic.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-BoldItalic.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Bold.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Black.ttf"); - QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-BlackItalic.ttf"); + Q_INIT_RESOURCE(vsrtl_icons); + Q_INIT_RESOURCE(vsrtl_fonts); + + // Initialize fonts + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Thin.ttf"); + QFontDatabase::addApplicationFont( + ":/vsrtl_fonts/Roboto/Roboto-ThinItalic.ttf"); + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Regular.ttf"); + QFontDatabase::addApplicationFont( + ":/vsrtl_fonts/Roboto/Roboto-MediumItalic.ttf"); + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Medium.ttf"); + QFontDatabase::addApplicationFont( + ":/vsrtl_fonts/Roboto/Roboto-LightItalic.ttf"); + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Light.ttf"); + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Italic.ttf"); + QFontDatabase::addApplicationFont( + ":/vsrtl_fonts/Roboto/Roboto-BoldItalic.ttf"); + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Bold.ttf"); + QFontDatabase::addApplicationFont(":/vsrtl_fonts/Roboto/Roboto-Black.ttf"); + QFontDatabase::addApplicationFont( + ":/vsrtl_fonts/Roboto/Roboto-BlackItalic.ttf"); } namespace vsrtl { -VSRTLWidget::VSRTLWidget(QWidget* parent) : QWidget(parent), ui(new Ui::VSRTLWidget) { - ui->setupUi(this); - initVsrtlResources(); - - m_view = new VSRTLView(this); - m_view->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); - m_scene = new VSRTLScene(this); - m_view->setScene(m_scene); - ui->viewLayout->addWidget(m_view); - connect(m_scene, &QGraphicsScene::selectionChanged, this, (&VSRTLWidget::handleSceneSelectionChanged)); - - /** - * runFinished will always be emitted asynchronously within the run call. - * When a run is finished, we need to ensure that the graphical view is fully up to date with the underlying - * circuit. This is performed through sync, but ensured to be performed on the main gui thread, and not - * on whatever thread the running was performed on. - */ - connect(this, &VSRTLWidget::runFinished, this, &VSRTLWidget::sync, Qt::QueuedConnection); +VSRTLWidget::VSRTLWidget(QWidget *parent) + : QWidget(parent), ui(new Ui::VSRTLWidget) { + ui->setupUi(this); + initVsrtlResources(); + + m_view = new VSRTLView(this); + m_view->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + m_scene = new VSRTLScene(this); + m_view->setScene(m_scene); + ui->viewLayout->addWidget(m_view); + connect(m_scene, &QGraphicsScene::selectionChanged, this, + (&VSRTLWidget::handleSceneSelectionChanged)); + + /** + * runFinished will always be emitted asynchronously within the run call. + * When a run is finished, we need to ensure that the graphical view is fully + * up to date with the underlying circuit. This is performed through sync, but + * ensured to be performed on the main gui thread, and not on whatever thread + * the running was performed on. + */ + connect(this, &VSRTLWidget::runFinished, this, &VSRTLWidget::sync, + Qt::QueuedConnection); } void VSRTLWidget::clearDesign() { - if (m_topLevelComponent) { - // Clear previous design - delete m_topLevelComponent; - m_topLevelComponent = nullptr; - } - m_design = nullptr; + if (m_topLevelComponent) { + // Clear previous design + delete m_topLevelComponent; + m_topLevelComponent = nullptr; + } + m_design = nullptr; } -void VSRTLWidget::setDesign(SimDesign* design, bool doPlaceAndRoute) { - clearDesign(); - m_design = design; - initializeDesign(doPlaceAndRoute); - setLocked(m_scene->isLocked()); - setDarkmode(m_scene->darkmode()); +void VSRTLWidget::setDesign(SimDesign *design, bool doPlaceAndRoute) { + clearDesign(); + m_design = design; + initializeDesign(doPlaceAndRoute); + setLocked(m_scene->isLocked()); + setDarkmode(m_scene->darkmode()); } -void VSRTLWidget::zoomToFit() { - m_view->zoomToFit(getTopLevelComponent()); -} +void VSRTLWidget::zoomToFit() { m_view->zoomToFit(getTopLevelComponent()); } void VSRTLWidget::setOutputPortValuesVisible(bool visible) { - m_scene->setPortValuesVisibleForType(vsrtl::SimPort::PortType::out, visible); + m_scene->setPortValuesVisibleForType(vsrtl::SimPort::PortType::out, visible); } -void VSRTLWidget::setDarkmode(bool enabled) { - m_scene->setDarkmode(enabled); -} +void VSRTLWidget::setDarkmode(bool enabled) { m_scene->setDarkmode(enabled); } -void VSRTLWidget::setLocked(bool locked) { - m_scene->setLocked(locked); -} +void VSRTLWidget::setLocked(bool locked) { m_scene->setLocked(locked); } -VSRTLWidget::~VSRTLWidget() { - delete ui; -} +VSRTLWidget::~VSRTLWidget() { delete ui; } void VSRTLWidget::handleSceneSelectionChanged() { - std::vector selectedComponents; - std::vector selectedPorts; - const auto selectedItems = m_scene->selectedItems(); - for (auto* i : qAsConst(selectedItems)) { - ComponentGraphic* i_c = dynamic_cast(i); - if (i_c) { - selectedComponents.push_back(i_c->getComponent()); - continue; - } - PortGraphic* i_p = dynamic_cast(i); - if (i_p) { - selectedPorts = i_p->getPort()->getPortsInConnection(); - continue; - } + std::vector selectedComponents; + std::vector selectedPorts; + const auto selectedItems = m_scene->selectedItems(); + for (auto *i : qAsConst(selectedItems)) { + ComponentGraphic *i_c = dynamic_cast(i); + if (i_c) { + selectedComponents.push_back(i_c->getComponent()); + continue; } - - emit componentSelectionChanged(selectedComponents); - emit portSelectionChanged(selectedPorts); -} // namespace vsrtl - -void VSRTLWidget::handleSelectionChanged(const std::vector& selected, - const std::vector& deselected) { - // Block signals from scene to disable selectionChange emission. - m_scene->blockSignals(true); - for (const auto& c : selected) { - auto* c_g = c->getGraphic(); - c_g->setSelected(true); - } - for (const auto& c : deselected) { - auto* c_g = c->getGraphic(); - c_g->setSelected(false); + PortGraphic *i_p = dynamic_cast(i); + if (i_p) { + selectedPorts = i_p->getPort()->getPortsInConnection(); + continue; } - m_scene->blockSignals(false); + } + + emit componentSelectionChanged(selectedComponents); + emit portSelectionChanged(selectedPorts); +} // namespace vsrtl + +void VSRTLWidget::handleSelectionChanged( + const std::vector &selected, + const std::vector &deselected) { + // Block signals from scene to disable selectionChange emission. + m_scene->blockSignals(true); + for (const auto &c : selected) { + auto *c_g = c->getGraphic(); + c_g->setSelected(true); + } + for (const auto &c : deselected) { + auto *c_g = c->getGraphic(); + c_g->setSelected(false); + } + m_scene->blockSignals(false); } void VSRTLWidget::initializeDesign(bool doPlaceAndRoute) { - // Verify the design in case user forgot to - m_design->verifyAndInitialize(); - - // Create a ComponentGraphic for the top component. This will expand the component tree and create graphics for all - // ports, wires etc. within the design. This is done through the initialize call, which must be called after the - // item has been added to the scene. - m_topLevelComponent = new ComponentGraphic(m_design, nullptr); - m_topLevelComponent->initialize(doPlaceAndRoute); - // At this point, all graphic items have been created, and the post scene construction initialization may take - // place. Similar to the initialize call, postSceneConstructionInitialization will recurse through the entire tree - // which is the graphics items in the scene. - m_topLevelComponent->postSceneConstructionInitialize1(); - m_topLevelComponent->postSceneConstructionInitialize2(); - - // Expand top widget - m_topLevelComponent->setExpanded(true); - - // Add top level component to scene at the end. Do _not_ move this before initialization - initialization will be - // massively slowed down if items are modified while already in the scene. - addComponent(m_topLevelComponent); + // Verify the design in case user forgot to + m_design->verifyAndInitialize(); + + // Create a ComponentGraphic for the top component. This will expand the + // component tree and create graphics for all ports, wires etc. within the + // design. This is done through the initialize call, which must be called + // after the item has been added to the scene. + m_topLevelComponent = new ComponentGraphic(m_design, nullptr); + m_topLevelComponent->initialize(doPlaceAndRoute); + // At this point, all graphic items have been created, and the post scene + // construction initialization may take place. Similar to the initialize call, + // postSceneConstructionInitialization will recurse through the entire tree + // which is the graphics items in the scene. + m_topLevelComponent->postSceneConstructionInitialize1(); + m_topLevelComponent->postSceneConstructionInitialize2(); + + // Expand top widget + m_topLevelComponent->setExpanded(true); + + // Add top level component to scene at the end. Do _not_ move this before + // initialization - initialization will be massively slowed down if items are + // modified while already in the scene. + addComponent(m_topLevelComponent); } -void VSRTLWidget::expandAllComponents(ComponentGraphic* fromThis) { - if (fromThis == nullptr) - fromThis = m_topLevelComponent; +void VSRTLWidget::expandAllComponents(ComponentGraphic *fromThis) { + if (fromThis == nullptr) + fromThis = m_topLevelComponent; - fromThis->setExpanded(true); + fromThis->setExpanded(true); - // Components are expanded and routed from leaf nodes and up - for (const auto& sub : fromThis->getGraphicSubcomponents()) - expandAllComponents(sub); + // Components are expanded and routed from leaf nodes and up + for (const auto &sub : fromThis->getGraphicSubcomponents()) + expandAllComponents(sub); - fromThis->placeAndRouteSubcomponents(); + fromThis->placeAndRouteSubcomponents(); } bool VSRTLWidget::isReversible() { - if (!m_design) - return false; + if (!m_design) + return false; - if (m_designCanreverse != m_design->canReverse()) { - // Reverse state just changed, notify listeners - m_designCanreverse = m_design->canReverse(); - emit canReverse(m_designCanreverse); - } - return m_designCanreverse; + if (m_designCanreverse != m_design->canReverse()) { + // Reverse state just changed, notify listeners + m_designCanreverse = m_design->canReverse(); + emit canReverse(m_designCanreverse); + } + return m_designCanreverse; } void VSRTLWidget::clock() { - if (m_design) { - m_design->clock(); - isReversible(); - } + if (m_design) { + m_design->clock(); + isReversible(); + } } void VSRTLWidget::sync() { - // Since the design does not emit signals during running, we need to manually tell all labels to reset their text - // value, given that labels manually must have their text updated (ie. text is not updated in the redraw call). - const auto sceneItems = m_scene->items(); - for (auto* item : qAsConst(sceneItems)) { - if (auto* simobject = dynamic_cast(item)) { - simobject->simUpdateSlot(); - } + // Since the design does not emit signals during running, we need to manually + // tell all labels to reset their text value, given that labels manually must + // have their text updated (ie. text is not updated in the redraw call). + const auto sceneItems = m_scene->items(); + for (auto *item : qAsConst(sceneItems)) { + if (auto *simobject = dynamic_cast(item)) { + simobject->simUpdateSlot(); } + } - m_scene->update(); + m_scene->update(); } -QFuture VSRTLWidget::run(const std::function& cycleFunctor) { - auto future = QtConcurrent::run([=] { - if (m_design) { - m_design->setEnableSignals(false); - if (cycleFunctor) { - while (!m_stop) { - m_design->clock(); - cycleFunctor(); - } - } else { - while (!m_stop) { - m_design->clock(); - } - } - m_stop = false; - m_design->setEnableSignals(true); - - emit runFinished(); +QFuture VSRTLWidget::run(const std::function &cycleFunctor) { + auto future = QtConcurrent::run([=] { + if (m_design) { + m_design->setEnableSignals(false); + if (cycleFunctor) { + while (!m_stop) { + m_design->clock(); + cycleFunctor(); + } + } else { + while (!m_stop) { + m_design->clock(); } - }); - return future; + } + m_stop = false; + m_design->setEnableSignals(true); + + emit runFinished(); + } + }); + return future; } void VSRTLWidget::reverse() { - if (m_design) { - m_design->reverse(); - isReversible(); - } + if (m_design) { + m_design->reverse(); + isReversible(); + } } void VSRTLWidget::reset() { - if (m_design) { - m_design->reset(); - isReversible(); - } + if (m_design) { + m_design->reset(); + isReversible(); + } } -void VSRTLWidget::addComponent(ComponentGraphic* g) { - m_scene->addItem(g); -} -} // namespace vsrtl +void VSRTLWidget::addComponent(ComponentGraphic *g) { m_scene->addItem(g); } +} // namespace vsrtl diff --git a/graphics/vsrtl_widget.h b/graphics/vsrtl_widget.h index 7cca454..4866c9c 100644 --- a/graphics/vsrtl_widget.h +++ b/graphics/vsrtl_widget.h @@ -1,9 +1,9 @@ #ifndef VSRTL_WIDGET_H #define VSRTL_WIDGET_H -#include #include "vsrtl_componentgraphic.h" #include "vsrtl_portgraphic.h" +#include #include @@ -19,79 +19,83 @@ class VSRTLWidget; } class VSRTLWidget : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit VSRTLWidget(QWidget* parent = nullptr); - ~VSRTLWidget(); + explicit VSRTLWidget(QWidget *parent = nullptr); + ~VSRTLWidget(); - void addComponent(ComponentGraphic* g); - void expandAllComponents(ComponentGraphic* fromThis = nullptr); - ComponentGraphic* getTopLevelComponent() { return m_topLevelComponent; } + void addComponent(ComponentGraphic *g); + void expandAllComponents(ComponentGraphic *fromThis = nullptr); + ComponentGraphic *getTopLevelComponent() { return m_topLevelComponent; } - void setDesign(SimDesign* design, bool doPlaceAndRoute = false); - void clearDesign(); - bool isReversible(); + void setDesign(SimDesign *design, bool doPlaceAndRoute = false); + void clearDesign(); + bool isReversible(); - void setOutputPortValuesVisible(bool visible); - void setDarkmode(bool enabled); - void setLocked(bool locked); - void zoomToFit(); + void setOutputPortValuesVisible(bool visible); + void setDarkmode(bool enabled); + void setLocked(bool locked); + void zoomToFit(); - /// Called whenever the state of the simulator and the visualization is out of sync, i.e., after running the - /// processor. Updates the scene and all text items within it to reflect the current state of the processor. - void sync(); + /// Called whenever the state of the simulator and the visualization is out of + /// sync, i.e., after running the processor. Updates the scene and all text + /// items within it to reflect the current state of the processor. + void sync(); public slots: - /** - * @brief run - * Asynchronously run the design until m_stop is asserted. @returns a future which may be watched to monitor run - * finishing. - * Additionally, a functor @param cycleFunctor can be passed to the function. This functor will be - * executed after each clock cycle. Example uses of such functor could be; ie. if simulating a processor, whether - * the prcoessor has hit a breakpoint and running needs to be terminated. - */ - QFuture run(const std::function& cycleFunctor = std::function()); - void stop() { m_stop = true; } - void clock(); - void reset(); - void reverse(); - - // Selections which are imposed on the scene from external objects (ie. selecting items in the netlist) - void handleSelectionChanged(const std::vector& selected, - const std::vector& deselected); + /** + * @brief run + * Asynchronously run the design until m_stop is asserted. @returns a future + * which may be watched to monitor run finishing. Additionally, a functor + * @param cycleFunctor can be passed to the function. This functor will be + * executed after each clock cycle. Example uses of such functor could be; ie. + * if simulating a processor, whether the prcoessor has hit a breakpoint and + * running needs to be terminated. + */ + QFuture + run(const std::function &cycleFunctor = std::function()); + void stop() { m_stop = true; } + void clock(); + void reset(); + void reverse(); + + // Selections which are imposed on the scene from external objects (ie. + // selecting items in the netlist) + void handleSelectionChanged(const std::vector &selected, + const std::vector &deselected); signals: - void runFinished(); + void runFinished(); private slots: signals: - void canReverse(bool); - void componentSelectionChanged(const std::vector&); - void portSelectionChanged(const std::vector&); + void canReverse(bool); + void componentSelectionChanged(const std::vector &); + void portSelectionChanged(const std::vector &); private slots: - void handleSceneSelectionChanged(); + void handleSceneSelectionChanged(); private: - // State variable for reducing the number of emitted canReverse signals - bool m_designCanreverse = false; + // State variable for reducing the number of emitted canReverse signals + bool m_designCanreverse = false; - std::atomic m_stop = false; + std::atomic m_stop = false; - void initializeDesign(bool doPlaceAndRoute); - Ui::VSRTLWidget* ui; + void initializeDesign(bool doPlaceAndRoute); + Ui::VSRTLWidget *ui; - ComponentGraphic* m_topLevelComponent = nullptr; + ComponentGraphic *m_topLevelComponent = nullptr; - VSRTLView* m_view; - VSRTLScene* m_scene; + VSRTLView *m_view; + VSRTLScene *m_scene; - SimDesign* m_design = nullptr; + SimDesign *m_design = nullptr; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_WIDGET_H +#endif // VSRTL_WIDGET_H diff --git a/graphics/vsrtl_wiregraphic.cpp b/graphics/vsrtl_wiregraphic.cpp index f261d11..bf9843e 100644 --- a/graphics/vsrtl_wiregraphic.cpp +++ b/graphics/vsrtl_wiregraphic.cpp @@ -17,214 +17,222 @@ namespace vsrtl { namespace { -unsigned int visibleOutputWires(PortPoint* p) { - unsigned int visibleWires = 0; - for (const auto& w : p->getOutputWires()) { - visibleWires += w->isVisible() && w->isDrawn() ? 1 : 0; - } - return visibleWires; +unsigned int visibleOutputWires(PortPoint *p) { + unsigned int visibleWires = 0; + for (const auto &w : p->getOutputWires()) { + visibleWires += w->isVisible() && w->isDrawn() ? 1 : 0; + } + return visibleWires; } -} // namespace +} // namespace -std::vector getPortParentNameSeq(SimPort* p) { - std::vector seq; - seq.push_back(p->getName()); - seq.push_back(p->getParent()->getName()); - return seq; +std::vector getPortParentNameSeq(SimPort *p) { + std::vector seq; + seq.push_back(p->getName()); + seq.push_back(p->getParent()->getName()); + return seq; } -PortPoint::PortPoint(QGraphicsItem* parent) : GraphicsBaseItem(parent) { - setAcceptHoverEvents(false); - m_portParent = dynamic_cast(parent); +PortPoint::PortPoint(QGraphicsItem *parent) : GraphicsBaseItem(parent) { + setAcceptHoverEvents(false); + m_portParent = dynamic_cast(parent); - m_shape = QPainterPath(); - m_shape.addEllipse({0, 0}, WIRE_WIDTH * 1.5, WIRE_WIDTH * 1.5); - m_shape.setFillRule(Qt::WindingFill); - m_br = m_shape.boundingRect().adjusted(-WIRE_WIDTH, -WIRE_WIDTH, WIRE_WIDTH, WIRE_WIDTH); + m_shape = QPainterPath(); + m_shape.addEllipse({0, 0}, WIRE_WIDTH * 1.5, WIRE_WIDTH * 1.5); + m_shape.setFillRule(Qt::WindingFill); + m_br = m_shape.boundingRect().adjusted(-WIRE_WIDTH, -WIRE_WIDTH, WIRE_WIDTH, + WIRE_WIDTH); } -void PortPoint::modulePositionHasChanged() { - portPosChanged(); -} +void PortPoint::modulePositionHasChanged() { portPosChanged(); } void PortPoint::portPosChanged() { - // Propagate a redraw call to all segments connecting to this - if (m_inputWire) { - m_inputWire->geometryModified(); - } - for (const auto& wire : m_outputWires) { - wire->geometryModified(); - } -} - -QVariant PortPoint::itemChange(GraphicsItemChange change, const QVariant& value) { - if (change == QGraphicsItem::ItemPositionHasChanged) { - portPosChanged(); - } - - return GraphicsBaseItem::itemChange(change, value); -} + // Propagate a redraw call to all segments connecting to this + if (m_inputWire) { + m_inputWire->geometryModified(); + } + for (const auto &wire : m_outputWires) { + wire->geometryModified(); + } +} + +QVariant PortPoint::itemChange(GraphicsItemChange change, + const QVariant &value) { + if (change == QGraphicsItem::ItemPositionHasChanged) { + portPosChanged(); + } -QRectF PortPoint::boundingRect() const { - return m_br; -} -QPainterPath PortPoint::shape() const { - return m_shape; + return GraphicsBaseItem::itemChange(change, value); } -void PortPoint::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*) { - const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); - if (lod < 0.35) - return; - - // Do not draw point when only a single output wire exists, and we are not currently interacting with the point - if (visibleOutputWires(this) <= 1 && m_draggedOnThis == nullptr && - !(option->state & (QStyle::State_Selected | QStyle::State_MouseOver))) { - return; - } - - painter->save(); - QPen pen = getPen(); +QRectF PortPoint::boundingRect() const { return m_br; } +QPainterPath PortPoint::shape() const { return m_shape; } - QColor fillColor = (option->state & QStyle::State_Selected) ? QColorConstants::Yellow : pen.color(); - if (option->state & QStyle::State_MouseOver) - fillColor = fillColor.lighter(125); +void PortPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *) { + const qreal lod = + option->levelOfDetailFromTransform(painter->worldTransform()); + if (lod < 0.35) + return; + // Do not draw point when only a single output wire exists, and we are not + // currently interacting with the point + if (visibleOutputWires(this) <= 1 && m_draggedOnThis == nullptr && + !(option->state & (QStyle::State_Selected | QStyle::State_MouseOver))) { + return; + } + + painter->save(); + QPen pen = getPen(); + + QColor fillColor = (option->state & QStyle::State_Selected) + ? QColorConstants::Yellow + : pen.color(); + if (option->state & QStyle::State_MouseOver) + fillColor = fillColor.lighter(125); + + painter->setPen(pen); + if (option->state & QStyle::State_Selected) { + pen.setWidth(1); + painter->drawPath(shape()); + } + painter->fillPath(shape(), + QBrush(fillColor.darker( + (option->state & QStyle::State_Sunken) ? 120 : 100))); + + if (m_draggedOnThis != nullptr) { + pen.setColor(Qt::red); painter->setPen(pen); - if (option->state & QStyle::State_Selected) { - pen.setWidth(1); - painter->drawPath(shape()); - } - painter->fillPath(shape(), QBrush(fillColor.darker((option->state & QStyle::State_Sunken) ? 120 : 100))); - - if (m_draggedOnThis != nullptr) { - pen.setColor(Qt::red); - painter->setPen(pen); - painter->setBrush(Qt::transparent); - painter->drawRect( - shape().boundingRect().adjusted(-WIRE_WIDTH / 2, -WIRE_WIDTH / 2, WIRE_WIDTH / 2, WIRE_WIDTH / 2)); - } - painter->restore(); + painter->setBrush(Qt::transparent); + painter->drawRect(shape().boundingRect().adjusted( + -WIRE_WIDTH / 2, -WIRE_WIDTH / 2, WIRE_WIDTH / 2, WIRE_WIDTH / 2)); + } + painter->restore(); } -const QPen& PortPoint::getPen() { - // If getPen is called on a PortPoint, it is required that the parent item is a PortGraphic. PortGraphics are the - // only classes which should instatiate a PortPoint. - Q_ASSERT(m_portParent); - return m_portParent->getPen(); +const QPen &PortPoint::getPen() { + // If getPen is called on a PortPoint, it is required that the parent item is + // a PortGraphic. PortGraphics are the only classes which should instatiate a + // PortPoint. + Q_ASSERT(m_portParent); + return m_portParent->getPen(); } -void PortPoint::removeOutputWire(WireSegment* wire) { - auto iter = std::find(m_outputWires.begin(), m_outputWires.end(), wire); - Q_ASSERT(iter != m_outputWires.end()); - m_outputWires.erase(iter); +void PortPoint::removeOutputWire(WireSegment *wire) { + auto iter = std::find(m_outputWires.begin(), m_outputWires.end(), wire); + Q_ASSERT(iter != m_outputWires.end()); + m_outputWires.erase(iter); } -bool WirePoint::canMergeWith(WirePoint* point) { - return m_parent->canMergePoints(this, point) != WireGraphic::MergeType::CannotMerge; +bool WirePoint::canMergeWith(WirePoint *point) { + return m_parent->canMergePoints(this, point) != + WireGraphic::MergeType::CannotMerge; } -void WirePoint::pointDrop(WirePoint* point) { - if (!canMergeWith(point)) - return; - - m_parent->mergePoints(this, point); -} +void WirePoint::pointDrop(WirePoint *point) { + if (!canMergeWith(point)) + return; -void WirePoint::pointDragEnter(WirePoint* point) { - if (m_parent->canMergePoints(this, point) != WireGraphic::MergeType::CannotMerge) { - m_draggedOnThis = point; - update(); - } + m_parent->mergePoints(this, point); } -void WirePoint::pointDragLeave(WirePoint*) { - m_draggedOnThis = nullptr; +void WirePoint::pointDragEnter(WirePoint *point) { + if (m_parent->canMergePoints(this, point) != + WireGraphic::MergeType::CannotMerge) { + m_draggedOnThis = point; update(); + } } -WirePoint::WirePoint(WireGraphic* parent) : PortPoint(parent), m_parent(parent) { - setFlag(ItemIsSelectable, true); - setAcceptHoverEvents(true); - setMoveable(); +void WirePoint::pointDragLeave(WirePoint *) { + m_draggedOnThis = nullptr; + update(); } -const QPen& WirePoint::getPen() { - return m_parent->getPen(); +WirePoint::WirePoint(WireGraphic *parent) + : PortPoint(parent), m_parent(parent) { + setFlag(ItemIsSelectable, true); + setAcceptHoverEvents(true); + setMoveable(); } -QVariant WirePoint::itemChange(GraphicsItemChange change, const QVariant& value) { - if (change == QGraphicsItem::ItemPositionChange) { - // Disallow WirePoint moving when scene is locked - if (isLocked() && !m_parent->isSerializing()) - return pos(); +const QPen &WirePoint::getPen() { return m_parent->getPen(); } - // Snap to grid - QPointF newPos = value.toPointF(); - qreal x = round(newPos.x() / GRID_SIZE) * GRID_SIZE; - qreal y = round(newPos.y() / GRID_SIZE) * GRID_SIZE; - return QPointF(x, y); - } +QVariant WirePoint::itemChange(GraphicsItemChange change, + const QVariant &value) { + if (change == QGraphicsItem::ItemPositionChange) { + // Disallow WirePoint moving when scene is locked + if (isLocked() && !m_parent->isSerializing()) + return pos(); + + // Snap to grid + QPointF newPos = value.toPointF(); + qreal x = round(newPos.x() / GRID_SIZE) * GRID_SIZE; + qreal y = round(newPos.y() / GRID_SIZE) * GRID_SIZE; + return QPointF(x, y); + } - return PortPoint::itemChange(change, value); + return PortPoint::itemChange(change, value); } -void WirePoint::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - if (isLocked()) - return; +void WirePoint::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + if (isLocked()) + return; - QMenu menu; - auto removePointAction = menu.addAction("Remove point"); - connect(removePointAction, &QAction::triggered, [this](bool) { m_parent->removeWirePoint(this); }); - menu.exec(event->screenPos()); + QMenu menu; + auto removePointAction = menu.addAction("Remove point"); + connect(removePointAction, &QAction::triggered, + [this](bool) { m_parent->removeWirePoint(this); }); + menu.exec(event->screenPos()); } // -------------------------------------------------------------------------------- -WireSegment::WireSegment(WireGraphic* parent) : GraphicsBaseItem(parent), m_parent(parent) { - setAcceptHoverEvents(true); - setFlag(ItemIsSelectable); +WireSegment::WireSegment(WireGraphic *parent) + : GraphicsBaseItem(parent), m_parent(parent) { + setAcceptHoverEvents(true); + setFlag(ItemIsSelectable); } -void WireSegment::setStart(PortPoint* start) { - if (m_start) - m_start->removeOutputWire(this); - m_start = start; - if (m_start) - m_start->addOutputWire(this); +void WireSegment::setStart(PortPoint *start) { + if (m_start) + m_start->removeOutputWire(this); + m_start = start; + if (m_start) + m_start->addOutputWire(this); - geometryModified(); + geometryModified(); } -void WireSegment::setEnd(PortPoint* end) { - if (m_end) - m_end->clearInputWire(); - m_end = end; - if (m_end) - m_end->setInputWire(this); +void WireSegment::setEnd(PortPoint *end) { + if (m_end) + m_end->clearInputWire(); + m_end = end; + if (m_end) + m_end->setInputWire(this); - geometryModified(); + geometryModified(); } QLineF WireSegment::getLine() const { - if (m_start == nullptr || m_end == nullptr) - return QLine(); + if (m_start == nullptr || m_end == nullptr) + return QLine(); - QPointF startPos; - if (auto* p = dynamic_cast(m_start->parentItem())) { - startPos = mapFromItem(p, p->getOutputPoint()); - } else { - startPos = mapFromItem(m_start->parentItem(), m_start->pos()); - } + QPointF startPos; + if (auto *p = dynamic_cast(m_start->parentItem())) { + startPos = mapFromItem(p, p->getOutputPoint()); + } else { + startPos = mapFromItem(m_start->parentItem(), m_start->pos()); + } - QPointF endPos; - if (auto* p = dynamic_cast(m_end->parentItem())) { - endPos = mapFromItem(p, p->getInputPoint()); - } else { - endPos = mapFromItem(m_end->parentItem(), m_end->pos()); - } + QPointF endPos; + if (auto *p = dynamic_cast(m_end->parentItem())) { + endPos = mapFromItem(p, p->getInputPoint()); + } else { + endPos = mapFromItem(m_end->parentItem(), m_end->pos()); + } - return QLineF(startPos, endPos); + return QLineF(startPos, endPos); } /** @@ -232,403 +240,441 @@ QLineF WireSegment::getLine() const { * Given a line, returns a box of width 2xsideWidth around the line */ QPolygonF expandLine(const QLineF line, const qreal sideWidth) { - const auto norm1 = line.normalVector(); - const auto norm2 = norm1.translated(line.p2() - line.p1()); + const auto norm1 = line.normalVector(); + const auto norm2 = norm1.translated(line.p2() - line.p1()); - const auto lineWidthParam = qreal(sideWidth) / line.length(); + const auto lineWidthParam = qreal(sideWidth) / line.length(); - const QPointF rp1 = norm1.pointAt(lineWidthParam); - const QPointF rp2 = norm2.pointAt(lineWidthParam); - const QPointF rp3 = norm2.pointAt(-lineWidthParam); - const QPointF rp4 = norm1.pointAt(-lineWidthParam); - return QPolygonF({rp1, rp2, rp3, rp4}); + const QPointF rp1 = norm1.pointAt(lineWidthParam); + const QPointF rp2 = norm2.pointAt(lineWidthParam); + const QPointF rp3 = norm2.pointAt(-lineWidthParam); + const QPointF rp4 = norm1.pointAt(-lineWidthParam); + return QPolygonF({rp1, rp2, rp3, rp4}); } void WireSegment::invalidate() { - setStart(nullptr); - setEnd(nullptr); + setStart(nullptr); + setEnd(nullptr); } void WireSegment::geometryModified() { - QPointF p1_mod, p2_mod; - prepareGeometryChange(); - if (!isValid()) { - goto geometryModified_invalidate; - } - - m_cachedLine = getLine(); - if (m_cachedLine.p1() == m_cachedLine.p2()) { - goto geometryModified_invalidate; - } - - // The shape of the line is defined as a box of WIRE_WIDTH around the entire line between the two end points. Except - // for WIRE_WIDTH/2 at the edges of the line, wherein we would like to be able to click on a potential WirePoint. - m_cachedShape = QPainterPath(); - p1_mod = m_cachedLine.pointAt(WIRE_WIDTH / m_cachedLine.length()); - p2_mod = m_cachedLine.pointAt((m_cachedLine.length() - WIRE_WIDTH) / m_cachedLine.length()); - m_cachedShape.addPolygon(expandLine(QLineF(p1_mod, p2_mod), WIRE_WIDTH)); - - // The bounding rect is an equivalently expanded box around the current line, but with full length. - m_cachedBoundingRect = expandLine(m_cachedLine, WIRE_WIDTH).boundingRect(); - return; + QPointF p1_mod, p2_mod; + prepareGeometryChange(); + if (!isValid()) { + goto geometryModified_invalidate; + } + + m_cachedLine = getLine(); + if (m_cachedLine.p1() == m_cachedLine.p2()) { + goto geometryModified_invalidate; + } + + // The shape of the line is defined as a box of WIRE_WIDTH around the entire + // line between the two end points. Except for WIRE_WIDTH/2 at the edges of + // the line, wherein we would like to be able to click on a potential + // WirePoint. + m_cachedShape = QPainterPath(); + p1_mod = m_cachedLine.pointAt(WIRE_WIDTH / m_cachedLine.length()); + p2_mod = m_cachedLine.pointAt((m_cachedLine.length() - WIRE_WIDTH) / + m_cachedLine.length()); + m_cachedShape.addPolygon(expandLine(QLineF(p1_mod, p2_mod), WIRE_WIDTH)); + + // The bounding rect is an equivalently expanded box around the current line, + // but with full length. + m_cachedBoundingRect = expandLine(m_cachedLine, WIRE_WIDTH).boundingRect(); + return; geometryModified_invalidate: - m_cachedShape = QPainterPath(); - m_cachedLine = QLine(); - m_cachedBoundingRect = QRectF(); - return; + m_cachedShape = QPainterPath(); + m_cachedLine = QLine(); + m_cachedBoundingRect = QRectF(); + return; } QPainterPath WireSegment::shape() const { - if (!isValid()) - return QPainterPath(); + if (!isValid()) + return QPainterPath(); - return m_cachedShape; + return m_cachedShape; } QRectF WireSegment::boundingRect() const { - if (!isValid()) - return QRectF(); + if (!isValid()) + return QRectF(); - return m_cachedBoundingRect; + return m_cachedBoundingRect; } -void WireSegment::hoverMoveEvent(QGraphicsSceneHoverEvent*) { - setToolTip(m_parent->getFromPort()->getTooltipString()); +void WireSegment::hoverMoveEvent(QGraphicsSceneHoverEvent *) { + setToolTip(m_parent->getFromPort()->getTooltipString()); } bool WireSegment::isValid() const { - return m_start != nullptr && m_end != nullptr; + return m_start != nullptr && m_end != nullptr; } bool WireSegment::isDrawn() const { - return isValid() && m_start->isVisible() && m_end->isVisible(); + return isValid() && m_start->isVisible() && m_end->isVisible(); } -void WireSegment::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { - if (!isDrawn()) - return; +void WireSegment::paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) { + if (!isDrawn()) + return; - painter->save(); - painter->setPen(m_parent->getPen()); - painter->drawLine(m_cachedLine); - painter->restore(); + painter->save(); + painter->setPen(m_parent->getPen()); + painter->drawLine(m_cachedLine); + painter->restore(); #ifdef VSRTL_DEBUG_DRAW - DRAW_BOUNDING_RECT(painter) + DRAW_BOUNDING_RECT(painter) #endif } -void WireSegment::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - if (isLocked()) - return; +void WireSegment::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + if (isLocked()) + return; - // Todo: allow adding points. Should possibly just be done by a click on the wire - QMenu menu; - auto createPointAction = menu.addAction("Add wire point"); - connect(createPointAction, &QAction::triggered, - [this, &event](bool) { m_parent->createWirePointOnSeg(event->scenePos(), this); }); - menu.exec(event->screenPos()); + // Todo: allow adding points. Should possibly just be done by a click on the + // wire + QMenu menu; + auto createPointAction = menu.addAction("Add wire point"); + connect(createPointAction, &QAction::triggered, [this, &event](bool) { + m_parent->createWirePointOnSeg(event->scenePos(), this); + }); + menu.exec(event->screenPos()); } -QVariant WireSegment::itemChange(GraphicsItemChange change, const QVariant& value) { - if (change == QGraphicsItem::ItemSelectedChange) { - m_parent->getFromPort()->itemChange(change, value); - } +QVariant WireSegment::itemChange(GraphicsItemChange change, + const QVariant &value) { + if (change == QGraphicsItem::ItemSelectedChange) { + m_parent->getFromPort()->itemChange(change, value); + } - return GraphicsBaseItem::itemChange(change, value); + return GraphicsBaseItem::itemChange(change, value); } // -------------------------------------------------------------------------------- -WirePoint* WireGraphic::createWirePoint() { - // Create new point managed by this graphic. - auto* point = new WirePoint(this); - m_points.insert(point); - return point; +WirePoint *WireGraphic::createWirePoint() { + // Create new point managed by this graphic. + auto *point = new WirePoint(this); + m_points.insert(point); + return point; } -void WireGraphic::moveWirePoint(PortPoint* point, const QPointF scenePos) { - // Move new point to its creation position - point->setPos(mapToItem(this, mapFromScene(scenePos))); +void WireGraphic::moveWirePoint(PortPoint *point, const QPointF scenePos) { + // Move new point to its creation position + point->setPos(mapToItem(this, mapFromScene(scenePos))); } -WireSegment* WireGraphic::createSegment(PortPoint* start, PortPoint* end) { - auto* newSeg = new WireSegment(this); - newSeg->setStart(start); - newSeg->setEnd(end); - m_wires.insert(newSeg); - return newSeg; +WireSegment *WireGraphic::createSegment(PortPoint *start, PortPoint *end) { + auto *newSeg = new WireSegment(this); + newSeg->setStart(start); + newSeg->setEnd(end); + m_wires.insert(newSeg); + return newSeg; } -std::pair WireGraphic::createWirePointOnSeg(const QPointF scenePos, WireSegment* onSegment) { - const auto& endPoint = onSegment->getEnd(); +std::pair +WireGraphic::createWirePointOnSeg(const QPointF scenePos, + WireSegment *onSegment) { + const auto &endPoint = onSegment->getEnd(); - auto* newPoint = createWirePoint(); + auto *newPoint = createWirePoint(); - // Set the wire segment of which this point was created, to terminate at the new point - onSegment->setEnd(newPoint); + // Set the wire segment of which this point was created, to terminate at the + // new point + onSegment->setEnd(newPoint); - // Create wire segment between new point and previous end point - auto* newSeg = createSegment(newPoint, endPoint); + // Create wire segment between new point and previous end point + auto *newSeg = createSegment(newPoint, endPoint); - // Move new point to its creation position - moveWirePoint(newPoint, scenePos); + // Move new point to its creation position + moveWirePoint(newPoint, scenePos); - // Return a pointer to the newly created wire segment and point - return {newPoint, newSeg}; + // Return a pointer to the newly created wire segment and point + return {newPoint, newSeg}; } -void WireGraphic::removeWirePoint(WirePoint* pointToRemove) { - prepareGeometryChange(); - Q_ASSERT(std::find(m_points.begin(), m_points.end(), pointToRemove) != m_points.end()); - auto* wireToRemove = pointToRemove->getInputWire(); - auto* newStartPoint = wireToRemove->getStart(); - Q_ASSERT(newStartPoint != nullptr); - Q_ASSERT(newStartPoint != pointToRemove); +void WireGraphic::removeWirePoint(WirePoint *pointToRemove) { + prepareGeometryChange(); + Q_ASSERT(std::find(m_points.begin(), m_points.end(), pointToRemove) != + m_points.end()); + auto *wireToRemove = pointToRemove->getInputWire(); + auto *newStartPoint = wireToRemove->getStart(); + Q_ASSERT(newStartPoint != nullptr); + Q_ASSERT(newStartPoint != pointToRemove); - // Set all wires previously connecting to the input wire to connect with the new start point - for (auto& wire : pointToRemove->getOutputWires()) { - Q_ASSERT(wire->getStart() == pointToRemove); - wire->setStart(newStartPoint); - } - Q_ASSERT(pointToRemove->getOutputWires().size() == 0); + // Set all wires previously connecting to the input wire to connect with the + // new start point + for (auto &wire : pointToRemove->getOutputWires()) { + Q_ASSERT(wire->getStart() == pointToRemove); + wire->setStart(newStartPoint); + } + Q_ASSERT(pointToRemove->getOutputWires().size() == 0); - // Delete the (now defunct) wire between the new start point and the point to be removed - auto iter = std::find(m_wires.begin(), m_wires.end(), wireToRemove); - Q_ASSERT(iter != m_wires.end()); - m_wires.erase(iter); - wireToRemove->invalidate(); - delete wireToRemove; + // Delete the (now defunct) wire between the new start point and the point to + // be removed + auto iter = std::find(m_wires.begin(), m_wires.end(), wireToRemove); + Q_ASSERT(iter != m_wires.end()); + m_wires.erase(iter); + wireToRemove->invalidate(); + delete wireToRemove; - // Finally, delete the point. At this point, no wires should be referencing the point - Q_ASSERT(pointToRemove->getInputWire() == nullptr && pointToRemove->getOutputWires().empty()); - auto p_iter = std::find(m_points.begin(), m_points.end(), pointToRemove); - Q_ASSERT(p_iter != m_points.end()); - m_points.erase(p_iter); - pointToRemove->deleteLater(); + // Finally, delete the point. At this point, no wires should be referencing + // the point + Q_ASSERT(pointToRemove->getInputWire() == nullptr && + pointToRemove->getOutputWires().empty()); + auto p_iter = std::find(m_points.begin(), m_points.end(), pointToRemove); + Q_ASSERT(p_iter != m_points.end()); + m_points.erase(p_iter); + pointToRemove->deleteLater(); } -WireGraphic::WireGraphic(ComponentGraphic* parent, PortGraphic* from, const std::vector& to, WireType type) - : GraphicsBaseItem(parent), m_parent(parent), m_fromPort(from), m_toPorts(to), m_type(type) { - m_parent->registerWire(this); - setFlag(QGraphicsItem::ItemHasNoContents, true); +WireGraphic::WireGraphic(ComponentGraphic *parent, PortGraphic *from, + const std::vector &to, WireType type) + : GraphicsBaseItem(parent), m_parent(parent), m_fromPort(from), + m_toPorts(to), m_type(type) { + m_parent->registerWire(this); + setFlag(QGraphicsItem::ItemHasNoContents, true); } -bool WireGraphic::managesPoint(WirePoint* point) const { - return std::find(m_points.begin(), m_points.end(), point) != m_points.end(); +bool WireGraphic::managesPoint(WirePoint *point) const { + return std::find(m_points.begin(), m_points.end(), point) != m_points.end(); } -void WireGraphic::mergePoints(WirePoint* base, WirePoint* toMerge) { - const auto mergeType = canMergePoints(base, toMerge); - Q_ASSERT(mergeType != MergeType::CannotMerge); +void WireGraphic::mergePoints(WirePoint *base, WirePoint *toMerge) { + const auto mergeType = canMergePoints(base, toMerge); + Q_ASSERT(mergeType != MergeType::CannotMerge); - if (mergeType == MergeType::MergeSourceWithSink) { - auto* tmp = base; - base = toMerge; - toMerge = tmp; - } + if (mergeType == MergeType::MergeSourceWithSink) { + auto *tmp = base; + base = toMerge; + toMerge = tmp; + } - // Move all output wires of the point to merge, to have their source being the base point - for (const auto& wire : toMerge->getOutputWires()) { - wire->setStart(base); - } + // Move all output wires of the point to merge, to have their source being the + // base point + for (const auto &wire : toMerge->getOutputWires()) { + wire->setStart(base); + } - removeWirePoint(toMerge); + removeWirePoint(toMerge); } void WireGraphic::clearWirePoints() { - // removeWirePoint modifies m_points. Iterate over a copy. - auto pointsCopy = m_points; - for (auto& p : pointsCopy) { - removeWirePoint(p); - } + // removeWirePoint modifies m_points. Iterate over a copy. + auto pointsCopy = m_points; + for (auto &p : pointsCopy) { + removeWirePoint(p); + } } void WireGraphic::clearWires() { - for (const auto& w : m_wires) { - w->invalidate(); - delete w; - } - m_wires.clear(); + for (const auto &w : m_wires) { + w->invalidate(); + delete w; + } + m_wires.clear(); } /** * @brief WireGraphic::canMergePoints * Points which are adjacent may be merged. - * Points which both have their input wires connected to the source port may be merged. + * Points which both have their input wires connected to the source port may be + * merged. */ -WireGraphic::MergeType WireGraphic::canMergePoints(WirePoint* base, WirePoint* toMerge) const { - if (!managesPoint(base) || !managesPoint(toMerge)) - return MergeType::CannotMerge; +WireGraphic::MergeType WireGraphic::canMergePoints(WirePoint *base, + WirePoint *toMerge) const { + if (!managesPoint(base) || !managesPoint(toMerge)) + return MergeType::CannotMerge; - auto* baseptr = static_cast(base); + auto *baseptr = static_cast(base); - // is toMerge fed by base? - if (baseptr == toMerge->getInputWire()->getStart()) - return MergeType::MergeSinkWithSource; + // is toMerge fed by base? + if (baseptr == toMerge->getInputWire()->getStart()) + return MergeType::MergeSinkWithSource; - // does toMerge feed into base? - for (const auto& wire : toMerge->getOutputWires()) { - if (wire->getEnd() == baseptr) - return MergeType::MergeSourceWithSink; - } + // does toMerge feed into base? + for (const auto &wire : toMerge->getOutputWires()) { + if (wire->getEnd() == baseptr) + return MergeType::MergeSourceWithSink; + } - // is toMerge and base fed by the same source port? - if (base->getInputWire()->getStart() == toMerge->getInputWire()->getStart()) - return MergeType::MergeParallelSinks; + // is toMerge and base fed by the same source port? + if (base->getInputWire()->getStart() == toMerge->getInputWire()->getStart()) + return MergeType::MergeParallelSinks; - return MergeType::CannotMerge; + return MergeType::CannotMerge; } -void WireGraphic::createRectilinearSegments(PortPoint* start, PortPoint* end) { - // 1. Create the initial segment between the two terminating points - auto* seg = createSegment(start, end); - - // 2. Determine intermediate rectilinear points - auto line = seg->getLine().toLine(); - if ((line.y1() == line.y2() && line.x1() <= line.x2()) || line.x1() == line.x2()) { - // Nothing to do, already linear and going from left to right - return; - } - - QPoint intermediate1, intermediate2; - bool createTwoPoints = true; +void WireGraphic::createRectilinearSegments(PortPoint *start, PortPoint *end) { + // 1. Create the initial segment between the two terminating points + auto *seg = createSegment(start, end); + // 2. Determine intermediate rectilinear points + auto line = seg->getLine().toLine(); + if ((line.y1() == line.y2() && line.x1() <= line.x2()) || + line.x1() == line.x2()) { + // Nothing to do, already linear and going from left to right + return; + } + + QPoint intermediate1, intermediate2; + bool createTwoPoints = true; + + if (line.x1() < line.x2()) { + // left to right wire, route directly + intermediate1 = {line.x1() + line.dx() / 2, line.y1()}; + intermediate2 = {line.x1() + line.dx() / 2, line.y2()}; + } else if (dynamic_cast(start->parentItem()) && + dynamic_cast(end->parentItem())) { + // Routing between two components + // Route underneath the source and destination components + auto *compSource = + dynamic_cast(start->parentItem()->parentItem()); + auto *compDst = + dynamic_cast(end->parentItem()->parentItem()); + + QRectF sourceRect = mapRectFromItem(compSource, compSource->boundingRect()); + QRectF destRect = mapRectFromItem(compDst, compDst->boundingRect()); + + const int y = static_cast(sourceRect.bottom() < destRect.bottom() + ? sourceRect.bottom() + : destRect.bottom()); + intermediate1 = QPoint{line.x1(), y}; + intermediate2 = QPoint{line.x2(), y}; + } else { + // Routing between wire points, just create a single intermediate point + createTwoPoints = false; if (line.x1() < line.x2()) { - // left to right wire, route directly - intermediate1 = {line.x1() + line.dx() / 2, line.y1()}; - intermediate2 = {line.x1() + line.dx() / 2, line.y2()}; - } else if (dynamic_cast(start->parentItem()) && dynamic_cast(end->parentItem())) { - // Routing between two components - // Route underneath the source and destination components - auto* compSource = dynamic_cast(start->parentItem()->parentItem()); - auto* compDst = dynamic_cast(end->parentItem()->parentItem()); - - QRectF sourceRect = mapRectFromItem(compSource, compSource->boundingRect()); - QRectF destRect = mapRectFromItem(compDst, compDst->boundingRect()); - - const int y = - static_cast(sourceRect.bottom() < destRect.bottom() ? sourceRect.bottom() : destRect.bottom()); - intermediate1 = QPoint{line.x1(), y}; - intermediate2 = QPoint{line.x2(), y}; + intermediate1 = QPoint{line.x1(), line.y2()}; } else { - // Routing between wire points, just create a single intermediate point - createTwoPoints = false; - if (line.x1() < line.x2()) { - intermediate1 = QPoint{line.x1(), line.y2()}; - } else { - intermediate1 = QPoint{line.x2(), line.y1()}; - } + intermediate1 = QPoint{line.x2(), line.y1()}; } - // 3. Create points on wire segments - auto pointAndSeg = createWirePointOnSeg(mapToScene(intermediate1), seg); - if (createTwoPoints) - createWirePointOnSeg(mapToScene(intermediate2), pointAndSeg.second); + } + // 3. Create points on wire segments + auto pointAndSeg = createWirePointOnSeg(mapToScene(intermediate1), seg); + if (createTwoPoints) + createWirePointOnSeg(mapToScene(intermediate2), pointAndSeg.second); } /** * @brief WireGraphic::setWiresVisibleToPort - * Used when a component graphic toggles its visibility. In this case, wires and points which traverse towards at an - * input port to that component should be hidden + * Used when a component graphic toggles its visibility. In this case, wires and + * points which traverse towards at an input port to that component should be + * hidden * @param p port * @param visible */ -void WireGraphic::setWiresVisibleToPort(const PortPoint* p, bool visible) { - /// Find segment which terminates in @param p - auto iter = std::find_if(m_wires.begin(), m_wires.end(), [p](WireSegment* wire) { return wire->getEnd() == p; }); - Q_ASSERT(iter != m_wires.end()); - - WireSegment* seg = *iter; - PortPoint* start = seg->getStart(); - while (seg->isVisible() ^ visible) { - seg->setVisible(visible); - // Recurse? - bool recurse = false; - if (visible) { - /// All wire points between this WireGraphic and the root port should be enabled. - recurse = dynamic_cast(start); - } else { - /// Traverse the sequence of wires from @param p towards @this source port, until we encounter a WirePoint - /// which has more than 0 >visible< wire enabled - recurse = dynamic_cast(start) && (visibleOutputWires(start) == 0); - } - if (recurse) { - start->setVisible(visible); - seg = start->getInputWire(); - start = seg->getStart(); - } else { - break; - } +void WireGraphic::setWiresVisibleToPort(const PortPoint *p, bool visible) { + /// Find segment which terminates in @param p + auto iter = + std::find_if(m_wires.begin(), m_wires.end(), + [p](WireSegment *wire) { return wire->getEnd() == p; }); + Q_ASSERT(iter != m_wires.end()); + + WireSegment *seg = *iter; + PortPoint *start = seg->getStart(); + while (seg->isVisible() ^ visible) { + seg->setVisible(visible); + // Recurse? + bool recurse = false; + if (visible) { + /// All wire points between this WireGraphic and the root port should be + /// enabled. + recurse = dynamic_cast(start); + } else { + /// Traverse the sequence of wires from @param p towards @this source + /// port, until we encounter a WirePoint which has more than 0 >visible< + /// wire enabled + recurse = + dynamic_cast(start) && (visibleOutputWires(start) == 0); } + if (recurse) { + start->setVisible(visible); + seg = start->getInputWire(); + start = seg->getStart(); + } else { + break; + } + } } -SimComponent* WireGraphic::getParentComponent() const { - return m_parent->getComponent(); +SimComponent *WireGraphic::getParentComponent() const { + return m_parent->getComponent(); } /** * @brief WireGraphic::postSceneConstructionInitialize1 - * With all ports and components created during circuit construction, wires may now register themselves with their - * attached input- and output ports + * With all ports and components created during circuit construction, wires may + * now register themselves with their attached input- and output ports */ void WireGraphic::postSceneConstructionInitialize1() { - std::function addGraphicToPort = [&](SimPort* portPtr) { - if (auto* portGraphic = portPtr->getGraphic()) { - m_toGraphicPorts.push_back(portGraphic); - } - if (portPtr->type() == vsrtl::SimPort::PortType::signal) { - for (auto toPort : portPtr->getOutputPorts()) { - // Signals are only relevant for the underlying circuit - traverse to the output ports of the signal - addGraphicToPort(toPort); - } - } - }; - - for (const auto& toPort : m_toPorts) { + std::function addGraphicToPort = [&](SimPort *portPtr) { + if (auto *portGraphic = portPtr->getGraphic()) { + m_toGraphicPorts.push_back(portGraphic); + } + if (portPtr->type() == vsrtl::SimPort::PortType::signal) { + for (auto toPort : portPtr->getOutputPorts()) { + // Signals are only relevant for the underlying circuit - traverse to + // the output ports of the signal addGraphicToPort(toPort); + } } - - // Make the wire destination ports aware of this WireGraphic, and create wire segments between all source and sink - // ports. - for (const auto& sink : m_toGraphicPorts) { - sink->setInputWire(this); - // Create a rectilinear segment between the the closest point managed by this wire and the sink destination - std::pair fromPoint; - const QPointF sinkPos = sink->getPortPoint(vsrtl::SimPort::PortType::in)->scenePos(); - fromPoint.first = - (sinkPos - m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out)->scenePos()).manhattanLength(); - fromPoint.second = m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out); - for (const auto& p : m_points) { - const qreal len = (sinkPos - p->scenePos()).manhattanLength(); - if (len < fromPoint.first) { - fromPoint = {len, p}; - } - } - createRectilinearSegments(fromPoint.second, sink->getPortPoint(vsrtl::SimPort::PortType::in)); + }; + + for (const auto &toPort : m_toPorts) { + addGraphicToPort(toPort); + } + + // Make the wire destination ports aware of this WireGraphic, and create wire + // segments between all source and sink ports. + for (const auto &sink : m_toGraphicPorts) { + sink->setInputWire(this); + // Create a rectilinear segment between the the closest point managed by + // this wire and the sink destination + std::pair fromPoint; + const QPointF sinkPos = + sink->getPortPoint(vsrtl::SimPort::PortType::in)->scenePos(); + fromPoint.first = + (sinkPos - + m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out)->scenePos()) + .manhattanLength(); + fromPoint.second = m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out); + for (const auto &p : m_points) { + const qreal len = (sinkPos - p->scenePos()).manhattanLength(); + if (len < fromPoint.first) { + fromPoint = {len, p}; + } } + createRectilinearSegments(fromPoint.second, + sink->getPortPoint(vsrtl::SimPort::PortType::in)); + } - GraphicsBaseItem::postSceneConstructionInitialize1(); + GraphicsBaseItem::postSceneConstructionInitialize1(); } void WireGraphic::postSerializeInit() { - if (m_fromPort) { - m_fromPort->setUserVisible(!m_fromPort->userHidden()); - } + if (m_fromPort) { + m_fromPort->setUserVisible(!m_fromPort->userHidden()); + } - for (const auto& p : m_toGraphicPorts) { - p->setUserVisible(!p->userHidden()); - } + for (const auto &p : m_toGraphicPorts) { + p->setUserVisible(!p->userHidden()); + } - // Geometry updating is disabled whilst serializing. Make all wires update geometry after serialization has - // completed. - for (const auto& wire : m_wires) { - wire->geometryModified(); - } + // Geometry updating is disabled whilst serializing. Make all wires update + // geometry after serialization has completed. + for (const auto &wire : m_wires) { + wire->geometryModified(); + } } -const QPen& WireGraphic::getPen() { - // propagate the source port pen to callers - return m_fromPort->getPen(); +const QPen &WireGraphic::getPen() { + // propagate the source port pen to callers + return m_fromPort->getPen(); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/graphics/vsrtl_wiregraphic.h b/graphics/vsrtl_wiregraphic.h index 664d5ce..6155fa2 100644 --- a/graphics/vsrtl_wiregraphic.h +++ b/graphics/vsrtl_wiregraphic.h @@ -22,323 +22,352 @@ class WireGraphic; class WireSegment; class ComponentGraphic; -std::vector getPortParentNameSeq(SimPort* p); +std::vector getPortParentNameSeq(SimPort *p); /** * @brief The PortPoint class - * Base class for wire graphic points. This is the point type which is assigned to PortGraphics. They are not moveable, - * but provide an interface between moveable WirePoints (on WireSegments) and immovable PortPoints, on ports. + * Base class for wire graphic points. This is the point type which is assigned + * to PortGraphics. They are not moveable, but provide an interface between + * moveable WirePoints (on WireSegments) and immovable PortPoints, on ports. */ class PortPoint : public QObject, public GraphicsBaseItem { - Q_OBJECT + Q_OBJECT public: - PortPoint(QGraphicsItem* parent); - - QPainterPath shape() const override; - QRectF boundingRect() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget*) override; - QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - - // Must be returned by copy; may get used to invalidate a wire, wherein each output wire will get dereferenced with - // this point (and hence modifying m_outputWires) - std::vector getOutputWires() { return m_outputWires; } - WireSegment* getInputWire() { return m_inputWire; } - virtual const QPen& getPen(); - - void addOutputWire(WireSegment* wire) { m_outputWires.push_back(wire); } - void removeOutputWire(WireSegment* wire); - void clearOutputWires() { m_outputWires.clear(); } - void setInputWire(WireSegment* wire) { m_inputWire = wire; } - void clearInputWire() { m_inputWire = nullptr; } - - void modulePositionHasChanged() override; + PortPoint(QGraphicsItem *parent); + + QPainterPath shape() const override; + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *) override; + QVariant itemChange(GraphicsItemChange change, + const QVariant &value) override; + + // Must be returned by copy; may get used to invalidate a wire, wherein each + // output wire will get dereferenced with this point (and hence modifying + // m_outputWires) + std::vector getOutputWires() { return m_outputWires; } + WireSegment *getInputWire() { return m_inputWire; } + virtual const QPen &getPen(); + + void addOutputWire(WireSegment *wire) { m_outputWires.push_back(wire); } + void removeOutputWire(WireSegment *wire); + void clearOutputWires() { m_outputWires.clear(); } + void setInputWire(WireSegment *wire) { m_inputWire = wire; } + void clearInputWire() { m_inputWire = nullptr; } + + void modulePositionHasChanged() override; protected: - WireSegment* m_inputWire = nullptr; - WirePoint* m_draggedOnThis = nullptr; - std::vector m_outputWires; + WireSegment *m_inputWire = nullptr; + WirePoint *m_draggedOnThis = nullptr; + std::vector m_outputWires; - // Cached shape and bounding rect - QPainterPath m_shape; - QRectF m_br; + // Cached shape and bounding rect + QPainterPath m_shape; + QRectF m_br; private: - void portPosChanged(); + void portPosChanged(); - PortGraphic* m_portParent = nullptr; + PortGraphic *m_portParent = nullptr; }; /** * @brief The WirePoint class - * A point on a wire. Shall display a context menu to delete. is select- and moveable. + * A point on a wire. Shall display a context menu to delete. is select- and + * moveable. */ class WirePoint : public PortPoint { public: - WirePoint(WireGraphic* parent); + WirePoint(WireGraphic *parent); - QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - const QPen& getPen() override; + QVariant itemChange(GraphicsItemChange change, + const QVariant &value) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + const QPen &getPen() override; - bool canMergeWith(WirePoint* point); - void pointDrop(WirePoint* point); - void pointDragEnter(WirePoint* point); - void pointDragLeave(WirePoint* point); + bool canMergeWith(WirePoint *point); + void pointDrop(WirePoint *point); + void pointDragEnter(WirePoint *point); + void pointDragLeave(WirePoint *point); private: - WireGraphic* m_parent = nullptr; + WireGraphic *m_parent = nullptr; }; class WireSegment : public QObject, public GraphicsBaseItem { - Q_OBJECT - friend class WirePoint; + Q_OBJECT + friend class WirePoint; public: - WireSegment(WireGraphic* parent); - - void invalidate(); - bool isDrawn() const; - bool isValid() const; - void setStart(PortPoint* start); - void setEnd(PortPoint* end); - PortPoint* getStart() const { return m_start; } - PortPoint* getEnd() const { return m_end; } - QLineF getLine() const; - - /** - * @brief geometryModified - * Called whenever one of the lines end points has moved or changed. Prompts a recalculation of the Wires geometry. - */ - void geometryModified(); - - QPainterPath shape() const override; - QRectF boundingRect() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget*) override; - void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - - QString deleted = "False"; + WireSegment(WireGraphic *parent); + + void invalidate(); + bool isDrawn() const; + bool isValid() const; + void setStart(PortPoint *start); + void setEnd(PortPoint *end); + PortPoint *getStart() const { return m_start; } + PortPoint *getEnd() const { return m_end; } + QLineF getLine() const; + + /** + * @brief geometryModified + * Called whenever one of the lines end points has moved or changed. Prompts a + * recalculation of the Wires geometry. + */ + void geometryModified(); + + QPainterPath shape() const override; + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, + QWidget *) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + QVariant itemChange(GraphicsItemChange change, + const QVariant &value) override; + + QString deleted = "False"; private: - PortPoint* m_start = nullptr; - PortPoint* m_end = nullptr; - WireGraphic* m_parent = nullptr; - QLineF m_cachedLine; - QPainterPath m_cachedShape; - QRectF m_cachedBoundingRect; + PortPoint *m_start = nullptr; + PortPoint *m_end = nullptr; + WireGraphic *m_parent = nullptr; + QLineF m_cachedLine; + QPainterPath m_cachedShape; + QRectF m_cachedBoundingRect; }; class WireGraphic : public QObject, public GraphicsBaseItem { - Q_OBJECT - friend class PortGraphic; + Q_OBJECT + friend class PortGraphic; public: - enum class MergeType { CannotMerge, MergeSinkWithSource, MergeSourceWithSink, MergeParallelSinks }; - enum class WireType { BorderOutput, ComponentOutput }; - - WireGraphic(ComponentGraphic* parent, PortGraphic* from, const std::vector& to, WireType type); - - QRectF boundingRect() const override { return QRectF(); } - const QPen& getPen(); - void postSceneConstructionInitialize1() override; - void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override {} - - void setWiresVisibleToPort(const PortPoint* p, bool visible); - PortGraphic* getFromPort() const { return m_fromPort; } - const std::vector& getToPorts() const { return m_toGraphicPorts; } - std::pair createWirePointOnSeg(const QPointF scenePos, WireSegment* onSegment); - void removeWirePoint(WirePoint* point); - - /** - * @brief clearWirePoints - * All wire points managed by this WireGraphic are deleted. As a result, the only remaining WireSegment's will be - * between PortPoints. - */ - void clearWirePoints(); - void clearWires(); - - bool managesPoint(WirePoint* point) const; - void mergePoints(WirePoint* base, WirePoint* toMerge); - MergeType canMergePoints(WirePoint* base, WirePoint* toMerge) const; - - void clearLayout(); - - /** - * @brief postSerializeInit - * Called after a WireGraphic has been loaded through serialization. This will call the in- and output ports of the - * WireGraphic and re-execute their visibility state functions, to propagate their visibility state to the wire - * segments, post-serialization. - */ - void postSerializeInit(); - - template - void load(Archive& archive) { - prepareGeometryChange(); - setSerializing(true); - // Deserialize the layout - - // This layout might originate from a similar component, but with a different name. Get the serialized parent - // name as a reference for what must be exchanged with the current parent name - std::string inParent; - archive(cereal::make_nvp("Parent", inParent)); - - std::pair> from; - archive(cereal::make_nvp("From port", from)); - std::replace(from.second.begin(), from.second.end(), inParent, getParentComponent()->getName()); - - std::map> idxToOutportNameSeq; - archive(cereal::make_nvp("To ports", idxToOutportNameSeq)); - for (auto& iter : idxToOutportNameSeq) { - std::replace(iter.second.begin(), iter.second.end(), inParent, getParentComponent()->getName()); - } - - std::map idxToWirePointPos; - archive(cereal::make_nvp("points", idxToWirePointPos)); - - std::vector> wires; - archive(cereal::make_nvp("wires", wires)); - - // Clear current layout - clearWirePoints(); - clearWires(); - - std::map idxToPointPtr; - idxToPointPtr[from.first] = m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out); - - // Locate output ports from the layout indicies - for (const auto& iter : idxToOutportNameSeq) { - Q_ASSERT(idxToPointPtr.count(iter.first) == 0); - PortPoint* point = nullptr; - for (const auto& p : m_toGraphicPorts) { - const auto nameSeq = getPortParentNameSeq(p->getPort()); - if (nameSeq == iter.second) { - point = p->getPortPoint(vsrtl::SimPort::PortType::in); - idxToPointPtr[iter.first] = point; - break; - } - // Point in serialized layout was not found in this layout, continue... - } - } + enum class MergeType { + CannotMerge, + MergeSinkWithSource, + MergeSourceWithSink, + MergeParallelSinks + }; + enum class WireType { BorderOutput, ComponentOutput }; + + WireGraphic(ComponentGraphic *parent, PortGraphic *from, + const std::vector &to, WireType type); + + QRectF boundingRect() const override { return QRectF(); } + const QPen &getPen(); + void postSceneConstructionInitialize1() override; + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override { + } + + void setWiresVisibleToPort(const PortPoint *p, bool visible); + PortGraphic *getFromPort() const { return m_fromPort; } + const std::vector &getToPorts() const { + return m_toGraphicPorts; + } + std::pair + createWirePointOnSeg(const QPointF scenePos, WireSegment *onSegment); + void removeWirePoint(WirePoint *point); + + /** + * @brief clearWirePoints + * All wire points managed by this WireGraphic are deleted. As a result, the + * only remaining WireSegment's will be between PortPoints. + */ + void clearWirePoints(); + void clearWires(); + + bool managesPoint(WirePoint *point) const; + void mergePoints(WirePoint *base, WirePoint *toMerge); + MergeType canMergePoints(WirePoint *base, WirePoint *toMerge) const; + + void clearLayout(); + + /** + * @brief postSerializeInit + * Called after a WireGraphic has been loaded through serialization. This will + * call the in- and output ports of the WireGraphic and re-execute their + * visibility state functions, to propagate their visibility state to the wire + * segments, post-serialization. + */ + void postSerializeInit(); + + template + void load(Archive &archive) { + prepareGeometryChange(); + setSerializing(true); + // Deserialize the layout + + // This layout might originate from a similar component, but with a + // different name. Get the serialized parent name as a reference for what + // must be exchanged with the current parent name + std::string inParent; + archive(cereal::make_nvp("Parent", inParent)); + + std::pair> from; + archive(cereal::make_nvp("From port", from)); + std::replace(from.second.begin(), from.second.end(), inParent, + getParentComponent()->getName()); + + std::map> idxToOutportNameSeq; + archive(cereal::make_nvp("To ports", idxToOutportNameSeq)); + for (auto &iter : idxToOutportNameSeq) { + std::replace(iter.second.begin(), iter.second.end(), inParent, + getParentComponent()->getName()); + } - // Construct PointGraphic's for intermediate points - for (const auto& p : idxToWirePointPos) { - Q_ASSERT(idxToPointPtr.count(p.first) == 0); - idxToPointPtr[p.first] = createWirePoint(); + std::map idxToWirePointPos; + archive(cereal::make_nvp("points", idxToWirePointPos)); + + std::vector> wires; + archive(cereal::make_nvp("wires", wires)); + + // Clear current layout + clearWirePoints(); + clearWires(); + + std::map idxToPointPtr; + idxToPointPtr[from.first] = + m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out); + + // Locate output ports from the layout indicies + for (const auto &iter : idxToOutportNameSeq) { + Q_ASSERT(idxToPointPtr.count(iter.first) == 0); + PortPoint *point = nullptr; + for (const auto &p : m_toGraphicPorts) { + const auto nameSeq = getPortParentNameSeq(p->getPort()); + if (nameSeq == iter.second) { + point = p->getPortPoint(vsrtl::SimPort::PortType::in); + idxToPointPtr[iter.first] = point; + break; } + // Point in serialized layout was not found in this layout, continue... + } + } - // Construct wires denoted in the layout - for (const auto& w : wires) { - if (idxToPointPtr.count(w.first) == 0) { - continue; // Wire start point not found - } - if (idxToPointPtr.count(w.second) == 0) { - continue; // Wire end point not found - } - auto* fromPort = idxToPointPtr[w.first]; - auto* toPort = idxToPointPtr[w.second]; - - createSegment(fromPort, toPort); - } + // Construct PointGraphic's for intermediate points + for (const auto &p : idxToWirePointPos) { + Q_ASSERT(idxToPointPtr.count(p.first) == 0); + idxToPointPtr[p.first] = createWirePoint(); + } - // It may be that not all ports of a wire was denoted in the layout (ie. changes to the design which the layout - // is applied on has been made). Construct all missing wires between the source port - for (const auto& p : m_toGraphicPorts) { - const auto iter = std::find_if(idxToPointPtr.begin(), idxToPointPtr.end(), [=](const auto& itp) { - return itp.second == p->getPortPoint(vsrtl::SimPort::PortType::in); - }); - if (iter == idxToPointPtr.end()) { - createSegment(m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out), - p->getPortPoint(vsrtl::SimPort::PortType::in)); - } - } + // Construct wires denoted in the layout + for (const auto &w : wires) { + if (idxToPointPtr.count(w.first) == 0) { + continue; // Wire start point not found + } + if (idxToPointPtr.count(w.second) == 0) { + continue; // Wire end point not found + } + auto *fromPort = idxToPointPtr[w.first]; + auto *toPort = idxToPointPtr[w.second]; + + createSegment(fromPort, toPort); + } - // Move wire points (must be done >after< the point has been associated with wires) - for (const auto& p : idxToWirePointPos) { - idxToPointPtr[p.first]->setPos(p.second); - } + // It may be that not all ports of a wire was denoted in the layout (ie. + // changes to the design which the layout is applied on has been made). + // Construct all missing wires between the source port + for (const auto &p : m_toGraphicPorts) { + const auto iter = std::find_if( + idxToPointPtr.begin(), idxToPointPtr.end(), [=](const auto &itp) { + return itp.second == p->getPortPoint(vsrtl::SimPort::PortType::in); + }); + if (iter == idxToPointPtr.end()) { + createSegment(m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out), + p->getPortPoint(vsrtl::SimPort::PortType::in)); + } + } - setSerializing(false); - postSerializeInit(); + // Move wire points (must be done >after< the point has been associated with + // wires) + for (const auto &p : idxToWirePointPos) { + idxToPointPtr[p.first]->setPos(p.second); } - template - void save(Archive& archive) const { - std::string outParent; - archive(cereal::make_nvp("Parent", getParentComponent()->getName())); - - int i = 0; - // serialize the incoming port - std::pair> from(i++, getPortParentNameSeq(m_fromPort->getPort())); - archive(cereal::make_nvp("From port", from)); - - // serialize the outgoing, connecting ports - std::map> idxToOutportNameSeq; - std::map outportToIdx; - for (const auto& p : m_toGraphicPorts) { - // @todo: this is not sufficient, ports may be named identically. - // It should be a hierarchical list including its parent components - idxToOutportNameSeq[i] = getPortParentNameSeq(p->getPort()); - outportToIdx[p->getPortPoint(vsrtl::SimPort::PortType::in)] = i; - i++; - } - archive(cereal::make_nvp("To ports", idxToOutportNameSeq)); - - // Each point managed by this wire is enumerated and associated with its position - std::map idxToPos; - std::map pointToIdx; - for (const auto& p : m_points) { - idxToPos[i] = p->pos().toPoint(); - pointToIdx[p] = i; - i++; - } - archive(cereal::make_nvp("points", idxToPos)); - - // Each wire segment will connect to either a point or a port - std::vector> wires; - for (auto& w : m_wires) { - auto* start = w->getStart(); - auto* end = w->getEnd(); - - int startIdx = 0; - int endIdx = 0; - - if (m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out) == start) { - startIdx = 0; - } else if (outportToIdx.count(start)) { - startIdx = outportToIdx[start]; - } else if (pointToIdx.count(start)) { - startIdx = pointToIdx[start]; - } else { - Q_ASSERT(false && "Could not serialize wire"); - } - - if (outportToIdx.count(end)) { - endIdx = outportToIdx[end]; - } else if (pointToIdx.count(end)) { - endIdx = pointToIdx[end]; - } else { - Q_ASSERT(false && "Could not serialize wire"); - } - wires.push_back({startIdx, endIdx}); - } - archive(cereal::make_nvp("wires", wires)); + setSerializing(false); + postSerializeInit(); + } + + template + void save(Archive &archive) const { + std::string outParent; + archive(cereal::make_nvp("Parent", getParentComponent()->getName())); + + int i = 0; + // serialize the incoming port + std::pair> from( + i++, getPortParentNameSeq(m_fromPort->getPort())); + archive(cereal::make_nvp("From port", from)); + + // serialize the outgoing, connecting ports + std::map> idxToOutportNameSeq; + std::map outportToIdx; + for (const auto &p : m_toGraphicPorts) { + // @todo: this is not sufficient, ports may be named identically. + // It should be a hierarchical list including its parent components + idxToOutportNameSeq[i] = getPortParentNameSeq(p->getPort()); + outportToIdx[p->getPortPoint(vsrtl::SimPort::PortType::in)] = i; + i++; + } + archive(cereal::make_nvp("To ports", idxToOutportNameSeq)); + + // Each point managed by this wire is enumerated and associated with its + // position + std::map idxToPos; + std::map pointToIdx; + for (const auto &p : m_points) { + idxToPos[i] = p->pos().toPoint(); + pointToIdx[p] = i; + i++; + } + archive(cereal::make_nvp("points", idxToPos)); + + // Each wire segment will connect to either a point or a port + std::vector> wires; + for (auto &w : m_wires) { + auto *start = w->getStart(); + auto *end = w->getEnd(); + + int startIdx = 0; + int endIdx = 0; + + if (m_fromPort->getPortPoint(vsrtl::SimPort::PortType::out) == start) { + startIdx = 0; + } else if (outportToIdx.count(start)) { + startIdx = outportToIdx[start]; + } else if (pointToIdx.count(start)) { + startIdx = pointToIdx[start]; + } else { + Q_ASSERT(false && "Could not serialize wire"); + } + + if (outportToIdx.count(end)) { + endIdx = outportToIdx[end]; + } else if (pointToIdx.count(end)) { + endIdx = pointToIdx[end]; + } else { + Q_ASSERT(false && "Could not serialize wire"); + } + wires.push_back({startIdx, endIdx}); } + archive(cereal::make_nvp("wires", wires)); + } private: - SimComponent* getParentComponent() const; - WirePoint* createWirePoint(); - void moveWirePoint(PortPoint* point, const QPointF scenePos); - WireSegment* createSegment(PortPoint* start, PortPoint* end); - void createRectilinearSegments(PortPoint* start, PortPoint* end); - - ComponentGraphic* m_parent = nullptr; - PortGraphic* m_fromPort = nullptr; - std::vector m_toPorts; - std::vector m_toGraphicPorts; - std::set m_wires; - std::set m_points; - WireType m_type; + SimComponent *getParentComponent() const; + WirePoint *createWirePoint(); + void moveWirePoint(PortPoint *point, const QPointF scenePos); + WireSegment *createSegment(PortPoint *start, PortPoint *end); + void createRectilinearSegments(PortPoint *start, PortPoint *end); + + ComponentGraphic *m_parent = nullptr; + PortGraphic *m_fromPort = nullptr; + std::vector m_toPorts; + std::vector m_toGraphicPorts; + std::set m_wires; + std::set m_points; + WireType m_type; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_WIREGRAPHIC_H +#endif // VSRTL_WIREGRAPHIC_H diff --git a/interface/vsrtl_binutils.h b/interface/vsrtl_binutils.h index b0d34a5..b8e3e64 100644 --- a/interface/vsrtl_binutils.h +++ b/interface/vsrtl_binutils.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include #include @@ -15,70 +15,71 @@ namespace vsrtl { // Sign extension of arbitrary bitfield size. -// Courtesy of http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend +// Courtesy of +// http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend template inline T2 signextend(const T x) { - static_assert(std::is_signed::value, "T2 must be signed type"); - struct { - T2 x : B; - } s; - return s.x = x; + static_assert(std::is_signed::value, "T2 must be signed type"); + struct { + T2 x : B; + } s; + return s.x = x; } // Runtime signextension template inline T2 signextend(const T x, unsigned B) { - static_assert(std::is_signed::value, "T2 must be signed type"); - const int m = CHAR_BIT * sizeof(T2) - B; - return (static_cast(x) << m) >> m; + static_assert(std::is_signed::value, "T2 must be signed type"); + const int m = CHAR_BIT * sizeof(T2) - B; + return (static_cast(x) << m) >> m; } constexpr VSRTL_VT_U generateBitmask(VSRTL_VT_U n) { - if (n >= (sizeof(VSRTL_VT_U) * CHAR_BIT)) { - return VT_U(VT_S(-1)); - } - if (n == 0) { - return 0; - } - return VT_U((VT_U(1) << n) - 1); + if (n >= (sizeof(VSRTL_VT_U) * CHAR_BIT)) { + return VT_U(VT_S(-1)); + } + if (n == 0) { + return 0; + } + return VT_U((VT_U(1) << n) - 1); } constexpr unsigned bitcount(VSRTL_VT_U n) { - unsigned count = 0; - while (n > 0) { - count += 1; - n = n & (n - 1); - } - return count; + unsigned count = 0; + while (n > 0) { + count += 1; + n = n & (n - 1); + } + return count; } template -inline T accBVec(const std::array& v) { - T r = 0; - for (auto i = 0; i < width; i++) { - r |= v[i] << i; - } - return r; +inline T accBVec(const std::array &v) { + T r = 0; + for (auto i = 0; i < width; i++) { + r |= v[i] << i; + } + return r; } template inline std::array buildUnsignedArr(VSRTL_VT_U v) { - std::array r; - for (size_t i = 0; i < width; i++) { - r[i] = v & 0b1; - v >>= 1; - } - return r; + std::array r; + for (size_t i = 0; i < width; i++) { + r[i] = v & 0b1; + v >>= 1; + } + return r; } template -constexpr T floorlog2(const T& x) { - return x == 1 ? 0 : 1 + floorlog2(x >> 1); +constexpr T floorlog2(const T &x) { + return x == 1 ? 0 : 1 + floorlog2(x >> 1); } template -constexpr const T ceillog2(const T& x) { - return x == 1 || x == 0 ? 1 : floorlog2(x - 1) + 1; +constexpr const T ceillog2(const T &x) { + return x == 1 || x == 0 ? 1 : floorlog2(x - 1) + 1; } -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_defines.h b/interface/vsrtl_defines.h index 4281444..27f079f 100644 --- a/interface/vsrtl_defines.h +++ b/interface/vsrtl_defines.h @@ -1,25 +1,28 @@ #pragma once -#include #include +#include #include namespace vsrtl { using VSRTL_VT_U = uint64_t; using VSRTL_VT_S = int64_t; static_assert(CHAR_BIT == 8, "VSRTL only supports systems with CHAR_BIT = 8"); -static_assert(sizeof(VSRTL_VT_S) == sizeof(VSRTL_VT_U), "Base value types must be equal in size"); -static_assert(std::is_unsigned::value, "VSRTL_VT_U must be an unsigned data type"); -static_assert(std::is_signed::value, "VSRTL_VT_S must be a signed data type"); +static_assert(sizeof(VSRTL_VT_S) == sizeof(VSRTL_VT_U), + "Base value types must be equal in size"); +static_assert(std::is_unsigned::value, + "VSRTL_VT_U must be an unsigned data type"); +static_assert(std::is_signed::value, + "VSRTL_VT_S must be a signed data type"); constexpr VSRTL_VT_U VSRTL_VT_BITS = sizeof(VSRTL_VT_U) * CHAR_BIT; template -constexpr VSRTL_VT_U VT_U(const T& v) { - return static_cast(v); +constexpr VSRTL_VT_U VT_U(const T &v) { + return static_cast(v); } template -constexpr VSRTL_VT_S VT_S(const T& v) { - return static_cast(v); +constexpr VSRTL_VT_S VT_S(const T &v) { + return static_cast(v); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_gfxobjecttypes.h b/interface/vsrtl_gfxobjecttypes.h index 211f0a9..2bdd8bf 100644 --- a/interface/vsrtl_gfxobjecttypes.h +++ b/interface/vsrtl_gfxobjecttypes.h @@ -11,81 +11,92 @@ namespace vsrtl { inline std::string str_toLower(std::string str) { - std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); }); - return str; + std::transform(str.begin(), str.end(), str.begin(), + [](unsigned char c) { return std::tolower(c); }); + return str; } -// @todo: describe why this is needed (graphical objects need not know about the templated versions of objects) +// @todo: describe why this is needed (graphical objects need not know about the +// templated versions of objects) class GraphicsType; class GraphicsTypeFromName { public: - static const GraphicsType* get(const std::string& name) { - const auto name_lower = str_toLower(name); - auto it = _get()->m_nameToType.find(name_lower); - if (it != _get()->m_nameToType.end()) { - return it->second; - } - return nullptr; + static const GraphicsType *get(const std::string &name) { + const auto name_lower = str_toLower(name); + auto it = _get()->m_nameToType.find(name_lower); + if (it != _get()->m_nameToType.end()) { + return it->second; } - - static void registerGraphicsType(const std::string& name, const GraphicsType* obj) { - auto* instance = _get(); - const auto name_lower = str_toLower(name); - if (instance->m_nameToType.count(name_lower) != 0) { - throw std::runtime_error("Graphics type already registerred for type '" + name + "'"); - } - instance->m_nameToType[name_lower] = obj; + return nullptr; + } + + static void registerGraphicsType(const std::string &name, + const GraphicsType *obj) { + auto *instance = _get(); + const auto name_lower = str_toLower(name); + if (instance->m_nameToType.count(name_lower) != 0) { + throw std::runtime_error("Graphics type already registerred for type '" + + name + "'"); } + instance->m_nameToType[name_lower] = obj; + } private: - GraphicsTypeFromName() {} + GraphicsTypeFromName() {} - static GraphicsTypeFromName* _get() { - static GraphicsTypeFromName instance; - return &instance; - } + static GraphicsTypeFromName *_get() { + static GraphicsTypeFromName instance; + return &instance; + } - std::map m_nameToType; + std::map m_nameToType; }; class GraphicsType { public: - bool hasSpecialPortID(const std::string& id) const { - const auto& v = specialPortIDs(); - return std::find(v.begin(), v.end(), id) != v.end(); - } - virtual const std::vector specialPortIDs() const = 0; - virtual std::string getName() const = 0; - virtual ~GraphicsType() {} + bool hasSpecialPortID(const std::string &id) const { + const auto &v = specialPortIDs(); + return std::find(v.begin(), v.end(), id) != v.end(); + } + virtual const std::vector specialPortIDs() const = 0; + virtual std::string getName() const = 0; + virtual ~GraphicsType() {} }; #define GraphicsTypeFor(type) (GraphicsTypeForComponent(type)::get()) #define GraphicsTypeForComponent(name) name##GraphicsType -#define DefineGraphicsType(type, requiredSpecialPorts) \ - class GraphicsTypeForComponent(type) \ - : public GraphicsType{public : const std::vector specialPortIDs() \ - const override{return requiredSpecialPorts; \ - } \ - static const GraphicsType* get() { \ - static GraphicsTypeForComponent(type) instance; \ - return &instance; \ - } \ - std::string getName() const override { return #type; } \ - \ -private: \ - GraphicsTypeForComponent(type)() { GraphicsTypeFromName::registerGraphicsType(#type, this); } \ - } \ - ; \ - static auto* __##type##gfxtypeinstance__ = GraphicsTypeFor(type); // Static initializer to register type on startup - -// All simulator components should use the following macro for defining how the component type should be drawn. Any of -// the supported objects listed below may be specified. -#define SetGraphicsType(name) \ - const GraphicsType* getGraphicsType() const override { return GraphicsTypeForComponent(name)::get(); } +#define DefineGraphicsType(type, requiredSpecialPorts) \ + class GraphicsTypeForComponent(type) \ + : public \ + GraphicsType{public : const std::vector specialPortIDs() \ + const override{return requiredSpecialPorts; \ + } \ + static const GraphicsType *get() { \ + static GraphicsTypeForComponent(type) instance; \ + return &instance; \ + } \ + std::string getName() const override { return #type; } \ + \ +private: \ + GraphicsTypeForComponent(type)() { \ + GraphicsTypeFromName::registerGraphicsType(#type, this); \ + } \ + } \ + ; \ + static auto *__##type##gfxtypeinstance__ = \ + GraphicsTypeFor(type); // Static initializer to register type on startup + +// All simulator components should use the following macro for defining how the +// component type should be drawn. Any of the supported objects listed below may +// be specified. +#define SetGraphicsType(name) \ + const GraphicsType *getGraphicsType() const override { \ + return GraphicsTypeForComponent(name)::get(); \ + } #define L(...) __VA_ARGS__ @@ -105,4 +116,4 @@ DefineGraphicsType(Multiplexer, {GFX_MUX_SELECT}); DefineGraphicsType(ALU, {}); DefineGraphicsType(Adder, {}); -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_interface.cpp b/interface/vsrtl_interface.cpp index 8f04ad9..398dd9b 100644 --- a/interface/vsrtl_interface.cpp +++ b/interface/vsrtl_interface.cpp @@ -1,25 +1,23 @@ #include "vsrtl_interface.h" namespace vsrtl { -void SimPort::queueVcdVarChange() { - getDesign()->queueVcdVarChange(this); -} +void SimPort::queueVcdVarChange() { getDesign()->queueVcdVarChange(this); } -SimDesign* SimBase::getDesign() { - if (m_design) - return m_design; +SimDesign *SimBase::getDesign() { + if (m_design) + return m_design; - // m_design has yet to be initialized. (This cannot be done during construction, so we lazily initialize the design - // pointer upon the first request to such). - // Recurse until locating a parent which either has its SimDesign set, or the component has no parent (ie. it is the - // design) - if (!m_parent) { - m_design = dynamic_cast(this); - } else { - m_design = m_parent->getDesign(); - } - assert(m_design != nullptr); + // m_design has yet to be initialized. (This cannot be done during + // construction, so we lazily initialize the design pointer upon the first + // request to such). Recurse until locating a parent which either has its + // SimDesign set, or the component has no parent (ie. it is the design) + if (!m_parent) { + m_design = dynamic_cast(this); + } else { + m_design = m_parent->getDesign(); + } + assert(m_design != nullptr); - return m_design; + return m_design; } -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_interface.h b/interface/vsrtl_interface.h index 3cdc941..94adb2f 100644 --- a/interface/vsrtl_interface.h +++ b/interface/vsrtl_interface.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include #include @@ -28,722 +28,801 @@ class SimSynchronous; class SimBase { public: - SimBase(const std::string& name, SimBase* parent) : m_name(name), m_parent(parent) {} - virtual ~SimBase() {} - - SimDesign* getDesign(); - - template - void throwError(const std::string& message) const { - throw T(getName() + ": " + message); - } - - const std::string& getName() const { return m_name; } - const std::string& getDisplayName() const { return m_displayName.empty() ? m_name : m_displayName; } - const std::string& getDescription() const { return m_description; } - const std::string getHierName() const { - if (m_parent) { - return m_parent->getHierName() + "->" + getName(); - } else { - return getName(); - } - } - - template - T* getParent() const { - return dynamic_cast(m_parent); - } - - void setDisplayName(const std::string& name) { m_displayName = name; } - void setDescription(const std::string& description) { m_description = description; } - - template - void registerGraphic(T* obj) { - if (m_graphicObject != nullptr) { - throw std::runtime_error("Graphic object already registered for '" + getHierName() + "'"); - } - m_graphicObject = obj; - } - - template - T* getGraphic() const { - return static_cast(m_graphicObject); - } + SimBase(const std::string &name, SimBase *parent) + : m_name(name), m_parent(parent) {} + virtual ~SimBase() {} + + SimDesign *getDesign(); + + template + void throwError(const std::string &message) const { + throw T(getName() + ": " + message); + } + + const std::string &getName() const { return m_name; } + const std::string &getDisplayName() const { + return m_displayName.empty() ? m_name : m_displayName; + } + const std::string &getDescription() const { return m_description; } + const std::string getHierName() const { + if (m_parent) { + return m_parent->getHierName() + "->" + getName(); + } else { + return getName(); + } + } + + template + T *getParent() const { + return dynamic_cast(m_parent); + } + + void setDisplayName(const std::string &name) { m_displayName = name; } + void setDescription(const std::string &description) { + m_description = description; + } + + template + void registerGraphic(T *obj) { + if (m_graphicObject != nullptr) { + throw std::runtime_error("Graphic object already registered for '" + + getHierName() + "'"); + } + m_graphicObject = obj; + } + + template + T *getGraphic() const { + return static_cast(m_graphicObject); + } protected: - /// Name of this component. - std::string m_name; - /// Parent of this component. - SimBase* m_parent = nullptr; - /// Cached pointer to the top-level design. - SimDesign* m_design = nullptr; - /// Display name of this component. If set, a UI should prefer showing this name over m_name. - std::string m_displayName; - /// An optional description of this component. - std::string m_description; - /// An opaque pointer to a graphical counterpart to this component. - void* m_graphicObject = nullptr; + /// Name of this component. + std::string m_name; + /// Parent of this component. + SimBase *m_parent = nullptr; + /// Cached pointer to the top-level design. + SimDesign *m_design = nullptr; + /// Display name of this component. If set, a UI should prefer showing this + /// name over m_name. + std::string m_displayName; + /// An optional description of this component. + std::string m_description; + /// An opaque pointer to a graphical counterpart to this component. + void *m_graphicObject = nullptr; }; template struct BaseSorter { - bool operator()(const T& lhs, const T& rhs) const { - return std::lexicographical_compare(lhs->getName().begin(), lhs->getName().end(), rhs->getName().begin(), - rhs->getName().end()); - } + bool operator()(const T &lhs, const T &rhs) const { + return std::lexicographical_compare( + lhs->getName().begin(), lhs->getName().end(), rhs->getName().begin(), + rhs->getName().end()); + } }; class SimPort : public SimBase { - friend class SimDesign; + friend class SimDesign; public: - enum class PortType { in, out, signal }; - SimPort(const std::string& name, SimBase* parent, PortType type) : SimBase(name, parent), m_type(type) {} - virtual ~SimPort() {} - virtual unsigned int getWidth() const = 0; - virtual VSRTL_VT_U uValue() const = 0; - virtual VSRTL_VT_S sValue() const = 0; - - template - std::vector getOutputPorts() { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - if constexpr (std::is_same::value) { - return m_outputPorts; - } else { - std::vector outputPorts; - for (const auto& p : m_outputPorts) { - outputPorts.push_back(p->cast()); - } - return outputPorts; - } - } - - template - T* getInputPort() { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - return m_inputPort->cast(); - } - - virtual bool isConstant() const = 0; - - template - T* cast() { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - if constexpr (std::is_same::value) { - return this; - } else { - return static_cast(this); - } - } - - /* traverse from any given port towards its root (source) port, while executing nodeFunc(args...) in each port - which is visited along the way*/ - template - void traverseToRoot(const F& nodeFunc, Args&... args) { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - nodeFunc(this, args...); - if (getInputPort()) { - getInputPort()->traverseToRoot(nodeFunc, args...); - } - } - - /* From this port, visit all directly and implicitely connected port to this port, and execute nodeFunc(args...) - in each visited port */ - template - void traverseConnection(const F& nodeFunc, Args&... args) { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - if (m_traversingConnection) - return; - m_traversingConnection = true; - - nodeFunc(this, args...); - if (getInputPort()) { - getInputPort()->traverseConnection(nodeFunc, args...); - } - for (const auto& p : getOutputPorts()) { - p->traverseConnection(nodeFunc, args...); - } - - m_traversingConnection = false; - } - - /* Traverse from any given port towards its endpoint sinks, executing nodeFunc(args...) in each visited port */ - template - void traverseToSinks(const F& nodeFunc, Args&... args) { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - nodeFunc(this, args...); - for (const auto& p : getOutputPorts()) { - p->traverseToSinks(nodeFunc, args...); - } - } - - template - std::vector getPortsInConnection() { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - std::vector portsInConnection; - traverseConnection([=](T* port, std::vector& ports) { ports.push_back(port); }, portsInConnection); - return portsInConnection; - } - - void writeVar(VCDFile& file) { - m_vcdId = file.varDef(getName(), getWidth()); - file.varInitVal(m_vcdId, uValue()); - } - - /** @todo: Figure out whether these should be defined in the interface */ - /** - * @brief stringValue - * A port may define special string formatting to be displayed in the graphical library. If so, owning - * components should set the string value function to provide such values. - */ - virtual bool isEnumPort() const { return false; } - virtual std::string valueToEnumString() const { throw std::runtime_error("This is not an enum port!"); } - virtual VSRTL_VT_U enumStringToValue(const char*) const { throw std::runtime_error("This is not an enum port!"); } - const std::string& vcdId() const { return m_vcdId; } - PortType type() const { return m_type; } - - Gallant::Signal0<> changed; + enum class PortType { in, out, signal }; + SimPort(const std::string &name, SimBase *parent, PortType type) + : SimBase(name, parent), m_type(type) {} + virtual ~SimPort() {} + virtual unsigned int getWidth() const = 0; + virtual VSRTL_VT_U uValue() const = 0; + virtual VSRTL_VT_S sValue() const = 0; + + template + std::vector getOutputPorts() { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + if constexpr (std::is_same::value) { + return m_outputPorts; + } else { + std::vector outputPorts; + for (const auto &p : m_outputPorts) { + outputPorts.push_back(p->cast()); + } + return outputPorts; + } + } + + template + T *getInputPort() { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + return m_inputPort->cast(); + } + + virtual bool isConstant() const = 0; + + template + T *cast() { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + if constexpr (std::is_same::value) { + return this; + } else { + return static_cast(this); + } + } + + /* traverse from any given port towards its root (source) port, while + executing nodeFunc(args...) in each port which is visited along the way*/ + template + void traverseToRoot(const F &nodeFunc, Args &...args) { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + nodeFunc(this, args...); + if (getInputPort()) { + getInputPort()->traverseToRoot(nodeFunc, args...); + } + } + + /* From this port, visit all directly and implicitely connected port to this + port, and execute nodeFunc(args...) in each visited port */ + template + void traverseConnection(const F &nodeFunc, Args &...args) { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + if (m_traversingConnection) + return; + m_traversingConnection = true; + + nodeFunc(this, args...); + if (getInputPort()) { + getInputPort()->traverseConnection(nodeFunc, args...); + } + for (const auto &p : getOutputPorts()) { + p->traverseConnection(nodeFunc, args...); + } + + m_traversingConnection = false; + } + + /* Traverse from any given port towards its endpoint sinks, executing + * nodeFunc(args...) in each visited port */ + template + void traverseToSinks(const F &nodeFunc, Args &...args) { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + nodeFunc(this, args...); + for (const auto &p : getOutputPorts()) { + p->traverseToSinks(nodeFunc, args...); + } + } + + template + std::vector getPortsInConnection() { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + std::vector portsInConnection; + traverseConnection( + [=](T *port, std::vector &ports) { ports.push_back(port); }, + portsInConnection); + return portsInConnection; + } + + void writeVar(VCDFile &file) { + m_vcdId = file.varDef(getName(), getWidth()); + file.varInitVal(m_vcdId, uValue()); + } + + /** @todo: Figure out whether these should be defined in the interface */ + /** + * @brief stringValue + * A port may define special string formatting to be displayed in the + * graphical library. If so, owning components should set the string value + * function to provide such values. + */ + virtual bool isEnumPort() const { return false; } + virtual std::string valueToEnumString() const { + throw std::runtime_error("This is not an enum port!"); + } + virtual VSRTL_VT_U enumStringToValue(const char *) const { + throw std::runtime_error("This is not an enum port!"); + } + const std::string &vcdId() const { return m_vcdId; } + PortType type() const { return m_type; } + + Gallant::Signal0<> changed; protected: - std::vector m_outputPorts; - SimPort* m_inputPort = nullptr; + std::vector m_outputPorts; + SimPort *m_inputPort = nullptr; private: - void queueVcdVarChange(); - bool m_traversingConnection = false; - std::string m_vcdId; - /** - * @brief m_type - * @note: The type of the port determines the type of the port with respect to the component that instantiated it. - */ - PortType m_type; + void queueVcdVarChange(); + bool m_traversingConnection = false; + std::string m_vcdId; + /** + * @brief m_type + * @note: The type of the port determines the type of the port with respect to + * the component that instantiated it. + */ + PortType m_type; }; #define TYPE(...) __VA_ARGS__ -#define SUBCOMPONENT(name, type, ...) type* name = SimComponent::create_component(#name, ##__VA_ARGS__) -#define SUBCOMPONENTS(name, type, n, ...) \ - std::vector name = SimComponent::create_components(#name, n, ##__VA_ARGS__) -#define PARAMETER(name, type, initial) Parameter& name = this->template createParameter(#name, initial) +#define SUBCOMPONENT(name, type, ...) \ + type *name = SimComponent::create_component(#name, ##__VA_ARGS__) +#define SUBCOMPONENTS(name, type, n, ...) \ + std::vector name = \ + SimComponent::create_components(#name, n, ##__VA_ARGS__) +#define PARAMETER(name, type, initial) \ + Parameter &name = this->template createParameter(#name, initial) namespace { struct NoPredicate {}; -} // namespace +} // namespace class SimComponent : public SimBase { public: - using PortBaseCompT = BaseSorter>; - using ComponentCompT = BaseSorter>; - - SimComponent(const std::string& name, SimBase* parent) : SimBase(name, parent) {} - virtual ~SimComponent() {} - - /** - * @brief getBaseType - * Used to identify the component type, which is used when determining how to draw a component. Introduced to - * avoid intermediate base classes for many (All) components, which is a template class. For instance, it is - * desireable to identify all instances of "Constant<...>" objects, but without introducing a "BaseConstant" - * class. - * @return String identifier for the component type - */ - virtual const GraphicsType* getGraphicsType() const { return GraphicsTypeForComponent(Component)::get(); } - - /** - * getInput&OutputComponents does not return a set, although it naturally should. In partitioning the circuit - * graph, it is beneficial to know whether two components have multiple edges between each other. - */ - template - std::vector getInputComponents() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific component type"); - std::vector v; - for (const auto& s : m_inputPorts) { - if (auto* inputPort = s->getInputPort()) { - v.push_back(inputPort->getParent()); - } - } - return v; - } - - template - std::vector getOutputComponents() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific component type"); - std::vector v; - for (const auto& p : m_outputPorts) { - for (const auto& pc : p->getOutputPorts()) - v.push_back(pc->getParent()); - } - return v; - } - - template - std::vector getPorts() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - std::vector ports; - if constexpr (d == SimPort::PortType::in) { - for (const auto& p : m_inputPorts) - ports.push_back(p->cast()); - } else if constexpr (d == SimPort::PortType::out) { - for (const auto& p : m_outputPorts) - ports.push_back(p->cast()); - } else if constexpr (d == SimPort::PortType::signal) { - for (const auto& p : m_signals) - ports.push_back(p->cast()); - } - return ports; - } - - template - std::vector getOutputPorts() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - return getPorts(); - } - - template - std::vector getInputPorts() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - return getPorts(); - } - - template - T* findPort(const std::string& name) const { - T* p_ptr = nullptr; - for (const auto& p : getAllPorts()) { - if (p->getName() == name) { - p_ptr = p; - break; - } - } - return p_ptr; - } - - template - T* findSignal(const std::string& name) const { - T* p_ptr = nullptr; - for (const auto& p : getSignals()) { - if (p->getName() == name) { - p_ptr = p; - break; - } - } - return p_ptr; - } - - template - std::vector getAllPorts() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - std::vector ports; - for (auto portsForDir : {getPorts(), getPorts()}) { - ports.insert(ports.end(), portsForDir.begin(), portsForDir.end()); - } - return ports; - } - - template - std::vector getSignals() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - return getPorts(); - } - - void verifyHasSpecialPortID(const std::string& id) const { - const auto* type = getGraphicsType(); - if (!type) { - throwError("No graphics type registerred for component '" + getHierName() + "'"); - } - - if (!type->hasSpecialPortID(id)) { - throwError("Special port ID '" + id + "' is not a special port of the graphics type '" + type->getName() + - "'"); - } - } - - template - T* getSpecialPort(const std::string& id) const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - verifyHasSpecialPortID(id); - if (m_specialPorts.count(id) != 0) - return m_specialPorts.at(id); - return nullptr; - } - - template - std::vector getSpecialPorts() const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific port type"); - std::vector ports; - for (const auto& p : m_specialPorts) { - ports.push_back(p.second); - } - return ports; - } - - void setSpecialPort(const std::string& id, SimPort* port) { - verifyHasSpecialPortID(id); - if (getSpecialPort(id) != nullptr) - throwError("Special port '" + id + "' already set"); - m_specialPorts[id] = port; - } - - template - std::vector getSubComponents(const P& predicate = {}) const { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific component type"); - std::vector subcomponents; - for (const auto& c : m_subcomponents) { - if constexpr (!std::is_same::value) { - if (!predicate(*c)) - continue; - } - subcomponents.push_back(c->cast()); - } - return subcomponents; - } - - bool hasSubcomponents() const { return m_subcomponents.size() != 0; } - - template - void getComponentGraph(std::map>& componentGraph) { - // Register adjacent components (child components) in the graph, and add subcomponents to graph - componentGraph[this->cast()]; - for (const auto& c : getSubComponents()) { - componentGraph[this->cast()].push_back(c); - c->getComponentGraph(componentGraph); - } - } - - // Component object generator that registers objects in parent upon creation - template - T* create_component(const std::string& name, Args... args) { - verifyIsUniqueComponentName(name); - auto sptr = std::make_unique(name, this, args...); - auto* ptr = sptr.get(); - m_subcomponents.emplace(std::move(sptr)); - return ptr->template cast(); - } - - template - std::vector create_components(const std::string& name, unsigned int n, Args... args) { - std::vector components; - for (unsigned int i = 0; i < n; i++) { - std::string i_name = name + "_" + std::to_string(i); - components.push_back(create_component(i_name, args...)); - } - return components; - } - - template - Parameter& createParameter(const std::string& name, const T& value) { - verifyIsUniqueParameterName(name); - auto sptr = std::make_unique>(name, value); - auto* ptr = sptr.get(); - m_parameters.emplace(std::move(sptr)); - return *ptr; - } - - std::vector getParameters() const { - std::vector parameters; - for (const auto& p : m_parameters) { - parameters.push_back(p.get()); - } - return parameters; - } - - void verifyIsUniquePortName(const std::string& name) { - if (!(isUniqueName(name, m_outputPorts) && isUniqueName(name, m_inputPorts))) { - throw std::runtime_error("Duplicate port name: '" + name + "' in component: '" + getName() + - "'. Port names must be unique."); - } - } - - void verifyIsUniqueComponentName(const std::string& name) { - if (!isUniqueName(name, m_subcomponents)) { - throw std::runtime_error("Duplicate subcomponent name: '" + name + "' in component: '" + getName() + - "'. Subcomponent names must be unique."); - } - } - - void verifyIsUniqueParameterName(const std::string& name) { - if (!isUniqueName(name, m_parameters)) { - throw std::runtime_error("Duplicate parameter name: '" + name + "' in component: '" + getName() + - "'. Parameter names must be unique."); - } - } - - template - bool isUniqueName(const std::string& name, std::set, C_T>& container) { - return std::find_if(container.begin(), container.end(), - [name](const auto& p) { return p->getName() == name; }) == container.end(); - } - - void writeScope(VCDFile& file) { - auto d = file.scopeDef(getName()); - for (const auto& p : getAllPorts()) { - p->writeVar(file); - } - for (const auto& sc : m_subcomponents) { - sc->writeScope(file); - } - } - - template - T* cast() { - static_assert(std::is_base_of::value, "Must cast to a simulator-specific component type"); - if constexpr (std::is_same::value) { - return this; - } else { - return dynamic_cast(this); - } - } - unsigned reserveConstantId() { return m_constantCount++; } - - void registerSynchronous(SimSynchronous* s) { - if (m_synchronous != nullptr) { - throw std::runtime_error("A synchronous object has already been registered to this component"); - } - m_synchronous = s; - } - bool isSynchronous() const { return m_synchronous != nullptr; } - SimSynchronous* getSynchronous() { return m_synchronous; } - - Gallant::Signal0<> changed; + using PortBaseCompT = BaseSorter>; + using ComponentCompT = BaseSorter>; + + SimComponent(const std::string &name, SimBase *parent) + : SimBase(name, parent) {} + virtual ~SimComponent() {} + + /** + * @brief getBaseType + * Used to identify the component type, which is used when determining how to + * draw a component. Introduced to avoid intermediate base classes for many + * (All) components, which is a template class. For instance, it is desireable + * to identify all instances of "Constant<...>" objects, but without + * introducing a "BaseConstant" class. + * @return String identifier for the component type + */ + virtual const GraphicsType *getGraphicsType() const { + return GraphicsTypeForComponent(Component)::get(); + } + + /** + * getInput&OutputComponents does not return a set, although it naturally + * should. In partitioning the circuit graph, it is beneficial to know whether + * two components have multiple edges between each other. + */ + template + std::vector getInputComponents() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific component type"); + std::vector v; + for (const auto &s : m_inputPorts) { + if (auto *inputPort = s->getInputPort()) { + v.push_back(inputPort->getParent()); + } + } + return v; + } + + template + std::vector getOutputComponents() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific component type"); + std::vector v; + for (const auto &p : m_outputPorts) { + for (const auto &pc : p->getOutputPorts()) + v.push_back(pc->getParent()); + } + return v; + } + + template + std::vector getPorts() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + std::vector ports; + if constexpr (d == SimPort::PortType::in) { + for (const auto &p : m_inputPorts) + ports.push_back(p->cast()); + } else if constexpr (d == SimPort::PortType::out) { + for (const auto &p : m_outputPorts) + ports.push_back(p->cast()); + } else if constexpr (d == SimPort::PortType::signal) { + for (const auto &p : m_signals) + ports.push_back(p->cast()); + } + return ports; + } + + template + std::vector getOutputPorts() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + return getPorts(); + } + + template + std::vector getInputPorts() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + return getPorts(); + } + + template + T *findPort(const std::string &name) const { + T *p_ptr = nullptr; + for (const auto &p : getAllPorts()) { + if (p->getName() == name) { + p_ptr = p; + break; + } + } + return p_ptr; + } + + template + T *findSignal(const std::string &name) const { + T *p_ptr = nullptr; + for (const auto &p : getSignals()) { + if (p->getName() == name) { + p_ptr = p; + break; + } + } + return p_ptr; + } + + template + std::vector getAllPorts() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + std::vector ports; + for (auto portsForDir : {getPorts(), + getPorts()}) { + ports.insert(ports.end(), portsForDir.begin(), portsForDir.end()); + } + return ports; + } + + template + std::vector getSignals() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + return getPorts(); + } + + void verifyHasSpecialPortID(const std::string &id) const { + const auto *type = getGraphicsType(); + if (!type) { + throwError("No graphics type registerred for component '" + + getHierName() + "'"); + } + + if (!type->hasSpecialPortID(id)) { + throwError("Special port ID '" + id + + "' is not a special port of the graphics type '" + + type->getName() + "'"); + } + } + + template + T *getSpecialPort(const std::string &id) const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + verifyHasSpecialPortID(id); + if (m_specialPorts.count(id) != 0) + return m_specialPorts.at(id); + return nullptr; + } + + template + std::vector getSpecialPorts() const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific port type"); + std::vector ports; + for (const auto &p : m_specialPorts) { + ports.push_back(p.second); + } + return ports; + } + + void setSpecialPort(const std::string &id, SimPort *port) { + verifyHasSpecialPortID(id); + if (getSpecialPort(id) != nullptr) + throwError("Special port '" + id + "' already set"); + m_specialPorts[id] = port; + } + + template + std::vector getSubComponents(const P &predicate = {}) const { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific component type"); + std::vector subcomponents; + for (const auto &c : m_subcomponents) { + if constexpr (!std::is_same::value) { + if (!predicate(*c)) + continue; + } + subcomponents.push_back(c->cast()); + } + return subcomponents; + } + + bool hasSubcomponents() const { return m_subcomponents.size() != 0; } + + template + void getComponentGraph(std::map> &componentGraph) { + // Register adjacent components (child components) in the graph, and add + // subcomponents to graph + componentGraph[this->cast()]; + for (const auto &c : getSubComponents()) { + componentGraph[this->cast()].push_back(c); + c->getComponentGraph(componentGraph); + } + } + + // Component object generator that registers objects in parent upon creation + template + T *create_component(const std::string &name, Args... args) { + verifyIsUniqueComponentName(name); + auto sptr = std::make_unique(name, this, args...); + auto *ptr = sptr.get(); + m_subcomponents.emplace(std::move(sptr)); + return ptr->template cast(); + } + + template + std::vector create_components(const std::string &name, unsigned int n, + Args... args) { + std::vector components; + for (unsigned int i = 0; i < n; i++) { + std::string i_name = name + "_" + std::to_string(i); + components.push_back(create_component(i_name, args...)); + } + return components; + } + + template + Parameter &createParameter(const std::string &name, const T &value) { + verifyIsUniqueParameterName(name); + auto sptr = std::make_unique>(name, value); + auto *ptr = sptr.get(); + m_parameters.emplace(std::move(sptr)); + return *ptr; + } + + std::vector getParameters() const { + std::vector parameters; + for (const auto &p : m_parameters) { + parameters.push_back(p.get()); + } + return parameters; + } + + void verifyIsUniquePortName(const std::string &name) { + if (!(isUniqueName(name, m_outputPorts) && + isUniqueName(name, m_inputPorts))) { + throw std::runtime_error("Duplicate port name: '" + name + + "' in component: '" + getName() + + "'. Port names must be unique."); + } + } + + void verifyIsUniqueComponentName(const std::string &name) { + if (!isUniqueName(name, m_subcomponents)) { + throw std::runtime_error("Duplicate subcomponent name: '" + name + + "' in component: '" + getName() + + "'. Subcomponent names must be unique."); + } + } + + void verifyIsUniqueParameterName(const std::string &name) { + if (!isUniqueName(name, m_parameters)) { + throw std::runtime_error("Duplicate parameter name: '" + name + + "' in component: '" + getName() + + "'. Parameter names must be unique."); + } + } + + template + bool isUniqueName(const std::string &name, + std::set, C_T> &container) { + return std::find_if(container.begin(), container.end(), + [name](const auto &p) { + return p->getName() == name; + }) == container.end(); + } + + void writeScope(VCDFile &file) { + auto d = file.scopeDef(getName()); + for (const auto &p : getAllPorts()) { + p->writeVar(file); + } + for (const auto &sc : m_subcomponents) { + sc->writeScope(file); + } + } + + template + T *cast() { + static_assert(std::is_base_of::value, + "Must cast to a simulator-specific component type"); + if constexpr (std::is_same::value) { + return this; + } else { + return dynamic_cast(this); + } + } + unsigned reserveConstantId() { return m_constantCount++; } + + void registerSynchronous(SimSynchronous *s) { + if (m_synchronous != nullptr) { + throw std::runtime_error( + "A synchronous object has already been registered to this component"); + } + m_synchronous = s; + } + bool isSynchronous() const { return m_synchronous != nullptr; } + SimSynchronous *getSynchronous() { return m_synchronous; } + + Gallant::Signal0<> changed; protected: - // Ports and subcomponents should be maintained as sorted sets based on port and component names, ensuring - // consistent ordering between executions - using PortSet = std::set, PortBaseCompT>; - PortSet m_outputPorts; - PortSet m_inputPorts; - PortSet m_signals; - std::set, ComponentCompT> m_subcomponents; - std::set> m_parameters; - std::map m_specialPorts; + // Ports and subcomponents should be maintained as sorted sets based on port + // and component names, ensuring consistent ordering between executions + using PortSet = std::set, PortBaseCompT>; + PortSet m_outputPorts; + PortSet m_inputPorts; + PortSet m_signals; + std::set, ComponentCompT> m_subcomponents; + std::set> m_parameters; + std::map m_specialPorts; private: - unsigned m_constantCount = 0; // Number of constants currently initialized in the component - SimSynchronous* m_synchronous = nullptr; + unsigned m_constantCount = + 0; // Number of constants currently initialized in the component + SimSynchronous *m_synchronous = nullptr; }; /** * @brief The SimSynchronous class - * Seen as addition to the interface of the SimComponent class, but not through inheritance. This is due to the fact - * that simulators may have their synchronous components as inheriting from the simulator-specific component type. - * To avoid a constraint saying that all interface and simulator level inheritance declarations must be declared - * virtual, we define synchronous elements as being an optional part which may be included in a normal SimComponent. + * Seen as addition to the interface of the SimComponent class, but not through + * inheritance. This is due to the fact that simulators may have their + * synchronous components as inheriting from the simulator-specific component + * type. To avoid a constraint saying that all interface and simulator level + * inheritance declarations must be declared virtual, we define synchronous + * elements as being an optional part which may be included in a normal + * SimComponent. */ class SimSynchronous { public: - SimSynchronous(SimComponent* parent) : m_parent(parent) { m_parent->registerSynchronous(this); } - virtual ~SimSynchronous() {} - virtual void reset() = 0; - virtual void reverse() = 0; - virtual void forceValue(VSRTL_VT_U addr, VSRTL_VT_U value) = 0; + SimSynchronous(SimComponent *parent) : m_parent(parent) { + m_parent->registerSynchronous(this); + } + virtual ~SimSynchronous() {} + virtual void reset() = 0; + virtual void reverse() = 0; + virtual void forceValue(VSRTL_VT_U addr, VSRTL_VT_U value) = 0; private: - /** - * @brief m_parent - * Defined as the object of which this synchronous declaration extends - */ - SimComponent* m_parent = nullptr; + /** + * @brief m_parent + * Defined as the object of which this synchronous declaration extends + */ + SimComponent *m_parent = nullptr; }; class SimDesign : public SimComponent { public: - SimDesign(const std::string& name, SimBase* parent) : SimComponent(name, parent) {} - virtual ~SimDesign() {} - /** - * @brief clock - * Simulates clocking the circuit. Registers are clocked and the propagation algorithm is run - * @pre A call to propagate() must be done, to set the initial state of the circuit - */ - virtual void clock() { + SimDesign(const std::string &name, SimBase *parent) + : SimComponent(name, parent) {} + virtual ~SimDesign() {} + /** + * @brief clock + * Simulates clocking the circuit. Registers are clocked and the propagation + * algorithm is run + * @pre A call to propagate() must be done, to set the initial state of the + * circuit + */ + virtual void clock() { #ifndef NDEBUG - assert(m_cycleCount != m_cycleCountPre && "Sim library should update cycle count!"); - m_cycleCountPre = m_cycleCount; + assert(m_cycleCount != m_cycleCountPre && + "Sim library should update cycle count!"); + m_cycleCountPre = m_cycleCount; #endif - if (clockedSignalsEnabled()) { - designWasClocked.Emit(); - } + if (clockedSignalsEnabled()) { + designWasClocked.Emit(); + } - if (vcdDump()) { - dumpVcdVarChanges(); - } + if (vcdDump()) { + dumpVcdVarChanges(); } + } - /** - * @brief reverse - * Undo the last clock operation. Registers will assert their previous state value. Memory elements will undo - * their last transaction. The circuit shall be repropagated and assume its previous-cycle state. - */ - virtual void reverse() { + /** + * @brief reverse + * Undo the last clock operation. Registers will assert their previous state + * value. Memory elements will undo their last transaction. The circuit shall + * be repropagated and assume its previous-cycle state. + */ + virtual void reverse() { #ifndef NDEBUG - assert(m_cycleCount != m_cycleCountPre && "Sim library should update cycle count!"); - m_cycleCountPre = m_cycleCount; + assert(m_cycleCount != m_cycleCountPre && + "Sim library should update cycle count!"); + m_cycleCountPre = m_cycleCount; #endif - if (clockedSignalsEnabled()) { - designWasReversed.Emit(); - } - } - - /** - * @brief propagate - * Propagate the circuit. - */ - virtual void propagate() = 0; - - /** - * @brief reset - * Resets the circuit, setting all registers to 0 and propagates the circuit. Constants might have an affect on - * the circuit in terms of not all component values being 0. - */ - virtual void reset() { + if (clockedSignalsEnabled()) { + designWasReversed.Emit(); + } + } + + /** + * @brief propagate + * Propagate the circuit. + */ + virtual void propagate() = 0; + + /** + * @brief reset + * Resets the circuit, setting all registers to 0 and propagates the circuit. + * Constants might have an affect on the circuit in terms of not all component + * values being 0. + */ + virtual void reset() { #ifndef NDEBUG - assert(m_cycleCount == 0 && "Sim library should have reset cycle count!"); - m_cycleCountPre = -1; + assert(m_cycleCount == 0 && "Sim library should have reset cycle count!"); + m_cycleCountPre = -1; #endif - if (clockedSignalsEnabled()) { - designWasReset.Emit(); - } - - if (m_dumpVcdFiles) { - resetVcdFile(); - } - } - - /** - * @brief canReverse - * @return is the simulator able to reverse? - */ - virtual bool canReverse() const = 0; - - /** - * @brief verifyAndInitialize - * Any post-construction initialization should be included in this function. - */ - virtual void verifyAndInitialize() { m_isVerifiedAndInitialized = true; } - bool isVerifiedAndInitialized() const { return m_isVerifiedAndInitialized; } - - /** - * m_emitsSignals related functions - * signalsEnabled() may be used by child components and ports of this design, to emit status change signals. - */ - void setEnableSignals(bool state) { m_emitsSignals = state; } - bool signalsEnabled() const { return m_emitsSignals; } - - /** - * m_emitsClockedSignals related functions - * clockedSignalsEnabled() may be used by the design to control whether the clocked/reversed signals are emitted. - */ - void setEnableClockedSignals(bool state) { m_emitsClockedSignals = state; } - bool clockedSignalsEnabled() const { return m_emitsClockedSignals; } - - virtual std::vector getRegisters() const { - return getSubComponents([=](SimComponent& c) { return c.isSynchronous(); }); - } - - long long getCycleCount() const { return m_cycleCount; } - - /** - * @brief vcdDump - * @param enabled; enables dumping of all ports to a vcd file. For each port in the circuit, we connect an - * additional slot which will queue a notice to this top-level Design that the variable change is to be written to - * the VCD file. - */ - void vcdDump(bool enabled) { - m_dumpVcdFiles = enabled; - std::map> componentGraph; - getComponentGraph(componentGraph); - for (const auto& compIt : componentGraph) { - for (const auto& port : compIt.first->getAllPorts()) { - port->changed.Disconnect(port, &SimPort::queueVcdVarChange); - if (enabled) { - port->changed.Connect(port, &SimPort::queueVcdVarChange); - } - } - } - } - - /** - * @brief vcdDump - * @returns whether the simulation is dumped to a .vcd file. - */ - bool vcdDump() const { return m_dumpVcdFiles; } - - /** - * @brief resetVcdFile - * Prepares a new VCD file for the circuit. A header is written containing all ports in the design, as vcd - * variables, scoped by the SimComponent hierarchy wherein they reside. - */ - void resetVcdFile() { - m_vcdFile = std::make_unique(getName() + ".vcd"); - { - auto def1 = m_vcdFile->writeHeader(); - auto def2 = m_vcdFile->scopeDef("TOP"); - m_vcdClkId = m_vcdFile->varDef("clk", 1); - for (const auto& it : m_subcomponents) { - it->writeScope(*m_vcdFile); - } - }; - { auto def3 = m_vcdFile->dumpVars(); } - m_vcdFile->writeTime(getCycleCount() * 2); - m_vcdVarChangeQueue.clear(); - } - - virtual void setSynchronousValue(SimSynchronous* c, VSRTL_VT_U addr, VSRTL_VT_U value) = 0; - - /** - * @brief queueVcdVarChange - * Caled by @param port to enqueue a notice of the fact that the port has changed its value in the current cycle. - */ - void queueVcdVarChange(const SimPort* port) { - if (m_vcdVarChangeQueue.count(port) != 0) { - throw std::runtime_error("Multiple changes for port " + port->getHierName() + " during a single cycle"); - } - m_vcdVarChangeQueue.insert(port); - } - - /** - * @brief dumpVcdVarChanges - * Increments simulation time in the .vcd file and dumps all enqueued variable changes to the file. - */ - void dumpVcdVarChanges() { - m_vcdFile->writeVarChange(m_vcdClkId, 1); - - for (const auto& port : m_vcdVarChangeQueue) { - m_vcdFile->writeVarChange(port->vcdId(), port->uValue()); - } - - m_vcdVarChangeQueue.clear(); - - m_vcdFile->writeTime(getCycleCount() * 2); - m_vcdFile->writeVarChange(m_vcdClkId, 0); - m_vcdFile->writeTime(getCycleCount() * 2 + 1); - - m_vcdFile->flush(); - } - - /** - * @brief clocked, reversed & reset signals - * These signals are emitted whenever the design has finished an entire clockcycle (clock + signal propagation). - * Signals are emitted if m_emitsClockedSignals is set. - */ - Gallant::Signal0<> designWasClocked; - Gallant::Signal0<> designWasReversed; - Gallant::Signal0<> designWasReset; + if (clockedSignalsEnabled()) { + designWasReset.Emit(); + } + + if (m_dumpVcdFiles) { + resetVcdFile(); + } + } + + /** + * @brief canReverse + * @return is the simulator able to reverse? + */ + virtual bool canReverse() const = 0; + + /** + * @brief verifyAndInitialize + * Any post-construction initialization should be included in this function. + */ + virtual void verifyAndInitialize() { m_isVerifiedAndInitialized = true; } + bool isVerifiedAndInitialized() const { return m_isVerifiedAndInitialized; } + + /** + * m_emitsSignals related functions + * signalsEnabled() may be used by child components and ports of this design, + * to emit status change signals. + */ + void setEnableSignals(bool state) { m_emitsSignals = state; } + bool signalsEnabled() const { return m_emitsSignals; } + + /** + * m_emitsClockedSignals related functions + * clockedSignalsEnabled() may be used by the design to control whether the + * clocked/reversed signals are emitted. + */ + void setEnableClockedSignals(bool state) { m_emitsClockedSignals = state; } + bool clockedSignalsEnabled() const { return m_emitsClockedSignals; } + + virtual std::vector getRegisters() const { + return getSubComponents([=](SimComponent &c) { return c.isSynchronous(); }); + } + + long long getCycleCount() const { return m_cycleCount; } + + /** + * @brief vcdDump + * @param enabled; enables dumping of all ports to a vcd file. For each port + * in the circuit, we connect an additional slot which will queue a notice to + * this top-level Design that the variable change is to be written to the VCD + * file. + */ + void vcdDump(bool enabled) { + m_dumpVcdFiles = enabled; + std::map> componentGraph; + getComponentGraph(componentGraph); + for (const auto &compIt : componentGraph) { + for (const auto &port : compIt.first->getAllPorts()) { + port->changed.Disconnect(port, &SimPort::queueVcdVarChange); + if (enabled) { + port->changed.Connect(port, &SimPort::queueVcdVarChange); + } + } + } + } + + /** + * @brief vcdDump + * @returns whether the simulation is dumped to a .vcd file. + */ + bool vcdDump() const { return m_dumpVcdFiles; } + + /** + * @brief resetVcdFile + * Prepares a new VCD file for the circuit. A header is written containing all + * ports in the design, as vcd variables, scoped by the SimComponent hierarchy + * wherein they reside. + */ + void resetVcdFile() { + m_vcdFile = std::make_unique(getName() + ".vcd"); + { + auto def1 = m_vcdFile->writeHeader(); + auto def2 = m_vcdFile->scopeDef("TOP"); + m_vcdClkId = m_vcdFile->varDef("clk", 1); + for (const auto &it : m_subcomponents) { + it->writeScope(*m_vcdFile); + } + }; + { auto def3 = m_vcdFile->dumpVars(); } + m_vcdFile->writeTime(getCycleCount() * 2); + m_vcdVarChangeQueue.clear(); + } + + virtual void setSynchronousValue(SimSynchronous *c, VSRTL_VT_U addr, + VSRTL_VT_U value) = 0; + + /** + * @brief queueVcdVarChange + * Caled by @param port to enqueue a notice of the fact that the port has + * changed its value in the current cycle. + */ + void queueVcdVarChange(const SimPort *port) { + if (m_vcdVarChangeQueue.count(port) != 0) { + throw std::runtime_error("Multiple changes for port " + + port->getHierName() + " during a single cycle"); + } + m_vcdVarChangeQueue.insert(port); + } + + /** + * @brief dumpVcdVarChanges + * Increments simulation time in the .vcd file and dumps all enqueued variable + * changes to the file. + */ + void dumpVcdVarChanges() { + m_vcdFile->writeVarChange(m_vcdClkId, 1); + + for (const auto &port : m_vcdVarChangeQueue) { + m_vcdFile->writeVarChange(port->vcdId(), port->uValue()); + } + + m_vcdVarChangeQueue.clear(); + + m_vcdFile->writeTime(getCycleCount() * 2); + m_vcdFile->writeVarChange(m_vcdClkId, 0); + m_vcdFile->writeTime(getCycleCount() * 2 + 1); + + m_vcdFile->flush(); + } + + /** + * @brief clocked, reversed & reset signals + * These signals are emitted whenever the design has finished an entire + * clockcycle (clock + signal propagation). Signals are emitted if + * m_emitsClockedSignals is set. + */ + Gallant::Signal0<> designWasClocked; + Gallant::Signal0<> designWasReversed; + Gallant::Signal0<> designWasReset; protected: - long long m_cycleCount = 0; - bool m_emitsSignals = true; + long long m_cycleCount = 0; + bool m_emitsSignals = true; private: - bool m_emitsClockedSignals = true; - bool m_isVerifiedAndInitialized = false; + bool m_emitsClockedSignals = true; + bool m_isVerifiedAndInitialized = false; - // VCD dump members - std::unique_ptr m_vcdFile; - std::set m_vcdVarChangeQueue; - std::string m_vcdClkId; - bool m_dumpVcdFiles = false; + // VCD dump members + std::unique_ptr m_vcdFile; + std::set m_vcdVarChangeQueue; + std::string m_vcdClkId; + bool m_dumpVcdFiles = false; #ifndef NDEBUG - long long m_cycleCountPre = 0; + long long m_cycleCountPre = 0; #endif }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_parameter.h b/interface/vsrtl_parameter.h index ed45581..16bed68 100644 --- a/interface/vsrtl_parameter.h +++ b/interface/vsrtl_parameter.h @@ -9,51 +9,57 @@ namespace vsrtl { class ParameterBase { public: - ParameterBase(const std::string& name) : m_name(name) {} - virtual ~ParameterBase() {} - const std::string& getName() const { return m_name; } - const std::string& getTooltip() const { return m_tooltip; } - void setTooltip(const std::string& tooltip) { m_tooltip = tooltip; } + ParameterBase(const std::string &name) : m_name(name) {} + virtual ~ParameterBase() {} + const std::string &getName() const { return m_name; } + const std::string &getTooltip() const { return m_tooltip; } + void setTooltip(const std::string &tooltip) { m_tooltip = tooltip; } protected: - std::string m_name; - std::string m_tooltip; + std::string m_name; + std::string m_tooltip; }; /** * @brief The Parameter class - * If the parameter of type @p T is supported by the GUI, the parameter may be used in the component through the get/set - * functions to retrieve values as set through the GUI. - * Alternate type @p IT is provided for specifying the initial value of the parameter, in cases where parameter type and - * initial type differ (such as if a parameter is of type List, but has an initial value of type String. + * If the parameter of type @p T is supported by the GUI, the parameter may be + * used in the component through the get/set functions to retrieve values as set + * through the GUI. Alternate type @p IT is provided for specifying the initial + * value of the parameter, in cases where parameter type and initial type differ + * (such as if a parameter is of type List, but has an initial value of + * type String. */ template class Parameter : public ParameterBase { - static_assert(std::is_same::value || std::is_same::value || std::is_same::value || - std::is_same, T>::value || std::is_same, T>::value, - "Unsupported parameter type"); + static_assert(std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same, T>::value || + std::is_same, T>::value, + "Unsupported parameter type"); public: - Parameter(const std::string& name, const T& value = T()) : ParameterBase(name), m_value(value) {} - T& getValue() { return m_value; } - void setValue(const T& value) { - m_value = value; - changed.Emit(); - } - - /** - * @brief Option semantics - * T=int : Value range = [options[0], options[1]] - * Any vector type : Allowed values - */ - void setOptions(const std::vector& options) { m_options = options; } - const std::vector& getOptions() const { return m_options; } - - Gallant::Signal0<> changed; + Parameter(const std::string &name, const T &value = T()) + : ParameterBase(name), m_value(value) {} + T &getValue() { return m_value; } + void setValue(const T &value) { + m_value = value; + changed.Emit(); + } + + /** + * @brief Option semantics + * T=int : Value range = [options[0], options[1]] + * Any vector type : Allowed values + */ + void setOptions(const std::vector &options) { m_options = options; } + const std::vector &getOptions() const { return m_options; } + + Gallant::Signal0<> changed; protected: - T m_value; - std::vector m_options; + T m_value; + std::vector m_options; }; -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_vcdfile.cpp b/interface/vsrtl_vcdfile.cpp index 7f966a8..1e3cd53 100644 --- a/interface/vsrtl_vcdfile.cpp +++ b/interface/vsrtl_vcdfile.cpp @@ -7,89 +7,89 @@ namespace vsrtl { -std::string vcdSafeString(const std::string& string) { - auto cp = string; - std::replace(cp.begin(), cp.end(), ' ', '_'); - return cp; +std::string vcdSafeString(const std::string &string) { + auto cp = string; + std::replace(cp.begin(), cp.end(), ' ', '_'); + return cp; } template std::string binStr(T val, unsigned width) { - std::string s; - for (unsigned i = 0; i < width; i++) { - s = ((val & 0b1) ? "1" : "0") + s; - val >>= 1; - } - return s; + std::string s; + for (unsigned i = 0; i < width; i++) { + s = ((val & 0b1) ? "1" : "0") + s; + val >>= 1; + } + return s; } -VCDFile::VCDFile(const std::string& filename) { - m_file.open(filename, std::ios_base::trunc); +VCDFile::VCDFile(const std::string &filename) { + m_file.open(filename, std::ios_base::trunc); } -VCDFile::~VCDFile() { - m_file.close(); -} +VCDFile::~VCDFile() { m_file.close(); } -void VCDFile::writeLine(const std::string& line) { - if (!m_file.is_open()) { - throw std::runtime_error("Tried to write to file, but file was not open"); - } - const std::string indent = std::string(m_scopeLevel * 4, ' '); - m_file << indent << line + "\n"; +void VCDFile::writeLine(const std::string &line) { + if (!m_file.is_open()) { + throw std::runtime_error("Tried to write to file, but file was not open"); + } + const std::string indent = std::string(m_scopeLevel * 4, ' '); + m_file << indent << line + "\n"; }; Defer VCDFile::dumpVars() { - writeLine("$dumpvars"); - for (const auto& it : m_dumpVars) { - writeVarChange(it.first, it.second); - } - return Defer([=] { writeLine("$end"); }); + writeLine("$dumpvars"); + for (const auto &it : m_dumpVars) { + writeVarChange(it.first, it.second); + } + return Defer([=] { writeLine("$end"); }); } Defer VCDFile::writeHeader() { - writeLine("$version Generated by VSRTL $end"); - auto t = std::time(nullptr); - auto tm = *std::localtime(&t); - std::ostringstream oss; - oss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S"); - - writeLine("$date " + oss.str() + " $end"); - writeLine("$timescale 1ns $end"); - return Defer([=] { writeLine("$enddefinitions $end"); }); + writeLine("$version Generated by VSRTL $end"); + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + std::ostringstream oss; + oss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S"); + + writeLine("$date " + oss.str() + " $end"); + writeLine("$timescale 1ns $end"); + return Defer([=] { writeLine("$enddefinitions $end"); }); } -Defer VCDFile::scopeDef(const std::string& name) { - writeLine("$scope module " + vcdSafeString(name) + " $end"); - m_scopeLevel++; - return Defer([=] { - m_scopeLevel--; - writeLine("$upscope $end"); - }); +Defer VCDFile::scopeDef(const std::string &name) { + writeLine("$scope module " + vcdSafeString(name) + " $end"); + m_scopeLevel++; + return Defer([=] { + m_scopeLevel--; + writeLine("$upscope $end"); + }); } -std::string VCDFile::varDef(const std::string& name, unsigned int width) { - const std::string id = "id" + std::to_string(m_varCntr++); - writeLine("$var wire " + std::to_string(width) + " " + id + " " + vcdSafeString(name) + - (width > 0 ? "[" + std::to_string(width - 1) + ":0]" : "") + " $end"); - m_varWidths[id] = width; - return id; +std::string VCDFile::varDef(const std::string &name, unsigned int width) { + const std::string id = "id" + std::to_string(m_varCntr++); + writeLine("$var wire " + std::to_string(width) + " " + id + " " + + vcdSafeString(name) + + (width > 0 ? "[" + std::to_string(width - 1) + ":0]" : "") + + " $end"); + m_varWidths[id] = width; + return id; } void VCDFile::writeTime(uint64_t time) { - writeLine("#" + std::to_string(time)); + writeLine("#" + std::to_string(time)); } -void VCDFile::writeVarChange(const std::string& ref, uint64_t value) { - std::string valStr; - const unsigned width = m_varWidths.at(ref); - if (width == 1) { - valStr = (static_cast(value) ? "1" : "0") + ref; - } else { - valStr = "b" + binStr(value, width) + " " + ref; - } +void VCDFile::writeVarChange(const std::string &ref, uint64_t value) { + std::string valStr; + const unsigned width = m_varWidths.at(ref); + if (width == 1) { + valStr = (static_cast(value) ? "1" : "0") + ref; + } else { + valStr = "b" + binStr(value, width) + " " + ref; + } - writeLine(valStr); + writeLine(valStr); } -} // namespace vsrtl +} // namespace vsrtl diff --git a/interface/vsrtl_vcdfile.h b/interface/vsrtl_vcdfile.h index c096226..802f428 100644 --- a/interface/vsrtl_vcdfile.h +++ b/interface/vsrtl_vcdfile.h @@ -10,40 +10,42 @@ namespace vsrtl { struct Defer { - Defer(const std::function f) : m_f(f) {} - ~Defer() { m_f(); } + Defer(const std::function f) : m_f(f) {} + ~Defer() { m_f(); } private: - std::function m_f; + std::function m_f; }; class VCDFile { public: - VCDFile(const std::string& filename); - ~VCDFile(); - Defer writeHeader(); - Defer scopeDef(const std::string& name); - Defer dumpVars(); - void flush() { m_file.flush(); } - - // Defines the variable @p name within the current scope, and returns a unique identifier associated with the - // variable. - std::string varDef(const std::string& name, unsigned width); - void writeTime(uint64_t time); - void writeVarChange(const std::string& ref, uint64_t value); - void varInitVal(const std::string& ref, uint64_t value) { m_dumpVars[ref] = value; } + VCDFile(const std::string &filename); + ~VCDFile(); + Defer writeHeader(); + Defer scopeDef(const std::string &name); + Defer dumpVars(); + void flush() { m_file.flush(); } + + // Defines the variable @p name within the current scope, and returns a unique + // identifier associated with the variable. + std::string varDef(const std::string &name, unsigned width); + void writeTime(uint64_t time); + void writeVarChange(const std::string &ref, uint64_t value); + void varInitVal(const std::string &ref, uint64_t value) { + m_dumpVars[ref] = value; + } private: - std::string genId(); - void writeLine(const std::string& line); - std::ofstream m_file; - std::map m_varWidths; - std::map m_dumpVars; - - unsigned m_varCntr = 0; - unsigned m_scopeLevel = 0; + std::string genId(); + void writeLine(const std::string &line); + std::ofstream m_file; + std::map m_varWidths; + std::map m_dumpVars; + + unsigned m_varCntr = 0; + unsigned m_scopeLevel = 0; }; -} // namespace vsrtl +} // namespace vsrtl -#endif // VSRTL_VCDUTILS_H +#endif // VSRTL_VCDUTILS_H diff --git a/test/tst_adderandreg.cpp b/test/tst_adderandreg.cpp index 935df0d..ef71893 100644 --- a/test/tst_adderandreg.cpp +++ b/test/tst_adderandreg.cpp @@ -3,23 +3,24 @@ #include "vsrtl_adderandreg.h" class tst_adderAndReg : public QObject { - Q_OBJECT private slots : void functionalTest(); + Q_OBJECT private slots : void functionalTest(); }; void tst_adderAndReg::functionalTest() { - vsrtl::core::AdderAndReg a; + vsrtl::core::AdderAndReg a; - // Verify that all instantiated objects in the circuit have been connected as they require - a.verifyAndInitialize(); - std::cout << "value " << std::endl; - const int n = 10; - const int expectedValue = n * a.m_cVal; - // Clock the circuit n times - for (int i = 0; i < n; i++) - a.clock(); + // Verify that all instantiated objects in the circuit have been connected as + // they require + a.verifyAndInitialize(); + std::cout << "value " << std::endl; + const int n = 10; + const int expectedValue = n * a.m_cVal; + // Clock the circuit n times + for (int i = 0; i < n; i++) + a.clock(); - // We expect that m_cVal has been added to the register value n times - QVERIFY(a.reg->out.uValue() == expectedValue); + // We expect that m_cVal has been added to the register value n times + QVERIFY(a.reg->out.uValue() == expectedValue); } QTEST_APPLESS_MAIN(tst_adderAndReg) diff --git a/test/tst_aluandreg.cpp b/test/tst_aluandreg.cpp index 1aede50..52d2481 100644 --- a/test/tst_aluandreg.cpp +++ b/test/tst_aluandreg.cpp @@ -3,23 +3,24 @@ #include "vsrtl_aluandreg.h" class tst_ALUAndReg : public QObject { - Q_OBJECT private slots : void functionalTest(); + Q_OBJECT private slots : void functionalTest(); }; void tst_ALUAndReg::functionalTest() { - vsrtl::core::ALUAndReg a; + vsrtl::core::ALUAndReg a; - // Verify that all instantiated objects in the circuit have been connected as they require - a.verifyAndInitialize(); - std::cout << "value " << std::endl; - const int n = 10; - const int expectedValue = n * a.m_cVal; - // Clock the circuit n times - for (int i = 0; i < n; i++) - a.clock(); + // Verify that all instantiated objects in the circuit have been connected as + // they require + a.verifyAndInitialize(); + std::cout << "value " << std::endl; + const int n = 10; + const int expectedValue = n * a.m_cVal; + // Clock the circuit n times + for (int i = 0; i < n; i++) + a.clock(); - // We expect that m_cVal has been added to the register value n times - QVERIFY(a.reg->out.uValue() == expectedValue); + // We expect that m_cVal has been added to the register value n times + QVERIFY(a.reg->out.uValue() == expectedValue); } QTEST_APPLESS_MAIN(tst_ALUAndReg) diff --git a/test/tst_counter.cpp b/test/tst_counter.cpp index 1dc1c68..334c45a 100644 --- a/test/tst_counter.cpp +++ b/test/tst_counter.cpp @@ -8,38 +8,38 @@ using namespace vsrtl; using namespace core; class tst_counter : public QObject { - Q_OBJECT + Q_OBJECT private slots: - void clockTest(); + void clockTest(); }; template void testCounter() { - vsrtl::core::Counter counter; - counter.verifyAndInitialize(); + vsrtl::core::Counter counter; + counter.verifyAndInitialize(); - const VSRTL_VT_U powVal = std::pow(2, n); + const VSRTL_VT_U powVal = std::pow(2, n); - QVERIFY(counter.value->out.uValue() == 0); + QVERIFY(counter.value->out.uValue() == 0); + counter.clock(); + QVERIFY(counter.value->out.uValue() == 1); + counter.reset(); + QVERIFY(counter.value->out.uValue() == 0); + for (unsigned i = 0; i < powVal - 1; i++) { counter.clock(); - QVERIFY(counter.value->out.uValue() == 1); - counter.reset(); - QVERIFY(counter.value->out.uValue() == 0); - for (unsigned i = 0; i < powVal - 1; i++) { - counter.clock(); - } - // Counter should be at max value - QVERIFY(counter.value->out.uValue() == (powVal - 1)); - - // counter should overflow - counter.clock(); - QVERIFY(counter.value->out.uValue() == 0); + } + // Counter should be at max value + QVERIFY(counter.value->out.uValue() == (powVal - 1)); + + // counter should overflow + counter.clock(); + QVERIFY(counter.value->out.uValue() == 0); } void tst_counter::clockTest() { - testCounter<1>(); - testCounter<4>(); - testCounter<8>(); + testCounter<1>(); + testCounter<4>(); + testCounter<8>(); } QTEST_APPLESS_MAIN(tst_counter) #include "tst_counter.moc" diff --git a/test/tst_enumandmux.cpp b/test/tst_enumandmux.cpp index 5c29e47..2f18ad8 100644 --- a/test/tst_enumandmux.cpp +++ b/test/tst_enumandmux.cpp @@ -3,12 +3,10 @@ #include "vsrtl_enumandmux.h" class tst_enumAndMux : public QObject { - Q_OBJECT private slots : void functionalTest(); + Q_OBJECT private slots : void functionalTest(); }; -void tst_enumAndMux::functionalTest() { - vsrtl::core::EnumAndMux a; -} +void tst_enumAndMux::functionalTest() { vsrtl::core::EnumAndMux a; } QTEST_APPLESS_MAIN(tst_enumAndMux) #include "tst_enumandmux.moc" diff --git a/test/tst_leros.cpp b/test/tst_leros.cpp index 6c054ad..a2cd266 100644 --- a/test/tst_leros.cpp +++ b/test/tst_leros.cpp @@ -3,125 +3,130 @@ #include "Leros/SingleCycleLeros/SingleCycleLeros.h" class tst_Leros : public QObject { - Q_OBJECT + Q_OBJECT private slots: - void functionalTest(); - void incInAccumulator(); - void incInRegister(); - void incInMemory(); - void startupInc(); + void functionalTest(); + void incInAccumulator(); + void incInRegister(); + void incInMemory(); + void startupInc(); }; void tst_Leros::startupInc() { - std::vector program = { - 0x2100, 0x3064, 0x2101, 0x3065, 0x2180, 0x2900, 0x3066, 0x2100, 0x2980, 0x2a00, 0x3067, 0x2100, 0x2b80, 0x3068, - 0x21ff, 0x2900, 0x3069, 0x29ff, 0x2a00, 0x306a, 0x2100, 0x2aff, 0x306b, 0x21ff, 0x2b7f, 0x306c, 0x21cc, 0x2900, - 0x2a00, 0x2b00, 0x3078, 0x21e4, 0x2900, 0x2a00, 0x2b00, 0x3079, 0x21fc, 0x2900, 0x2a00, 0x2b00, 0x307a, 0x2100, - 0x2900, 0x2a00, 0x2b00, 0x9011, 0x3001, 0x2100, 0x2900, 0x2a00, 0x2b20, 0x3002, 0x5002, 0x2100, 0x7000, 0x2002, - 0x0904, 0x3002, 0x2001, 0x0d01, 0x3001, 0xaff7, 0x21fc, 0x290f, 0x2a00, 0x2b20, 0x3001, 0x2194, 0x2900, 0x2a00, - 0x2b00, 0x4000, 0x8000, 0x0000, 0x2001, 0x09f0, 0x3001, 0x2000, 0x5001, 0x7003, 0x2002, 0x7002, 0x2001, 0x0910, - 0x3002, 0x8001, 0x5002, 0x60fd, 0x0901, 0x70fd, 0x60fd, 0x2304, 0x3004, 0x9008, 0x8001, 0x5002, 0x60fc, 0x0901, - 0x3004, 0x70fc, 0x8001, 0x8ff1, 0x2005, 0x9009, 0x2004, 0x0804, 0x3004, 0x2005, 0x0d01, 0x9003, 0x3005, 0x8ff9, - 0x2000, 0x4000, 0x2005, 0x9009, 0x2004, 0x1000, 0x3004, 0x2005, 0x0d01, 0x9003, 0x3005, 0x8ff9, 0x2000, 0x4000, - 0x2005, 0x900a, 0x2004, 0x1000, 0x226c, 0x3004, 0x2005, 0x0d01, 0x9003, 0x3005, 0x8ff8, 0x2000, 0x4000}; - Q_UNUSED(program); + std::vector program = { + 0x2100, 0x3064, 0x2101, 0x3065, 0x2180, 0x2900, 0x3066, 0x2100, 0x2980, + 0x2a00, 0x3067, 0x2100, 0x2b80, 0x3068, 0x21ff, 0x2900, 0x3069, 0x29ff, + 0x2a00, 0x306a, 0x2100, 0x2aff, 0x306b, 0x21ff, 0x2b7f, 0x306c, 0x21cc, + 0x2900, 0x2a00, 0x2b00, 0x3078, 0x21e4, 0x2900, 0x2a00, 0x2b00, 0x3079, + 0x21fc, 0x2900, 0x2a00, 0x2b00, 0x307a, 0x2100, 0x2900, 0x2a00, 0x2b00, + 0x9011, 0x3001, 0x2100, 0x2900, 0x2a00, 0x2b20, 0x3002, 0x5002, 0x2100, + 0x7000, 0x2002, 0x0904, 0x3002, 0x2001, 0x0d01, 0x3001, 0xaff7, 0x21fc, + 0x290f, 0x2a00, 0x2b20, 0x3001, 0x2194, 0x2900, 0x2a00, 0x2b00, 0x4000, + 0x8000, 0x0000, 0x2001, 0x09f0, 0x3001, 0x2000, 0x5001, 0x7003, 0x2002, + 0x7002, 0x2001, 0x0910, 0x3002, 0x8001, 0x5002, 0x60fd, 0x0901, 0x70fd, + 0x60fd, 0x2304, 0x3004, 0x9008, 0x8001, 0x5002, 0x60fc, 0x0901, 0x3004, + 0x70fc, 0x8001, 0x8ff1, 0x2005, 0x9009, 0x2004, 0x0804, 0x3004, 0x2005, + 0x0d01, 0x9003, 0x3005, 0x8ff9, 0x2000, 0x4000, 0x2005, 0x9009, 0x2004, + 0x1000, 0x3004, 0x2005, 0x0d01, 0x9003, 0x3005, 0x8ff9, 0x2000, 0x4000, + 0x2005, 0x900a, 0x2004, 0x1000, 0x226c, 0x3004, 0x2005, 0x0d01, 0x9003, + 0x3005, 0x8ff8, 0x2000, 0x4000}; + Q_UNUSED(program); } -void tst_Leros::functionalTest() { - vsrtl::leros::SingleCycleLeros design; -} +void tst_Leros::functionalTest() { vsrtl::leros::SingleCycleLeros design; } void tst_Leros::incInRegister() { - vsrtl::leros::SingleCycleLeros design; - - /** - * load 0 - * addi 1 - * store 0 - * br -6 - */ - std::vector program = {0x2000, 0x0901, 0x3000, 0x8FFD}; - design.m_memory->addInitializationMemory(0x0, program.data(), program.size()); - design.verifyAndInitialize(); + vsrtl::leros::SingleCycleLeros design; + + /** + * load 0 + * addi 1 + * store 0 + * br -6 + */ + std::vector program = {0x2000, 0x0901, 0x3000, 0x8FFD}; + design.m_memory->addInitializationMemory(0x0, program.data(), program.size()); + design.verifyAndInitialize(); } void tst_Leros::incInMemory() { - vsrtl::leros::SingleCycleLeros design; - - /** - loadhi 1 -- 0x100 - store 0 - ldaddr 0 - loadi 0 - stind 0 -- store 0 at 0x100[0] - .loop: - ldind 0 - addi 1 - stind 0 - loadi 0 -- ensure that value is not staying in accummulator - br -8 - */ - std::vector program = {0x2901, 0x3000, 0x5000, 0x2100, 0x7000, - 0x6000, 0x0901, 0x7000, 0x2100, 0x8FFC}; - - design.m_memory->addInitializationMemory(0x0, program.data(), program.size()); - design.verifyAndInitialize(); - - constexpr int accTarget = 10; - - // Go to .loop - for (int i = 0; i < 5; i++) - design.clock(); - - // Increment - for (unsigned int i = 0; i < accTarget; i++) { - design.clock(); // Execute ldind 0 - QVERIFY(design.acc_reg->out.uValue() == i); - design.clock(); - design.clock(); - design.clock(); - design.clock(); - } - - // Reverse - for (unsigned int i = accTarget - 1; i >= accTarget / 2; i--) { - design.reverse(); - design.reverse(); - design.reverse(); - design.reverse(); - QCOMPARE(design.acc_reg->out.uValue(), i); - design.reverse(); - } + vsrtl::leros::SingleCycleLeros design; + + /** + loadhi 1 -- 0x100 + store 0 + ldaddr 0 + loadi 0 + stind 0 -- store 0 at 0x100[0] + .loop: + ldind 0 + addi 1 + stind 0 + loadi 0 -- ensure that value is not staying in accummulator + br -8 + */ + std::vector program = {0x2901, 0x3000, 0x5000, 0x2100, + 0x7000, 0x6000, 0x0901, 0x7000, + 0x2100, 0x8FFC}; + + design.m_memory->addInitializationMemory(0x0, program.data(), program.size()); + design.verifyAndInitialize(); + + constexpr int accTarget = 10; + + // Go to .loop + for (int i = 0; i < 5; i++) + design.clock(); + + // Increment + for (unsigned int i = 0; i < accTarget; i++) { + design.clock(); // Execute ldind 0 + QVERIFY(design.acc_reg->out.uValue() == i); + design.clock(); + design.clock(); + design.clock(); + design.clock(); + } + + // Reverse + for (unsigned int i = accTarget - 1; i >= accTarget / 2; i--) { + design.reverse(); + design.reverse(); + design.reverse(); + design.reverse(); + QCOMPARE(design.acc_reg->out.uValue(), i); + design.reverse(); + } } void tst_Leros::incInAccumulator() { - vsrtl::leros::SingleCycleLeros design; - - /** - * addi 1 - * br -2 - */ - std::vector program = {0x0901, 0x8FFF}; - design.m_memory->addInitializationMemory(0x0, program.data(), program.size()); - design.verifyAndInitialize(); - - constexpr int accTarget = 10; - QVERIFY(design.acc_reg->out.uValue() == 0); - - // Increment - for (uint32_t i = 0; i < accTarget; i++) { - QVERIFY(design.acc_reg->out.uValue() == i); - // cycle 1: addi, cycle 2: br - design.clock(); - design.clock(); - } - - // Reverse - for (uint32_t i = accTarget; i >= accTarget / 2; i--) { - QVERIFY(design.acc_reg->out.uValue() == i); - design.reverse(); - design.reverse(); - } + vsrtl::leros::SingleCycleLeros design; + + /** + * addi 1 + * br -2 + */ + std::vector program = {0x0901, 0x8FFF}; + design.m_memory->addInitializationMemory(0x0, program.data(), program.size()); + design.verifyAndInitialize(); + + constexpr int accTarget = 10; + QVERIFY(design.acc_reg->out.uValue() == 0); + + // Increment + for (uint32_t i = 0; i < accTarget; i++) { + QVERIFY(design.acc_reg->out.uValue() == i); + // cycle 1: addi, cycle 2: br + design.clock(); + design.clock(); + } + + // Reverse + for (uint32_t i = accTarget; i >= accTarget / 2; i--) { + QVERIFY(design.acc_reg->out.uValue() == i); + design.reverse(); + design.reverse(); + } } QTEST_APPLESS_MAIN(tst_Leros) diff --git a/test/tst_memory.cpp b/test/tst_memory.cpp index a9a02f5..6e0774f 100644 --- a/test/tst_memory.cpp +++ b/test/tst_memory.cpp @@ -12,108 +12,108 @@ using namespace core; */ class ContinuousIncrement : public Design { public: - ContinuousIncrement() : Design("Registerfile Tester") { - mem->setMemory(m_memory); - - idx_reg->out >> mem->addr; - mem->data_out >> acc_reg->in; - inc_adder->out >> mem->data_in; - 1 >> idx_adder->op1; - idx_reg->out >> idx_adder->op2; - 1 >> inc_adder->op1; - acc_reg->out >> inc_adder->op2; - - wr_en_reg->out >> idx_next_mux->select; - idx_adder->out >> *idx_next_mux->ins[0]; - idx_reg->out >> *idx_next_mux->ins[1]; - - idx_reg->out >> comp->op1; - (regSize - 1) >> comp->op2; - idx_next_mux->out >> idx_reg->in; - - // Write/Read state - wr_en_mux->out >> wr_en_reg->in; - wr_en_reg->out >> wr_en_mux->select; - wr_en_reg->out >> mem->wr_en; - 4 >> mem->wr_width; - 0 >> *wr_en_mux->ins[1]; - 1 >> *wr_en_mux->ins[0]; - } - static constexpr unsigned int regSize = 32; - - // Create objects - SUBCOMPONENT(mem, TYPE(MemoryAsyncRd)); - - SUBCOMPONENT(idx_adder, Adder); - SUBCOMPONENT(inc_adder, Adder); - SUBCOMPONENT(wr_en_mux, TYPE(Multiplexer<2, 1>)); - SUBCOMPONENT(idx_next_mux, TYPE(Multiplexer<2, regSize>)); - - SUBCOMPONENT(comp, Eq); - - SUBCOMPONENT(wr_en_reg, Register<1>); - SUBCOMPONENT(idx_reg, Register); - SUBCOMPONENT(acc_reg, Register); - - ADDRESSSPACEMM(m_memory); + ContinuousIncrement() : Design("Registerfile Tester") { + mem->setMemory(m_memory); + + idx_reg->out >> mem->addr; + mem->data_out >> acc_reg->in; + inc_adder->out >> mem->data_in; + 1 >> idx_adder->op1; + idx_reg->out >> idx_adder->op2; + 1 >> inc_adder->op1; + acc_reg->out >> inc_adder->op2; + + wr_en_reg->out >> idx_next_mux->select; + idx_adder->out >> *idx_next_mux->ins[0]; + idx_reg->out >> *idx_next_mux->ins[1]; + + idx_reg->out >> comp->op1; + (regSize - 1) >> comp->op2; + idx_next_mux->out >> idx_reg->in; + + // Write/Read state + wr_en_mux->out >> wr_en_reg->in; + wr_en_reg->out >> wr_en_mux->select; + wr_en_reg->out >> mem->wr_en; + 4 >> mem->wr_width; + 0 >> *wr_en_mux->ins[1]; + 1 >> *wr_en_mux->ins[0]; + } + static constexpr unsigned int regSize = 32; + + // Create objects + SUBCOMPONENT(mem, TYPE(MemoryAsyncRd)); + + SUBCOMPONENT(idx_adder, Adder); + SUBCOMPONENT(inc_adder, Adder); + SUBCOMPONENT(wr_en_mux, TYPE(Multiplexer<2, 1>)); + SUBCOMPONENT(idx_next_mux, TYPE(Multiplexer<2, regSize>)); + + SUBCOMPONENT(comp, Eq); + + SUBCOMPONENT(wr_en_reg, Register<1>); + SUBCOMPONENT(idx_reg, Register); + SUBCOMPONENT(acc_reg, Register); + + ADDRESSSPACEMM(m_memory); }; class WriteSameIdx : public Design { public: - WriteSameIdx() : Design("Registerfile Tester") { - mem->data_out >> inc_adder->op1; - 1 >> inc_adder->op2; - inc_adder->out >> mem->data_in; - 1 >> mem->wr_en; - 4 >> mem->wr_width; - 0x0 >> mem->addr; - - mem->setMemory(m_memory); - } - static constexpr unsigned int regs = 32; - static constexpr unsigned int regWidth = CHAR_BIT * sizeof(VSRTL_VT_U); - - // Create objects - SUBCOMPONENT(mem, TYPE(MemorySyncRd)); - SUBCOMPONENT(inc_adder, Adder); - - ADDRESSSPACEMM(m_memory); + WriteSameIdx() : Design("Registerfile Tester") { + mem->data_out >> inc_adder->op1; + 1 >> inc_adder->op2; + inc_adder->out >> mem->data_in; + 1 >> mem->wr_en; + 4 >> mem->wr_width; + 0x0 >> mem->addr; + + mem->setMemory(m_memory); + } + static constexpr unsigned int regs = 32; + static constexpr unsigned int regWidth = CHAR_BIT * sizeof(VSRTL_VT_U); + + // Create objects + SUBCOMPONENT(mem, TYPE(MemorySyncRd)); + SUBCOMPONENT(inc_adder, Adder); + + ADDRESSSPACEMM(m_memory); }; -} // namespace vsrtl +} // namespace vsrtl class tst_memory : public QObject { - Q_OBJECT; + Q_OBJECT; private slots: - void repeatedWriteSameIdxSync(); - void functionalTest(); + void repeatedWriteSameIdxSync(); + void functionalTest(); }; void tst_memory::functionalTest() { - vsrtl::ContinuousIncrement a; + vsrtl::ContinuousIncrement a; - a.verifyAndInitialize(); + a.verifyAndInitialize(); - const int n = 1234; - // Clock the circuit n times - for (int i = 0; i < n; i++) - a.clock(); + const int n = 1234; + // Clock the circuit n times + for (int i = 0; i < n; i++) + a.clock(); - QVERIFY(a.mem->data_out.uValue() == n / 2); + QVERIFY(a.mem->data_out.uValue() == n / 2); } void tst_memory::repeatedWriteSameIdxSync() { - vsrtl::WriteSameIdx a; + vsrtl::WriteSameIdx a; - a.verifyAndInitialize(); + a.verifyAndInitialize(); - const int n = 10; - // Clock the circuit n times - for (unsigned i = 0; i < n; i++) { - QVERIFY(a.mem->data_out.uValue() == i); - a.clock(); - } + const int n = 10; + // Clock the circuit n times + for (unsigned i = 0; i < n; i++) { + QVERIFY(a.mem->data_out.uValue() == i); + a.clock(); + } } QTEST_APPLESS_MAIN(tst_memory) diff --git a/test/tst_nestedcomponent.cpp b/test/tst_nestedcomponent.cpp index b14d4ef..a1dc289 100644 --- a/test/tst_nestedcomponent.cpp +++ b/test/tst_nestedcomponent.cpp @@ -3,22 +3,23 @@ #include "vsrtl_nestedexponenter.h" class tst_NestedComponents : public QObject { - Q_OBJECT private slots : void functionalTest(); + Q_OBJECT private slots : void functionalTest(); }; void tst_NestedComponents::functionalTest() { - vsrtl::core::NestedExponenter a; + vsrtl::core::NestedExponenter a; - // Verify that all instantiated objects in the circuit have been connected as they require - a.verifyAndInitialize(); + // Verify that all instantiated objects in the circuit have been connected as + // they require + a.verifyAndInitialize(); - const int n = 10; - // Clock the circuit n times - for (int i = 0; i < n; i++) - a.clock(); + const int n = 10; + // Clock the circuit n times + for (int i = 0; i < n; i++) + a.clock(); - // We expect that m_cVal has been added to the register value n times - // REQUIRE(a.regs->value(5) == 40); + // We expect that m_cVal has been added to the register value n times + // REQUIRE(a.regs->value(5) == 40); } QTEST_APPLESS_MAIN(tst_NestedComponents) #include "tst_nestedcomponent.moc" diff --git a/test/tst_rannumgen.cpp b/test/tst_rannumgen.cpp index 39961f9..5149e28 100644 --- a/test/tst_rannumgen.cpp +++ b/test/tst_rannumgen.cpp @@ -6,28 +6,28 @@ using namespace vsrtl; using namespace core; class tst_rannumgen : public QObject { - Q_OBJECT private slots : void functionalTest(); + Q_OBJECT private slots : void functionalTest(); }; VSRTL_VT_U xorshift(VSRTL_VT_U x) { - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - return x; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; } void tst_rannumgen::functionalTest() { - RanNumGen a; - a.verifyAndInitialize(); + RanNumGen a; + a.verifyAndInitialize(); - VSRTL_VT_U seed = 0x13fb27a3; - // Clock the circuit n times - for (int i = 0; i < 10; i++) { - a.clock(); - VSRTL_VT_U circuit_val = a.rngResReg->out.uValue(); - QVERIFY(circuit_val == seed); - seed = xorshift(seed); - } + VSRTL_VT_U seed = 0x13fb27a3; + // Clock the circuit n times + for (int i = 0; i < 10; i++) { + a.clock(); + VSRTL_VT_U circuit_val = a.rngResReg->out.uValue(); + QVERIFY(circuit_val == seed); + seed = xorshift(seed); + } } QTEST_APPLESS_MAIN(tst_rannumgen) diff --git a/test/tst_registerfile.cpp b/test/tst_registerfile.cpp index 0b357ce..a22917a 100644 --- a/test/tst_registerfile.cpp +++ b/test/tst_registerfile.cpp @@ -3,13 +3,13 @@ #include "vsrtl_registerfilecmp.h" class tst_registerfile : public QObject { - Q_OBJECT private slots : void functionalTest(); + Q_OBJECT private slots : void functionalTest(); }; void tst_registerfile::functionalTest() { - vsrtl::core::RegisterFileTester a; + vsrtl::core::RegisterFileTester a; - a.verifyAndInitialize(); + a.verifyAndInitialize(); } QTEST_APPLESS_MAIN(tst_registerfile)