Compare commits

...

10 Commits

44 changed files with 2043 additions and 133 deletions

460
out.svg Normal file
View File

@ -0,0 +1,460 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:s="https://github.com/nturley/netlistsvg" width="569" height="868.5">
<style>svg {
stroke:#000;
fill:none;
}
text {
fill:#000;
stroke:none;
font-size:10px;
font-weight: bold;
font-family: "Courier New", monospace;
}
.nodelabel {
text-anchor: middle;
}
.inputPortLabel {
text-anchor: end;
}
.splitjoinBody {
fill:#000;
}</style>
<g s:type="add" transform="translate(94.66666666666667,699.5)" s:width="25" s:height="25" id="cell_$add$/dide/user/src/language/vlog/netlistview/netlistview.v:15$3">
<s:alias val="$add"/>
<circle r="12.5" cx="12.5" cy="12.5" class="cell_$add$/dide/user/src/language/vlog/netlistview/netlistview.v:15$3"/>
<line x1="7.5" x2="17.5" y1="12.5" y2="12.5" class="cell_$add$/dide/user/src/language/vlog/netlistview/netlistview.v:15$3"/>
<line x1="12.5" x2="12.5" y1="7.5" y2="17.5" class="cell_$add$/dide/user/src/language/vlog/netlistview/netlistview.v:15$3"/>
<g s:x="3" s:y="5" s:pid="A"/>
<g s:x="3" s:y="20" s:pid="B"/>
<g s:x="25" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="and" transform="translate(365,814)" s:width="30" s:height="25" id="cell_$and$/dide/user/src/language/vlog/netlistview/netlistview.v:31$5">
<s:alias val="$and"/>
<s:alias val="$logic_and"/>
<s:alias val="$_AND_"/>
<path d="M0,0 L0,25 L15,25 A15 12.5 0 0 0 15,0 Z" class="cell_$and$/dide/user/src/language/vlog/netlistview/netlistview.v:31$5"/>
<g s:x="0" s:y="5" s:pid="A"/>
<g s:x="0" s:y="20" s:pid="B"/>
<g s:x="30" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="and" transform="translate(365,19.5)" s:width="30" s:height="25" id="cell_$and$/dide/user/src/language/vlog/netlistview/netlistview.v:32$6">
<s:alias val="$and"/>
<s:alias val="$logic_and"/>
<s:alias val="$_AND_"/>
<path d="M0,0 L0,25 L15,25 A15 12.5 0 0 0 15,0 Z" class="cell_$and$/dide/user/src/language/vlog/netlistview/netlistview.v:32$6"/>
<g s:x="0" s:y="5" s:pid="A"/>
<g s:x="0" s:y="20" s:pid="B"/>
<g s:x="30" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="dff" transform="translate(432,302)" s:width="30" s:height="40" id="cell_$procdff$12">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$12"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$12"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="dff" transform="translate(432,816.5)" s:width="30" s:height="40" id="cell_$procdff$13">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$13"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$13"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="dff" transform="translate(432,22)" s:width="30" s:height="40" id="cell_$procdff$14">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$14"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$14"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="dff" transform="translate(432,162)" s:width="30" s:height="40" id="cell_$procdff$15">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$15"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$15"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="dff" transform="translate(497,731.5)" s:width="30" s:height="40" id="cell_$procdff$16">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$16"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$16"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="dff" transform="translate(497,77)" s:width="30" s:height="40" id="cell_$procdff$17">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$17"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$17"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="dff" transform="translate(497,217)" s:width="30" s:height="40" id="cell_$procdff$18">
<s:alias val="$dff"/>
<s:alias val="$_DFF_"/>
<s:alias val="$_DFF_P_"/>
<rect width="30" height="40" x="0" y="0" class="cell_$procdff$18"/>
<path d="M0,35 L5,30 L0,25" class="cell_$procdff$18"/>
<g s:x="30" s:y="10" s:pid="Q"/>
<g s:x="0" s:y="30" s:pid="CLK"/>
<g s:x="0" s:y="30" s:pid="C"/>
<g s:x="0" s:y="10" s:pid="D"/>
</g>
<g s:type="eq" transform="translate(162.66666666666666,579)" s:width="25" s:height="25" id="cell_$procmux$10_CMP0">
<s:alias val="$eq"/>
<circle r="12.5" cx="12.5" cy="12.5" class="cell_$procmux$10_CMP0"/>
<line x1="7.5" x2="17.5" y1="10" y2="10" class="cell_$procmux$10_CMP0"/>
<line x1="7.5" x2="17.5" y1="15" y2="15" class="cell_$procmux$10_CMP0"/>
<g s:x="3" s:y="5" s:pid="A"/>
<g s:x="3" s:y="20" s:pid="B"/>
<g s:x="25" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="not" transform="translate(163,639)" s:width="30" s:height="20" id="cell_$procmux$11_CMP0">
<s:alias val="$_NOT_"/>
<s:alias val="$not"/>
<s:alias val="$logic_not"/>
<path d="M0,0 L0,20 L20,10 Z" class="cell_$procmux$11_CMP0"/>
<circle cx="23" cy="10" r="3" class="cell_$procmux$11_CMP0"/>
<g s:x="0" s:y="10" s:pid="A"/>
<g s:x="25" s:y="10" s:pid="Y"/>
</g>
<g s:type="mux" transform="translate(309,292)" s:width="20" s:height="40" id="cell_$procmux$7">
<s:alias val="$pmux"/>
<s:alias val="$mux"/>
<s:alias val="$_MUX_"/>
<path d="M0,0 L20,10 L20,30 L0,40 Z" class="cell_$procmux$7"/>
<g s:x="0" s:y="10" s:pid="A"/>
<g s:x="0" s:y="30" s:pid="B"/>
<g s:x="10" s:y="35" s:pid="S"/>
<g s:x="20" s:y="20" s:pid="Y"/>
</g>
<g s:type="eq" transform="translate(162.66666666666666,436.5)" s:width="25" s:height="25" id="cell_$procmux$8_CMP0">
<s:alias val="$eq"/>
<circle r="12.5" cx="12.5" cy="12.5" class="cell_$procmux$8_CMP0"/>
<line x1="7.5" x2="17.5" y1="10" y2="10" class="cell_$procmux$8_CMP0"/>
<line x1="7.5" x2="17.5" y1="15" y2="15" class="cell_$procmux$8_CMP0"/>
<g s:x="3" s:y="5" s:pid="A"/>
<g s:x="3" s:y="20" s:pid="B"/>
<g s:x="25" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="eq" transform="translate(162.66666666666666,496.5)" s:width="25" s:height="25" id="cell_$procmux$9_CMP0">
<s:alias val="$eq"/>
<circle r="12.5" cx="12.5" cy="12.5" class="cell_$procmux$9_CMP0"/>
<line x1="7.5" x2="17.5" y1="10" y2="10" class="cell_$procmux$9_CMP0"/>
<line x1="7.5" x2="17.5" y1="15" y2="15" class="cell_$procmux$9_CMP0"/>
<g s:x="3" s:y="5" s:pid="A"/>
<g s:x="3" s:y="20" s:pid="B"/>
<g s:x="25" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="reduce_xor" transform="translate(364,159.5)" s:width="33" s:height="25" id="cell_$reduce_xor$/dide/user/src/language/vlog/netlistview/netlistview.v:30$4">
<s:alias val="$xor"/>
<s:alias val="$reduce_xor"/>
<s:alias val="$_XOR_"/>
<path d="M3,0 A30 25 0 0 1 3,25 A30 25 0 0 0 33,12.5 A30 25 0 0 0 3,0" class="cell_$reduce_xor$/dide/user/src/language/vlog/netlistview/netlistview.v:30$4"/>
<path d="M0,0 A30 25 0 0 1 0,25" class="cell_$reduce_xor$/dide/user/src/language/vlog/netlistview/netlistview.v:30$4"/>
<g s:x="3" s:y="5" s:pid="A"/>
<g s:x="3" s:y="20" s:pid="B"/>
<g s:x="33" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="sub" transform="translate(94.66666666666667,74.5)" s:width="25" s:height="25" id="cell_$sub$/dide/user/src/language/vlog/netlistview/netlistview.v:14$2">
<s:alias val="$sub"/>
<circle r="12.5" cx="12.5" cy="12.5" class="cell_$sub$/dide/user/src/language/vlog/netlistview/netlistview.v:14$2"/>
<line x1="7.5" x2="17.5" y1="12.5" y2="12.5" class="cell_$sub$/dide/user/src/language/vlog/netlistview/netlistview.v:14$2"/>
<g s:x="3" s:y="5" s:pid="A"/>
<g s:x="3" s:y="20" s:pid="B"/>
<g s:x="25" s:y="12.5" s:pid="Y"/>
</g>
<g s:type="inputExt" transform="translate(367,248)" s:width="30" s:height="20" id="cell_clock">
<text x="15" y="-4" class="nodelabel cell_clock" s:attribute="ref">clock</text>
<s:alias val="$_inputExt_"/>
<path d="M0,0 L0,20 L15,20 L30,10 L15,0 Z" class="cell_clock"/>
<g s:x="28" s:y="10" s:pid="Y"/>
</g>
<g s:type="inputExt" transform="translate(157,381.5)" s:width="30" s:height="20" id="cell_data_in">
<text x="15" y="-4" class="nodelabel cell_data_in" s:attribute="ref">data_in</text>
<s:alias val="$_inputExt_"/>
<path d="M0,0 L0,20 L15,20 L30,10 L15,0 Z" class="cell_data_in"/>
<g s:x="28" s:y="10" s:pid="Y"/>
</g>
<g s:type="inputExt" transform="translate(12,809)" s:width="30" s:height="20" id="cell_up">
<text x="15" y="-4" class="nodelabel cell_up" s:attribute="ref">up</text>
<s:alias val="$_inputExt_"/>
<path d="M0,0 L0,20 L15,20 L30,10 L15,0 Z" class="cell_up"/>
<g s:x="28" s:y="10" s:pid="Y"/>
</g>
<g s:type="inputExt" transform="translate(12,564)" s:width="30" s:height="20" id="cell_down">
<text x="15" y="-4" class="nodelabel cell_down" s:attribute="ref">down</text>
<s:alias val="$_inputExt_"/>
<path d="M0,0 L0,20 L15,20 L30,10 L15,0 Z" class="cell_down"/>
<g s:x="28" s:y="10" s:pid="Y"/>
</g>
<g s:type="outputExt" transform="translate(497,816.5)" s:width="30" s:height="20" id="cell_carry_out">
<text x="15" y="-4" class="nodelabel cell_carry_out" s:attribute="ref">carry_out</text>
<s:alias val="$_outputExt_"/>
<path d="M30,0 L30,20 L15,20 L0,10 L15,0 Z" class="cell_carry_out"/>
<g s:x="0" s:y="10" s:pid="A"/>
</g>
<g s:type="outputExt" transform="translate(497,22)" s:width="30" s:height="20" id="cell_borrow_out">
<text x="15" y="-4" class="nodelabel cell_borrow_out" s:attribute="ref">borrow_out</text>
<s:alias val="$_outputExt_"/>
<path d="M30,0 L30,20 L15,20 L0,10 L15,0 Z" class="cell_borrow_out"/>
<g s:x="0" s:y="10" s:pid="A"/>
</g>
<g s:type="outputExt" transform="translate(497,302)" s:width="30" s:height="20" id="cell_count_out">
<text x="15" y="-4" class="nodelabel cell_count_out" s:attribute="ref">count_out</text>
<s:alias val="$_outputExt_"/>
<path d="M30,0 L30,20 L15,20 L0,10 L15,0 Z" class="cell_count_out"/>
<g s:x="0" s:y="10" s:pid="A"/>
</g>
<g s:type="outputExt" transform="translate(497,162)" s:width="30" s:height="20" id="cell_parity_out">
<text x="15" y="-4" class="nodelabel cell_parity_out" s:attribute="ref">parity_out</text>
<s:alias val="$_outputExt_"/>
<path d="M30,0 L30,20 L15,20 L0,10 L15,0 Z" class="cell_parity_out"/>
<g s:x="0" s:y="10" s:pid="A"/>
</g>
<g s:type="constant" transform="translate(12,709.5)" s:width="30" s:height="20" id="cell_11">
<text x="15" y="-4" class="nodelabel cell_11" s:attribute="ref">11</text>
<s:alias val="$_constant_"/>
<rect width="30" height="20" class="cell_11"/>
<g s:x="30" s:y="10" s:pid="Y"/>
</g>
<g s:type="constant" transform="translate(92,648.5)" s:width="30" s:height="20" id="cell_01">
<text x="15" y="-4" class="nodelabel cell_01" s:attribute="ref">01</text>
<s:alias val="$_constant_"/>
<rect width="30" height="20" class="cell_01"/>
<g s:x="30" s:y="10" s:pid="Y"/>
</g>
<g s:type="constant" transform="translate(92,506.5)" s:width="30" s:height="20" id="cell_10">
<text x="15" y="-4" class="nodelabel cell_10" s:attribute="ref">10</text>
<s:alias val="$_constant_"/>
<rect width="30" height="20" class="cell_10"/>
<g s:x="30" s:y="10" s:pid="Y"/>
</g>
<g s:type="constant" transform="translate(12,84.5)" s:width="30" s:height="20" id="cell_101">
<text x="15" y="-4" class="nodelabel cell_101" s:attribute="ref">101</text>
<s:alias val="$_constant_"/>
<rect width="30" height="20" class="cell_101"/>
<g s:x="30" s:y="10" s:pid="Y"/>
</g>
<g s:type="join" transform="translate(103,563.5)" s:width="4" s:height="40" id="cell_$join$,13,12,">
<rect width="5" height="40" class="splitjoinBody" s:generic="body"/>
<s:alias val="$_join_"/>
<g s:x="5" s:y="20" s:pid="out"/>
<g transform="translate(0,10)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">0</text>
</g>
<g transform="translate(0,30)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">1</text>
</g>
</g>
<g s:type="join" transform="translate(268,301.5)" s:width="4" s:height="40" id="cell_$join$,16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,59,60,61,62,63,64,65,66,67,3,4,5,6,7,8,9,10,11,">
<rect width="5" height="80" class="splitjoinBody" s:generic="body"/>
<s:alias val="$_join_"/>
<g s:x="5" s:y="20" s:pid="out"/>
<g transform="translate(0,10)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">0:8</text>
</g>
<g transform="translate(0,30)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">9:17</text>
</g>
<g transform="translate(0,50)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">18:26</text>
</g>
<g transform="translate(0,70)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">27:35</text>
</g>
</g>
<g s:type="join" transform="translate(263.75,438.5)" s:width="4" s:height="40" id="cell_$join$,89,90,87,88,">
<rect width="5" height="80" class="splitjoinBody" s:generic="body"/>
<s:alias val="$_join_"/>
<g s:x="5" s:y="20" s:pid="out"/>
<g transform="translate(0,10)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">0</text>
</g>
<g transform="translate(0,30)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">1</text>
</g>
<g transform="translate(0,50)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">2</text>
</g>
<g transform="translate(0,70)" s:x="0" s:y="10" s:pid="in0">
<text x="-3" y="-4" class="inputPortLabel">3</text>
</g>
</g>
<g s:type="split" transform="translate(177.66666666666666,691.5)" s:width="5" s:height="40" id="cell_$split$,26,27,28,29,30,31,32,33,34,35,">
<rect width="5" height="40" class="splitjoinBody" s:generic="body"/>
<s:alias val="$_split_"/>
<g s:x="0" s:y="20" s:pid="in"/>
<g transform="translate(4,10)" s:x="4" s:y="10" s:pid="out0">
<text x="5" y="-4">9</text>
</g>
<g transform="translate(4,30)" s:x="4" s:y="10" s:pid="out0">
<text x="5" y="-4">0:8</text>
</g>
</g>
<g s:type="split" transform="translate(177.66666666666666,100)" s:width="5" s:height="40" id="cell_$split$,59,60,61,62,63,64,65,66,67,37,">
<rect width="5" height="40" class="splitjoinBody" s:generic="body"/>
<s:alias val="$_split_"/>
<g s:x="0" s:y="20" s:pid="in"/>
<g transform="translate(4,10)" s:x="4" s:y="10" s:pid="out0">
<text x="5" y="-4">9</text>
</g>
<g transform="translate(4,30)" s:x="4" s:y="10" s:pid="out0">
<text x="5" y="-4">0:8</text>
</g>
</g>
<line x1="462" x2="472" y1="312" y2="312" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="472" x2="472" y1="312" y2="678.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="472" x2="82" y1="678.5" y2="678.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="82" x2="82" y1="678.5" y2="704.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="82" x2="97.66666666666667" y1="704.5" y2="704.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="462" x2="472" y1="312" y2="312" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="472" x2="472" y1="312" y2="278" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="472" x2="82" y1="278" y2="278" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="82" x2="82" y1="278" y2="79.5" class="net_16,17,18,19,20,21,22,23,24"/>
<circle cx="472" cy="312" r="2" style="fill:#000" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="82" x2="97.66666666666667" y1="79.5" y2="79.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="462" x2="497" y1="312" y2="312" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="462" x2="472" y1="312" y2="312" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="472" x2="472" y1="312" y2="278" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="472" x2="349" y1="278" y2="278" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="349" x2="349" y1="278" y2="678.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="349" x2="219" y1="678.5" y2="678.5" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="219" x2="219" y1="678.5" y2="312" class="net_16,17,18,19,20,21,22,23,24"/>
<circle cx="219" cy="678.5" r="2" style="fill:#000" class="net_16,17,18,19,20,21,22,23,24"/>
<circle cx="349" cy="678.5" r="2" style="fill:#000" class="net_16,17,18,19,20,21,22,23,24"/>
<circle cx="349" cy="278" r="2" style="fill:#000" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="219" x2="268" y1="312" y2="312" class="net_16,17,18,19,20,21,22,23,24"/>
<line x1="42" x2="97.66666666666667" y1="719.5" y2="719.5" class="net_91,92"/>
<line x1="42" x2="62" y1="719.5" y2="719.5" class="net_91,92"/>
<line x1="62" x2="62" y1="719.5" y2="456.5" class="net_91,92"/>
<circle cx="62" cy="719.5" r="2" style="fill:#000" class="net_91,92"/>
<line x1="62" x2="165.66666666666666" y1="456.5" y2="456.5" class="net_91,92"/>
<line x1="40" x2="365" y1="819" y2="819" class="net_12"/>
<line x1="40" x2="72" y1="819" y2="819" class="net_12"/>
<line x1="72" x2="72" y1="819" y2="594" class="net_12"/>
<circle cx="72" cy="819" r="2" style="fill:#000" class="net_12"/>
<line x1="72" x2="103" y1="594" y2="594" class="net_12"/>
<line x1="182.66666666666666" x2="239" y1="702" y2="702" class="net_35"/>
<line x1="239" x2="239" y1="702" y2="834" class="net_35"/>
<line x1="239" x2="365" y1="834" y2="834" class="net_35"/>
<line x1="40" x2="52" y1="574" y2="574" class="net_13"/>
<line x1="52" x2="52" y1="574" y2="24.5" class="net_13"/>
<circle cx="52" cy="574" r="2" style="fill:#000" class="net_13"/>
<line x1="52" x2="365" y1="24.5" y2="24.5" class="net_13"/>
<line x1="40" x2="103" y1="574" y2="574" class="net_13"/>
<line x1="182.66666666666666" x2="209" y1="110.5" y2="110.5" class="net_37"/>
<line x1="209" x2="209" y1="110.5" y2="39.5" class="net_37"/>
<line x1="209" x2="365" y1="39.5" y2="39.5" class="net_37"/>
<line x1="395" x2="407" y1="258" y2="258" class="net_2"/>
<line x1="407" x2="407" y1="258" y2="332" class="net_2"/>
<circle cx="407" cy="258" r="2" style="fill:#000" class="net_2"/>
<circle cx="407" cy="332" r="2" style="fill:#000" class="net_2"/>
<line x1="407" x2="432" y1="332" y2="332" class="net_2"/>
<line x1="395" x2="407" y1="258" y2="258" class="net_2"/>
<line x1="407" x2="407" y1="258" y2="846.5" class="net_2"/>
<line x1="407" x2="432" y1="846.5" y2="846.5" class="net_2"/>
<line x1="395" x2="407" y1="258" y2="258" class="net_2"/>
<line x1="407" x2="407" y1="258" y2="52" class="net_2"/>
<line x1="407" x2="432" y1="52" y2="52" class="net_2"/>
<line x1="395" x2="407" y1="258" y2="258" class="net_2"/>
<line x1="407" x2="407" y1="258" y2="192" class="net_2"/>
<circle cx="407" cy="192" r="2" style="fill:#000" class="net_2"/>
<line x1="407" x2="432" y1="192" y2="192" class="net_2"/>
<line x1="395" x2="407" y1="258" y2="258" class="net_2"/>
<line x1="407" x2="407" y1="258" y2="761.5" class="net_2"/>
<circle cx="407" cy="761.5" r="2" style="fill:#000" class="net_2"/>
<line x1="407" x2="497" y1="761.5" y2="761.5" class="net_2"/>
<line x1="395" x2="407" y1="258" y2="258" class="net_2"/>
<line x1="407" x2="407" y1="258" y2="107" class="net_2"/>
<circle cx="407" cy="107" r="2" style="fill:#000" class="net_2"/>
<line x1="407" x2="497" y1="107" y2="107" class="net_2"/>
<line x1="395" x2="472" y1="258" y2="258" class="net_2"/>
<line x1="472" x2="472" y1="258" y2="247" class="net_2"/>
<line x1="472" x2="497" y1="247" y2="247" class="net_2"/>
<line x1="329" x2="432" y1="312" y2="312" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="329" x2="339" y1="312" y2="312" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="339" x2="339" y1="312" y2="227" class="net_39,40,41,42,43,44,45,46,47"/>
<circle cx="339" cy="312" r="2" style="fill:#000" class="net_39,40,41,42,43,44,45,46,47"/>
<circle cx="339" cy="227" r="2" style="fill:#000" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="339" x2="497" y1="227" y2="227" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="329" x2="339" y1="312" y2="312" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="339" x2="339" y1="312" y2="164.5" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="339" x2="367" y1="164.5" y2="164.5" class="net_39,40,41,42,43,44,45,46,47"/>
<line x1="395" x2="432" y1="826.5" y2="826.5" class="net_36"/>
<line x1="395" x2="432" y1="32" y2="32" class="net_38"/>
<line x1="397" x2="432" y1="172" y2="172" class="net_48"/>
<line x1="119.66666666666667" x2="132" y1="712" y2="712" class="net_26,27,28,29,30,31,32,33,34,35"/>
<line x1="132" x2="132" y1="712" y2="741.5" class="net_26,27,28,29,30,31,32,33,34,35"/>
<circle cx="132" cy="712" r="2" style="fill:#000" class="net_26,27,28,29,30,31,32,33,34,35"/>
<line x1="132" x2="497" y1="741.5" y2="741.5" class="net_26,27,28,29,30,31,32,33,34,35"/>
<line x1="119.66666666666667" x2="177.66666666666666" y1="712" y2="712" class="net_26,27,28,29,30,31,32,33,34,35"/>
<line x1="119.66666666666667" x2="497" y1="87" y2="87" class="net_59,60,61,62,63,64,65,66,67,37"/>
<line x1="119.66666666666667" x2="132" y1="87" y2="87" class="net_59,60,61,62,63,64,65,66,67,37"/>
<line x1="132" x2="132" y1="87" y2="120.5" class="net_59,60,61,62,63,64,65,66,67,37"/>
<circle cx="132" cy="87" r="2" style="fill:#000" class="net_59,60,61,62,63,64,65,66,67,37"/>
<line x1="132" x2="177.66666666666666" y1="120.5" y2="120.5" class="net_59,60,61,62,63,64,65,66,67,37"/>
<line x1="109" x2="165.66666666666666" y1="584" y2="584" class="net_13,12"/>
<line x1="109" x2="142" y1="584" y2="584" class="net_13,12"/>
<line x1="142" x2="142" y1="584" y2="649" class="net_13,12"/>
<circle cx="142" cy="584" r="2" style="fill:#000" class="net_13,12"/>
<line x1="142" x2="163" y1="649" y2="649" class="net_13,12"/>
<line x1="109" x2="142" y1="584" y2="584" class="net_13,12"/>
<line x1="142" x2="142" y1="584" y2="441.5" class="net_13,12"/>
<line x1="142" x2="165.66666666666666" y1="441.5" y2="441.5" class="net_13,12"/>
<line x1="109" x2="142" y1="584" y2="584" class="net_13,12"/>
<line x1="142" x2="142" y1="584" y2="501.5" class="net_13,12"/>
<circle cx="142" cy="501.5" r="2" style="fill:#000" class="net_13,12"/>
<line x1="142" x2="165.66666666666666" y1="501.5" y2="501.5" class="net_13,12"/>
<line x1="122" x2="132" y1="658.5" y2="658.5" class="net_93,94"/>
<line x1="132" x2="132" y1="658.5" y2="599" class="net_93,94"/>
<line x1="132" x2="165.66666666666666" y1="599" y2="599" class="net_93,94"/>
<line x1="274" x2="309" y1="322" y2="322" class="net_16,17,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,59,60,61,62,63,64,65,66,67,3,4,5,6,7,8,9,10,11"/>
<line x1="269.75" x2="319" y1="459" y2="459" class="net_89,90,87,88"/>
<line x1="319" x2="319" y1="459" y2="327" class="net_89,90,87,88"/>
<line x1="122" x2="165.66666666666666" y1="516.5" y2="516.5" class="net_97,98"/>
<line x1="42" x2="97.66666666666667" y1="94.5" y2="94.5" class="net_99,100,101"/>
<line x1="462" x2="497" y1="826.5" y2="826.5" class="net_14"/>
<line x1="462" x2="497" y1="32" y2="32" class="net_15"/>
<line x1="462" x2="497" y1="172" y2="172" class="net_25"/>
<line x1="182.66666666666666" x2="229" y1="722" y2="722" class="net_26,27,28,29,30,31,32,33,34"/>
<line x1="229" x2="229" y1="722" y2="332" class="net_26,27,28,29,30,31,32,33,34"/>
<line x1="229" x2="268" y1="332" y2="332" class="net_26,27,28,29,30,31,32,33,34"/>
<line x1="182.66666666666666" x2="239" y1="130.5" y2="130.5" class="net_59,60,61,62,63,64,65,66,67"/>
<line x1="239" x2="239" y1="130.5" y2="352" class="net_59,60,61,62,63,64,65,66,67"/>
<line x1="239" x2="268" y1="352" y2="352" class="net_59,60,61,62,63,64,65,66,67"/>
<line x1="185" x2="268.5" y1="391.5" y2="391.5" class="net_3,4,5,6,7,8,9,10,11"/>
<line x1="268.5" x2="268.5" y1="391.5" y2="372.5" class="net_3,4,5,6,7,8,9,10,11"/>
<line x1="187.66666666666666" x2="263.75" y1="449" y2="449" class="net_89"/>
<line x1="187.66666666666666" x2="209" y1="509" y2="509" class="net_90"/>
<line x1="209" x2="209" y1="509" y2="469" class="net_90"/>
<line x1="209" x2="263.75" y1="469" y2="469" class="net_90"/>
<line x1="187.66666666666666" x2="239" y1="591.5" y2="591.5" class="net_87"/>
<line x1="239" x2="239" y1="591.5" y2="489" class="net_87"/>
<line x1="239" x2="263.75" y1="489" y2="489" class="net_87"/>
<line x1="188" x2="264.25" y1="649" y2="649" class="net_88"/>
<line x1="264.25" x2="264.25" y1="649" y2="509.5" class="net_88"/>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

62
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "digital-netlist-render", "name": "digital-netlist-render",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"axios": "^1.7.9",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"d3": "^7.9.0", "d3": "^7.9.0",
"d3-drag": "^3.0.0", "d3-drag": "^3.0.0",
@ -15,6 +16,7 @@
"elkjs": "^0.9.3", "elkjs": "^0.9.3",
"fflate": "^0.8.2", "fflate": "^0.8.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"pako": "^2.1.0",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-i18n": "10.0.5", "vue-i18n": "10.0.5",
"web-worker": "^1.3.0" "web-worker": "^1.3.0"
@ -3498,6 +3500,11 @@
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
}, },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/at-least-node": { "node_modules/at-least-node": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
@ -3544,6 +3551,16 @@
"postcss": "^8.1.0" "postcss": "^8.1.0"
} }
}, },
"node_modules/axios": {
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/babel-loader": { "node_modules/babel-loader": {
"version": "8.4.1", "version": "8.4.1",
"resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.4.1.tgz", "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.4.1.tgz",
@ -4174,6 +4191,17 @@
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true "dev": true
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "8.3.0", "version": "8.3.0",
"resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz", "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz",
@ -5343,6 +5371,14 @@
"robust-predicates": "^3.0.2" "robust-predicates": "^3.0.2"
} }
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@ -6571,7 +6607,6 @@
"version": "1.15.9", "version": "1.15.9",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@ -6587,6 +6622,19 @@
} }
} }
}, },
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
@ -8097,7 +8145,6 @@
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -8106,7 +8153,6 @@
"version": "2.1.35", "version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": { "dependencies": {
"mime-db": "1.52.0" "mime-db": "1.52.0"
}, },
@ -8723,6 +8769,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
},
"node_modules/param-case": { "node_modules/param-case": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz", "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz",
@ -9641,6 +9692,11 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/pseudomap": { "node_modules/pseudomap": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz",

View File

@ -8,6 +8,7 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"axios": "^1.7.9",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"d3": "^7.9.0", "d3": "^7.9.0",
"d3-drag": "^3.0.0", "d3-drag": "^3.0.0",
@ -15,6 +16,7 @@
"elkjs": "^0.9.3", "elkjs": "^0.9.3",
"fflate": "^0.8.2", "fflate": "^0.8.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"pako": "^2.1.0",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-i18n": "10.0.5", "vue-i18n": "10.0.5",
"web-worker": "^1.3.0" "web-worker": "^1.3.0"

View File

@ -1,6 +1,6 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4797805 */ font-family: "iconfont"; /* Project id 4797805 */
src: url('iconfont.woff2?t=1735470843861') format('woff2'); src: url('iconfont.woff2?t=1735991360293') format('woff2');
} }
.iconfont { .iconfont {
@ -11,6 +11,22 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-link:before {
content: "\e6fa";
}
.icon-Listinterface:before {
content: "\e66b";
}
.icon-info-port:before {
content: "\f2e1";
}
.icon-sanjiao_fill-:before {
content: "\e6a0";
}
.icon-memory-chip:before { .icon-memory-chip:before {
content: "\e600"; content: "\e600";
} }
@ -243,3 +259,7 @@
content: "\e693"; content: "\e693";
} }
.icon-info-module:before {
content: "\f2e2";
}

Binary file not shown.

View File

@ -15,7 +15,7 @@
<script> <script>
window.readNetFile = async () => { window.readNetFile = async () => {
const inputVcdFile = 'IF_ID.json'; const inputVcdFile = 'full_adder.json';
const skin = 'dide.skin'; const skin = 'dide.skin';
const r1 = await fetch(inputVcdFile); const r1 = await fetch(inputVcdFile);
const r2 = await fetch(skin); const r2 = await fetch(skin);
@ -23,8 +23,7 @@
const skinBinary = await r2.arrayBuffer(); const skinBinary = await r2.arrayBuffer();
return [ netJson, skinBinary ]; return [ netJson, skinBinary ];
} }
window.moduleName = 'IF_ID'; window.moduleName = 'full_adder';
// window.avoidWasm = 'avoid.wasm';
</script> </script>
</head> </head>

View File

@ -13,13 +13,24 @@ exclude_folders = [
os.system('npm run build') os.system('npm run build')
for file in os.listdir('dist'): for file in os.listdir('dist'):
if file in exclude_files or file.endswith('.json'): if file in exclude_files or file.endswith('.json') or file.endswith('.v'):
os.remove('dist/' + file) os.remove('dist/' + file)
with open('./dist/index.html', 'r', encoding='utf-8') as fp: with open('./dist/index.html', 'r', encoding='utf-8') as fp:
html = fp.read() html = fp.read()
index = html.index('</script>')
html = html[:index] + '\n' + html[index:]
new_html = []
for line in html.split('\n'):
if 'const inputVcdFile =' in line:
new_html.append("const inputVcdFile = 'test.json';")
elif 'window.moduleName' in line:
new_html.append("window.moduleName = 'test.module';")
else:
new_html.append(line.strip())
with open('./dist/index.html', 'w', encoding='utf-8') as fp: with open('./dist/index.html', 'w', encoding='utf-8') as fp:
fp.write(html) fp.write('\n'.join(new_html))

View File

@ -1,4 +1,6 @@
<template> <template>
<!-- 顶部工具栏 -->
<ToolBar></ToolBar>
<!-- 渲染区域 --> <!-- 渲染区域 -->
<Render></Render> <Render></Render>
<!-- 右侧工具合集 --> <!-- 右侧工具合集 -->
@ -7,6 +9,7 @@
<script setup> <script setup>
import Render from '@/components/render'; import Render from '@/components/render';
import ToolBar from '@/components/toolbar';
import RightNav from '@/components/right-nav.vue'; import RightNav from '@/components/right-nav.vue';
import { onMounted, watch } from 'vue'; import { onMounted, watch } from 'vue';
import { setDefaultCss } from './hook/css'; import { setDefaultCss } from './hook/css';

81
src/api/definition.js Normal file
View File

@ -0,0 +1,81 @@
import axios from 'axios';
import { globalLookup } from "@/hook/global";
import { pinkLog } from "@/hook/utils";
import { mode, vscode } from ".";
/**
*
* @param {string} definition
*/
export async function gotoDefinition(definition) {
const defs = processDefinition(definition);
if (mode === 'debug') {
const res = await axios.post('http://localhost:3000/netlist/goto-definition', { defs });
} else {
vscode.postMessage({
command: 'goto-definition',
data: { defs }
});
}
}
/**
*
* @typedef FileRange
* @property {string} path
* @property {import("@/hook/jsdoc").Range} [range]
*
* @param {string | undefined} definition
* @returns {FileRange[]}
*/
function processDefinition(definition) {
if (typeof definition !== 'string') {
return [];
}
if (definition.includes('|')) {
const defs = [];
for (const def of definition.split('|')) {
defs.push(...processDefinition(def));
}
return defs;
}
if (definition.includes(':')) {
const [path, rangeString] = definition.split(':');
const [startString, endString] = rangeString.split('-');
const [startLine, startCharacter] = startString.split('.');
const [endLine, endCharacter] = endString.split('.');
const range = {
start: {
line: parseRangeInt(startLine),
character: parseRangeInt(startCharacter)
},
end: {
line: parseRangeInt(endLine),
character: parseRangeInt(endCharacter)
}
};
return [
{ path, range }
];
}
return [
{
path: definition,
range: undefined
}
];
}
function parseRangeInt(s) {
const i = parseInt(s);
if (i > 0) {
// 因为 yosys 是 one index 的,但是 vscode 内部跳转都是 zero index
return i - 1;
}
return i;
}

161
src/api/index.js Normal file
View File

@ -0,0 +1,161 @@
import { colorManager } from "@/components/setting/color";
import { increaseBrightness, isLightColorTheme, lowerBrightness, MacroColor, parseColor } from "@/hook/color";
import { globalLookup } from "@/hook/global";
import { pinkLog } from "@/hook/utils";
import axios from 'axios';
import pako from 'pako';
export const mode = window.acquireVsCodeApi === undefined ? 'debug' : 'release';
pinkLog('digital-netlist-render mode: ' + mode);
export let vscode = window.acquireVsCodeApi === undefined ? undefined : acquireVsCodeApi();
function getColorFromMacro(rootStyles, optionName, theme, isLight) {
if (theme === 'dark') {
return rootStyles.getPropertyValue(optionName);
} else {
// 亮色模式pdf 类型的导出一定是这个颜色,
// 此时需要对特殊的几类颜色进行处理
// 如果此时的颜色主题本来就是偏亮色的,直接返回如下几个的颜色
switch (optionName) {
case '--foreground':
case '--wire-color':
case '--cross-dot-color':
if (!isLight) {
return '#2D323B';
}
}
const color = rootStyles.getPropertyValue(optionName);
if (!color) {
return color;
}
const rgb = parseColor(color);
const { r, g, b } = increaseBrightness(rgb, 10);
return `rgb(${r}, ${g}, ${b})`;
}
}
/**
*
* @param {import("@/hook/color").GetColorOption} option
* @returns
*/
function makeStyleString(option = {}) {
// 加入全局颜色宏
const colorMacros = [];
const macroColor = new MacroColor({
BaseForegroundColorMacroName: '--foreground',
BaseBackgroundColorMacroName: '--background'
});
for (const item of colorManager.generals) {
const colorOption = `--${item.type}-color`;
const colorFillOption = `--${item.type}-fill-color`;
const color = macroColor.getColor(colorOption, option);
const colorFill = macroColor.getColor(colorFillOption, option);
if (color) {
colorMacros.push(`${colorOption}: ${color};`);
}
if (colorFill) {
colorMacros.push(`${colorFillOption}: ${colorFill};`);
}
}
for (const item of colorManager.cells) {
const colorOption = `--${item.type}-color`;
const colorFillOption = `--${item.type}-fill-color`;
const color = macroColor.getColor(colorOption, option);
const colorFill = macroColor.getColor(colorFillOption, option);
if (color) {
colorMacros.push(`${colorOption}: ${color};`);
}
if (colorFill) {
colorMacros.push(`${colorFillOption}: ${colorFill};`);
}
}
const globalSetting = [
'--foreground',
'--background',
'--main-color'
];
for (const setting of globalSetting) {
const color = macroColor.getColor(setting, option);
if (color) {
colorMacros.push(`${setting}: ${color};`);
}
}
const styleString = `:root {\n${colorMacros.join('\n')}\n}`;
return styleString;
}
/**
*
* @param {import("@/hook/color").GetColorOption} option
* @returns
*/
function getCompressedSvgBuffer(option = {}) {
const selection = globalLookup.netlistRender.selection;
const node = selection.node();
const styleString = makeStyleString(option);
const style = document.createElement('style');
style.textContent = styleString;
node.insertBefore(style, node.firstChild);
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(node);
const array = pako.gzip(svgString);
// 还原
const styleElement = node.querySelector('style');
if (styleElement) {
styleElement.remove();
}
return array;
}
export async function saveAsSvg() {
const svgBuffer = getCompressedSvgBuffer();
const moduleName = globalLookup.topModuleName;
if (mode === 'debug') {
const res = await axios.post('http://localhost:3000/netlist/save-as-svg', { svgBuffer, moduleName });
if (res.success) {
pinkLog('成功保存到”' + res.savePath);
}
} else {
vscode.postMessage({
command: 'save-as-svg',
data: { svgBuffer, moduleName }
});
}
}
export async function saveAsPdf() {
const svgBuffer = getCompressedSvgBuffer({ mode: 'pdf' });
const moduleName = globalLookup.topModuleName;
const node = globalLookup.netlistRender.selection.node();
const width = node.getAttribute('width');
const height = node.getAttribute('height');
if (mode === 'debug') {
const res = await axios.post('http://localhost:3000/netlist/save-as-pdf', { svgBuffer, moduleName, width, height });
if (res.success) {
pinkLog('成功保存到”' + res.savePath);
}
} else {
vscode.postMessage({
command: 'save-as-pdf',
data: { svgBuffer, moduleName, width, height }
});
}
}

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="about-wrapper"> <div class="about-wrapper" :style="aboutWrapper">
<div class="usermanual"> <div class="usermanual">
<h2>{{ t('usermanual') }}</h2> <h2>{{ t('usermanual') }}</h2>
<div class="usermanual-item"> <div class="usermanual-item">
@ -39,8 +39,9 @@
<script setup> <script setup>
import { defineComponent } from 'vue'; import { defineComponent, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { horizontalResizer } from '../right-nav';
defineComponent({ name: 'dide-about' }); defineComponent({ name: 'dide-about' });
@ -48,7 +49,9 @@ function goto(url) {
window.open(url, '_blank'); window.open(url, '_blank');
} }
const platform = navigator.platform; const aboutWrapper = computed(() => ({
width: horizontalResizer.width - 10 + 'px'
}));
const { t } = useI18n(); const { t } = useI18n();
</script> </script>
@ -56,6 +59,7 @@ const { t } = useI18n();
<style> <style>
.about-wrapper { .about-wrapper {
margin-top: 10px; margin-top: 10px;
min-width: 300px;
} }
.version-caption { .version-caption {

View File

@ -18,7 +18,7 @@ export const controlPanel = reactive({
this.currentIndex = -1; this.currentIndex = -1;
} else { } else {
if (horizontalResizer.width < 5) { if (horizontalResizer.width < 5) {
horizontalResizer.width = 362; horizontalResizer.width = 410;
} }
this.currentIndex = index; this.currentIndex = index;
} }
@ -29,7 +29,7 @@ export const controlPanel = reactive({
export const horizontalResizer = reactive({ export const horizontalResizer = reactive({
active: false, active: false,
hover: false, hover: false,
width: 362, width: 400,
mousedown() { mousedown() {
this.active = true; this.active = true;
document.addEventListener('mousemove', resize); document.addEventListener('mousemove', resize);

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="setting-wrapper"> <div class="setting-wrapper" :style="settingStyle">
<el-scrollbar height="98vh"> <el-scrollbar height="98vh">
<div class="setting-section"> <div class="setting-section">
<h2>{{ t('general-setting') }}</h2> <h2>{{ t('general-setting') }}</h2>
@ -75,6 +75,7 @@
{{ t('bold-multi-width-wire') }} {{ t('bold-multi-width-wire') }}
</span> </span>
<el-switch <el-switch
@change="onBoldMultiWidthWireChange"
v-model="globalSetting.boldMultiWidthWire" v-model="globalSetting.boldMultiWidthWire"
active-text="ON" active-text="ON"
inactive-text="OFF" inactive-text="OFF"
@ -130,6 +131,7 @@
v-model="colorManager.generals[colorManager.currentGerneralIndex].color" v-model="colorManager.generals[colorManager.currentGerneralIndex].color"
@change="onGeneralColorChange" @change="onGeneralColorChange"
:predefine="predefinedColors" :predefine="predefinedColors"
:teleported="false"
/> />
</div> </div>
</div> </div>
@ -156,22 +158,35 @@
v-model="colorManager.cells[colorManager.currentCellIndex].color" v-model="colorManager.cells[colorManager.currentCellIndex].color"
@change="onCellColorChange" @change="onCellColorChange"
:predefine="predefinedColors" :predefine="predefinedColors"
:teleported="false"
/> />
</div> </div>
</div> </div>
<br>
<div class="setting-option">
<span class="option-title">
{{ t('setting.pulsation-speed') }}
</span>
<span style="width: 200px;">
<el-slider v-model="globalSetting.pulsationSpeed" show-stops :min="1" :max="11" :step="2"/>
</span>
</div>
<br>
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
</template> </template>
<script setup> <script setup>
import { globalSetting } from '@/hook/global'; import { globalLookup, globalSetting } from '@/hook/global';
import { reactive, defineComponent, watch, ref, onMounted } from 'vue'; import { computed, defineComponent, watch, ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { languageSetting } from './language'; import { languageSetting } from './language';
import { crossDotStyle, onConnectStyleChange } from './cross-dot-style'; import { crossDotStyle, onConnectStyleChange } from './cross-dot-style';
import { colorManager, onCellColorChange, onGeneralColorChange, predefinedColors } from './color'; import { colorManager, onCellColorChange, onGeneralColorChange, predefinedColors } from './color';
import { LINE_WIDTH } from '@/hook/render/layout';
import { horizontalResizer } from '../right-nav';
defineComponent({ name: "dide-setting" }); defineComponent({ name: "dide-setting" });
const { t, locale } = useI18n(); const { t, locale } = useI18n();
@ -199,7 +214,6 @@ function onlanguagechange(code) {
} }
function onRenderArrowChange() { function onRenderArrowChange() {
const rootStyles = getComputedStyle(document.documentElement);
if (globalSetting.renderArrow) { if (globalSetting.renderArrow) {
document.documentElement.style.setProperty(`--line-arrow-opacity`, '1'); document.documentElement.style.setProperty(`--line-arrow-opacity`, '1');
} else { } else {
@ -207,6 +221,32 @@ function onRenderArrowChange() {
} }
} }
function onBoldMultiWidthWireChange() {
const netlist = globalLookup.netlistRender;
const renderView = netlist.renderView;
const stack = [renderView];
while (stack.length > 0) {
const view = stack.pop();
view.wireRender.lineSelections
.attr('stroke-width', d => {
console.log('enter');
const incrementWidth = globalSetting.boldMultiWidthWire ? 2 : 0;
return d.width > 1 ? LINE_WIDTH + incrementWidth: LINE_WIDTH;
});
for (const id of view.id2children.keys()) {
const subView = view.getChild(id);
stack.push(subView);
}
}
}
const settingStyle = computed(() => ({
width: horizontalResizer.width - 10 + 'px'
}));
onMounted(() => { onMounted(() => {
colorManager.initColor(); colorManager.initColor();
}); });
@ -215,7 +255,7 @@ onMounted(() => {
<style> <style>
.setting-wrapper { .setting-wrapper {
width: fit-content; min-width: 260px;
padding: 10px; padding: 10px;
} }
@ -235,7 +275,6 @@ onMounted(() => {
margin: 3px; margin: 3px;
padding: 8px 12px; padding: 8px 12px;
height: 40px; height: 40px;
width: 280px !important;
border-radius: .5em; border-radius: .5em;
background-color: var(--background); background-color: var(--background);
display: flex; display: flex;

View File

@ -0,0 +1,5 @@
import { reactive } from "vue";
export const fileMenuContext = reactive({
show: false,
});

View File

@ -0,0 +1,101 @@
<template>
<div class="file-menu" :style="btnStyle">
<div
class="btn"
@click.stop="fileMenuContext.show = !fileMenuContext.show"
>
<span class="iconfont icon-menu"></span>
</div>
<!-- 菜单列表 -->
<transition name="collapse-from-top" mode="out-in">
<div class="list" v-show="fileMenuContext.show">
<SaveAsSvg></SaveAsSvg>
<SaveAsPdf></SaveAsPdf>
</div>
</transition>
</div>
</template>
<script setup>
import { computed, defineComponent } from 'vue';
import { fileMenuContext } from './control';
import SaveAsSvg from './save-as-svg.vue';
import SaveAsPdf from './save-as-pdf.vue';
defineComponent({ name: 'file-menu' });
const btnStyle = computed(() => ({
borderRadius: fileMenuContext.show ? '99em': '.5em'
}));
</script>
<style>
:root {
--menu-btn-width: calc(var(--toolbar-item-height) + 3px);
}
.file-menu {
height: var(--menu-btn-width);
width: var(--menu-btn-width);
background-color: var(--sidebar);
position: relative;
}
/* 这是按钮主体的 css */
.file-menu .btn {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
cursor: pointer;
}
.file-menu .list {
position: absolute;
top: calc(var(--menu-btn-width) + 10px);
left: 5px;
border-radius: .5em;
box-shadow: var(--gray-box-shadow-1);
background-color: var(--sidebar);
border: solid 1px var(--sidebar-border);
display: flex;
flex-direction: column;
padding: 10px 7px;
font-size: .9rem;
}
.file-menu .list .item {
margin: 2.5px;
padding: 4px;
width: 230px;
display: flex;
border-radius: .5em;
justify-content: space-between;
cursor: pointer;
transition: var(--animation-3s);
}
.file-menu .list > div {
display: flex;
align-items: center;
}
.file-menu .list .item:hover {
background-color: var(--sidebar-item-selected);
transition: var(--animation-3s);
}
.file-menu .list .status {
display: flex;
align-items: center;
font-size: 1.2rem;
width: 17px;
font-weight: 800;
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<div>
<div class="status"></div>
<div class="item" @click="manualLoadView()">
<span>{{ t('toolbar.save-as-pdf') }}</span>
<span></span>
</div>
</div>
</template>
<script setup>
import { saveAsPdf } from '@/api';
import { ElLoading } from 'element-plus';
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
defineComponent({ name: 'save-as-pdf' });
async function manualLoadView() {
const loading = new ElLoading.service({
lock: true,
text: t('saving'),
background: 'rgba(0, 0, 0, 0.7)'
});
const res = await saveAsPdf();
loading.close();
}
// document.addEventListener('keydown', async event => {
// if (event.ctrlKey && event.key === 'k') {
// event.preventDefault();
// manualLoadView();
// }
// });
</script>

View File

@ -0,0 +1,39 @@
<template>
<div>
<div class="status"></div>
<div class="item" @click="manualLoadView()">
<span>{{ t('toolbar.save-as-svg') }}</span>
<span><code>Shift S</code></span>
</div>
</div>
</template>
<script setup>
import { saveAsSvg } from '@/api';
import { ElLoading } from 'element-plus';
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
defineComponent({ name: 'save-as-svg' });
async function manualLoadView() {
const loading = new ElLoading.service({
lock: true,
text: t('saving'),
background: 'rgba(0, 0, 0, 0.7)'
});
const res = await saveAsSvg();
loading.close();
}
document.addEventListener('keydown', async event => {
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
manualLoadView();
}
});
</script>

View File

@ -0,0 +1,51 @@
<template>
<div
class="toolbar-container"
@mouseenter="onEnter()"
@click="toolBarClick()"
>
<div class="toolbar-body">
<FileMenu></FileMenu>
</div>
</div>
</template>
<script setup>
import { defineComponent } from 'vue';
import { fileMenuContext } from './file-menu/control';
import FileMenu from './file-menu';
/* eslint-disable vue/multi-word-component-names */
defineComponent({ name: 'toolbar' });
function onEnter() {
}
function toolBarClick() {
fileMenuContext.show = false;
}
</script>
<style>
.toolbar-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
min-width: 1000px;
height: var(--toolbar-height);
user-select: none;
background-color: var(--background);
z-index: 220;
}
.toolbar-body {
display: flex;
height: 100%;
align-items: top;
padding-left: 7px;
padding-top: 8px;
}
</style>

View File

@ -1,24 +1,36 @@
<template> <template>
<div class="netlist-tree-view"> <div class="netlist-tree-view">
<div class="netlist-module-info"> <div class="netlist-module-info" :style="netlistModuleStyle">
<div class="netlist-signal-title">{{ t('module') }}</div> <div class="netlist-signal-title">{{ t('module') }}</div>
<hr style="width: 100%;"> <hr style="width: 100%;">
<el-scrollbar height="86vh" style="padding-right: 7px;">
<el-scrollbar height="40vh" style="padding-right: 7px;height: 50%;">
<modules <modules
v-for="mod of treeviewData.modules" v-for="mod of treeviewData.modules"
:key="mod.name" :key="mod.name"
:module="mod" :module="mod"
></modules> ></modules>
</el-scrollbar> </el-scrollbar>
<div class="netlist-signal-title">{{ t('entity-info') }}</div>
<hr style="width: 100%;">
<el-scrollbar height="40vh" style="padding-right: 7px;height: 50%;">
<info></info>
</el-scrollbar>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { defineComponent, onMounted } from 'vue'; import { defineComponent, onMounted, computed } from 'vue';
import modules from './modules.vue'; import modules from './modules.vue';
import info from './info.vue';
import { resize, treeviewData, verticalResizer } from './tree'; import { resize, treeviewData, verticalResizer } from './tree';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { horizontalResizer } from '../right-nav';
const { t } = useI18n(); const { t } = useI18n();
@ -31,11 +43,17 @@ onMounted(() => {
}) })
}); });
const netlistModuleStyle = computed(() => ({
width: horizontalResizer.width - 30 + 'px'
}));
</script> </script>
<style> <style>
.netlist-module-info { .netlist-module-info {
padding-right: 5px; padding-right: 5px;
min-width: 260px;
} }
.netlist-signal-title { .netlist-signal-title {

View File

@ -0,0 +1,116 @@
import { globalLookup, globalSetting } from '@/hook/global';
import { Cell, ModuleView, Port } from '@/hook/render/yosys';
import { SkinManager } from '@/hook/skin';
import i18n from '@/i18n';
import { reactive } from 'vue';
const { t, locale } = i18n.global;
locale.value = globalSetting.language;
export const infoView = reactive({
name: undefined,
type: undefined,
/**
* @type {'port' | 'module'}
*/
typeId: undefined,
module: undefined,
definition: undefined,
width: undefined,
direction: undefined,
// module 渲染属性
inputCount: undefined,
outputCount: undefined,
inoutCount: undefined,
instanceCount: undefined,
cellCount: undefined,
instanceName: undefined,
instanceDefinition: undefined,
// edge 渲染属性
from: undefined,
to: undefined,
/**
* @description 显示一个 port 到当前信息窗口
* @param {Port} port
*/
displayPort(port) {
this.clear();
this.name = port.name;
this.module = port.view.name;
this.type = t("common.port");
this.typeId = 'port';
this.definition = port.definition;
this.width = port.width;
this.direction = port.direction;
},
/**
* @description 显示一个 module 到当前窗口
* @param {ModuleView} module
* @param {Cell} [cell]
*/
displayModule(module, cell) {
this.clear();
const isTop = (cell === undefined);
this.name = module.name;
this.type = (!isTop) ?
t("common.instance"):
t('common.top-module');
this.typeId = 'module';
this.definition = module.definition;
this.instanceName = (cell || {}).name;
this.instanceDefinition = (cell || {}).definition;
const portInfo = module.portInfo;
this.inputCount = portInfo.input;
this.outputCount = portInfo.output;
this.inoutCount = portInfo.inout;
const skinManager = globalLookup.skinManager;
let instanceCount = 0;
let cellCount = 0;
this.instanceCount = 0;
this.cellCount = 0;
for (const cellname of module.nameToCell.keys()) {
const cell = module.nameToCell.get(cellname);
const skin = skinManager.querySkin(cell.type);
if (skin) {
cellCount ++;
} else {
instanceCount ++;
}
}
this.instanceCount = instanceCount;
this.cellCount = cellCount;
},
clear() {
this.name = undefined;
this.type = undefined;
this.module = undefined;
this.definition = undefined;
this.direction = undefined;
this.width = undefined;
this.from = undefined;
this.to = undefined;
this.instanceName = undefined;
}
});

View File

@ -1,14 +1,186 @@
<template> <template>
<div> <div class="treeview-info">
<div class="treeview-info-blank" v-if="!globalLookup.currentSelectContext">
{{ t('info.no-entity-select.cannot-display-view') }}
</div>
<div v-else-if="infoView.typeId === 'module'"
class="info-wrapper">
<span class="info-item-container">
<span>{{ t("common.type") }}</span>
<span>
<span class="iconfont icon-info-module"></span>{{ infoView.type }}
</span>
</span>
<span class="info-item-container">
<span>{{ t("common.name") }}</span>
<span><code>{{ infoView.name }}</code></span>
</span>
<span class="info-item-container">
<span>
{{ t("common.definition") }}
</span>
<span class="definition" @click="gotoDefinition(infoView.definition)">
<span class="iconfont icon-link"></span>{{ renderDefinition(infoView.definition) }}
</span>
</span>
<span class="info-item-container" v-if="infoView.instanceName">
<span>{{ t('common.instance-name') }}</span>
<span><code>{{ infoView.instanceName }}</code></span>
</span>
<span class="info-item-container" v-if="infoView.instanceDefinition">
<span>{{ t('common.instance-definition') }}</span>
<span class="definition" @click="gotoDefinition(infoView.instanceDefinition)">
<span class="iconfont icon-link"></span>{{ renderDefinition(infoView.instanceDefinition) }}
</span>
</span>
<span class="info-item-container">
<span>{{ t('common.input-number') }}</span>
<span>{{ infoView.inputCount }}</span>
</span>
<span class="info-item-container">
<span>{{ t('common.output-number') }}</span>
<span>{{ infoView.outputCount }}</span>
</span>
<span class="info-item-container">
<span>{{ t('common.inout-number') }}</span>
<span>{{ infoView.inoutCount }}</span>
</span>
<span class="info-item-container">
<span>{{ t('common.instance-number') }}</span>
<span>{{ infoView.instanceCount }}</span>
</span>
<span class="info-item-container">
<span>{{ t('common.general-cell-number') }}</span>
<span>{{ infoView.cellCount }}</span>
</span>
</div>
<div v-else-if="infoView.typeId === 'port'"
class="info-wrapper">
<span class="info-item-container">
<span>{{ t('common.type') }}</span>
<span>
<span class="iconfont icon-info-port"></span>{{ infoView.type }}
</span>
</span>
<span class="info-item-container">
<span>{{ t('common.name') }}</span>
<span><code>{{ infoView.name }}</code></span>
</span>
<span class="info-item-container">
<span>
{{ t('common.definition') }}
</span>
<span class="definition" @click="gotoDefinition(infoView.definition)">
<span class="iconfont icon-link"></span>{{ renderDefinition(infoView.definition) }}
</span>
</span>
<span class="info-item-container">
<span>{{ t('common.belong-module') }}</span>
<span><span class="iconfont icon-info-module"></span><code>{{ infoView.module }}</code></span>
</span>
<span class="info-item-container">
<span>{{ t('common.width') }}</span>
<span>{{ infoView.width }}</span>
</span>
<span class="info-item-container">
<span>{{ t('common.direction') }}</span>
<span>{{ infoView.direction }}</span>
</span>
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { infoView } from './info';
import { useI18n } from 'vue-i18n';
import { globalLookup } from '@/hook/global';
import { gotoDefinition } from '@/api/definition';
const { t } = useI18n();
defineComponent({ name: 'treeview-item-info' }); defineComponent({ name: 'treeview-item-info' });
function renderDefinition(definition) {
if (typeof definition !== 'string') {
return 'unknown';
}
if (definition.includes('|')) {
const filenames = [];
for (const def of definition.split('|')) {
filenames.push(renderDefinition(def));
}
return filenames.join('').split(':').at(0);
}
const filename = definition.split('/').at(-1);
if (!filename) {
return definition;
}
return filename.split(':').at(0);
}
</script> </script>
<style>
.treeview-info {
padding: 10px;
}
.treeview-info-blank {
font-size: .9rem;
}
.info-item-container {
display: flex;
margin: 2px;
align-items: center;
width: 100%;
justify-content: space-between;
font-size: .8rem;
}
.info-item-container span:nth-child(1) {
padding-left: 10px;
}
.info-item-container span:nth-child(2) {
overflow: hidden;
max-width: 220px;
text-overflow: ellipsis;
white-space: nowrap;
padding-right: 10px;
}
.info-wrapper > span {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: solid 1px var(--sidebar-item-selected);
}
.info-wrapper > span:last-child {
margin-bottom: 0;
border-bottom: unset;
}
.info-item-container .definition {
cursor: pointer;
transition: var(--animation-3s);
}
.info-item-container .iconfont {
margin-right: 5px;
}
.info-item-container .definition:hover {
cursor: pointer;
color: var(--main-color);
transition: var(--animation-3s);
}
</style>

View File

@ -1,6 +1,9 @@
<template> <template>
<div class="module"> <div class="module">
<div @click="clickItem()" class="netlist-treeview-item"> <div @click="clickModule()"
class="netlist-treeview-item"
:class="{'active': sameModule() }"
>
<span class="module-tag-status" @click.stop="expandManage.click"> <span class="module-tag-status" @click.stop="expandManage.click">
<div :class="expandManage.expandTagClass"></div> <div :class="expandManage.expandTagClass"></div>
</span> </span>
@ -17,10 +20,14 @@
<div style="width: 20px;"></div> <div style="width: 20px;"></div>
<div style="width: 100%;"> <div style="width: 100%;">
<!-- ports --> <!-- ports -->
<div v-for="port in ports" :key="port.name" class="netlist-treeview-item"> <div v-for="port in ports" :key="port.name"
class="netlist-treeview-item"
:class="{'active': samePort(port)}"
@click="clickPort(port)"
>
<span class="module-tag-status"> <span class="module-tag-status">
</span> </span>
<span class="iconfont icon-wave-square"></span> <span class="iconfont icon-info-port"></span>
&ensp;{{ port.name }} &ensp;{{ port.name }}
</div> </div>
@ -28,6 +35,8 @@
<modules <modules
v-for="(cell, index) in cells" v-for="(cell, index) in cells"
:module="cell.view" :module="cell.view"
:cell="cell.data"
:prefix-name="cell.prefixName"
:render-name="cell.renderName" :render-name="cell.renderName"
:key="index" :key="index"
></modules> ></modules>
@ -39,8 +48,9 @@
<script setup> <script setup>
/* eslint-disable */ /* eslint-disable */
import { globalLookup } from '@/hook/global'; import { globalLookup } from '@/hook/global';
import { ModuleView } from '@/hook/render/yosys'; import { dotConnect, ModuleView } from '@/hook/render/yosys';
import { defineComponent, reactive, computed } from 'vue'; import { defineComponent, reactive, computed } from 'vue';
import { infoView } from './info';
defineComponent({ name: 'modules' }); defineComponent({ name: 'modules' });
@ -49,12 +59,19 @@ const props = defineProps({
type: ModuleView, type: ModuleView,
required: true required: true
}, },
cell: {
type: Object
},
renderName: { renderName: {
type: String type: String
},
prefixName: {
type: String
} }
}); });
const module = props.module; const module = props.module;
const id = props.cell ? props.cell.id: module.id;
const renderName = computed(() => { const renderName = computed(() => {
return props.renderName ? props.renderName : module.name return props.renderName ? props.renderName : module.name
@ -66,7 +83,16 @@ const cells = [];
// ports & modules // ports & modules
for (const portName of module.nameToPort.keys()) { for (const portName of module.nameToPort.keys()) {
const port = module.nameToPort.get(portName);
// prefixName treeview port id
// 使 port.id port.id port
const portId = props.prefixName ?
dotConnect(props.prefixName, port.name) :
port.id;
ports.push({ ports.push({
id: portId,
data: port,
name: portName name: portName
}); });
} }
@ -80,8 +106,14 @@ for (const cellName of module.nameToCell.keys()) {
continue; continue;
} }
if (cell.isInstantiation) { if (cell.isInstantiation) {
const prefixName = props.prefixName ?
dotConnect(props.prefixName, cell.name) :
cell.id;
cells.push({ cells.push({
name: cellName, name: cellName,
data: cell,
prefixName,
view: cell.belongModuleView, view: cell.belongModuleView,
renderName: `${cellName} (${cell.belongModuleView.name})` renderName: `${cellName} (${cell.belongModuleView.name})`
}); });
@ -90,8 +122,42 @@ for (const cellName of module.nameToCell.keys()) {
function clickItem() { function sameModule() {
if (!globalLookup.currentSelectContext) {
return false;
}
return globalLookup.currentSelectContext.id === id;
}
function samePort(port) {
if (!globalLookup.currentSelectContext) {
return false;
}
return globalLookup.currentSelectContext.id === port.id;
}
function clickPort(port) {
const portView = module.nameToPort.get(port.name);
infoView.displayPort(portView);
globalLookup.currentSelectContext = {
id: port.id,
data: port,
type: 'port',
moduleView: props.module
};
}
function clickModule() {
infoView.displayModule(module, props.cell);
globalLookup.currentSelectContext = {
id: id,
data: props.module,
type: 'module',
moduleView: props.module,
cell: props.cell
};
} }
function getExpandStatus() { function getExpandStatus() {
@ -110,16 +176,20 @@ const expandManage = reactive({
}); });
function makeIconClass() { function makeIconClass() {
return 'icon-memory-chip'; return 'icon-info-module';
} }
</script> </script>
<style> <style>
.icon-memory-chip { .module .icon-info-module {
color: #FF7043; color: #FF7043;
} }
.module .icon-info-port {
color: var(--main-color);
}
.module { .module {
text-align: left; text-align: left;
} }
@ -148,6 +218,12 @@ function makeIconClass() {
border-radius: .3em; border-radius: .3em;
} }
.netlist-treeview-item.active {
background-color: var(--sidebar-item-selected);
border-radius: .3em;
transition: var(--animation-3s);
}
.netlist-treeview-selected { .netlist-treeview-selected {
color: #ddd; color: #ddd;
background-color: var(--button-active) !important; background-color: var(--button-active) !important;

View File

@ -3,6 +3,7 @@
* @property {number} r * @property {number} r
* @property {number} g * @property {number} g
* @property {number} b * @property {number} b
* @property {number} [a] 透明度值置于 0 - 1 之间
*/ */
/** /**
@ -29,7 +30,8 @@ export function parseColor(colorString) {
const r = parseInt(matches[1], 10); const r = parseInt(matches[1], 10);
const g = parseInt(matches[2], 10); const g = parseInt(matches[2], 10);
const b = parseInt(matches[3], 10); const b = parseInt(matches[3], 10);
return { r, g, b }; const a = parseFloat(matches[4]);
return { r, g, b, a };
} }
} }
// 检查是否是 RGB 颜色 // 检查是否是 RGB 颜色
@ -68,6 +70,28 @@ export function increaseBrightness(rgb, percent) {
} }
/**
* @description 降低颜色的亮度
* @param {RgbColor} rgb
* @param {number} percent 0 - 100 的数字代表增强的亮度比例
* @returns {RgbColor}
*/
export function lowerBrightness(rgb, percent) {
// 确保 percent 在 0 到 100 之间
percent = Math.max(0, Math.min(100, percent));
// 计算每个颜色分量的增量
const increment = (percent / 100) * 255;
// 提升每个颜色分量的亮度
const r = Math.min(255, Math.round(rgb.r - increment));
const g = Math.min(255, Math.round(rgb.g - increment));
const b = Math.min(255, Math.round(rgb.b - increment));
return { r, g, b };
}
/** /**
* @description gamma 修正 * @description gamma 修正
* @param {number} c 颜色通道值取值范围为 0 - 255 * @param {number} c 颜色通道值取值范围为 0 - 255
@ -90,3 +114,114 @@ export function isLightColorTheme(r, g, b) {
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
return luminance > 0.5; return luminance > 0.5;
} }
/**
* @description 导出为 rgb css 样式的字符串
* @param {RgbColor} rgb
*/
export function toRgbCssString(rgb) {
const r = rgb.r;
const g = rgb.g;
const b = rgb.b;
return `rgb(${r}, ${g}, ${b})`;
}
/**
* @description 导出为 rgba css 样式的字符串
* @param {RgbColor} rgb
*/
export function toRgbaCssString(rgb) {
const r = rgb.r;
const g = rgb.g;
const b = rgb.b;
const a = rgb.a;
return `rgb(${r}, ${g}, ${b}, ${a})`;
}
/**
* @typedef ComputedColorOption
* @property {string} BaseForegroundColorMacroName
* @property {string} BaseBackgroundColorMacroName
*/
/**
* @typedef GetColorOption
* @property {'pdf' | 'svg'} mode
*/
export class MacroColor {
/**
*
* @param {ComputedColorOption} option
*/
constructor(option = {}) {
this.option = option;
this.rootStyles = getComputedStyle(document.documentElement);
const foregroundColorString = this.rootStyles.getPropertyValue(option.BaseForegroundColorMacroName || '--foreground');
const backgroundColorString = this.rootStyles.getPropertyValue(option.BaseBackgroundColorMacroName || '--background');
const foregroundColor = parseColor(foregroundColorString);
const backgroundColor = parseColor(backgroundColorString);
// 通过背景颜色来判断当前颜色主题的类别
const isLight = isLightColorTheme(backgroundColor.r, backgroundColor.g, backgroundColor.b);
/**
* @type {'light' | 'dark'}
*/
this.theme = isLight ? 'light' : 'dark';
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
}
/**
*
* @param {string} macroName
* @param {GetColorOption} option
*/
getColor(macroName, option = {}) {
const theme = this.theme;
const rootStyles = this.rootStyles;
const mode = option.mode || 'svg';
if (mode === 'svg') {
// svg 模式下,导出的效果和 webview 渲染效果基本一致,直接导出即可
return rootStyles.getPropertyValue(macroName);
}
// pdf 模式需要对 黑色主题 的几个特殊颜色进行一定的处理,并对所有透明颜色进行混合处理
switch (macroName) {
case '--foreground':
case '--wire-color':
case '--cross-dot-color':
if (theme === 'dark') {
return '#2D323B';
}
}
const colorString = rootStyles.getPropertyValue(macroName);
if (!colorString) {
// 如果 macroName 不存在,那么此时的 colorString 是空的
return colorString;
}
const color = parseColor(colorString);
if (!color.a) {
// 不具有透明通道,在 pdf 中渲染效果和 svg 中一致,直接返回即可
return toRgbCssString(color);
}
// 透明度插值公式为 c = c_f * \alpha + c_b * (1 - \alpha)
// c_f: 前景颜色
// c_b: 背景颜色
// alpha: 透明度
// 该操作是 channel-wise 的
const mixedBg = parseColor('#ffffff');
const mixedColor = { r: 0, g: 0, b: 0 };
const alpha = color.a;
mixedColor.r = color.r * alpha + mixedBg.r * (1 - alpha);
mixedColor.g = color.g * alpha + mixedBg.g * (1 - alpha);
mixedColor.b = color.b * alpha + mixedBg.b * (1 - alpha);
return toRgbCssString(mixedColor);
}
}

View File

@ -33,9 +33,15 @@ export const globalSetting = reactive({
/** /**
* @description 当前交叉点的样式 * @description 当前交叉点的样式
* @type {'connect' | 'slice' | 'concat'} currentStyle * @type {'connect' | 'slice' | 'concat'}
*/ */
crossDotStyle: 'connect' crossDotStyle: 'connect',
/**
* @description 脉动动效的速度
* @type {number}
*/
pulsationSpeed: 5
}); });
export const globalLookup = reactive({ export const globalLookup = reactive({
@ -62,14 +68,15 @@ export const globalLookup = reactive({
/** /**
* @description 当前选择的实体可以是 wire也可以是 cell * @description 当前选择的实体可以是 wire也可以是 cell
* @type {import('./jsdoc').ICurrentSelectContext}
*/ */
currentSelectEntity: undefined, currentSelectContext: undefined,
/** /**
* @description 右侧 treeview 选择的需要展示数据的实体 * @description 右侧 treeview 选择的需要展示数据的实体
* @type {Record<string, string>} * @type {Record<string, string>}
*/ */
currentSelectEntityInfo: {} currentSelectContextInfo: {}
}); });
function loadSetting() { function loadSetting() {

View File

@ -6,6 +6,7 @@
*/ */
import { Module } from "./render/layout"; import { Module } from "./render/layout";
import { Cell, ModuleView } from "./render/yosys";
/** /**
* @typedef {number | string} WireId 信号ID * @typedef {number | string} WireId 信号ID
@ -269,5 +270,27 @@ import { Module } from "./render/layout";
/** /**
* @typedef ElkMakerConfig * @typedef ElkMakerConfig
* @param {string} [parentId] * @property {string} [parentId]
*/
/**
* @typedef ICurrentSelectContext
* @property {string} id
* @property {any} data
* @property {'module' | 'port'} type 类型
* @property {ModuleView} moduleView 所在模块的数据视图
* @property {Cell} [cell] 例化模块在原模块中的视图
*/
/**
* @typedef Position
* @property {number} line
* @property {number} charater
*/
/**
* @typedef Range
* @property {Position} start
* @property {Position} end
*/ */

View File

@ -15,6 +15,7 @@ import { dotConnect } from './yosys';
import { CrossDotRender } from './cross-dot'; import { CrossDotRender } from './cross-dot';
import { RangeTreeMap } from '../algorithm/range-tree'; import { RangeTreeMap } from '../algorithm/range-tree';
import { ConstantRender } from './constant'; import { ConstantRender } from './constant';
import { RenderViewNode } from './render-view';
export class NetlistRender { export class NetlistRender {
/** /**
@ -206,15 +207,19 @@ export class NetlistRender {
// debug // debug
console.log(computedLayout); console.log(computedLayout);
// 生成连接 // 底层模块
await this.renderLine(g, computedLayout); const topModule = this.nameToModule.get(this.topModuleName);
// 生成实体
await this.renderEntity(g, computedLayout);
// svg 挂载为全局注册的 selection // svg 挂载为全局注册的 selection
this.selection = svg; this.selection = svg;
this.g = g;
this.renderView = new RenderViewNode(g);
// 生成连接
await this.renderLine(this.renderView, computedLayout, topModule);
// 生成实体
await this.renderEntity(this.renderView, computedLayout, topModule);
// 注册平移和缩放 // 注册平移和缩放
this.registerRenderTransform(g); this.registerRenderTransform(g);
@ -232,10 +237,10 @@ export class NetlistRender {
/** /**
* @description 绘制实体 * @description 绘制实体
* @param {d3.Selection} parentSelection * @param {RenderViewNode} view
* @param {import('../jsdoc').ElkNode} computedLayout * @param {import('../jsdoc').ElkNode} computedLayout
*/ */
async renderEntity(parentSelection, computedLayout) { async renderEntity(view, computedLayout) {
// node 可能是如下的几类 // node 可能是如下的几类
// - module 的 port // - module 的 port
// - 器件(基础器件 & 例化模块) // - 器件(基础器件 & 例化模块)
@ -243,11 +248,11 @@ export class NetlistRender {
const skinManager = globalLookup.skinManager; const skinManager = globalLookup.skinManager;
// 创建各个主要实体的 render // 创建各个主要实体的 render
this.cellRender = new CellRender(parentSelection, this); view.cellRender = new CellRender(view.g, this);
this.portRender = new PortRender(parentSelection, this); view.portRender = new PortRender(view.g, this);
this.instantiationRender = new InstantiationRender(parentSelection, this); view.instantiationRender = new InstantiationRender(view.g, this);
this.connectionRender = new ConnectionRender(parentSelection, this); view.connectionRender = new ConnectionRender(view.g, this);
this.constantRender = new ConstantRender(parentSelection, this); view.constantRender = new ConstantRender(view.g, this);
for (const node of computedLayout.children) { for (const node of computedLayout.children) {
// 只计算形体的,因为 连接点 非常小,几乎不影响布局 // 只计算形体的,因为 连接点 非常小,几乎不影响布局
@ -257,41 +262,40 @@ export class NetlistRender {
if (skin) { if (skin) {
// 具有 skin 的器件 // 具有 skin 的器件
const element = skin.meta.svgDoc.documentElement.cloneNode(true); const element = skin.meta.svgDoc.documentElement.cloneNode(true);
this.cellRender.addAsD3DataItem(node, element); view.cellRender.addAsD3DataItem(node, element);
} else { } else {
if (node.renderType === 'port') { if (node.renderType === 'port') {
this.portRender.addAsD3DataItem(node); view.portRender.addAsD3DataItem(node);
} else if (node.renderType === 'instance') { } else if (node.renderType === 'instance') {
// 没有 skin 的器件或者端口 // 没有 skin 的器件或者端口
this.instantiationRender.addAsD3DataItem(node); view.instantiationRender.addAsD3DataItem(node);
} else if (node.renderType === 'constant') { } else if (node.renderType === 'constant') {
this.constantRender.addAsD3DataItem(node); view.constantRender.addAsD3DataItem(node);
} }
} }
// 如果存在 port绘制 port // 如果存在 port绘制 port
for (const cellPort of node.ports || []) { for (const cellPort of node.ports || []) {
this.connectionRender.addAsD3DataItem(cellPort, node); view.connectionRender.addAsD3DataItem(cellPort, node);
} }
} }
const ports = this.portRender.render(); view.portRender.render();
const instances = this.instantiationRender.render(); view.instantiationRender.render();
const cells = this.cellRender.render(); view.cellRender.render();
const connections = this.connectionRender.render(); view.connectionRender.render();
const constants = this.constantRender.render(); view.constantRender.render();
return { ports, instances, cells, connections };
} }
/** /**
* @description 绘制连线和交叉点 * @description 绘制连线和交叉点
* @param {d3.Selection} parentSelection * @param {RenderViewNode} view
* @param {ElkNode} computedLayout * @param {ElkNode} computedLayout
* @param {Module} module
*/ */
async renderLine(parentSelection, computedLayout) { async renderLine(view, computedLayout, module) {
this.wireRender = new WireRender(parentSelection, this); view.wireRender = new WireRender(view.g, this);
this.crossDotRender = new CrossDotRender(parentSelection, this); view.crossDotRender = new CrossDotRender(view.g, this);
// 建立关于 port 的索引 // 建立关于 port 的索引
const id2port = new Map(); const id2port = new Map();
@ -304,6 +308,7 @@ export class NetlistRender {
const rangeTree = new RangeTreeMap(); const rangeTree = new RangeTreeMap();
for (const edge of computedLayout.edges) { for (const edge of computedLayout.edges) {
const width = module.id2EdgeCount.get(edge.id);
for (const section of edge.sections || []) { for (const section of edge.sections || []) {
const points = []; const points = [];
points.push(section.startPoint); points.push(section.startPoint);
@ -311,7 +316,7 @@ export class NetlistRender {
points.push(point); points.push(point);
} }
points.push(section.endPoint); points.push(section.endPoint);
this.wireRender.addAsD3DataItems(points, edge, id2port); view.wireRender.addAsD3DataItems(points, edge, id2port, width);
// 加入 range tree 中 // 加入 range tree 中
for (let i = 0; i < points.length - 1; ++ i) { for (let i = 0; i < points.length - 1; ++ i) {
@ -332,14 +337,14 @@ export class NetlistRender {
for (const point of section.bendPoints || []) { for (const point of section.bendPoints || []) {
const degree = rangeTree.getDegree(point); const degree = rangeTree.getDegree(point);
if (degree >= 3) { if (degree >= 3) {
this.crossDotRender.addAsD3DataItem(point.x, point.y); view.crossDotRender.addAsD3DataItem(point.x, point.y);
} }
} }
} }
} }
this.wireRender.render(); view.wireRender.render();
this.crossDotRender.render(); view.crossDotRender.render();
} }
/** /**
@ -585,7 +590,6 @@ export class NetlistRender {
elkNode.edges = []; elkNode.edges = [];
elkNode.layoutOptions = this.defaultLayoutOptions; elkNode.layoutOptions = this.defaultLayoutOptions;
// elkNode.children.push(...portNodes); // elkNode.children.push(...portNodes);
elkNode.children.push(...cellNodes); elkNode.children.push(...cellNodes);
elkNode.children.push(...constantNodes); elkNode.children.push(...constantNodes);
@ -610,31 +614,31 @@ export class NetlistRender {
this.zoom.translateBy(this.selection, deltaX, deltaY); this.zoom.translateBy(this.selection, deltaX, deltaY);
const svg = this.selection; const svg = this.selection;
const g = this.g; const g = this.renderView.g;
g.selectAll('*').remove(); g.selectAll('*').remove();
this.renderView.id2children.clear();
// 开始递归地进行渲染 // 开始递归地进行渲染
const renderStack = [ const renderStack = [
{ {
parentSelection: g, view: this.renderView,
layout: computedLayout layout: computedLayout
} }
]; ];
while (renderStack.length > 0) { while (renderStack.length > 0) {
const s = renderStack.pop(); const s = renderStack.pop();
const parentSelection = s.parentSelection; const view = s.view;
const layout = s.layout; const layout = s.layout;
await this.renderLine(parentSelection, layout); const layoutName = layout.renderName || this.topModuleName;
const { instances } = await this.renderEntity(parentSelection, layout); const module = this.nameToModule.get(layoutName);
const id2selection = new Map(); await this.renderLine(view, layout, module);
instances.each(function(data) { await this.renderEntity(view, layout, module);
const selection = d3.select(this);
id2selection.set(data.id, selection); const id2selection = view.instantiationRender.id2selection;
});
// 将需要渲染子图的部分扔进渲染器 // 将需要渲染子图的部分扔进渲染器
for (const node of layout.children || []) { for (const node of layout.children || []) {
@ -642,8 +646,10 @@ export class NetlistRender {
if (subChildren.length > 0) { if (subChildren.length > 0) {
const selection = id2selection.get(node.id); const selection = id2selection.get(node.id);
const subView = new RenderViewNode(selection);
view.setChild(node.id, subView);
renderStack.push({ renderStack.push({
parentSelection: selection, view: subView,
layout: node layout: node
}); });
} }
@ -695,37 +701,41 @@ export class NetlistRender {
this.zoom.translateBy(this.selection, deltaX, deltaY); this.zoom.translateBy(this.selection, deltaX, deltaY);
const svg = this.selection; const svg = this.selection;
const g = this.g; const g = this.renderView.g;
g.selectAll('*').remove(); g.selectAll('*').remove();
this.renderView.id2children.clear();
// 开始递归地进行渲染 // 开始递归地进行渲染
const renderStack = [ const renderStack = [
{ {
parentSelection: g, view: this.renderView,
layout: computedLayout layout: computedLayout
} }
]; ];
while (renderStack.length > 0) { while (renderStack.length > 0) {
const s = renderStack.pop(); const s = renderStack.pop();
const parentSelection = s.parentSelection; const view = s.view;
const layout = s.layout; const layout = s.layout;
await this.renderLine(parentSelection, layout);
const { instances } = await this.renderEntity(parentSelection, layout); const layoutName = layout.renderName || this.topModuleName;
const id2selection = new Map(); const module = this.nameToModule.get(layoutName);
instances.each(function(data) {
const selection = d3.select(this); await this.renderLine(view, layout, module);
id2selection.set(data.id, selection); await this.renderEntity(view, layout, module);
});
const id2selection = view.instantiationRender.id2selection;
// 将需要渲染子图的部分扔进渲染器 // 将需要渲染子图的部分扔进渲染器
for (const node of layout.children || []) { for (const node of layout.children || []) {
const subChildren = node.children || []; const subChildren = node.children || [];
if (subChildren.length > 0) { if (subChildren.length > 0) {
const selection = id2selection.get(node.id); const selection = id2selection.get(node.id);
const subView = new RenderViewNode(selection);
view.id2children.set(node.id, subView);
renderStack.push({ renderStack.push({
parentSelection: selection, view: subView,
layout: node layout: node
}); });
} }

View File

@ -235,6 +235,15 @@ export class InstantiationRender {
}); });
this.selections = instantiationSelections; this.selections = instantiationSelections;
const id2selection = new Map();
instantiationSelections.each(function(data) {
const selection = d3.select(this);
id2selection.set(data.id, selection);
});
this.id2selection = id2selection;
return instantiationSelections; return instantiationSelections;
} }

View File

@ -233,7 +233,6 @@ export class Module {
} }
const constantWidth = Math.max(12 * 0.6 * renderString.toString().length + 10, LAYOUT_CONSTANT.CONSTANT_WIDTH); const constantWidth = Math.max(12 * 0.6 * renderString.toString().length + 10, LAYOUT_CONSTANT.CONSTANT_WIDTH);
console.log(constantWidth);
const constantConnection = { const constantConnection = {
id: dotConnect(id, '0'), id: dotConnect(id, '0'),
@ -330,7 +329,7 @@ export class Module {
continue; continue;
} }
if (conn.direction === 'input') { if (connection.direction === 'output') {
const edgeId = makeEdgeId(conn.id, connection.id); const edgeId = makeEdgeId(conn.id, connection.id);
if (!id2EdgeCount.has(edgeId)) { if (!id2EdgeCount.has(edgeId)) {
id2EdgeCount.set(edgeId, 0); id2EdgeCount.set(edgeId, 0);
@ -414,8 +413,6 @@ export class Module {
const connection = topSideConnections[i]; const connection = topSideConnections[i];
const xOffset = meta.getPortXOffset(connection.name); const xOffset = meta.getPortXOffset(connection.name);
console.log(xOffset);
ports.push({ ports.push({
id: connection.id, id: connection.id,
renderName: connection.name, renderName: connection.name,

View File

@ -6,6 +6,7 @@ import { ConnectionRender } from './connection';
import { CellRender } from './cell'; import { CellRender } from './cell';
import { CrossDotRender } from './cross-dot'; import { CrossDotRender } from './cross-dot';
import { globalLookup } from '../global'; import { globalLookup } from '../global';
import { ConstantRender } from './constant';
export class RenderViewNode { export class RenderViewNode {
/** /**
@ -45,10 +46,15 @@ export class RenderViewNode {
*/ */
this.portRender = undefined; this.portRender = undefined;
/**
* @type {ConstantRender}
*/
this.constantRender = undefined;
/** /**
* @type {Map<string, RenderViewNode>} * @type {Map<string, RenderViewNode>}
*/ */
this.id2children = new Map; this.id2children = new Map();
} }
hasChild(id) { hasChild(id) {
@ -119,4 +125,9 @@ export class RenderViewNode {
this.wireRender.render(); this.wireRender.render();
} }
findNode() {
}
} }

View File

@ -5,7 +5,7 @@ import { globalSetting } from '../global';
import { NetlistRender } from '.'; import { NetlistRender } from '.';
import { getMarginParamter, LAYOUT_CONSTANT, LINE_WIDTH } from './layout'; import { getMarginParamter, LAYOUT_CONSTANT, LINE_WIDTH } from './layout';
import { svgResource } from '../skin/draw'; import { svgResource } from '../skin/draw';
import { PulseLine } from '../skin/plusation'; import { PulseLine } from '../skin/pulsation';
export class WireRender { export class WireRender {
/** /**
@ -39,8 +39,9 @@ export class WireRender {
* @param {import('../jsdoc').ElkPoint[]} points 长度至少为 2 的数组代表经历过的点集 * @param {import('../jsdoc').ElkPoint[]} points 长度至少为 2 的数组代表经历过的点集
* @param {import('../jsdoc').ElkEdge} edge * @param {import('../jsdoc').ElkEdge} edge
* @param {Map<string, import('../jsdoc').ElkPort>} id2port * @param {Map<string, import('../jsdoc').ElkPort>} id2port
* @param {number} width
*/ */
addAsD3DataItems(points, edge, id2port) { addAsD3DataItems(points, edge, id2port, width) {
const linePaths = []; const linePaths = [];
const beginPoint = points.at(0); const beginPoint = points.at(0);
@ -86,6 +87,7 @@ export class WireRender {
id: this.idCounter, id: this.idCounter,
svg: lineSvg, svg: lineSvg,
endPoint: points.at(-1), endPoint: points.at(-1),
width: width,
arrow: { arrow: {
icon: direction + '-arrow', icon: direction + '-arrow',
x: arrowLocation.x, x: arrowLocation.x,
@ -107,7 +109,6 @@ export class WireRender {
const arrowHeight = 12; const arrowHeight = 12;
const arrowWidth = 12; const arrowWidth = 12;
let lineSelections = this.selection.selectAll('path.lines') let lineSelections = this.selection.selectAll('path.lines')
.data(data) .data(data)
.enter() .enter()
@ -115,7 +116,7 @@ export class WireRender {
.attr('d', d => d.svg) .attr('d', d => d.svg)
.attr('class', 'connection-line') .attr('class', 'connection-line')
.attr("fill", "none") .attr("fill", "none")
.attr('stroke', 'var(--wire-color)'); .attr('stroke', 'var(--wire-color)')
const arrows = new Map(); const arrows = new Map();
@ -194,18 +195,23 @@ export class WireRender {
}); });
if (globalSetting.renderAnimation) { // if (globalSetting.renderAnimation) {
lineSelections = lineSelections // lineSelections = lineSelections
.transition() // .transition()
.duration(1000); // .duration(1000);
} // }
lineSelections lineSelections
.attr('stroke-width', LINE_WIDTH) .attr('stroke-width', d => {
const incrementWidth = globalSetting.boldMultiWidthWire ? 2 : 0;
return d.width > 1 ? LINE_WIDTH + incrementWidth: LINE_WIDTH;
})
.each(function (data) { .each(function (data) {
const selection = d3.select(this); const selection = d3.select(this);
// const manager = _this.createDataManager(selection, data); // const manager = _this.createDataManager(selection, data);
}); });
this.lineSelections = lineSelections;
} }
/** /**

View File

@ -179,6 +179,35 @@ export class ModuleView {
get id() { get id() {
return this.name; return this.name;
} }
/**
* @typedef ModuleViewPortInfo
* @property {number} input
* @property {number} output
* @property {number} inout
*
* @returns {ModuleViewPortInfo}
*/
get portInfo() {
if (this._portInfo) {
return this._portInfo;
}
this._portInfo = {
input: 0,
output: 0,
inout: 0
};
for (const portName of this.nameToPort.keys()) {
const port = this.nameToPort.get(portName);
this._portInfo[port.direction] ++;
}
return this._portInfo;
}
get definition() {
const attributes = this.rawModule.attributes;
return definitionFromAttribute(attributes);
}
} }
export class Port { export class Port {
@ -186,7 +215,7 @@ export class Port {
* @description port 的抽象 * @description port 的抽象
* @param {ModuleView} view * @param {ModuleView} view
* @param {string} name * @param {string} name
* @param {YosysPort} rawPort * @param {import("../jsdoc").YosysPort} rawPort
*/ */
constructor(view, name, rawPort) { constructor(view, name, rawPort) {
this.view = view; this.view = view;
@ -198,6 +227,10 @@ export class Port {
return this.rawPort.bits; return this.rawPort.bits;
} }
get width() {
return (this.rawPort.bits || []).length;
}
get direction() { get direction() {
return this.rawPort.direction; return this.rawPort.direction;
} }
@ -205,6 +238,15 @@ export class Port {
get id() { get id() {
return dotConnect(this.view.id, this.name); return dotConnect(this.view.id, this.name);
} }
get definition() {
const netname = this.view.rawModule.netnames[this.name];
if (!netname) {
return undefined;
}
const attributes = netname.attributes;
return definitionFromAttribute(attributes);
}
} }
export class Cell { export class Cell {
@ -257,6 +299,11 @@ export class Cell {
get id() { get id() {
return dotConnect(this.view.id, this.name); return dotConnect(this.view.id, this.name);
} }
get definition() {
const attributes = this.rawCell.attributes;
return definitionFromAttribute(attributes);
}
} }
@ -326,3 +373,24 @@ export function dotConnect(...args) {
const stringArgs = args.map(arg => arg.toString()); const stringArgs = args.map(arg => arg.toString());
return stringArgs.join('.'); return stringArgs.join('.');
} }
/**
*
* @param {import("../jsdoc").ModuleAttribute} attributes
*/
function definitionFromAttribute(attributes) {
if (!attributes) {
return undefined;
}
const src = attributes.src;
if (!src) {
return undefined;
}
if (src.startsWith('/dide')) {
return src.replace('/dide', '{workspace}');
}
if (src.startsWith('/lib')) {
return src.replace('/lib', '{library}');
}
return src;
}

View File

@ -81,6 +81,9 @@ class SkinMeta {
const levelName = path.split('/')[1]; const levelName = path.split('/')[1];
const cssName = levelName2CssName[levelName]; const cssName = levelName2CssName[levelName];
this.path = path;
this.svgString = svgString;
const color = `var(--${cssName}-color)`; const color = `var(--${cssName}-color)`;
const fillColor = `var(--${cssName}-fill-color)`; const fillColor = `var(--${cssName}-fill-color)`;
@ -128,10 +131,13 @@ class SkinMeta {
return 0; return 0;
} }
const transform = pathElement.getAttribute('transform'); const transform = pathElement.getAttribute('transform');
if (transform) {
const yOffset = parseFloat(transform.split(' ').at(-1)); const yOffset = parseFloat(transform.split(' ').at(-1));
this.portToYOffset.set(portName, yOffset); this.portToYOffset.set(portName, yOffset);
return yOffset; return yOffset;
} }
return 0;
}
/** /**
* @description 获取指定 port 在所在 svg Y 轴的相对偏移量 * @description 获取指定 port 在所在 svg Y 轴的相对偏移量
@ -147,9 +153,12 @@ class SkinMeta {
return 0; return 0;
} }
const transform = pathElement.getAttribute('transform'); const transform = pathElement.getAttribute('transform');
if (transform) {
const offsetnumber = transform.split('(').at(-1).split(' ').at(0); const offsetnumber = transform.split('(').at(-1).split(' ').at(0);
const xOffset = parseFloat(offsetnumber); const xOffset = parseFloat(offsetnumber);
this.portToXOffset.set(portName, xOffset); this.portToXOffset.set(portName, xOffset);
return xOffset; return xOffset;
} }
return 0;
}
} }

View File

@ -1,4 +1,5 @@
import * as d3 from 'd3'; import * as d3 from 'd3';
import { globalSetting } from '../global';
let PluseIDCount = 0; let PluseIDCount = 0;
@ -15,10 +16,10 @@ export class PulseLine {
loadToSelection(parentSelection, data) { loadToSelection(parentSelection, data) {
this.pluseId = data.id; this.pluseId = data.id;
const gId = 'pluse-g' + data.id; const gId = 'pulse-g' + data.id;
const pId = 'pluse-p' + data.id; const pId = 'pulse-p' + data.id;
const mId = 'pluse-m' + data.id; const mId = 'pulse-m' + data.id;
const uId = 'pluse-u' + data.id; const uId = 'pulse-u' + data.id;
const g = parentSelection.append("g") const g = parentSelection.append("g")
.attr("mask", `url(#${mId})`); .attr("mask", `url(#${mId})`);
@ -94,7 +95,7 @@ export class PulseLine {
const pathLength = pathElement.getTotalLength(); const pathLength = pathElement.getTotalLength();
// 600 配合 3000 比较合适 // 600 配合 3000 比较合适
const v = 0.5; const v = globalSetting.pulsationSpeed / 10;
const duration = pathLength / v; const duration = pathLength / v;
const keyframes = new KeyframeEffect( const keyframes = new KeyframeEffect(

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "إعداد لون المكون", "setting.cell-color-setting": "إعداد لون المكون",
"cross-dot-style.slice": "فاصل", "cross-dot-style.slice": "فاصل",
"cross-dot-style.connect": "محول مباشر", "cross-dot-style.connect": "محول مباشر",
"cross-dot-style.concat": "رمز الدمج" "cross-dot-style.concat": "رمز الدمج",
"setting.pulsation-speed": "سرعة تأثير النبض",
"toolbar.save-as-svg": "حفظ العرض الحالي كـ SVG",
"toolbar.save-as-pdf": "حفظ العرض الحالي كـ PDF",
"saving": "جارٍ الحفظ",
"info.no-entity-select.cannot-display-view": "لم يتم تحديد أي كيان، لا يمكن عرض عرض البيانات",
"entity-info": "معلومات الكيان",
"common.type": "النوع",
"common.name": "الاسم",
"common.definition": "تعريف",
"common.belong-module": "الوحدة التابعة",
"common.width": "عرض البت",
"common.direction": "الاتجاه",
"common.input-number": "عدد الإدخال",
"common.output-number": "عدد الإخراج",
"common.inout-number": "الكمية",
"common.instance-number": "عدد الوحدات المثبتة",
"common.general-cell-number": "عدد الأجهزة العامة",
"common.top-module": "الوحدة العلوية",
"common.instance-name": "اسم مستعار",
"common.instance-definition": "تعريف التمثيل"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "Gerätefarbe einstellen", "setting.cell-color-setting": "Gerätefarbe einstellen",
"cross-dot-style.slice": "Trennzeichen", "cross-dot-style.slice": "Trennzeichen",
"cross-dot-style.connect": "Direktverbinder", "cross-dot-style.connect": "Direktverbinder",
"cross-dot-style.concat": "Zusammenführungszeichen" "cross-dot-style.concat": "Zusammenführungszeichen",
"setting.pulsation-speed": "Pulseffektgeschwindigkeit",
"toolbar.save-as-svg": "Aktuelle Ansicht als SVG speichern",
"toolbar.save-as-pdf": "Aktuelle Ansicht als PDF speichern",
"saving": "Wird gespeichert",
"info.no-entity-select.cannot-display-view": "Keine Entität ausgewählt, Datenansicht kann nicht angezeigt werden",
"entity-info": "Entitätsinformationen",
"common.type": "Typ",
"common.name": "Name",
"common.definition": "Definition",
"common.belong-module": "Zugehöriges Modul",
"common.width": "Bitbreite",
"common.direction": "Richtung",
"common.input-number": "Eingabemenge",
"common.output-number": "Ausgabemenge",
"common.inout-number": "Menge",
"common.instance-number": "Anzahl der instanziierten Module",
"common.general-cell-number": "Anzahl der allgemeinen Geräte",
"common.top-module": "Top-Modul",
"common.instance-name": "Pseudonym",
"common.instance-definition": "Instanziierungsdefinition"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "Device color setting", "setting.cell-color-setting": "Device color setting",
"cross-dot-style.slice": "Separator", "cross-dot-style.slice": "Separator",
"cross-dot-style.connect": "Direct connector", "cross-dot-style.connect": "Direct connector",
"cross-dot-style.concat": "Combining Character" "cross-dot-style.concat": "Combining Character",
"setting.pulsation-speed": "Pulse Effect Speed",
"toolbar.save-as-svg": "Save current view as SVG",
"toolbar.save-as-pdf": "Save current view as PDF",
"saving": "Saving",
"info.no-entity-select.cannot-display-view": "No entity selected, unable to display data view",
"entity-info": "Entity Information",
"common.type": "Type",
"common.name": "Name",
"common.definition": "Definition",
"common.belong-module": "Associated Module",
"common.width": "Bit Width",
"common.direction": "Direction",
"common.input-number": "input quantity",
"common.output-number": "output quantity",
"common.inout-number": "Quantity",
"common.instance-number": "Number of instantiated modules",
"common.general-cell-number": "Number of general devices",
"common.top-module": "Top Module",
"common.instance-name": "Alias",
"common.instance-definition": "Instantiation Definition"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "Réglage de la couleur de l'appareil", "setting.cell-color-setting": "Réglage de la couleur de l'appareil",
"cross-dot-style.slice": "Séparateur", "cross-dot-style.slice": "Séparateur",
"cross-dot-style.connect": "Connecteur direct", "cross-dot-style.connect": "Connecteur direct",
"cross-dot-style.concat": "Caractère de combinaison" "cross-dot-style.concat": "Caractère de combinaison",
"setting.pulsation-speed": "Vitesse de l'effet de pulsation",
"toolbar.save-as-svg": "Enregistrer la vue actuelle au format SVG",
"toolbar.save-as-pdf": "Enregistrer la vue actuelle en PDF",
"saving": "Enregistrement en cours",
"info.no-entity-select.cannot-display-view": "Aucune entité sélectionnée, impossible d'afficher la vue des données",
"entity-info": "Informations sur l'entité",
"common.type": "Type",
"common.name": "Nom",
"common.definition": "Définition",
"common.belong-module": "Module associé",
"common.width": "Largeur de bit",
"common.direction": "Direction",
"common.input-number": "quantité d'entrée",
"common.output-number": "quantité de sortie",
"common.inout-number": "Quantité",
"common.instance-number": "Nombre de modules instanciés",
"common.general-cell-number": "Nombre d'appareils généraux",
"common.top-module": "Module supérieur",
"common.instance-name": "Pseudonyme",
"common.instance-definition": "Définition d'instanciation"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "デバイスの色設定", "setting.cell-color-setting": "デバイスの色設定",
"cross-dot-style.slice": "区切り文字", "cross-dot-style.slice": "区切り文字",
"cross-dot-style.connect": "ダイレクトコネクタ", "cross-dot-style.connect": "ダイレクトコネクタ",
"cross-dot-style.concat": "結合文字" "cross-dot-style.concat": "結合文字",
"setting.pulsation-speed": "パルスエフェクト速度",
"toolbar.save-as-svg": "現在のビューをSVGとして保存",
"toolbar.save-as-pdf": "現在のビューをPDFとして保存",
"saving": "保存中",
"info.no-entity-select.cannot-display-view": "エンティティが選択されていないため、データビューを表示できません",
"entity-info": "エンティティ情報",
"common.type": "タイプ",
"common.name": "名前",
"common.definition": "定義",
"common.belong-module": "関連モジュール",
"common.width": "ビット幅",
"common.direction": "方向",
"common.input-number": "入力数量",
"common.output-number": "出力数量",
"common.inout-number": "数量",
"common.instance-number": "インスタンス化されたモジュールの数",
"common.general-cell-number": "一般デバイスの数",
"common.top-module": "トップモジュール",
"common.instance-name": "別名",
"common.instance-definition": "インスタンス化定義"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "장치 색상 설정", "setting.cell-color-setting": "장치 색상 설정",
"cross-dot-style.slice": "구분자", "cross-dot-style.slice": "구분자",
"cross-dot-style.connect": "직접 연결기", "cross-dot-style.connect": "직접 연결기",
"cross-dot-style.concat": "결합 문자" "cross-dot-style.concat": "결합 문자",
"setting.pulsation-speed": "펄스 효과 속도",
"toolbar.save-as-svg": "현재 뷰를 SVG로 저장",
"toolbar.save-as-pdf": "현재 보기를 PDF로 저장",
"saving": "저장 중",
"info.no-entity-select.cannot-display-view": "선택된 엔티티가 없어 데이터 뷰를 표시할 수 없습니다",
"entity-info": "엔티티 정보",
"common.type": "유형",
"common.name": "이름",
"common.definition": "정의",
"common.belong-module": "소속 모듈",
"common.width": "비트 폭",
"common.direction": "방향",
"common.input-number": "입력 수량",
"common.output-number": "출력 수량",
"common.inout-number": "수량",
"common.instance-number": "인스턴스화된 모듈 수",
"common.general-cell-number": "일반 장치 수",
"common.top-module": "최상위 모듈",
"common.instance-name": "가명",
"common.instance-definition": "인스턴스화 정의"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "Настройка цвета устройства", "setting.cell-color-setting": "Настройка цвета устройства",
"cross-dot-style.slice": "Разделитель", "cross-dot-style.slice": "Разделитель",
"cross-dot-style.connect": "Прямой соединитель", "cross-dot-style.connect": "Прямой соединитель",
"cross-dot-style.concat": "Комбинирующий символ" "cross-dot-style.concat": "Комбинирующий символ",
"setting.pulsation-speed": "Скорость импульсного эффекта",
"toolbar.save-as-svg": "Сохранить текущее представление как SVG",
"toolbar.save-as-pdf": "Сохранить текущее представление как PDF",
"saving": "Сохранение",
"info.no-entity-select.cannot-display-view": "Ни одна сущность не выбрана, невозможно отобразить представление данных",
"entity-info": "Информация о сущности",
"common.type": "Тип",
"common.name": "Имя",
"common.definition": "Определение",
"common.belong-module": "Связанный модуль",
"common.width": "Разрядность",
"common.direction": "Направление",
"common.input-number": "количество ввода",
"common.output-number": "количество вывода",
"common.inout-number": "Количество",
"common.instance-number": "Количество созданных модулей",
"common.general-cell-number": "Количество общих устройств",
"common.top-module": "Верхний модуль",
"common.instance-name": "Псевдоним",
"common.instance-definition": "Определение инстанцирования"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "器件颜色设置", "setting.cell-color-setting": "器件颜色设置",
"cross-dot-style.slice": "拆分符", "cross-dot-style.slice": "拆分符",
"cross-dot-style.connect": "直连符", "cross-dot-style.connect": "直连符",
"cross-dot-style.concat": "合并符" "cross-dot-style.concat": "合并符",
"setting.pulsation-speed": "脉冲特效速度",
"toolbar.save-as-svg": "将当前视图保存为 svg",
"toolbar.save-as-pdf": "将当前视图保存为 pdf",
"saving": "保存中",
"info.no-entity-select.cannot-display-view": "没有选中任何实体,无法展示数据视图",
"entity-info": "实体信息",
"common.type": "类型",
"common.name": "名字",
"common.definition": "定义",
"common.belong-module": "所属模块",
"common.width": "位宽",
"common.direction": "方向",
"common.input-number": "input 数量",
"common.output-number": "output 数量",
"common.inout-number": "inout 数量",
"common.instance-number": "例化模块数量",
"common.general-cell-number": "通用器件数量",
"common.top-module": "顶层模块",
"common.instance-name": "例化名",
"common.instance-definition": "例化定义"
} }

View File

@ -40,5 +40,25 @@
"setting.cell-color-setting": "器件顏色設置", "setting.cell-color-setting": "器件顏色設置",
"cross-dot-style.slice": "分隔符", "cross-dot-style.slice": "分隔符",
"cross-dot-style.connect": "直連符", "cross-dot-style.connect": "直連符",
"cross-dot-style.concat": "合併符" "cross-dot-style.concat": "合併符",
"setting.pulsation-speed": "脈衝特效速度",
"toolbar.save-as-svg": "將目前視圖儲存為SVG",
"toolbar.save-as-pdf": "將目前視圖儲存為PDF",
"saving": "保存中",
"info.no-entity-select.cannot-display-view": "沒有選中任何實體,無法展示數據視圖",
"entity-info": "實體資訊",
"common.type": "類型",
"common.name": "名字",
"common.definition": "定義",
"common.belong-module": "所屬模組",
"common.width": "位寬",
"common.direction": "方向",
"common.input-number": "輸入數量",
"common.output-number": "輸出數量",
"common.inout-number": "數量",
"common.instance-number": "實例化模組數量",
"common.general-cell-number": "通用器件數量",
"common.top-module": "頂層模組",
"common.instance-name": "別名",
"common.instance-definition": "實例化定義"
} }

View File

@ -17,6 +17,7 @@ module.exports = defineConfig({
}, },
devServer: { devServer: {
hot: false, hot: false,
host: 'localhost',
liveReload: false liveReload: false
} }
}) })