Skip to content

Commit 101491d

Browse files
committedSep 14, 2017
Merge branch 'master' of https://github.com/cliffordwolf/yosys into extract_bus
2 parents 0171c18 + 498526c commit 101491d

File tree

5 files changed

+387
-69
lines changed

5 files changed

+387
-69
lines changed
 

‎passes/opt/Makefile.inc

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ OBJS += passes/opt/opt_reduce.o
66
OBJS += passes/opt/opt_rmdff.o
77
OBJS += passes/opt/opt_clean.o
88
OBJS += passes/opt/opt_expr.o
9-
OBJS += passes/opt/rmports.o
109

1110
ifneq ($(SMALL),1)
1211
OBJS += passes/opt/share.o
1312
OBJS += passes/opt/wreduce.o
13+
OBJS += passes/opt/opt_demorgan.o
14+
OBJS += passes/opt/rmports.o
1415
endif
1516

‎passes/opt/opt_demorgan.cc

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* yosys -- Yosys Open SYnthesis Suite
3+
*
4+
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
5+
*
6+
* Permission to use, copy, modify, and/or distribute this software for any
7+
* purpose with or without fee is hereby granted, provided that the above
8+
* copyright notice and this permission notice appear in all copies.
9+
*
10+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*
18+
*/
19+
20+
#include "kernel/yosys.h"
21+
#include "kernel/sigtools.h"
22+
#include "kernel/modtools.h"
23+
24+
USING_YOSYS_NAMESPACE
25+
PRIVATE_NAMESPACE_BEGIN
26+
27+
void demorgan_worker(
28+
ModIndex& index,
29+
Cell *cell,
30+
unsigned int& cells_changed)
31+
{
32+
SigMap& sigmap = index.sigmap;
33+
auto m = cell->module;
34+
35+
//TODO: Add support for reduce_xor
36+
//DeMorgan of XOR is either XOR (if even number of inputs) or XNOR (if odd number)
37+
38+
if( (cell->type != "$reduce_and") && (cell->type != "$reduce_or") )
39+
return;
40+
41+
auto insig = sigmap(cell->getPort("\\A"));
42+
log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), GetSize(insig));
43+
int num_inverted = 0;
44+
for(int i=0; i<GetSize(insig); i++)
45+
{
46+
auto b = insig[i];
47+
48+
//See if this bit is driven by a $not cell
49+
//TODO: do other stuff like nor/nand?
50+
pool<ModIndex::PortInfo> ports = index.query_ports(b);
51+
bool inverted = false;
52+
for(auto x : ports)
53+
{
54+
if(x.port == "\\Y" && x.cell->type == "$_NOT_")
55+
{
56+
inverted = true;
57+
break;
58+
}
59+
}
60+
61+
if(inverted)
62+
num_inverted ++;
63+
}
64+
65+
//Stop if less than half of the inputs are inverted
66+
if(num_inverted*2 < GetSize(insig))
67+
{
68+
log(" %d / %d inputs are inverted, not pushing\n", num_inverted, GetSize(insig));
69+
return;
70+
}
71+
72+
//More than half of the inputs are inverted! Push through
73+
cells_changed ++;
74+
log(" %d / %d inputs are inverted, pushing inverter through reduction\n", num_inverted, GetSize(insig));
75+
76+
//For each input, either add or remove the inverter as needed
77+
//TODO: this duplicates the loop up above, can we refactor it?
78+
for(int i=0; i<GetSize(insig); i++)
79+
{
80+
auto b = insig[i];
81+
82+
//See if this bit is driven by a $not cell
83+
//TODO: do other stuff like nor/nand?
84+
pool<ModIndex::PortInfo> ports = index.query_ports(b);
85+
RTLIL::Cell* srcinv = NULL;
86+
for(auto x : ports)
87+
{
88+
if(x.port == "\\Y" && x.cell->type == "$_NOT_")
89+
{
90+
srcinv = x.cell;
91+
break;
92+
}
93+
}
94+
95+
//We are NOT inverted! Add an inverter
96+
if(!srcinv)
97+
{
98+
auto inverted_b = m->addWire(NEW_ID);
99+
m->addNot(NEW_ID, RTLIL::SigSpec(b), RTLIL::SigSpec(inverted_b));
100+
insig[i] = inverted_b;
101+
}
102+
103+
//We ARE inverted - bypass it
104+
//Don't automatically delete the inverter since other stuff might still use it
105+
else
106+
insig[i] = srcinv->getPort("\\A");
107+
}
108+
109+
//Cosmetic fixup: If our input is just a scrambled version of one bus, rearrange it
110+
//Reductions are all commutative, so there's no point in having them in a weird order
111+
bool same_signal = true;
112+
RTLIL::Wire* srcwire = insig[0].wire;
113+
dict<int, int> seen_bits;
114+
for(int i=0; i<GetSize(insig); i++)
115+
seen_bits[i] = 0;
116+
for(int i=0; i<GetSize(insig); i++)
117+
{
118+
seen_bits[insig[i].offset] ++;
119+
if(insig[i].wire != srcwire)
120+
{
121+
same_signal = false;
122+
break;
123+
}
124+
}
125+
if(same_signal)
126+
{
127+
//Make sure we've seen every bit exactly once
128+
bool every_bit_once = true;
129+
for(int i=0; i<GetSize(insig); i++)
130+
{
131+
if(seen_bits[i] != 1)
132+
{
133+
every_bit_once = false;
134+
break;
135+
}
136+
}
137+
138+
//All good? Just use the whole wire as-is without any reordering
139+
//We do have to swap MSB to LSB b/c that's the way the reduction cells seem to work?
140+
//Unclear on why this isn't sorting properly
141+
//TODO: can we do SigChunks instead of single bits if we have subsets of a bus?
142+
if(every_bit_once && (GetSize(insig) == srcwire->width) )
143+
{
144+
log("Rearranging bits\n");
145+
RTLIL::SigSpec newsig;
146+
for(int i=0; i<GetSize(insig); i++)
147+
newsig.append(RTLIL::SigBit(srcwire, GetSize(insig) - i - 1));
148+
insig = newsig;
149+
insig.sort();
150+
}
151+
}
152+
153+
//Push the new input signal back to the reduction (after bypassing/adding inverters)
154+
cell->setPort("\\A", insig);
155+
156+
//Change the cell type
157+
if(cell->type == "$reduce_and")
158+
cell->type = "$reduce_or";
159+
else if(cell->type == "$reduce_or")
160+
cell->type = "$reduce_and";
161+
//don't change XOR
162+
163+
//Add an inverter to the output
164+
auto inverted_output = cell->getPort("\\Y");
165+
auto uninverted_output = m->addWire(NEW_ID);
166+
m->addNot(NEW_ID, RTLIL::SigSpec(uninverted_output), inverted_output);
167+
cell->setPort("\\Y", uninverted_output);
168+
}
169+
170+
struct OptDemorganPass : public Pass {
171+
OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { }
172+
virtual void help()
173+
{
174+
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
175+
log("\n");
176+
log(" opt_demorgan [selection]\n");
177+
log("\n");
178+
log("This pass pushes inverters through $reduce_* cells if this will reduce the\n");
179+
log("overall gate count of the circuit\n");
180+
log("\n");
181+
}
182+
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
183+
{
184+
log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n");
185+
186+
int argidx = 0;
187+
extra_args(args, argidx, design);
188+
189+
unsigned int cells_changed = 0;
190+
for (auto module : design->selected_modules())
191+
{
192+
ModIndex index(module);
193+
for (auto cell : module->selected_cells())
194+
demorgan_worker(index, cell, cells_changed);
195+
}
196+
197+
if(cells_changed)
198+
log("Pushed inverters through %u reductions\n", cells_changed);
199+
}
200+
} OptDemorganPass;
201+
202+
PRIVATE_NAMESPACE_END

‎passes/techmap/extract_counter.cc

+116-47
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,13 @@ struct CounterExtraction
9292
int width; //counter width
9393
RTLIL::Wire* rwire; //the register output
9494
bool has_reset; //true if we have a reset
95+
bool has_ce; //true if we have a clock enable
9596
RTLIL::SigSpec rst; //reset pin
97+
bool rst_inverted; //true if reset is active low
98+
bool rst_to_max; //true if we reset to max instead of 0
9699
int count_value; //value we count from
97-
RTLIL::SigSpec clk; //clock signal
100+
RTLIL::SigSpec ce; //clock signal
101+
RTLIL::SigSpec clk; //clock enable, if any
98102
RTLIL::SigSpec outsig; //counter output signal
99103
RTLIL::Cell* count_mux; //counter mux
100104
RTLIL::Cell* count_reg; //counter register
@@ -190,24 +194,60 @@ int counter_tryextract(
190194
return 13;
191195
extract.underflow_inv = underflow_inv;
192196

193-
//Y connection of the mux must have exactly one load, the counter's internal register
197+
//Y connection of the mux must have exactly one load, the counter's internal register, if there's no clock enable
198+
//If we have a clock enable, Y drives the B input of a mux. A of that mux must come from our register
194199
const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y"));
195200
pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux);
196201
if(muxy_loads.size() != 1)
197202
return 14;
198-
Cell* count_reg = *muxy_loads.begin();
203+
Cell* muxload = *muxy_loads.begin();
204+
Cell* count_reg = muxload;
205+
Cell* cemux = NULL;
206+
RTLIL::SigSpec cey;
207+
if(muxload->type == "$mux")
208+
{
209+
//This mux is probably a clock enable mux.
210+
//Find our count register (should be our only load)
211+
cemux = muxload;
212+
cey = sigmap(cemux->getPort("\\Y"));
213+
pool<Cell*> cey_loads = get_other_cells(cey, index, cemux);
214+
if(cey_loads.size() != 1)
215+
return 24;
216+
count_reg = *cey_loads.begin();
217+
218+
//Mux should have A driven by count Q, and B by muxy
219+
//TODO: if A and B are swapped, CE polarity is inverted
220+
if(sigmap(cemux->getPort("\\B")) != muxy)
221+
return 24;
222+
if(sigmap(cemux->getPort("\\A")) != sigmap(count_reg->getPort("\\Q")))
223+
return 24;
224+
if(sigmap(cemux->getPort("\\Y")) != sigmap(count_reg->getPort("\\D")))
225+
return 24;
226+
227+
//Select of the mux is our clock enable
228+
extract.has_ce = true;
229+
extract.ce = sigmap(cemux->getPort("\\S"));
230+
}
231+
else
232+
extract.has_ce = false;
233+
199234
extract.count_reg = count_reg;
200235
if(count_reg->type == "$dff")
201236
extract.has_reset = false;
202237
else if(count_reg->type == "$adff")
203238
{
204239
extract.has_reset = true;
205240

206-
//Verify ARST_VALUE is zero and ARST_POLARITY is 1
207-
//TODO: infer an inverter to make it 1 if necessary, so we can support negative level resets?
208-
if(count_reg->getParam("\\ARST_POLARITY").as_int() != 1)
209-
return 22;
210-
if(count_reg->getParam("\\ARST_VALUE").as_int() != 0)
241+
//Check polarity of reset - we may have to add an inverter later on!
242+
extract.rst_inverted = (count_reg->getParam("\\ARST_POLARITY").as_int() != 1);
243+
244+
//Verify ARST_VALUE is zero or full scale
245+
int rst_value = count_reg->getParam("\\ARST_VALUE").as_int();
246+
if(rst_value == 0)
247+
extract.rst_to_max = false;
248+
else if(rst_value == extract.count_value)
249+
extract.rst_to_max = true;
250+
else
211251
return 23;
212252

213253
//Save the reset
@@ -216,54 +256,59 @@ int counter_tryextract(
216256
//TODO: support synchronous reset
217257
else
218258
return 15;
219-
if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D"))
259+
260+
//Sanity check that we use the ALU output properly
261+
if(extract.has_ce)
262+
{
263+
if(!is_full_bus(muxy, index, count_mux, "\\Y", cemux, "\\B"))
264+
return 16;
265+
if(!is_full_bus(cey, index, cemux, "\\Y", count_reg, "\\D"))
266+
return 16;
267+
}
268+
else if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D"))
220269
return 16;
221270

222271
//TODO: Verify count_reg CLK_POLARITY is 1
223272

224273
//Register output must have exactly two loads, the inverter and ALU
225274
//(unless we have a parallel output!)
275+
//If we have a clock enable, 3 is OK
226276
const RTLIL::SigSpec qport = count_reg->getPort("\\Q");
227277
const RTLIL::SigSpec cnout = sigmap(qport);
228278
pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg);
229-
if(cnout_loads.size() > 2)
279+
unsigned int max_loads = 2;
280+
if(extract.has_ce)
281+
max_loads = 3;
282+
if(cnout_loads.size() > max_loads)
230283
{
231-
//If we specified a limited set of cells for parallel output, check that we only drive them
232-
if(!parallel_cells.empty())
284+
for(auto c : cnout_loads)
233285
{
234-
for(auto c : cnout_loads)
235-
{
236-
if(c == underflow_inv)
237-
continue;
238-
if(c == cell)
239-
continue;
286+
if(c == underflow_inv)
287+
continue;
288+
if(c == cell)
289+
continue;
290+
if(c == muxload)
291+
continue;
240292

293+
//If we specified a limited set of cells for parallel output, check that we only drive them
294+
if(!parallel_cells.empty())
295+
{
241296
//Make sure we're in the whitelist
242297
if( parallel_cells.find(c->type) == parallel_cells.end())
243298
return 17;
299+
}
244300

245-
//Figure out what port(s) are driven by it
246-
//TODO: this can probably be done more efficiently w/o multiple iterations over our whole net?
247-
RTLIL::IdString portname;
248-
for(auto b : qport)
301+
//Figure out what port(s) are driven by it
302+
//TODO: this can probably be done more efficiently w/o multiple iterations over our whole net?
303+
for(auto b : qport)
304+
{
305+
pool<ModIndex::PortInfo> ports = index.query_ports(b);
306+
for(auto x : ports)
249307
{
250-
pool<ModIndex::PortInfo> ports = index.query_ports(b);
251-
for(auto x : ports)
252-
{
253-
if(x.cell != c)
254-
continue;
255-
if(portname == "")
256-
portname = x.port;
257-
258-
//somehow our counter output is going to multiple ports
259-
//this makes no sense, don't allow inference
260-
else if(portname != x.port)
261-
return 17;
262-
}
308+
if(x.cell != c)
309+
continue;
310+
extract.pouts.insert(ModIndex::PortInfo(c, x.port, 0));
263311
}
264-
265-
//Save the other loads
266-
extract.pouts.insert(ModIndex::PortInfo(c, portname, 0));
267312
}
268313
}
269314
}
@@ -346,7 +391,7 @@ void counter_worker(
346391
//Do nothing, unless extraction was forced in which case give an error
347392
if(reason != 0)
348393
{
349-
static const char* reasons[24]=
394+
static const char* reasons[25]=
350395
{
351396
"no problem", //0
352397
"counter is too large/small", //1
@@ -370,8 +415,9 @@ void counter_worker(
370415
"Register output is not full bus", //19
371416
"No init value found", //20
372417
"Underflow value is not equal to init value", //21
373-
"Reset polarity is not positive", //22
374-
"Reset is not to zero" //23
418+
"RESERVED, not implemented", //22, kept for compatibility but not used anymore
419+
"Reset is not to zero or COUNT_TO", //23
420+
"Clock enable configuration is unsupported" //24
375421
};
376422

377423
if(force_extract)
@@ -409,7 +455,16 @@ void counter_worker(
409455
{
410456
//TODO: support other kinds of reset
411457
cell->setParam("\\RESET_MODE", RTLIL::Const("LEVEL"));
412-
cell->setPort("\\RST", extract.rst);
458+
459+
//If the reset is active low, infer an inverter ($__COUNT_ cells always have active high reset)
460+
if(extract.rst_inverted)
461+
{
462+
auto realreset = cell->module->addWire(NEW_ID);
463+
cell->module->addNot(NEW_ID, extract.rst, RTLIL::SigSpec(realreset));
464+
cell->setPort("\\RST", realreset);
465+
}
466+
else
467+
cell->setPort("\\RST", extract.rst);
413468
}
414469
else
415470
{
@@ -424,12 +479,21 @@ void counter_worker(
424479
cell->setPort("\\CLK", extract.clk);
425480
cell->setPort("\\OUT", extract.outsig);
426481

427-
//Hook up hard-wired ports (for now CE and up/=down are not supported), default to no parallel output
482+
//Hook up clock enable
483+
if(extract.has_ce)
484+
{
485+
cell->setParam("\\HAS_CE", RTLIL::Const(1));
486+
cell->setPort("\\CE", extract.ce);
487+
}
488+
else
489+
cell->setParam("\\HAS_CE", RTLIL::Const(0));
490+
491+
//Hook up hard-wired ports (for now up/down are not supported), default to no parallel output
428492
cell->setParam("\\HAS_POUT", RTLIL::Const(0));
429-
cell->setParam("\\HAS_CE", RTLIL::Const(0));
493+
cell->setParam("\\RESET_TO_MAX", RTLIL::Const(0));
430494
cell->setParam("\\DIRECTION", RTLIL::Const("DOWN"));
431495
cell->setPort("\\CE", RTLIL::Const(1));
432-
cell->setPort("\\UP", RTLIL::Const(1));
496+
cell->setPort("\\UP", RTLIL::Const(0));
433497

434498
//Hook up any parallel outputs
435499
for(auto load : extract.pouts)
@@ -455,10 +519,15 @@ void counter_worker(
455519
string reset_type = "non-resettable";
456520
if(extract.has_reset)
457521
{
522+
if(extract.rst_inverted)
523+
reset_type = "negative";
524+
else
525+
reset_type = "positive";
526+
458527
//TODO: support other kind of reset
459-
reset_type = "async resettable";
528+
reset_type += " async resettable";
460529
}
461-
log(" Found %d-bit %s down counter %s (counting from %d) for register %s declared at %s\n",
530+
log(" Found %d-bit (%s) down counter %s (counting from %d) for register %s, declared at %s\n",
462531
extract.width,
463532
reset_type.c_str(),
464533
countname.c_str(),

‎techlibs/greenpak4/cells_blackbox.v

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP);
99

1010
parameter COUNT_TO = 1;
1111
parameter RESET_MODE = "RISING";
12+
parameter RESET_TO_MAX = "1";
1213
parameter HAS_POUT = 0;
1314
parameter HAS_CE = 0;
1415
parameter WIDTH = 8;

‎techlibs/greenpak4/cells_map.v

+66-21
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,14 @@ module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP);
156156

157157
parameter COUNT_TO = 1;
158158
parameter RESET_MODE = "RISING";
159+
parameter RESET_TO_MAX = 0;
159160
parameter HAS_POUT = 0;
160161
parameter HAS_CE = 0;
161162
parameter WIDTH = 8;
162163
parameter DIRECTION = "DOWN";
163164

164-
//If we have a CE, or DIRECTION other than DOWN fail... GP_COUNTx_ADV is not supported yet
165-
if(HAS_CE || (DIRECTION != "DOWN") ) begin
165+
//If we have a DIRECTION other than DOWN fail... GP_COUNTx_ADV is not supported yet
166+
if(DIRECTION != "DOWN") begin
166167
initial begin
167168
$display("ERROR: \$__COUNT_ support for GP_COUNTx_ADV is not yet implemented. This counter should never have been extracted (bug in extract_counter pass?).");
168169
$finish;
@@ -187,28 +188,72 @@ module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP);
187188

188189
//Looks like a legal counter! Do something with it
189190
else if(WIDTH <= 8) begin
190-
GP_COUNT8 #(
191-
.COUNT_TO(COUNT_TO),
192-
.RESET_MODE(RESET_MODE),
193-
.CLKIN_DIVIDE(1)
194-
) _TECHMAP_REPLACE_ (
195-
.CLK(CLK),
196-
.RST(RST),
197-
.OUT(OUT),
198-
.POUT(POUT)
199-
);
191+
if(HAS_CE) begin
192+
wire ce_not;
193+
GP_INV ceinv(
194+
.IN(CE),
195+
.OUT(ce_not)
196+
);
197+
GP_COUNT8_ADV #(
198+
.COUNT_TO(COUNT_TO),
199+
.RESET_MODE(RESET_MODE),
200+
.RESET_VALUE(RESET_TO_MAX ? "COUNT_TO" : "ZERO"),
201+
.CLKIN_DIVIDE(1)
202+
) _TECHMAP_REPLACE_ (
203+
.CLK(CLK),
204+
.RST(RST),
205+
.OUT(OUT),
206+
.UP(1'b0), //always count down for now
207+
.KEEP(ce_not),
208+
.POUT(POUT)
209+
);
210+
end
211+
else begin
212+
GP_COUNT8 #(
213+
.COUNT_TO(COUNT_TO),
214+
.RESET_MODE(RESET_MODE),
215+
.CLKIN_DIVIDE(1)
216+
) _TECHMAP_REPLACE_ (
217+
.CLK(CLK),
218+
.RST(RST),
219+
.OUT(OUT),
220+
.POUT(POUT)
221+
);
222+
end
200223
end
201224

202225
else begin
203-
GP_COUNT14 #(
204-
.COUNT_TO(COUNT_TO),
205-
.RESET_MODE(RESET_MODE),
206-
.CLKIN_DIVIDE(1)
207-
) _TECHMAP_REPLACE_ (
208-
.CLK(CLK),
209-
.RST(RST),
210-
.OUT(OUT)
211-
);
226+
if(HAS_CE) begin
227+
wire ce_not;
228+
GP_INV ceinv(
229+
.IN(CE),
230+
.OUT(ce_not)
231+
);
232+
GP_COUNT14_ADV #(
233+
.COUNT_TO(COUNT_TO),
234+
.RESET_MODE(RESET_TO_MAX ? "COUNT_TO" : "ZERO"),
235+
.RESET_VALUE("COUNT_TO"),
236+
.CLKIN_DIVIDE(1)
237+
) _TECHMAP_REPLACE_ (
238+
.CLK(CLK),
239+
.RST(RST),
240+
.OUT(OUT),
241+
.UP(1'b0), //always count down for now
242+
.KEEP(ce_not),
243+
.POUT(POUT)
244+
);
245+
end
246+
else begin
247+
GP_COUNT14 #(
248+
.COUNT_TO(COUNT_TO),
249+
.RESET_MODE(RESET_MODE),
250+
.CLKIN_DIVIDE(1)
251+
) _TECHMAP_REPLACE_ (
252+
.CLK(CLK),
253+
.RST(RST),
254+
.OUT(OUT)
255+
);
256+
end
212257
end
213258

214259
endmodule

0 commit comments

Comments
 (0)
Please sign in to comment.