aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--backends/aiger/aiger.cc25
-rw-r--r--backends/smt2/smtbmc.py195
-rw-r--r--backends/smt2/smtio.py12
-rw-r--r--kernel/mem.cc5
-rw-r--r--kernel/rtlil.cc5
-rw-r--r--misc/create_vcxsrc.sh2
-rw-r--r--passes/techmap/abc9.cc8
-rw-r--r--passes/techmap/abc9_ops.cc15
-rw-r--r--techlibs/gowin/cells_sim.v34
10 files changed, 230 insertions, 73 deletions
diff --git a/Makefile b/Makefile
index 6d90d14c4..4a823e68c 100644
--- a/Makefile
+++ b/Makefile
@@ -129,7 +129,7 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt
endif
-YOSYS_VER := 0.15+53
+YOSYS_VER := 0.15+73
GIT_REV := $(shell git -C $(YOSYS_SRC) rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index 35935b847..547d131ee 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -606,7 +606,7 @@ struct AigerWriter
f << stringf("c\nGenerated by %s\n", yosys_version_str);
}
- void write_map(std::ostream &f, bool verbose_map)
+ void write_map(std::ostream &f, bool verbose_map, bool no_startoffset)
{
dict<int, string> input_lines;
dict<int, string> init_lines;
@@ -627,32 +627,33 @@ struct AigerWriter
continue;
int a = aig_map.at(sig[i]);
+ int index = no_startoffset ? i : (wire->start_offset+i);
if (verbose_map)
- wire_lines[a] += stringf("wire %d %d %s\n", a, wire->start_offset+i, log_id(wire));
+ wire_lines[a] += stringf("wire %d %d %s\n", a, index, log_id(wire));
if (wire->port_input) {
log_assert((a & 1) == 0);
- input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, index, log_id(wire));
}
if (wire->port_output) {
int o = ordered_outputs.at(sig[i]);
- output_lines[o] += stringf("output %d %d %s\n", o, wire->start_offset+i, log_id(wire));
+ output_lines[o] += stringf("output %d %d %s\n", o, index, log_id(wire));
}
if (init_inputs.count(sig[i])) {
int a = init_inputs.at(sig[i]);
log_assert((a & 1) == 0);
- init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));
+ init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, index, log_id(wire));
}
if (ordered_latches.count(sig[i])) {
int l = ordered_latches.at(sig[i]);
if (zinit_mode && (aig_latchinit.at(l) == 1))
- latch_lines[l] += stringf("invlatch %d %d %s\n", l, wire->start_offset+i, log_id(wire));
+ latch_lines[l] += stringf("invlatch %d %d %s\n", l, index, log_id(wire));
else
- latch_lines[l] += stringf("latch %d %d %s\n", l, wire->start_offset+i, log_id(wire));
+ latch_lines[l] += stringf("latch %d %d %s\n", l, index, log_id(wire));
}
}
}
@@ -713,6 +714,9 @@ struct AigerBackend : public Backend {
log(" -vmap <filename>\n");
log(" like -map, but more verbose\n");
log("\n");
+ log(" -no-startoffset\n");
+ log(" make indexes zero based, enable using map files with smt solvers.\n");
+ log("\n");
log(" -I, -O, -B, -L\n");
log(" If the design contains no input/output/assert/flip-flop then create one\n");
log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n");
@@ -730,6 +734,7 @@ struct AigerBackend : public Backend {
bool omode = false;
bool bmode = false;
bool lmode = false;
+ bool no_startoffset = false;
std::string map_filename;
log_header(design, "Executing AIGER backend.\n");
@@ -762,6 +767,10 @@ struct AigerBackend : public Backend {
verbose_map = true;
continue;
}
+ if (args[argidx] == "-no-startoffset") {
+ no_startoffset = true;
+ continue;
+ }
if (args[argidx] == "-I") {
imode = true;
continue;
@@ -804,7 +813,7 @@ struct AigerBackend : public Backend {
mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail())
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
- writer.write_map(mapf, verbose_map);
+ writer.write_map(mapf, verbose_map, no_startoffset);
}
}
} AigerBackend;
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index 7527f4105..137182f33 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -50,6 +50,7 @@ smtcinit = False
smtctop = None
noinit = False
binarymode = False
+keep_going = False
so = SmtOpts()
@@ -143,7 +144,7 @@ def usage():
--dump-all
when using -g or -i, create a dump file for each
- step. The character '%' is replaces in all dump
+ step. The character '%' is replaced in all dump
filenames with the step number.
--append <num_steps>
@@ -153,6 +154,13 @@ def usage():
--binary
dump anyconst values as raw bit strings
+
+ --keep-going
+ continue BMC after the first failed assertion and report
+ further failed assertions. To output multiple traces
+ covering all found failed assertions, the character '%' is
+ replaced in all dump filenames with an increasing number.
+
""" + so.helpmsg())
sys.exit(1)
@@ -161,7 +169,7 @@ try:
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat",
"dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
- "smtc-init", "smtc-top=", "noinit", "binary"])
+ "smtc-init", "smtc-top=", "noinit", "binary", "keep-going"])
except:
usage()
@@ -234,6 +242,8 @@ for o, a in opts:
topmod = a
elif o == "--binary":
binarymode = True
+ elif o == "--keep-going":
+ keep_going = True
elif so.handle(o, a):
pass
else:
@@ -341,13 +351,13 @@ for fn in inconstr:
assert False
-def get_constr_expr(db, state, final=False, getvalues=False):
+def get_constr_expr(db, state, final=False, getvalues=False, individual=False):
if final:
if ("final-%d" % state) not in db:
- return ([], [], []) if getvalues else "true"
+ return ([], [], []) if getvalues or individual else "true"
else:
if state not in db:
- return ([], [], []) if getvalues else "true"
+ return ([], [], []) if getvalues or individual else "true"
netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*|\S*)\](?=[ )]|$)')
@@ -368,15 +378,18 @@ def get_constr_expr(db, state, final=False, getvalues=False):
expr_list = list()
for loc, expr in db[("final-%d" % state) if final else state]:
actual_expr = netref_regex.sub(replace_netref, expr)
- if getvalues:
+ if getvalues or individual:
expr_list.append((loc, expr, actual_expr))
else:
expr_list.append(actual_expr)
- if getvalues:
- loc_list, expr_list, acual_expr_list = zip(*expr_list)
- value_list = smt.get_list(acual_expr_list)
- return loc_list, expr_list, value_list
+ if getvalues or individual:
+ loc_list, expr_list, actual_expr_list = zip(*expr_list)
+ if individual:
+ return loc_list, expr_list, actual_expr_list
+ else:
+ value_list = smt.get_list(actual_expr_list)
+ return loc_list, expr_list, value_list
if len(expr_list) == 0:
return "true"
@@ -829,7 +842,7 @@ def char_ok_in_verilog(c,i):
return False
def escape_identifier(identifier):
- if type(identifier) is list:
+ if type(identifier) is list:
return map(escape_identifier, identifier)
if "." in identifier:
return ".".join(escape_identifier(identifier.split(".")))
@@ -1071,7 +1084,7 @@ def write_trace(steps_start, steps_stop, index):
write_constr_trace(steps_start, steps_stop, index)
-def print_failed_asserts_worker(mod, state, path, extrainfo):
+def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()):
assert mod in smt.modinfo
found_failed_assert = False
@@ -1079,29 +1092,31 @@ def print_failed_asserts_worker(mod, state, path, extrainfo):
return
for cellname, celltype in smt.modinfo[mod].cells.items():
- if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo):
+ cell_infokey = (mod, cellname, infokey)
+ if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo, infomap, cell_infokey):
found_failed_assert = True
for assertfun, assertinfo in smt.modinfo[mod].asserts.items():
if smt.get("(|%s| %s)" % (assertfun, state)) in ["false", "#b0"]:
- print_msg("Assert failed in %s: %s%s" % (path, assertinfo, extrainfo))
+ assert_key = (assertfun, infokey)
+ print_msg("Assert failed in %s: %s%s%s" % (path, assertinfo, extrainfo, infomap.get(assert_key, '')))
found_failed_assert = True
return found_failed_assert
-def print_failed_asserts(state, final=False, extrainfo=""):
+def print_failed_asserts(state, final=False, extrainfo="", infomap={}):
if noinfo: return
loc_list, expr_list, value_list = get_constr_expr(constr_asserts, state, final=final, getvalues=True)
found_failed_assert = False
for loc, expr, value in zip(loc_list, expr_list, value_list):
if smt.bv2int(value) == 0:
- print_msg("Assert %s failed: %s%s" % (loc, expr, extrainfo))
+ print_msg("Assert %s failed: %s%s%s" % (loc, expr, extrainfo, infomap.get(loc, '')))
found_failed_assert = True
if not final:
- if print_failed_asserts_worker(topmod, "s%d" % state, topmod, extrainfo):
+ if print_failed_asserts_worker(topmod, "s%d" % state, topmod, extrainfo, infomap):
found_failed_assert = True
return found_failed_assert
@@ -1148,6 +1163,43 @@ def get_cover_list(mod, base):
return cover_expr, cover_desc
+
+def get_assert_map(mod, base, path, key_base=()):
+ assert mod in smt.modinfo
+
+ assert_map = dict()
+
+ for expr, desc in smt.modinfo[mod].asserts.items():
+ assert_map[(expr, key_base)] = ("(|%s| %s)" % (expr, base), path, desc)
+
+ for cell, submod in smt.modinfo[mod].cells.items():
+ assert_map.update(get_assert_map(submod, "(|%s_h %s| %s)" % (mod, cell, base), path + "." + cell, (mod, cell, key_base)))
+
+ return assert_map
+
+
+def get_assert_keys():
+ keys = set()
+ keys.update(get_assert_map(topmod, 'state', topmod).keys())
+ for step_constr_asserts in constr_asserts.values():
+ keys.update(loc for loc, expr in step_constr_asserts)
+
+ return keys
+
+
+def get_active_assert_map(step, active):
+ assert_map = dict()
+ for key, assert_data in get_assert_map(topmod, "s%s" % step, topmod).items():
+ if key in active:
+ assert_map[key] = assert_data
+
+ for loc, expr, actual_expr in zip(*get_constr_expr(constr_asserts, step, individual=True)):
+ if loc in active:
+ assert_map[loc] = (actual_expr, None, (expr, loc))
+
+ return assert_map
+
+
states = list()
asserts_antecedent_cache = [list()]
asserts_consequent_cache = [list()]
@@ -1457,6 +1509,10 @@ elif covermode:
print_msg("Unreached cover statement at %s." % cover_desc[i])
else: # not tempind, covermode
+ active_assert_keys = get_assert_keys()
+ failed_assert_infomap = dict()
+ traceidx = 0
+
step = 0
retstatus = "PASSED"
while step < num_steps:
@@ -1510,44 +1566,83 @@ else: # not tempind, covermode
break
if not final_only:
- if last_check_step == step:
- print_msg("Checking assertions in step %d.." % (step))
- else:
- print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step))
- smt_push()
-
- smt_assert("(not (and %s))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
- [get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)]))
-
- if smt_check_sat() == "sat":
- print("%s BMC failed!" % smt.timestamp())
- if append_steps > 0:
- for i in range(last_check_step+1, last_check_step+1+append_steps):
- print_msg("Appending additional step %d." % i)
- smt_state(i)
- smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
- smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
- smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
- smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
- smt_assert_consequent(get_constr_expr(constr_assumes, i))
- print_msg("Re-solving with appended steps..")
- if smt_check_sat() == "unsat":
- print("%s Cannot append steps without violating assumptions!" % smt.timestamp())
- retstatus = "FAILED"
- break
- print_anyconsts(step)
+ recheck_current_step = True
+ while recheck_current_step:
+ recheck_current_step = False
+ if last_check_step == step:
+ print_msg("Checking assertions in step %d.." % (step))
+ else:
+ print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step))
+ smt_push()
+
+ active_assert_maps = dict()
+ active_assert_exprs = list()
for i in range(step, last_check_step+1):
- print_failed_asserts(i)
- write_trace(0, last_check_step+1+append_steps, '%')
- retstatus = "FAILED"
- break
+ assert_expr_map = get_active_assert_map(i, active_assert_keys)
+ active_assert_maps[i] = assert_expr_map
+ active_assert_exprs.extend(assert_data[0] for assert_data in assert_expr_map.values())
- smt_pop()
+ if active_assert_exprs:
+ if len(active_assert_exprs) == 1:
+ active_assert_expr = active_assert_exprs[0]
+ else:
+ active_assert_expr = "(and %s)" % " ".join(active_assert_exprs)
+
+ smt_assert("(not %s)" % active_assert_expr)
+ else:
+ smt_assert("false")
+
+
+ if smt_check_sat() == "sat":
+ if retstatus != "FAILED":
+ print("%s BMC failed!" % smt.timestamp())
+
+ if append_steps > 0:
+ for i in range(last_check_step+1, last_check_step+1+append_steps):
+ print_msg("Appending additional step %d." % i)
+ smt_state(i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i))
+ print_msg("Re-solving with appended steps..")
+ if smt_check_sat() == "unsat":
+ print("%s Cannot append steps without violating assumptions!" % smt.timestamp())
+ retstatus = "FAILED"
+ break
+ print_anyconsts(step)
+
+ for i in range(step, last_check_step+1):
+ print_failed_asserts(i, infomap=failed_assert_infomap)
+
+ if keep_going:
+ for i in range(step, last_check_step+1):
+ for key, (expr, path, desc) in active_assert_maps[i].items():
+ if key in active_assert_keys and not smt.bv2int(smt.get(expr)):
+ failed_assert_infomap[key] = " [failed before]"
+
+ active_assert_keys.remove(key)
+
+ if active_assert_keys:
+ recheck_current_step = True
+
+ write_trace(0, last_check_step+1+append_steps, "%d" % traceidx if keep_going else '%')
+ traceidx += 1
+ retstatus = "FAILED"
+
+ smt_pop()
+ if recheck_current_step:
+ print_msg("Checking remaining assertions..")
+
+ if retstatus == "FAILED" and not (keep_going and active_assert_keys):
+ break
if (constr_final_start is not None) or (last_check_step+1 != num_steps):
for i in range(step, last_check_step+1):
- smt_assert("(|%s_a| s%d)" % (topmod, i))
- smt_assert(get_constr_expr(constr_asserts, i))
+ assert_expr_map = get_active_assert_map(i, active_assert_keys)
+ for assert_data in assert_expr_map.values():
+ smt_assert(assert_data[0])
if constr_final_start is not None:
for i in range(step, last_check_step+1):
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 3d458e6cf..14feec30d 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -20,7 +20,7 @@ import sys, re, os, signal
import subprocess
if os.name == "posix":
import resource
-from copy import deepcopy
+from copy import copy
from select import select
from time import time
from queue import Queue, Empty
@@ -301,7 +301,7 @@ class SmtIo:
key = tuple(stmt)
if key not in self.unroll_cache:
- decl = deepcopy(self.unroll_decls[key[0]])
+ decl = copy(self.unroll_decls[key[0]])
self.unroll_cache[key] = "|UNROLL#%d|" % self.unroll_idcnt
decl[1] = self.unroll_cache[key]
@@ -442,10 +442,10 @@ class SmtIo:
if stmt == "(push 1)":
self.unroll_stack.append((
- deepcopy(self.unroll_sorts),
- deepcopy(self.unroll_objs),
- deepcopy(self.unroll_decls),
- deepcopy(self.unroll_cache),
+ copy(self.unroll_sorts),
+ copy(self.unroll_objs),
+ copy(self.unroll_decls),
+ copy(self.unroll_cache),
))
if stmt == "(pop 1)":
diff --git a/kernel/mem.cc b/kernel/mem.cc
index 059f8f934..e5e855ef7 100644
--- a/kernel/mem.cc
+++ b/kernel/mem.cc
@@ -1633,7 +1633,10 @@ void Mem::emulate_read_first(FfInitVals *initvals) {
ff_en.pol_clk = port.clk_polarity;
ff_en.sig_d = compressed.first;
ff_en.sig_q = new_en;;
- ff_en.val_init = Const(State::S0, ff_en.width);
+ if (inits.empty())
+ ff_en.val_init = Const(State::Sx, ff_en.width);
+ else
+ ff_en.val_init = Const(State::S0, ff_en.width);
ff_en.emit();
port.data = new_data;
port.addr = new_addr;
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index a89edd992..72dcb89af 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -207,6 +207,7 @@ RTLIL::Const::Const()
RTLIL::Const::Const(std::string str)
{
flags = RTLIL::CONST_FLAG_STRING;
+ bits.reserve(str.size() * 8);
for (int i = str.size()-1; i >= 0; i--) {
unsigned char ch = str[i];
for (int j = 0; j < 8; j++) {
@@ -219,6 +220,7 @@ RTLIL::Const::Const(std::string str)
RTLIL::Const::Const(int val, int width)
{
flags = RTLIL::CONST_FLAG_NONE;
+ bits.reserve(width);
for (int i = 0; i < width; i++) {
bits.push_back((val & 1) != 0 ? State::S1 : State::S0);
val = val >> 1;
@@ -228,6 +230,7 @@ RTLIL::Const::Const(int val, int width)
RTLIL::Const::Const(RTLIL::State bit, int width)
{
flags = RTLIL::CONST_FLAG_NONE;
+ bits.reserve(width);
for (int i = 0; i < width; i++)
bits.push_back(bit);
}
@@ -235,6 +238,7 @@ RTLIL::Const::Const(RTLIL::State bit, int width)
RTLIL::Const::Const(const std::vector<bool> &bits)
{
flags = RTLIL::CONST_FLAG_NONE;
+ this->bits.reserve(bits.size());
for (const auto &b : bits)
this->bits.emplace_back(b ? State::S1 : State::S0);
}
@@ -242,6 +246,7 @@ RTLIL::Const::Const(const std::vector<bool> &bits)
RTLIL::Const::Const(const RTLIL::Const &c)
{
flags = c.flags;
+ this->bits.reserve(c.size());
for (const auto &b : c.bits)
this->bits.push_back(b);
}
diff --git a/misc/create_vcxsrc.sh b/misc/create_vcxsrc.sh
index e3c1ad991..8b39d59e3 100644
--- a/misc/create_vcxsrc.sh
+++ b/misc/create_vcxsrc.sh
@@ -7,7 +7,7 @@ gitsha="$3"
rm -rf YosysVS-Tpl-v2.zip YosysVS
wget https://yosyshq.net/yosys/nogit/YosysVS-Tpl-v2.zip
-wget https://zlib.net/zlib-1.2.11.tar.gz
+wget https://www.zlib.net/fossils/zlib-1.2.11.tar.gz
unzip YosysVS-Tpl-v2.zip
rm -f YosysVS-Tpl-v2.zip
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 1f00fc3e7..fe0802d70 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -74,12 +74,18 @@ struct Abc9Pass : public ScriptPass
/* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
/* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\
"&load";
- // Based on ABC's &flow3
+ // Based on ABC's &flow3 -m
RTLIL::constpad["abc9.script.flow3"] = "+&scorr; &sweep;" \
"&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &save; &load;"\
"&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &save; &load;"\
"&mfs";
+ // As above, but with &mfs calls as in the original &flow3
+ RTLIL::constpad["abc9.script.flow3mfs"] = "+&scorr; &sweep;" \
+ "&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
+ "&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &mfs; &save; &load;"\
+ "&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &mfs; &save; &load;"\
+ "&mfs";
}
void help() override
{
diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc
index 29fe74ec7..b8975f178 100644
--- a/passes/techmap/abc9_ops.cc
+++ b/passes/techmap/abc9_ops.cc
@@ -167,10 +167,6 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
derived_module = inst_module;
}
else {
- // Check potential for any one of those three
- // (since its value may depend on a parameter, but not its existence)
- if (!inst_module->has_attribute(ID::abc9_flop) && !inst_module->has_attribute(ID::abc9_box) && !inst_module->get_bool_attribute(ID::abc9_bypass))
- continue;
derived_type = inst_module->derive(design, cell->parameters);
derived_module = design->module(derived_type);
}
@@ -180,7 +176,16 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
continue;
}
else {
- if (!derived_module->get_bool_attribute(ID::abc9_box) && !derived_module->get_bool_attribute(ID::abc9_bypass)) {
+ bool has_timing = false;
+ for (auto derived_cell : derived_module->cells()) {
+ if (derived_cell->type.in(ID($specify2), ID($specify3), ID($specrule))) {
+ // If the module contains timing; then we potentially care about deriving its content too,
+ // as timings (or associated port widths) could be dependent on parameters.
+ has_timing = true;
+ break;
+ }
+ }
+ if (!derived_module->get_bool_attribute(ID::abc9_box) && !derived_module->get_bool_attribute(ID::abc9_bypass) && !has_timing) {
if (unmap_design->module(derived_type)) {
// If derived_type is present in unmap_design, it means that it was processed previously, but found to be incompatible -- e.g. if
// it contained a non-zero initial state. In this case, continue to replace the cell type/parameters so that it has the same properties
diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v
index 64b76139c..736aa0707 100644
--- a/techlibs/gowin/cells_sim.v
+++ b/techlibs/gowin/cells_sim.v
@@ -1551,3 +1551,37 @@ parameter CLKOUTD3_SRC = "CLKOUT"; // CLKOUT, CLKOUTP
parameter DEVICE = "GW1N-1"; // "GW1N-1", "GW1N-4", "GW1N-9", "GW1NR-4", "GW1NR-9", "GW1N-4B", "GW1NR-4B", "GW1NS-2", "GW1NS-2C", "GW1NZ-1", "GW1NSR-2", "GW1NSR-2C", "GW1N-1S", "GW1NSE-2C", "GW1NRF-4B", "GW1N-9C", "GW1NR-9C", "GW1N-4C", "GW1NR-4C"
endmodule
+
+(* blackbox *)
+module OSC(OSCOUT);
+output OSCOUT;
+
+parameter FREQ_DIV = 100;
+parameter DEVICE = "GW1N-4";
+endmodule
+
+(* blackbox *)
+module OSCZ(OSCOUT, OSCEN);
+input OSCEN;
+
+output OSCOUT;
+
+parameter FREQ_DIV = 100;
+endmodule
+
+(* blackbox *)
+module OSCF(OSCOUT, OSCOUT30M, OSCEN);
+input OSCEN;
+
+output OSCOUT;
+output OSCOUT30M;
+
+parameter FREQ_DIV = 100;
+endmodule
+
+(* blackbox *)
+module OSCH(OSCOUT);
+output OSCOUT;
+
+parameter FREQ_DIV = 96;
+endmodule