mirror of https://github.com/YosysHQ/abc.git
Update word-level data-structure
This commit is contained in:
parent
371e53f93e
commit
324081b6a4
|
|
@ -63,14 +63,20 @@ set snslang /path/to/sn_slang
|
|||
`@slang` uses `sn_slang` from `PATH` unless the `snslang` setting overrides it. It accepts `-M` for the top module,
|
||||
repeatable `-D NAME` or `-D NAME=value` preprocessor definitions, `-F` for one additional source file, and any number
|
||||
of positional source files. For example, `-D WIDTH=8 -D SIGNED=1` defines two macros. `-T` is not used because ABC
|
||||
conventionally reserves it for a time limit. `-v` prints the external command and frontend timing. Black-box patterns
|
||||
and include-directory options remain unsupported.
|
||||
conventionally reserves it for a time limit. `-v` prints the external command and frontend timing. A module declared
|
||||
inside SystemVerilog `` `celldefine`` / `` `endcelldefine``, or marked by a nonzero `black_box` or `syn_black_box`
|
||||
module attribute, is imported as an opaque technology primitive with its elaborated PI/PO interface; its simulation
|
||||
body is not lowered. For example, both `` `celldefine`` around a module definition and
|
||||
`(* syn_black_box = 1 *) module macro (...);` create an opaque leaf. An explicit zero or false attribute does not.
|
||||
The declaration is still required: slang must know every port's name, direction, width, and signedness, so an
|
||||
undefined-module inst remains an error. Undefined-module patterns and include-directory options remain unsupported.
|
||||
|
||||
`@read` and `@write` provide binary persistence. `@write` selects SN or Verilog output from the `.sn`, `.v`, or
|
||||
`.sv` extension. `@read -M module` selects the top stored in a multi-top design; otherwise the last top is used.
|
||||
Before installing external binary data, `@read` validates the encoding and runs the same non-aborting structural and
|
||||
semantic checks as `@check`. A failed `@write` removes its incomplete output file. Every design installed in ABC is
|
||||
topologically ordered.
|
||||
topologically ordered. The current writer emits binary format version 6; the reader also accepts version 5 and treats
|
||||
its modules as ordinary non-black-box modules because that format predates module flags.
|
||||
|
||||
`@status` prints the current design and top names, SN revision, selected technology, hierarchy form, last extraction
|
||||
mode/module/revision, saved boundary hash, current GIA dimensions, and `@put` compatibility. A new `@read` or `@slang`
|
||||
|
|
@ -94,12 +100,14 @@ instance/FAN ordering, hierarchy recursion, LUTs, gates, and mapped primitive in
|
|||
line per module. Memory, DSP, and carry mapping commands run the same checker transactionally before and after each
|
||||
transformation, so an invalid result is diagnosed and rejected without replacing the current design.
|
||||
|
||||
`@ps` prints compact statistics for the selected top module. `@ps -v` adds the hierarchy and statistics for every
|
||||
module definition. Like `%ps -d`, `@ps -d` prints occurrences by object type and output/input width signature. Its
|
||||
counts cover the elaborated hierarchy rooted at the selected top, including the multiplicity of repeated insts. The
|
||||
hierarchical occurrence totals are accumulated over the module DAG rather than by recursively revisiting every inst,
|
||||
so statistics remain practical for deeply repeated hierarchy. Memory is reported as used/allocated storage with
|
||||
rounded K, M, or G suffixes.
|
||||
`@ps` prints compact statistics for every module definition by default. `@ps -M module` prints the selected module
|
||||
instead and uses it as the root for optional hierarchy and detailed reports. `@ps -v` adds the selected hierarchy and
|
||||
keeps opaque definitions annotated with `[blackbox]`. Like `%ps -d`, `@ps -d` prints occurrences by object type and
|
||||
output/input width signature. It also reports every reachable black-box type, its instance-occurrence multiplicity,
|
||||
PI/PO port and bit counts, and totals for abstract AIG inputs and outputs. Counts cover the elaborated hierarchy rooted
|
||||
at the selected module (or the current design top when `-M` is absent), including repeated insts. Hierarchical totals
|
||||
are accumulated over the module DAG rather than by recursively revisiting every inst, so statistics remain practical
|
||||
for deeply repeated hierarchy. Memory is reported as used/allocated storage with rounded K, M, or G suffixes.
|
||||
|
||||
`@map_mem`, `@map_dsp`, and `@map_add` map into the initial AMD/Xilinx UltraScale+ technology description.
|
||||
Transformations are transactional and keep the original user-visible top-module name. `@map_add` replaces word-level
|
||||
|
|
@ -108,6 +116,23 @@ operand inversion, extension, and final slicing remain ordinary SN logic for sub
|
|||
before carry mapping so future DSP preadder and postadder recognition is not hidden. `@collapse` flattens user hierarchy
|
||||
while retaining mapped hard-block leaf instances.
|
||||
|
||||
Opaque `SN_MODULE_BLACKBOX` insts are preserved by hierarchy collapse even when ordinary user hierarchy is flattened.
|
||||
During `@blast`, each opaque output is an additional GIA input and each opaque input is an additional GIA output, in
|
||||
natural port and LSB-first bit order. A black-box `SN_PO` has `SN_INVALID_ID` as its sole fanin, explicitly recording
|
||||
that its value has no SN implementation; no zero-valued placeholder is created. `@write` emits the preserved interface
|
||||
as a port-only `(* blackbox *)` module. Internally an opaque module contains only its declared `SN_PI` and `SN_PO`
|
||||
objects; an `inout` is a same-named PI/PO pair. Its body and descendants are absent from SN. `@check` permits the
|
||||
invalid PO fanin only for this boundary representation, and `@ps -v` / `@ps -d` expose the retained black boxes and
|
||||
their reachable occurrence counts.
|
||||
|
||||
`SN_CAST` is a one-fanin operator whose object width and signedness define the result type. It does not permute bits.
|
||||
An equal-width cast only changes the signedness annotation; widening sign-extends a signed result and zero-extends an
|
||||
unsigned result; narrowing discards high bits and retains the LSB-first low-order portion. `sn_slang` adds casts for
|
||||
explicit and implicit slang conversions, `$signed` / `$unsigned`, dynamic selected-value normalization, packed-value
|
||||
updates, and final normalization of `SN_MUX` data branches to the mux result width. Memory, DSP, and carry mapping may
|
||||
also introduce casts while adapting word-level values to primitive interfaces. The Verilog writer uses `$signed` or
|
||||
`$unsigned` on a result-width wire, and the bit-blaster implements the same extension or truncation directly.
|
||||
|
||||
`@opt_mux` restructures register mux cones by collecting root-to-terminal paths, grouping structurally identical
|
||||
LSB-first word values, and ORing the corresponding path conditions. A register-output terminal is converted into an
|
||||
explicit enable when the path controls are provably exclusive. The pass currently recognizes ordinary `SN_MUX`
|
||||
|
|
@ -194,10 +219,12 @@ instances as well as ordinary SN logic.
|
|||
|
||||
Mapped RAM/DSP/CARRY4 instances are reconstructed as technology leaf instances. SN loop-breaker pairs connect their
|
||||
output ports while the new flat module is built and are placed into a legal order by the final topological reorder.
|
||||
Temporary loop pairs are pruned after reconnection unless an actual feedback dependency remains, so acyclic datapaths
|
||||
do not gain artificial loop-breakers. Generic unmapped memory endpoints are recorded and abstracted by `@blast`, but
|
||||
`@put` currently rejects them because the boundary does not yet retain enough per-memory-port ownership data. This
|
||||
check prevents silent loss or misconnection of stateful memories.
|
||||
Temporary primitive-output loop pairs are pruned after reconnection unless an actual feedback dependency remains, so
|
||||
acyclic datapaths do not gain artificial loop-breakers. Explicit loop boundaries extracted from the original SN module
|
||||
are reconstructed unchanged; they are not currently re-proved unnecessary after `&`-space optimization. Generic
|
||||
unmapped memory endpoints are recorded and abstracted by `@blast`, but `@put` currently rejects them because the
|
||||
boundary does not yet retain enough per-memory-port ownership data. This check prevents silent loss or misconnection
|
||||
of stateful memories.
|
||||
|
||||
## Source files
|
||||
|
||||
|
|
|
|||
224
src/base/sn/sn.h
224
src/base/sn/sn.h
|
|
@ -82,6 +82,14 @@ ABC_NAMESPACE_HEADER_START
|
|||
// bidirectional lookup without storing another object ID. Constructors create
|
||||
// adjacent OUT/IN objects, but topologically reordered modules need not retain
|
||||
// that adjacency; the shared type ID is the authoritative pairing invariant.
|
||||
//
|
||||
// A module marked SN_MODULE_BLACKBOX retains only its declared PI/PO interface.
|
||||
// Its implementation is intentionally opaque: hierarchy collapse preserves its
|
||||
// insts, AIG construction abstracts their outputs as CIs and inputs as COs, and
|
||||
// the Verilog writer emits a black-box module declaration without a body. Each
|
||||
// black-box PO has SN_INVALID_ID as its sole fanin, denoting an unimplemented
|
||||
// boundary value rather than an ordinary undriven net or a zero constant. An
|
||||
// inout port is represented by same-named PI and PO objects.
|
||||
|
||||
#define SN_INVALID_ID UINT32_MAX
|
||||
|
||||
|
|
@ -91,6 +99,14 @@ typedef uint32_t sn_name_id_t;
|
|||
typedef uint32_t sn_type_id_t;
|
||||
typedef uint16_t sn_fanin_count_t;
|
||||
|
||||
typedef enum sn_module_flag_t
|
||||
{
|
||||
SN_MODULE_NO_FLAGS = 0,
|
||||
SN_MODULE_BLACKBOX = 1u << 0
|
||||
} sn_module_flag_t;
|
||||
|
||||
#define SN_MODULE_ALL_FLAGS ((uint32_t)SN_MODULE_BLACKBOX)
|
||||
|
||||
// A C-style generic vector. Cap and size are measured in elements.
|
||||
// Access macros take the element type explicitly, for example:
|
||||
//
|
||||
|
|
@ -272,6 +288,10 @@ enum sn_obj_type_enum
|
|||
// least-significant result bits, and later fanins supply successively more-
|
||||
// significant bits. Repetition has one fanin and a type-indexed repeat count.
|
||||
// Slice has one fanin and type-indexed left, right, and direction data.
|
||||
// Cast also has one fanin. Its object width and signedness define the result:
|
||||
// widening sign-extends a signed result and zero-extends an unsigned result;
|
||||
// narrowing retains the LSB-first low-order bits; equal-width conversion
|
||||
// changes only the signedness annotation.
|
||||
SN_CONCAT,
|
||||
SN_REPLICATE,
|
||||
SN_SLICE,
|
||||
|
|
@ -419,6 +439,7 @@ typedef struct sn_module_t
|
|||
sn_design_t* design;
|
||||
sn_module_id_t id;
|
||||
sn_name_id_t name;
|
||||
uint32_t flags;
|
||||
|
||||
// Core object attributes, all indexed by sn_obj_id_t.
|
||||
sn_vec_t obj_types;
|
||||
|
|
@ -838,6 +859,7 @@ static inline void sn_module_init(sn_module_t* module, sn_design_t* design, sn_m
|
|||
module->design = design;
|
||||
module->id = id;
|
||||
module->name = name;
|
||||
module->flags = SN_MODULE_NO_FLAGS;
|
||||
|
||||
sn_vec_init(&module->obj_types);
|
||||
sn_vec_init(&module->width_signed);
|
||||
|
|
@ -923,21 +945,27 @@ static inline void sn_design_destroy(sn_design_t* design)
|
|||
free(design);
|
||||
}
|
||||
|
||||
static inline sn_module_id_t sn_design_add_module_name_id(sn_design_t* design, sn_name_id_t name)
|
||||
{
|
||||
assert(design);
|
||||
assert(name < design->names.names.size);
|
||||
assert(design->modules.size < SN_INVALID_ID);
|
||||
sn_module_id_t id = (sn_module_id_t)design->modules.size;
|
||||
sn_module_t* module = (sn_module_t*)calloc(1, sizeof(sn_module_t));
|
||||
assert(module);
|
||||
sn_module_init(module, design, id, name);
|
||||
*sn_vec_push(sn_module_t*, &design->modules) = module;
|
||||
return id;
|
||||
}
|
||||
|
||||
static inline sn_module_id_t sn_design_add_module(sn_design_t* design, const char* name)
|
||||
{
|
||||
assert(design);
|
||||
assert(name);
|
||||
assert(design->modules.size < SN_INVALID_ID);
|
||||
sn_name_id_t name_id = sn_name_intern(&design->names, name);
|
||||
for (size_t i = 0; i < design->modules.size; i++)
|
||||
assert(sn_vec_at(sn_module_t*, &design->modules, i)->name != name_id);
|
||||
|
||||
sn_module_id_t id = (sn_module_id_t)design->modules.size;
|
||||
sn_module_t* module = (sn_module_t*)calloc(1, sizeof(sn_module_t));
|
||||
assert(module);
|
||||
sn_module_init(module, design, id, name_id);
|
||||
*sn_vec_push(sn_module_t*, &design->modules) = module;
|
||||
return id;
|
||||
return sn_design_add_module_name_id(design, name_id);
|
||||
}
|
||||
|
||||
static inline sn_module_t* sn_design_get_module(sn_design_t* design, sn_module_id_t id)
|
||||
|
|
@ -967,6 +995,21 @@ static inline sn_module_id_t sn_design_find_module(const sn_design_t* design, co
|
|||
return SN_INVALID_ID;
|
||||
}
|
||||
|
||||
static inline bool sn_module_is_blackbox(const sn_module_t* module)
|
||||
{
|
||||
assert(module);
|
||||
return (module->flags & SN_MODULE_BLACKBOX) != 0;
|
||||
}
|
||||
|
||||
static inline void sn_module_set_blackbox(sn_module_t* module, bool blackbox)
|
||||
{
|
||||
assert(module);
|
||||
if (blackbox)
|
||||
module->flags |= SN_MODULE_BLACKBOX;
|
||||
else
|
||||
module->flags &= ~((uint32_t)SN_MODULE_BLACKBOX);
|
||||
}
|
||||
|
||||
// Deep-copy the semantic design state directly in memory. Derived constant-interner tables are intentionally left
|
||||
// empty and rebuilt lazily, matching binary roundtrip behavior. Fanout caches and optional object-copy maps are
|
||||
// preserved because callers may intentionally retain them between transformations.
|
||||
|
|
@ -986,6 +1029,7 @@ static inline sn_design_t* sn_design_dup(const sn_design_t* source)
|
|||
sn_module_id_t new_id = sn_design_add_module(target, sn_name_get(&source->names, old_module->name));
|
||||
assert(new_id == module_id);
|
||||
sn_module_t* new_module = sn_design_get_module(target, new_id);
|
||||
new_module->flags = old_module->flags;
|
||||
new_module->fanouts_valid = old_module->fanouts_valid;
|
||||
new_module->interface_locked = old_module->interface_locked;
|
||||
new_module->copy_module = old_module->copy_module;
|
||||
|
|
@ -1224,36 +1268,46 @@ static inline void sn_obj_connect(sn_module_t* module, sn_obj_id_t object, uint3
|
|||
sn_module_invalidate_fanouts(module);
|
||||
}
|
||||
|
||||
static inline void sn_obj_add_fanin(sn_module_t* module, sn_obj_id_t object, sn_obj_id_t fanin)
|
||||
static inline void sn_obj_add_fanins(sn_module_t* module, sn_obj_id_t object, uint32_t added_count,
|
||||
const sn_obj_id_t* added_fanins)
|
||||
{
|
||||
assert(module);
|
||||
assert(object < module->obj_types.size);
|
||||
assert(fanin < module->obj_types.size);
|
||||
assert(module->fanins.size < UINT32_MAX);
|
||||
assert(added_count == 0 || added_fanins);
|
||||
assert(module->fanins.size + added_count <= UINT32_MAX);
|
||||
for (uint32_t i = 0; i < added_count; i++)
|
||||
assert(added_fanins[i] < module->obj_types.size);
|
||||
if (!added_count)
|
||||
return;
|
||||
|
||||
uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object);
|
||||
uint32_t count = sn_obj_fanin_count(module, object);
|
||||
assert(count < UINT16_MAX);
|
||||
assert(added_count <= UINT16_MAX - count);
|
||||
size_t insertion = (size_t)offset + count;
|
||||
assert(insertion <= module->fanins.size);
|
||||
|
||||
size_t old_size = module->fanins.size;
|
||||
sn_vec_resize(sn_obj_id_t, &module->fanins, old_size + 1);
|
||||
sn_vec_resize(sn_obj_id_t, &module->fanins, old_size + added_count);
|
||||
sn_obj_id_t* fanins = sn_vec_data(sn_obj_id_t, &module->fanins);
|
||||
memmove(fanins + insertion + 1, fanins + insertion, (old_size - insertion) * sizeof(*fanins));
|
||||
fanins[insertion] = fanin;
|
||||
sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object) = (sn_fanin_count_t)(count + 1);
|
||||
memmove(fanins + insertion + added_count, fanins + insertion, (old_size - insertion) * sizeof(*fanins));
|
||||
memcpy(fanins + insertion, added_fanins, (size_t)added_count * sizeof(*fanins));
|
||||
sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object) = (sn_fanin_count_t)(count + added_count);
|
||||
|
||||
// Preserve fanin-span order for every object after the modified object.
|
||||
for (sn_obj_id_t other = object + 1; other < module->obj_types.size; other++)
|
||||
{
|
||||
uint32_t other_offset = sn_vec_at(uint32_t, &module->fanin_offsets, other);
|
||||
if (other_offset >= insertion)
|
||||
sn_vec_at(uint32_t, &module->fanin_offsets, other) = other_offset + 1;
|
||||
sn_vec_at(uint32_t, &module->fanin_offsets, other) = other_offset + added_count;
|
||||
}
|
||||
sn_module_invalidate_fanouts(module);
|
||||
}
|
||||
|
||||
static inline void sn_obj_add_fanin(sn_module_t* module, sn_obj_id_t object, sn_obj_id_t fanin)
|
||||
{
|
||||
sn_obj_add_fanins(module, object, 1, &fanin);
|
||||
}
|
||||
|
||||
static inline sn_obj_id_t sn_module_add_pi(sn_module_t* module, uint32_t width, bool is_signed, const char* name)
|
||||
{
|
||||
assert(module);
|
||||
|
|
@ -1265,6 +1319,7 @@ static inline sn_obj_id_t sn_module_add_po(sn_module_t* module, uint32_t width,
|
|||
sn_obj_id_t driver)
|
||||
{
|
||||
assert(module);
|
||||
assert(!sn_module_is_blackbox(module));
|
||||
assert(!module->interface_locked);
|
||||
assert(driver < module->obj_types.size);
|
||||
assert(sn_obj_width(module, driver) == width);
|
||||
|
|
@ -1273,6 +1328,36 @@ static inline sn_obj_id_t sn_module_add_po(sn_module_t* module, uint32_t width,
|
|||
return output;
|
||||
}
|
||||
|
||||
// Adds an output port to an opaque module. Unlike an ordinary SN_PO, whose
|
||||
// only fanin is its RTL driver, a black-box output deliberately has no driver
|
||||
// inside SN. Parent insts expose it through their normal SN_INST/SN_FAN
|
||||
// boundary objects; collapse preserves that boundary and blasting abstracts
|
||||
// the value as a new combinational input.
|
||||
static inline sn_obj_id_t sn_module_add_blackbox_po(sn_module_t* module, uint32_t width, bool is_signed,
|
||||
const char* name)
|
||||
{
|
||||
assert(module);
|
||||
assert(sn_module_is_blackbox(module));
|
||||
assert(!module->interface_locked);
|
||||
return sn_module_add_named_obj(module, SN_PO, width, is_signed, 1, name);
|
||||
}
|
||||
|
||||
static inline bool sn_obj_fanin_may_be_invalid(const sn_module_t* module, sn_obj_type_t type, uint32_t index)
|
||||
{
|
||||
assert(module);
|
||||
if (type == SN_PO)
|
||||
return sn_module_is_blackbox(module) && index == 0;
|
||||
if (type == SN_REG_OUT)
|
||||
return index != SN_REG_DATA;
|
||||
if (type == SN_MEM_OUT)
|
||||
return index == SN_MEM_INIT_DATA || index == SN_MEM_INIT_MASK;
|
||||
if (type == SN_MEM_READ)
|
||||
return index == SN_MEM_READ_CLOCK || index == SN_MEM_READ_ENABLE;
|
||||
if (type == SN_MEM_WRITE)
|
||||
return index == SN_MEM_WRITE_ENABLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sn_obj_type_is_operator(sn_obj_type_t type)
|
||||
{
|
||||
return type == SN_BUF || (type >= SN_POS && type < SN_OBJ_TYPE_COUNT);
|
||||
|
|
@ -1858,9 +1943,10 @@ static inline sn_obj_id_t sn_module_add_mem_read(sn_module_t* module, sn_obj_id_
|
|||
return read;
|
||||
}
|
||||
|
||||
static inline sn_obj_id_t sn_module_add_mem_write(sn_module_t* module, sn_obj_id_t mem_in, sn_obj_id_t clock,
|
||||
sn_obj_id_t enable, sn_obj_id_t data, sn_obj_id_t address,
|
||||
const char* name)
|
||||
static inline sn_obj_id_t sn_module_add_mem_write_unlinked(sn_module_t* module, sn_obj_id_t mem_in,
|
||||
sn_obj_id_t clock, sn_obj_id_t enable,
|
||||
sn_obj_id_t data, sn_obj_id_t address,
|
||||
const char* name)
|
||||
{
|
||||
assert(module);
|
||||
assert(sn_obj_type(module, mem_in) == SN_MEM_IN);
|
||||
|
|
@ -1875,6 +1961,14 @@ static inline sn_obj_id_t sn_module_add_mem_write(sn_module_t* module, sn_obj_id
|
|||
sn_obj_connect(module, write, SN_MEM_WRITE_ENABLE, enable);
|
||||
sn_obj_connect(module, write, SN_MEM_WRITE_DATA, data);
|
||||
sn_obj_connect(module, write, SN_MEM_WRITE_ADDRESS, address);
|
||||
return write;
|
||||
}
|
||||
|
||||
static inline sn_obj_id_t sn_module_add_mem_write(sn_module_t* module, sn_obj_id_t mem_in, sn_obj_id_t clock,
|
||||
sn_obj_id_t enable, sn_obj_id_t data, sn_obj_id_t address,
|
||||
const char* name)
|
||||
{
|
||||
sn_obj_id_t write = sn_module_add_mem_write_unlinked(module, mem_in, clock, enable, data, address, name);
|
||||
sn_obj_add_fanin(module, mem_in, write);
|
||||
return write;
|
||||
}
|
||||
|
|
@ -2005,6 +2099,11 @@ static inline void sn_design_print_hierarchy_rec(FILE* out, const sn_design_t* d
|
|||
fputs(" [recursive]\n", out);
|
||||
return;
|
||||
}
|
||||
if (sn_module_is_blackbox(module))
|
||||
{
|
||||
fputs(" [blackbox]\n", out);
|
||||
return;
|
||||
}
|
||||
fputc('\n', out);
|
||||
|
||||
active_modules[module_id] = true;
|
||||
|
|
@ -2533,6 +2632,7 @@ static inline sn_module_id_t sn_design_dup_module_topo(sn_design_t* design, sn_m
|
|||
sn_vec_t order = sn_module_topo_order(source);
|
||||
sn_module_id_t target_module_id = sn_design_add_module(design, new_name);
|
||||
sn_module_t* target = sn_design_get_module(design, target_module_id);
|
||||
target->flags = source->flags;
|
||||
|
||||
sn_vec_resize(sn_obj_id_t, &source->copy_ids, source->obj_types.size);
|
||||
for (size_t i = 0; i < source->copy_ids.size; i++)
|
||||
|
|
@ -3054,6 +3154,7 @@ static inline sn_module_id_t sn_design_dup_module_clean_topo(sn_design_t* design
|
|||
|
||||
sn_module_id_t target_id = sn_design_add_module(design, new_name);
|
||||
sn_module_t* target = sn_design_get_module(design, target_id);
|
||||
target->flags = source->flags;
|
||||
sn_vec_resize(sn_obj_id_t, &source->copy_ids, object_count);
|
||||
for (size_t i = 0; i < object_count; i++)
|
||||
sn_vec_at(sn_obj_id_t, &source->copy_ids, i) = SN_INVALID_ID;
|
||||
|
|
@ -3071,9 +3172,19 @@ static inline sn_module_id_t sn_design_dup_module_clean_topo(sn_design_t* design
|
|||
continue;
|
||||
uint32_t bits = sn_obj_width(source, old_object);
|
||||
uint32_t* words = (uint32_t*)calloc(sn_const_word_count(bits), sizeof(uint32_t));
|
||||
sn_name_id_t source_name_id = sn_obj_name_id(source, old_object);
|
||||
const char* source_name = source_name_id == SN_INVALID_ID ? NULL :
|
||||
sn_name_get(&source->design->names, source_name_id);
|
||||
char* name = source_name ? (char*)malloc(strlen(source_name) + 1) : NULL;
|
||||
assert(words);
|
||||
if (source_name)
|
||||
{
|
||||
assert(name);
|
||||
memcpy(name, source_name, strlen(source_name) + 1);
|
||||
}
|
||||
sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) =
|
||||
sn_module_add_const(target, bits, sn_obj_is_signed(source, old_object), words, NULL);
|
||||
sn_module_add_const(target, bits, sn_obj_is_signed(source, old_object), words, name);
|
||||
free(name);
|
||||
free(words);
|
||||
}
|
||||
for (size_t i = input_count; i < order.size; i++)
|
||||
|
|
@ -3179,6 +3290,8 @@ typedef struct sn_collapse_context_t
|
|||
static inline bool sn_module_is_technology_primitive(const sn_module_t* module)
|
||||
{
|
||||
assert(module);
|
||||
if (sn_module_is_blackbox(module))
|
||||
return true;
|
||||
const char* name = sn_name_get(&module->design->names, module->name);
|
||||
return strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0 ||
|
||||
strncmp(name, "__sn_DSP", 8) == 0 || strncmp(name, "__sn_CARRY", 10) == 0;
|
||||
|
|
@ -3187,15 +3300,15 @@ static inline bool sn_module_is_technology_primitive(const sn_module_t* module)
|
|||
static inline bool sn_collapse_preserves_object(const sn_collapse_context_t* context,
|
||||
const sn_module_t* source, sn_obj_id_t object)
|
||||
{
|
||||
if (!context->preserve_technology_primitives)
|
||||
return false;
|
||||
sn_obj_type_t type = sn_obj_type(source, object);
|
||||
sn_obj_id_t inst = type == SN_INST ? object : type == SN_FAN ? sn_fan_inst_id(source, object)
|
||||
: SN_INVALID_ID;
|
||||
if (inst == SN_INVALID_ID)
|
||||
return false;
|
||||
return sn_module_is_technology_primitive(
|
||||
sn_design_get_module_const(context->design, sn_inst_module_id(source, inst)));
|
||||
const sn_module_t* child = sn_design_get_module_const(context->design, sn_inst_module_id(source, inst));
|
||||
if (sn_module_is_blackbox(child))
|
||||
return true;
|
||||
return context->preserve_technology_primitives && sn_module_is_technology_primitive(child);
|
||||
}
|
||||
|
||||
static inline bool sn_collapse_obj_is_copied(sn_obj_type_t type, bool is_top)
|
||||
|
|
@ -3427,11 +3540,9 @@ static inline sn_module_id_t sn_design_collapse_module_internal(sn_design_t* des
|
|||
sn_module_rebuild_pair_type_ids(flat, SN_MEM_OUT, SN_MEM_IN, SN_MEM_STATE);
|
||||
sn_module_rebuild_pair_type_ids(flat, SN_LOOP_OUT, SN_LOOP_IN, 0);
|
||||
|
||||
if (!preserve_technology_primitives)
|
||||
{
|
||||
assert(flat->type_objects[SN_INST].size == 0);
|
||||
assert(flat->type_objects[SN_FAN].size == 0);
|
||||
}
|
||||
for (size_t i = 0; i < flat->inst_modules.size; i++)
|
||||
assert(sn_module_is_technology_primitive(
|
||||
sn_design_get_module_const(design, sn_vec_at(sn_module_id_t, &flat->inst_modules, i))));
|
||||
assert(sn_module_is_topo(flat));
|
||||
return flat_module_id;
|
||||
}
|
||||
|
|
@ -4270,6 +4381,8 @@ static inline void sn_module_write_verilog_as(FILE* out, const sn_module_t* modu
|
|||
write_memories[write_id] = memory;
|
||||
}
|
||||
}
|
||||
if (sn_module_is_blackbox(module))
|
||||
fputs("(* blackbox *) ", out);
|
||||
fputs("module ", out);
|
||||
sn_write_verilog_identifier(out, emitted_name);
|
||||
fputs(" (", out);
|
||||
|
|
@ -4314,6 +4427,13 @@ static inline void sn_module_write_verilog_as(FILE* out, const sn_module_t* modu
|
|||
fputc(10, out);
|
||||
}
|
||||
|
||||
if (sn_module_is_blackbox(module))
|
||||
{
|
||||
fputs("endmodule\n\n", out);
|
||||
free(write_memories);
|
||||
return;
|
||||
}
|
||||
|
||||
for (sn_obj_id_t object = 0; object < module->obj_types.size; object++)
|
||||
{
|
||||
sn_obj_type_t type = sn_obj_type(module, object);
|
||||
|
|
@ -4527,7 +4647,8 @@ static inline void sn_design_write_verilog_file(const sn_design_t* design, const
|
|||
// data words are unsigned 32-bit values. A format change must increment the
|
||||
// version below.
|
||||
|
||||
#define SN_BINARY_FORMAT_VERSION 5u
|
||||
#define SN_BINARY_FORMAT_VERSION 6u
|
||||
#define SN_BINARY_MIN_READ_VERSION 5u
|
||||
|
||||
// The format version covers field-layout changes. This signature additionally binds every serialized object type to
|
||||
// its numeric value, so reordering the enum cannot silently reinterpret an otherwise same-sized binary design.
|
||||
|
|
@ -4839,6 +4960,7 @@ static inline void sn_module_assert_valid(const sn_module_t* module)
|
|||
assert(module->id < module->design->modules.size);
|
||||
assert(sn_design_get_module_const(module->design, module->id) == module);
|
||||
assert(module->name < module->design->names.names.size);
|
||||
assert((module->flags & ~SN_MODULE_ALL_FLAGS) == 0);
|
||||
|
||||
size_t object_count = module->obj_types.size;
|
||||
assert(object_count < SN_INVALID_ID);
|
||||
|
|
@ -4865,12 +4987,23 @@ static inline void sn_module_assert_valid(const sn_module_t* module)
|
|||
for (uint32_t i = 0; i < fanin_count; i++)
|
||||
{
|
||||
sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, fanin_offset + i);
|
||||
assert(fanin == SN_INVALID_ID || fanin < object_count);
|
||||
assert(fanin < object_count ||
|
||||
(fanin == SN_INVALID_ID && sn_obj_fanin_may_be_invalid(module, type, i)));
|
||||
}
|
||||
expected_fanin_offset += fanin_count;
|
||||
}
|
||||
assert(expected_fanin_offset == module->fanins.size);
|
||||
|
||||
if (sn_module_is_blackbox(module))
|
||||
for (sn_obj_id_t object = 0; object < object_count; object++)
|
||||
{
|
||||
sn_obj_type_t type = sn_obj_type(module, object);
|
||||
assert(type == SN_PI || type == SN_PO);
|
||||
if (type == SN_PO)
|
||||
assert(sn_obj_fanin_count(module, object) == 1 &&
|
||||
sn_obj_fanin(module, object, 0) == SN_INVALID_ID);
|
||||
}
|
||||
|
||||
for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++)
|
||||
for (uint32_t type_id = 0; type_id < module->type_objects[type].size; type_id++)
|
||||
{
|
||||
|
|
@ -5048,6 +5181,7 @@ static inline void sn_design_assert_valid(const sn_design_t* design)
|
|||
static inline void sn_binary_write_module(sn_binary_writer_t* writer, const sn_module_t* module)
|
||||
{
|
||||
sn_binary_write_u32(writer, module->name);
|
||||
sn_binary_write_u32(writer, module->flags);
|
||||
sn_binary_write_u32(writer, module->fanouts_valid ? 1u : 0u);
|
||||
sn_binary_write_u32(writer, module->interface_locked ? 1u : 0u);
|
||||
sn_binary_write_u32(writer, module->copy_module);
|
||||
|
|
@ -5075,23 +5209,27 @@ static inline void sn_binary_write_module(sn_binary_writer_t* writer, const sn_m
|
|||
sn_binary_write_u32_vec(writer, &module->copy_ids);
|
||||
}
|
||||
|
||||
static inline bool sn_binary_read_module(sn_binary_reader_t* reader, sn_design_t* design, sn_module_id_t expected_id)
|
||||
static inline bool sn_binary_read_module(sn_binary_reader_t* reader, sn_design_t* design, sn_module_id_t expected_id,
|
||||
uint32_t version, uint8_t* module_name_seen)
|
||||
{
|
||||
sn_name_id_t name = sn_binary_read_u32(reader);
|
||||
uint32_t flags = version >= 6 ? sn_binary_read_u32(reader) : SN_MODULE_NO_FLAGS;
|
||||
uint32_t fanouts_valid = sn_binary_read_u32(reader);
|
||||
uint32_t interface_locked = sn_binary_read_u32(reader);
|
||||
sn_module_id_t copy_module = sn_binary_read_u32(reader);
|
||||
if (!reader->valid || name >= design->names.names.size || fanouts_valid > 1 || interface_locked > 1 ||
|
||||
sn_design_find_module(design, sn_name_get(&design->names, name)) != SN_INVALID_ID)
|
||||
if (!reader->valid || name >= design->names.names.size || module_name_seen[name] ||
|
||||
(flags & ~SN_MODULE_ALL_FLAGS) != 0 || fanouts_valid > 1 || interface_locked > 1)
|
||||
{
|
||||
reader->valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
sn_module_id_t id = sn_design_add_module(design, sn_name_get(&design->names, name));
|
||||
module_name_seen[name] = 1;
|
||||
sn_module_id_t id = sn_design_add_module_name_id(design, name);
|
||||
assert(id == expected_id);
|
||||
sn_module_t* module = sn_design_get_module(design, id);
|
||||
assert(module->name == name);
|
||||
module->flags = flags;
|
||||
module->fanouts_valid = fanouts_valid != 0;
|
||||
module->interface_locked = interface_locked != 0;
|
||||
module->copy_module = copy_module;
|
||||
|
|
@ -5194,7 +5332,7 @@ static inline sn_design_t* sn_design_read_binary_raw_status(FILE* in, sn_binary_
|
|||
status = SN_BINARY_READ_IO;
|
||||
else if (memcmp(magic, expected_magic, sizeof(magic)) != 0)
|
||||
status = SN_BINARY_READ_MAGIC;
|
||||
else if (version != SN_BINARY_FORMAT_VERSION)
|
||||
else if (version < SN_BINARY_MIN_READ_VERSION || version > SN_BINARY_FORMAT_VERSION)
|
||||
status = SN_BINARY_READ_VERSION;
|
||||
else if (layout_signature != sn_binary_layout_signature() || object_type_count != SN_OBJ_TYPE_COUNT ||
|
||||
register_fanin_count != SN_REG_FANIN_COUNT ||
|
||||
|
|
@ -5220,7 +5358,11 @@ static inline sn_design_t* sn_design_read_binary_raw_status(FILE* in, sn_binary_
|
|||
break;
|
||||
}
|
||||
char* name = (char*)malloc(length + 1);
|
||||
assert(name);
|
||||
if (!name)
|
||||
{
|
||||
reader.valid = false;
|
||||
break;
|
||||
}
|
||||
sn_binary_read_bytes(&reader, name, length);
|
||||
name[length] = 0;
|
||||
if (memchr(name, 0, length) != NULL)
|
||||
|
|
@ -5241,8 +5383,12 @@ static inline sn_design_t* sn_design_read_binary_raw_status(FILE* in, sn_binary_
|
|||
size_t module_count = sn_binary_read_size(&reader);
|
||||
if (!reader.valid || module_count >= SN_INVALID_ID || module_count > reader.remaining / 16)
|
||||
reader.valid = false;
|
||||
uint8_t* module_name_seen = name_count ? (uint8_t*)calloc(name_count, 1) : NULL;
|
||||
if (reader.valid && module_count && !module_name_seen)
|
||||
reader.valid = false;
|
||||
for (sn_module_id_t i = 0; reader.valid && i < module_count; i++)
|
||||
sn_binary_read_module(&reader, design, i);
|
||||
sn_binary_read_module(&reader, design, i, version, module_name_seen);
|
||||
free(module_name_seen);
|
||||
if (!reader.valid)
|
||||
{
|
||||
sn_design_destroy(design);
|
||||
|
|
|
|||
|
|
@ -957,6 +957,7 @@ static inline int* sn_blast_shift(sn_blast_ctx_t* ctx, sn_obj_id_t object, bool
|
|||
int fill = arithmetic && !left && sn_obj_is_signed(m, value_id) ? current[work_width - 1]
|
||||
: Mini_AigLitConst0();
|
||||
uint32_t useful_stages = 0;
|
||||
// SN widths are capped below 2^31, so this unsigned shift never reaches 32.
|
||||
while ((UINT32_C(1) << useful_stages) < work_width)
|
||||
useful_stages++;
|
||||
uint32_t stage_count = amount_width < useful_stages ? amount_width : useful_stages;
|
||||
|
|
@ -1186,6 +1187,7 @@ static inline int* sn_blast_eval(sn_blast_ctx_t* ctx, sn_obj_id_t object)
|
|||
{
|
||||
uint32_t count = sn_obj_fanin_count(m, object);
|
||||
int inputs[6];
|
||||
assert(count <= sizeof(inputs) / sizeof(inputs[0]));
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
inputs[i] = sn_blast_eval(ctx, sn_obj_fanin(m, object, i))[0];
|
||||
result = sn_blast_alloc_bits(1);
|
||||
|
|
@ -1206,8 +1208,7 @@ static inline int* sn_blast_eval(sn_blast_ctx_t* ctx, sn_obj_id_t object)
|
|||
ctx->options.delay_comparators);
|
||||
result = sn_blast_alloc_bits(width);
|
||||
for (uint32_t i = 0; i < width; i++)
|
||||
result[i] = i < work_width ? quotient_or_remainder[i]
|
||||
: (signed_operands ? quotient_or_remainder[work_width - 1] : 0);
|
||||
result[i] = quotient_or_remainder[i];
|
||||
free(a);
|
||||
free(b);
|
||||
free(quotient_or_remainder);
|
||||
|
|
@ -1475,6 +1476,8 @@ static inline bool sn_blast_hier_is_abstract_inst(const sn_blast_hier_t* hierarc
|
|||
{
|
||||
const sn_module_t* child = sn_design_get_module_const(hierarchy->design,
|
||||
sn_inst_module_id(module, inst));
|
||||
if (sn_module_is_blackbox(child))
|
||||
return true;
|
||||
const char* name = sn_name_get(&hierarchy->design->names, child->name);
|
||||
bool memory = strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0;
|
||||
bool multiplier = strncmp(name, "__sn_DSP", 8) == 0;
|
||||
|
|
|
|||
|
|
@ -488,37 +488,99 @@ static inline sn_obj_id_t sn_boundary_co_word(sn_boundary_regs_t* regs, const sn
|
|||
return sn_boundary_pack_bits(regs->result, co_drivers + begin, width, "boundary_word");
|
||||
}
|
||||
|
||||
static inline bool sn_boundary_depends_on(const sn_module_t* module, sn_obj_id_t root, sn_obj_id_t dependency)
|
||||
typedef struct sn_boundary_dfs_frame_t
|
||||
{
|
||||
uint8_t* visited = (uint8_t*)calloc(module->obj_types.size, sizeof(uint8_t));
|
||||
sn_vec_t stack;
|
||||
assert(visited);
|
||||
sn_obj_id_t object;
|
||||
uint32_t next_fanout;
|
||||
} sn_boundary_dfs_frame_t;
|
||||
|
||||
// Marks tentative primitive-output substitutions that create combinational feedback. All temporary pair outputs
|
||||
// have already been replaced by the corresponding primitive outputs. One iterative Kosaraju traversal identifies
|
||||
// the resulting strongly connected components; a substituted edge whose endpoints share a component must retain
|
||||
// its loop pair. This replaces one complete cone walk per primitive output by linear whole-module graph work.
|
||||
static inline void sn_boundary_mark_feedback_pairs(sn_module_t* module, const sn_obj_id_t* actual_to_pair,
|
||||
uint8_t* keep)
|
||||
{
|
||||
size_t object_count = module->obj_types.size;
|
||||
uint8_t* visited = (uint8_t*)calloc(object_count, sizeof(uint8_t));
|
||||
uint32_t* components = object_count ? (uint32_t*)malloc(object_count * sizeof(uint32_t)) : NULL;
|
||||
sn_vec_t order, stack;
|
||||
assert(visited && (components || object_count == 0));
|
||||
sn_vec_init(&order);
|
||||
sn_vec_init(&stack);
|
||||
*sn_vec_push(sn_obj_id_t, &stack) = root;
|
||||
while (stack.size)
|
||||
sn_vec_reserve(sn_obj_id_t, &order, object_count);
|
||||
sn_module_build_fanouts(module);
|
||||
|
||||
for (sn_obj_id_t start = 0; start < object_count; start++)
|
||||
{
|
||||
sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &stack, --stack.size);
|
||||
if (object == dependency)
|
||||
{
|
||||
sn_vec_destroy(&stack);
|
||||
free(visited);
|
||||
return true;
|
||||
}
|
||||
if (visited[object])
|
||||
if (visited[start])
|
||||
continue;
|
||||
visited[object] = 1;
|
||||
if (sn_obj_type_is_pair_out(sn_obj_type(module, object)))
|
||||
continue;
|
||||
for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++)
|
||||
visited[start] = 1;
|
||||
sn_boundary_dfs_frame_t* first = sn_vec_push(sn_boundary_dfs_frame_t, &stack);
|
||||
first->object = start;
|
||||
first->next_fanout = 0;
|
||||
while (stack.size)
|
||||
{
|
||||
sn_obj_id_t fanin = sn_obj_fanin(module, object, i);
|
||||
if (fanin != SN_INVALID_ID && !visited[fanin])
|
||||
*sn_vec_push(sn_obj_id_t, &stack) = fanin;
|
||||
sn_boundary_dfs_frame_t* frame =
|
||||
&sn_vec_at(sn_boundary_dfs_frame_t, &stack, stack.size - 1);
|
||||
uint32_t count = sn_obj_fanout_count(module, frame->object);
|
||||
if (frame->next_fanout < count)
|
||||
{
|
||||
sn_obj_id_t fanout = sn_obj_fanout(module, frame->object, frame->next_fanout++);
|
||||
if (!visited[fanout])
|
||||
{
|
||||
visited[fanout] = 1;
|
||||
sn_boundary_dfs_frame_t* child = sn_vec_push(sn_boundary_dfs_frame_t, &stack);
|
||||
child->object = fanout;
|
||||
child->next_fanout = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
*sn_vec_push(sn_obj_id_t, &order) = frame->object;
|
||||
stack.size--;
|
||||
}
|
||||
}
|
||||
|
||||
for (sn_obj_id_t object = 0; object < object_count; object++)
|
||||
components[object] = UINT32_MAX;
|
||||
uint32_t component_count = 0;
|
||||
for (size_t i = order.size; i-- > 0;)
|
||||
{
|
||||
sn_obj_id_t start = sn_vec_at(sn_obj_id_t, &order, i);
|
||||
if (components[start] != UINT32_MAX)
|
||||
continue;
|
||||
components[start] = component_count;
|
||||
*sn_vec_push(sn_obj_id_t, &stack) = start;
|
||||
while (stack.size)
|
||||
{
|
||||
sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &stack, --stack.size);
|
||||
for (uint32_t k = 0; k < sn_obj_fanin_count(module, object); k++)
|
||||
{
|
||||
sn_obj_id_t fanin = sn_obj_fanin(module, object, k);
|
||||
if (fanin != SN_INVALID_ID && components[fanin] == UINT32_MAX)
|
||||
{
|
||||
components[fanin] = component_count;
|
||||
*sn_vec_push(sn_obj_id_t, &stack) = fanin;
|
||||
}
|
||||
}
|
||||
}
|
||||
component_count++;
|
||||
}
|
||||
|
||||
for (sn_obj_id_t object = 0; object < object_count; object++)
|
||||
for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++)
|
||||
{
|
||||
sn_obj_id_t actual = sn_obj_fanin(module, object, i);
|
||||
sn_obj_id_t pair_out = actual == SN_INVALID_ID ? SN_INVALID_ID : actual_to_pair[actual];
|
||||
if (pair_out != SN_INVALID_ID && components[actual] == components[object])
|
||||
keep[pair_out] = 1;
|
||||
}
|
||||
|
||||
sn_module_invalidate_fanouts(module);
|
||||
sn_vec_destroy(&order);
|
||||
sn_vec_destroy(&stack);
|
||||
free(components);
|
||||
free(visited);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Duplicates a module in topological order while omitting an explicitly unreferenced set of objects. This is used
|
||||
|
|
@ -618,8 +680,13 @@ static inline void sn_boundary_prune_primitive_pairs(sn_boundary_regs_t* regs)
|
|||
sn_module_t* source = regs->result;
|
||||
size_t object_count = source->obj_types.size;
|
||||
uint8_t* remove = (uint8_t*)calloc(object_count, sizeof(uint8_t));
|
||||
uint8_t* keep = (uint8_t*)calloc(object_count, sizeof(uint8_t));
|
||||
sn_obj_id_t* replacement = object_count ? (sn_obj_id_t*)malloc(object_count * sizeof(sn_obj_id_t)) : NULL;
|
||||
sn_obj_id_t* actual_to_pair = object_count ? (sn_obj_id_t*)malloc(object_count * sizeof(sn_obj_id_t)) : NULL;
|
||||
size_t remove_count = 0;
|
||||
assert(remove);
|
||||
assert(remove && keep && (replacement || object_count == 0) && (actual_to_pair || object_count == 0));
|
||||
for (sn_obj_id_t object = 0; object < object_count; object++)
|
||||
replacement[object] = actual_to_pair[object] = SN_INVALID_ID;
|
||||
for (size_t i = 0; i < regs->boundary->primitives.size; i++)
|
||||
{
|
||||
const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, ®s->boundary->primitives, i);
|
||||
|
|
@ -628,16 +695,39 @@ static inline void sn_boundary_prune_primitive_pairs(sn_boundary_regs_t* regs)
|
|||
{
|
||||
sn_obj_pair_t pair = regs->primitive_pairs[regs->primitive_offsets[i] + output];
|
||||
sn_obj_id_t actual = sn_obj_fanin(source, pair.in, 0);
|
||||
sn_obj_id_t inst = sn_obj_type(source, actual) == SN_FAN ? sn_fan_inst_id(source, actual) : actual;
|
||||
if (sn_boundary_depends_on(source, inst, pair.out))
|
||||
continue;
|
||||
for (size_t k = 0; k < source->fanins.size; k++)
|
||||
if (sn_vec_at(sn_obj_id_t, &source->fanins, k) == pair.out)
|
||||
sn_vec_at(sn_obj_id_t, &source->fanins, k) = actual;
|
||||
remove[pair.out] = remove[pair.in] = 1;
|
||||
remove_count += 2;
|
||||
assert(replacement[pair.out] == SN_INVALID_ID && actual_to_pair[actual] == SN_INVALID_ID);
|
||||
replacement[pair.out] = actual;
|
||||
actual_to_pair[actual] = pair.out;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < source->fanins.size; i++)
|
||||
{
|
||||
sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &source->fanins, i);
|
||||
if (fanin != SN_INVALID_ID && replacement[fanin] != SN_INVALID_ID)
|
||||
sn_vec_at(sn_obj_id_t, &source->fanins, i) = replacement[fanin];
|
||||
}
|
||||
sn_module_invalidate_fanouts(source);
|
||||
sn_boundary_mark_feedback_pairs(source, actual_to_pair, keep);
|
||||
for (sn_obj_id_t object = 0; object < object_count; object++)
|
||||
for (uint32_t i = 0; i < sn_obj_fanin_count(source, object); i++)
|
||||
{
|
||||
sn_obj_id_t actual = sn_obj_fanin(source, object, i);
|
||||
sn_obj_id_t pair_out = actual == SN_INVALID_ID ? SN_INVALID_ID : actual_to_pair[actual];
|
||||
if (pair_out != SN_INVALID_ID && keep[pair_out] && object != sn_obj_pair_in(source, pair_out))
|
||||
sn_obj_connect(source, object, i, pair_out);
|
||||
}
|
||||
for (sn_obj_id_t pair_out = 0; pair_out < object_count; pair_out++)
|
||||
if (replacement[pair_out] != SN_INVALID_ID && !keep[pair_out])
|
||||
{
|
||||
sn_obj_id_t actual = replacement[pair_out];
|
||||
sn_obj_id_t pair_in = sn_obj_pair_in(source, pair_out);
|
||||
if (sn_obj_type(source, actual) == SN_FAN && sn_obj_name_id(source, actual) == SN_INVALID_ID &&
|
||||
sn_obj_name_id(source, pair_out) != SN_INVALID_ID)
|
||||
sn_vec_at(sn_name_id_t, &source->name_ids, actual) = sn_obj_name_id(source, pair_out);
|
||||
remove[pair_out] = remove[pair_in] = 1;
|
||||
remove_count += 2;
|
||||
}
|
||||
sn_module_invalidate_fanouts(source);
|
||||
if (remove_count)
|
||||
{
|
||||
char name[96];
|
||||
|
|
@ -664,6 +754,9 @@ static inline void sn_boundary_prune_primitive_pairs(sn_boundary_regs_t* regs)
|
|||
sn_name_remove_last(®s->design->names, temporary_name);
|
||||
regs->result = filtered;
|
||||
}
|
||||
free(actual_to_pair);
|
||||
free(replacement);
|
||||
free(keep);
|
||||
free(remove);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,20 @@ static inline bool sn_check_const_type(sn_obj_type_t type)
|
|||
return type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST;
|
||||
}
|
||||
|
||||
static inline bool sn_check_name_is_emittable(const char* name)
|
||||
{
|
||||
size_t i;
|
||||
if (!name || !name[0])
|
||||
return false;
|
||||
for (i = 0; name[i]; i++)
|
||||
{
|
||||
unsigned char c = (unsigned char)name[i];
|
||||
if (c <= 32 || c >= 127 || c == '\\')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int sn_check_fixed_fanin_count(sn_obj_type_t type)
|
||||
{
|
||||
if (type == SN_PI || type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST)
|
||||
|
|
@ -108,19 +122,6 @@ static inline int sn_check_fixed_fanin_count(sn_obj_type_t type)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static inline bool sn_check_optional_fanin(sn_obj_type_t type, uint32_t index)
|
||||
{
|
||||
if (type == SN_REG_OUT)
|
||||
return index != SN_REG_DATA;
|
||||
if (type == SN_MEM_OUT)
|
||||
return index == SN_MEM_INIT_DATA || index == SN_MEM_INIT_MASK;
|
||||
if (type == SN_MEM_READ)
|
||||
return index == SN_MEM_READ_CLOCK || index == SN_MEM_READ_ENABLE;
|
||||
if (type == SN_MEM_WRITE)
|
||||
return index == SN_MEM_WRITE_ENABLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sn_check_module_core(sn_check_ctx_t* ctx, const sn_module_t* module)
|
||||
{
|
||||
const sn_design_t* design = module ? module->design : NULL;
|
||||
|
|
@ -136,12 +137,14 @@ static inline bool sn_check_module_core(sn_check_ctx_t* ctx, const sn_module_t*
|
|||
"module table does not point back to this module");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, module->name < design->names.names.size, "module name ID %u is out of range",
|
||||
module->name);
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, (module->flags & ~SN_MODULE_ALL_FLAGS) == 0,
|
||||
"module flags 0x%x contain unsupported bits", module->flags);
|
||||
if (module->name < design->names.names.size)
|
||||
{
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, sn_name_get(&design->names, module->name)[0] != '\0',
|
||||
"module name is empty");
|
||||
const char* module_name = sn_name_get(&design->names, module->name);
|
||||
if (strncmp(module_name, "__sn_", 5) == 0)
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, module_name != NULL && module_name[0] != '\0',
|
||||
"module name is null or empty");
|
||||
if (module_name && strncmp(module_name, "__sn_", 5) == 0)
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, sn_module_is_technology_primitive(module),
|
||||
"module uses the reserved internal prefix __sn_");
|
||||
}
|
||||
|
|
@ -194,9 +197,14 @@ static inline bool sn_check_module_core(sn_check_ctx_t* ctx, const sn_module_t*
|
|||
SN_CHECK(ctx, module, object, name_id == SN_INVALID_ID || name_id < design->names.names.size,
|
||||
"name ID %u is out of range", name_id);
|
||||
if (type_valid && (type == SN_PI || type == SN_PO || type == SN_GATE))
|
||||
{
|
||||
const char* object_name = name_id < design->names.names.size
|
||||
? sn_name_get(&design->names, name_id)
|
||||
: NULL;
|
||||
SN_CHECK(ctx, module, object,
|
||||
name_id < design->names.names.size && sn_name_get(&design->names, name_id)[0] != '\0',
|
||||
object_name != NULL && object_name[0] != '\0',
|
||||
"type %u requires a nonempty Verilog name", (unsigned)type);
|
||||
}
|
||||
if (type_valid)
|
||||
{
|
||||
int expected = sn_check_fixed_fanin_count(type);
|
||||
|
|
@ -213,7 +221,8 @@ static inline bool sn_check_module_core(sn_check_ctx_t* ctx, const sn_module_t*
|
|||
{
|
||||
sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + i);
|
||||
SN_CHECK(ctx, module, object, fanin < object_count ||
|
||||
(fanin == SN_INVALID_ID && type_valid && sn_check_optional_fanin(type, i)),
|
||||
(fanin == SN_INVALID_ID && type_valid &&
|
||||
sn_obj_fanin_may_be_invalid(module, type, i)),
|
||||
"fanin %u has invalid object ID %u", i, fanin);
|
||||
}
|
||||
offset += count;
|
||||
|
|
@ -221,6 +230,23 @@ static inline bool sn_check_module_core(sn_check_ctx_t* ctx, const sn_module_t*
|
|||
SN_CHECK(ctx, module, SN_INVALID_ID, offset == module->fanins.size,
|
||||
"fanin spans use %zu entries but storage contains %zu", offset, module->fanins.size);
|
||||
|
||||
if (sn_module_is_blackbox(module))
|
||||
for (sn_obj_id_t object = 0; object < object_count; object++)
|
||||
{
|
||||
sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, object);
|
||||
SN_CHECK(ctx, module, object, type == SN_PI || type == SN_PO,
|
||||
"black-box module contains non-port object of type %u", (unsigned)type);
|
||||
if (type == SN_PO)
|
||||
{
|
||||
uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object);
|
||||
uint32_t po_offset = sn_vec_at(uint32_t, &module->fanin_offsets, object);
|
||||
SN_CHECK(ctx, module, object,
|
||||
count == 1 && po_offset < module->fanins.size &&
|
||||
sn_vec_at(sn_obj_id_t, &module->fanins, po_offset) == SN_INVALID_ID,
|
||||
"black-box output must have one intentionally undriven fanin");
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++)
|
||||
for (size_t type_id = 0; type_id < module->type_objects[type].size; type_id++)
|
||||
{
|
||||
|
|
@ -940,47 +966,218 @@ static inline void sn_check_topology(sn_check_ctx_t* ctx, const sn_module_t* mod
|
|||
SN_CHECK(ctx, module, SN_INVALID_ID, valid, "objects are not in legal SN topological order");
|
||||
}
|
||||
|
||||
static inline bool sn_check_parse_u32(const char** cursor, uint32_t* value, char delimiter)
|
||||
{
|
||||
char* end;
|
||||
unsigned long parsed;
|
||||
if (!cursor || !*cursor || !value || **cursor < '0' || **cursor > '9')
|
||||
return false;
|
||||
parsed = strtoul(*cursor, &end, 10);
|
||||
if (end == *cursor || parsed > UINT32_MAX || *end != delimiter)
|
||||
return false;
|
||||
*value = (uint32_t)parsed;
|
||||
*cursor = delimiter ? end + 1 : end;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sn_check_slice_bit(const sn_module_t* module, sn_obj_id_t object, sn_obj_id_t source,
|
||||
uint32_t bit)
|
||||
{
|
||||
if (object == SN_INVALID_ID || object >= module->obj_types.size || sn_obj_type(module, object) != SN_SLICE ||
|
||||
sn_obj_fanin(module, object, 0) != source)
|
||||
return false;
|
||||
const sn_slice_info_t* info = sn_obj_slice_info(module, object);
|
||||
return info->left_index == (int32_t)bit && info->right_index == (int32_t)bit;
|
||||
}
|
||||
|
||||
static inline void sn_check_carry_primitive(sn_check_ctx_t* ctx, const sn_module_t* module)
|
||||
{
|
||||
size_t pi_count = module->type_objects[SN_PI].size;
|
||||
size_t po_count = module->type_objects[SN_PO].size;
|
||||
const uint32_t pi_widths[] = {1, 1, 4, 4};
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == 4 && po_count == 2,
|
||||
"carry primitive interface must have 4 inputs and 2 outputs");
|
||||
for (size_t i = 0; i < pi_count && i < 4; i++)
|
||||
{
|
||||
sn_obj_id_t pi = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i);
|
||||
SN_CHECK(ctx, module, pi, sn_obj_width(module, pi) == pi_widths[i],
|
||||
"carry primitive input %zu has the wrong width", i);
|
||||
}
|
||||
for (size_t i = 0; i < po_count; i++)
|
||||
{
|
||||
sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i);
|
||||
SN_CHECK(ctx, module, po, sn_obj_width(module, po) == 4,
|
||||
"carry primitive output %zu has the wrong width", i);
|
||||
}
|
||||
if (pi_count != 4 || po_count != 2)
|
||||
return;
|
||||
sn_obj_id_t ci = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 0);
|
||||
sn_obj_id_t cyinit = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 1);
|
||||
sn_obj_id_t di = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 2);
|
||||
sn_obj_id_t s = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 3);
|
||||
sn_obj_id_t o = sn_obj_fanin(module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], 0), 0);
|
||||
sn_obj_id_t co = sn_obj_fanin(module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], 1), 0);
|
||||
bool packed = sn_obj_type(module, o) == SN_CONCAT && sn_obj_fanin_count(module, o) == 4 &&
|
||||
sn_obj_type(module, co) == SN_CONCAT && sn_obj_fanin_count(module, co) == 4;
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, packed,
|
||||
"carry primitive outputs must be four-bit concatenations");
|
||||
if (!packed)
|
||||
return;
|
||||
sn_obj_id_t first_o = sn_obj_fanin(module, o, 0);
|
||||
sn_obj_id_t carry = sn_obj_type(module, first_o) == SN_BIT_XOR && sn_obj_fanin_count(module, first_o) == 2
|
||||
? sn_obj_fanin(module, first_o, 1)
|
||||
: SN_INVALID_ID;
|
||||
bool initial = carry != SN_INVALID_ID && sn_obj_type(module, carry) == SN_BIT_OR &&
|
||||
sn_obj_fanin_count(module, carry) == 2 &&
|
||||
((sn_obj_fanin(module, carry, 0) == ci && sn_obj_fanin(module, carry, 1) == cyinit) ||
|
||||
(sn_obj_fanin(module, carry, 0) == cyinit && sn_obj_fanin(module, carry, 1) == ci));
|
||||
SN_CHECK(ctx, module, carry, initial, "carry primitive has an invalid initial carry expression");
|
||||
for (uint32_t bit = 0; bit < 4; bit++)
|
||||
{
|
||||
sn_obj_id_t o_bit = sn_obj_fanin(module, o, bit);
|
||||
sn_obj_id_t co_bit = sn_obj_fanin(module, co, bit);
|
||||
bool o_valid = sn_obj_type(module, o_bit) == SN_BIT_XOR && sn_obj_fanin_count(module, o_bit) == 2;
|
||||
bool co_valid = sn_obj_type(module, co_bit) == SN_MUX && sn_obj_fanin_count(module, co_bit) == 3;
|
||||
sn_obj_id_t s_bit = o_valid ? sn_obj_fanin(module, o_bit, 0) : SN_INVALID_ID;
|
||||
sn_obj_id_t di_bit = co_valid ? sn_obj_fanin(module, co_bit, SN_MUX_DEFAULT) : SN_INVALID_ID;
|
||||
o_valid &= s_bit != SN_INVALID_ID && sn_obj_fanin(module, o_bit, 1) == carry &&
|
||||
sn_check_slice_bit(module, s_bit, s, bit);
|
||||
co_valid &= s_bit != SN_INVALID_ID && di_bit != SN_INVALID_ID &&
|
||||
sn_obj_fanin(module, co_bit, SN_MUX_SELECT) == s_bit &&
|
||||
sn_obj_fanin(module, co_bit, SN_MUX_SELECTED) == carry &&
|
||||
sn_check_slice_bit(module, di_bit, di, bit);
|
||||
SN_CHECK(ctx, module, o_bit, o_valid, "carry primitive O[%u] has invalid logic", bit);
|
||||
SN_CHECK(ctx, module, co_bit, co_valid, "carry primitive CO[%u] has invalid logic", bit);
|
||||
carry = co_bit;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sn_check_dsp_primitive(sn_check_ctx_t* ctx, const sn_module_t* module, const char* name)
|
||||
{
|
||||
size_t pi_count = module->type_objects[SN_PI].size;
|
||||
size_t po_count = module->type_objects[SN_PO].size;
|
||||
size_t mul_count = module->type_objects[SN_MUL].size;
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == 2 && po_count == 1,
|
||||
"DSP primitive interface must have 2 inputs and 1 output");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, mul_count == 1,
|
||||
"DSP primitive behavioral wrapper must contain one multiplier");
|
||||
const char* shape = strstr(name, "_mul_");
|
||||
uint32_t a_width = 0, b_width = 0, y_width = 0;
|
||||
bool parsed = shape != NULL;
|
||||
const char* cursor = parsed ? shape + 5 : NULL;
|
||||
parsed &= sn_check_parse_u32(&cursor, &a_width, '_');
|
||||
parsed &= sn_check_parse_u32(&cursor, &b_width, '_');
|
||||
parsed &= sn_check_parse_u32(&cursor, &y_width, '_');
|
||||
parsed &= cursor && cursor[0] == 's' && (cursor[1] == '0' || cursor[1] == '1') &&
|
||||
(cursor[2] == '0' || cursor[2] == '1') && cursor[3] == '\0';
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, parsed, "DSP primitive name does not encode a valid interface");
|
||||
if (pi_count != 2 || po_count != 1 || mul_count != 1 || !parsed)
|
||||
return;
|
||||
bool a_signed = cursor[1] == '1';
|
||||
bool b_signed = cursor[2] == '1';
|
||||
sn_obj_id_t a = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 0);
|
||||
sn_obj_id_t b = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 1);
|
||||
sn_obj_id_t mul = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MUL], 0);
|
||||
sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], 0);
|
||||
bool valid = sn_obj_width(module, a) == a_width && sn_obj_is_signed(module, a) == a_signed &&
|
||||
sn_obj_width(module, b) == b_width && sn_obj_is_signed(module, b) == b_signed &&
|
||||
sn_obj_width(module, mul) == y_width && sn_obj_is_signed(module, mul) == (a_signed || b_signed) &&
|
||||
sn_obj_fanin_count(module, mul) == 2 && sn_obj_fanin(module, mul, 0) == a &&
|
||||
sn_obj_fanin(module, mul, 1) == b && sn_obj_width(module, po) == y_width &&
|
||||
sn_obj_is_signed(module, po) == (a_signed || b_signed) && sn_obj_fanin(module, po, 0) == mul;
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, valid,
|
||||
"DSP primitive behavior does not match its encoded interface");
|
||||
}
|
||||
|
||||
static inline uint32_t sn_check_address_width(uint32_t depth)
|
||||
{
|
||||
uint32_t width = 0;
|
||||
for (uint32_t value = depth - 1; value; value >>= 1)
|
||||
width++;
|
||||
return width ? width : 1;
|
||||
}
|
||||
|
||||
static inline void sn_check_memory_primitive(sn_check_ctx_t* ctx, const sn_module_t* module, const char* name)
|
||||
{
|
||||
const char* marker = strstr(name, "_tdp_tile_");
|
||||
bool tdp = marker != NULL;
|
||||
bool legacy = false;
|
||||
if (!marker)
|
||||
marker = strstr(name, "_tile_");
|
||||
if (!marker)
|
||||
{
|
||||
marker = strstr(name, "_mem_");
|
||||
legacy = marker != NULL;
|
||||
}
|
||||
const char* cursor = marker ? marker + (tdp ? 10 : legacy ? 5 : 6) : NULL;
|
||||
uint32_t width = 0, depth = 0;
|
||||
bool parsed = marker && sn_check_parse_u32(&cursor, &width, '_') &&
|
||||
sn_check_parse_u32(&cursor, &depth, '\0') && cursor && *cursor == '\0' && width && depth;
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, parsed, "memory primitive name does not encode valid dimensions");
|
||||
size_t pi_count = module->type_objects[SN_PI].size;
|
||||
size_t po_count = module->type_objects[SN_PO].size;
|
||||
size_t reads = module->type_objects[SN_MEM_READ].size;
|
||||
size_t writes = module->type_objects[SN_MEM_WRITE].size;
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, module->type_objects[SN_MEM_OUT].size == 1,
|
||||
"memory primitive wrapper must contain one memory");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, reads == (tdp ? 2u : 1u) && writes == (tdp ? 2u : 1u),
|
||||
"memory primitive wrapper has the wrong number of read or write ports");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == (tdp ? 8u : 5u) && po_count == reads,
|
||||
"memory primitive interface has the wrong number of ports");
|
||||
if (!parsed || module->type_objects[SN_MEM_OUT].size != 1 || reads != (tdp ? 2u : 1u) ||
|
||||
writes != (tdp ? 2u : 1u) || pi_count != (tdp ? 8u : 5u) || po_count != reads)
|
||||
return;
|
||||
sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_OUT], 0);
|
||||
uint32_t address_width = legacy ? 32 : sn_check_address_width(depth);
|
||||
bool valid = sn_obj_width(module, memory) == width && sn_obj_mem_depth(module, memory) == depth;
|
||||
for (uint32_t port = 0; port < reads; port++)
|
||||
{
|
||||
uint32_t base = tdp ? 4 * port : 0;
|
||||
sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base);
|
||||
sn_obj_id_t enable = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base + 1);
|
||||
sn_obj_id_t address = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base + 2);
|
||||
sn_obj_id_t data = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base + 3);
|
||||
sn_obj_id_t read_address = tdp ? address : sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 4);
|
||||
sn_obj_id_t write = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_WRITE], port);
|
||||
sn_obj_id_t read = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_READ], port);
|
||||
sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], port);
|
||||
valid &= sn_obj_width(module, clock) == 1 && sn_obj_width(module, enable) == 1 &&
|
||||
sn_obj_width(module, address) == address_width && sn_obj_width(module, data) == width &&
|
||||
sn_obj_fanin(module, write, SN_MEM_WRITE_CLOCK) == clock &&
|
||||
sn_obj_fanin(module, write, SN_MEM_WRITE_ENABLE) == enable &&
|
||||
sn_obj_fanin(module, write, SN_MEM_WRITE_DATA) == data &&
|
||||
sn_obj_fanin(module, write, SN_MEM_WRITE_ADDRESS) == address &&
|
||||
sn_obj_fanin(module, read, SN_MEM_READ_MEMORY) == memory &&
|
||||
sn_obj_fanin(module, read, SN_MEM_READ_CLOCK) == SN_INVALID_ID &&
|
||||
sn_obj_fanin(module, read, SN_MEM_READ_ENABLE) == SN_INVALID_ID &&
|
||||
sn_obj_width(module, read_address) == address_width &&
|
||||
sn_obj_fanin(module, read, SN_MEM_READ_ADDRESS) == read_address &&
|
||||
sn_obj_width(module, po) == width && sn_obj_fanin(module, po, 0) == read;
|
||||
}
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, valid,
|
||||
"memory primitive behavior does not match its encoded interface");
|
||||
}
|
||||
|
||||
static inline void sn_check_primitive(sn_check_ctx_t* ctx, const sn_module_t* module)
|
||||
{
|
||||
const char* name = sn_check_module_name(module);
|
||||
size_t pi_count = module->type_objects[SN_PI].size;
|
||||
size_t po_count = module->type_objects[SN_PO].size;
|
||||
if (strncmp(name, "__sn_CARRY", 10) == 0)
|
||||
bool carry = strncmp(name, "__sn_CARRY", 10) == 0;
|
||||
bool dsp = strncmp(name, "__sn_DSP", 8) == 0;
|
||||
bool memory = strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0;
|
||||
if (!carry && !dsp && !memory)
|
||||
return;
|
||||
if (sn_module_is_blackbox(module))
|
||||
{
|
||||
const uint32_t pi_widths[] = {1, 1, 4, 4};
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == 4 && po_count == 2,
|
||||
"carry primitive interface must have 4 inputs and 2 outputs");
|
||||
for (size_t i = 0; i < pi_count && i < 4; i++)
|
||||
{
|
||||
sn_obj_id_t pi = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i);
|
||||
SN_CHECK(ctx, module, pi, (sn_vec_at(uint32_t, &module->width_signed, pi) >> 1) == pi_widths[i],
|
||||
"carry primitive input %zu has the wrong width", i);
|
||||
}
|
||||
for (size_t i = 0; i < po_count; i++)
|
||||
{
|
||||
sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i);
|
||||
SN_CHECK(ctx, module, po, (sn_vec_at(uint32_t, &module->width_signed, po) >> 1) == 4,
|
||||
"carry primitive output %zu has the wrong width", i);
|
||||
}
|
||||
}
|
||||
else if (strncmp(name, "__sn_DSP", 8) == 0)
|
||||
{
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == 2 && po_count == 1,
|
||||
"DSP primitive interface must have 2 inputs and 1 output");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, module->type_objects[SN_MUL].size == 1,
|
||||
"DSP primitive behavioral wrapper must contain one multiplier");
|
||||
}
|
||||
else if (strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0)
|
||||
{
|
||||
size_t reads = module->type_objects[SN_MEM_READ].size;
|
||||
size_t writes = module->type_objects[SN_MEM_WRITE].size;
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, module->type_objects[SN_MEM_OUT].size == 1,
|
||||
"memory primitive wrapper must contain one memory");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, reads >= 1 && reads <= 2 && writes >= 1 && writes <= 2,
|
||||
"memory primitive wrapper must contain one or two read and write ports");
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, po_count == reads,
|
||||
"memory primitive output count %zu differs from read-port count %zu", po_count, reads);
|
||||
SN_CHECK(ctx, module, SN_INVALID_ID, false,
|
||||
"reserved __sn_ technology primitives must have a validated behavioral body");
|
||||
return;
|
||||
}
|
||||
if (carry)
|
||||
sn_check_carry_primitive(ctx, module);
|
||||
else if (dsp)
|
||||
sn_check_dsp_primitive(ctx, module, name);
|
||||
else
|
||||
sn_check_memory_primitive(ctx, module, name);
|
||||
}
|
||||
|
||||
typedef struct sn_check_hierarchy_frame_t
|
||||
|
|
@ -1058,6 +1255,9 @@ static inline bool sn_design_check(const sn_design_t* design, FILE* out, bool ve
|
|||
{
|
||||
const char* name = sn_vec_at(char*, &design->names.names, i);
|
||||
SN_CHECK(&ctx, NULL, SN_INVALID_ID, name != NULL, "name %zu has a null string", i);
|
||||
if (name)
|
||||
SN_CHECK(&ctx, NULL, SN_INVALID_ID, sn_check_name_is_emittable(name),
|
||||
"name %zu cannot be emitted losslessly as a Verilog identifier", i);
|
||||
}
|
||||
uint8_t* name_seen = design->names.names.size ? (uint8_t*)calloc(design->names.names.size, 1) : NULL;
|
||||
SN_CHECK(&ctx, NULL, SN_INVALID_ID, design->names.names.size == 0 || name_seen != NULL,
|
||||
|
|
@ -1169,8 +1369,8 @@ static inline sn_design_t* sn_design_read_binary_checked(FILE* in, FILE* errors)
|
|||
{
|
||||
FILE* out = errors ? errors : stderr;
|
||||
if (status == SN_BINARY_READ_VERSION)
|
||||
fprintf(out, "Cannot read SN binary format version %u; this build requires version %u.\n", version,
|
||||
SN_BINARY_FORMAT_VERSION);
|
||||
fprintf(out, "Cannot read SN binary format version %u; this build supports versions %u through %u.\n",
|
||||
version, SN_BINARY_MIN_READ_VERSION, SN_BINARY_FORMAT_VERSION);
|
||||
else if (status == SN_BINARY_READ_MAGIC)
|
||||
fprintf(out, "Input is not an SN binary file.\n");
|
||||
else if (status == SN_BINARY_READ_LAYOUT)
|
||||
|
|
|
|||
|
|
@ -291,6 +291,9 @@ static int Sn_RunProcess( char ** ppArgs )
|
|||
if ( Child == 0 )
|
||||
{
|
||||
execvp( ppArgs[0], ppArgs );
|
||||
// execvp() returns only on failure. Release the child copy so memory checkers do not report it as leaked;
|
||||
// the parent's copy is unaffected and is freed by the caller.
|
||||
ABC_FREE( ppArgs );
|
||||
_exit( 127 );
|
||||
}
|
||||
if ( waitpid(Child, &Status, 0) != Child )
|
||||
|
|
@ -404,8 +407,8 @@ static int Sn_DistribEntryCompare( const void * pLeft, const void * pRight )
|
|||
return (int)pL->Signs - (int)pR->Signs;
|
||||
}
|
||||
|
||||
static void Sn_DistribAdd( Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, size_t * pnCap,
|
||||
const sn_module_t * pModule, sn_obj_id_t Obj, uint64_t Mult )
|
||||
static int Sn_DistribAdd( Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, size_t * pnCap,
|
||||
const sn_module_t * pModule, sn_obj_id_t Obj, uint64_t Mult )
|
||||
{
|
||||
Sn_DistribEntry_t Entry;
|
||||
uint32_t nFanins = sn_obj_fanin_count( pModule, Obj );
|
||||
|
|
@ -431,34 +434,44 @@ static void Sn_DistribAdd( Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, s
|
|||
if ( pOld->OutWidth == Entry.OutWidth && pOld->In0Width == Entry.In0Width &&
|
||||
pOld->In1Width == Entry.In1Width && pOld->FaninNum == Entry.FaninNum && pOld->Signs == Entry.Signs )
|
||||
{
|
||||
assert( UINT64_MAX - pOld->Occur >= Mult );
|
||||
if ( UINT64_MAX - pOld->Occur < Mult )
|
||||
return 0;
|
||||
pOld->Occur += Mult;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if ( *pnEntries == *pnCap )
|
||||
{
|
||||
Sn_DistribEntry_t * pNew;
|
||||
if ( *pnCap > SIZE_MAX / 2 / sizeof(Sn_DistribEntry_t) )
|
||||
return 0;
|
||||
*pnCap = *pnCap ? 2 * *pnCap : 8;
|
||||
*ppEntries = ABC_REALLOC( Sn_DistribEntry_t, *ppEntries, *pnCap );
|
||||
assert( *ppEntries != NULL );
|
||||
pNew = ABC_REALLOC( Sn_DistribEntry_t, *ppEntries, *pnCap );
|
||||
if ( pNew == NULL )
|
||||
return 0;
|
||||
*ppEntries = pNew;
|
||||
}
|
||||
Entry.Occur = Mult;
|
||||
(*ppEntries)[(*pnEntries)++] = Entry;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Sn_ModuleCollectDistrib( const sn_design_t * pDesign, sn_module_id_t ModuleId, uint64_t Mult,
|
||||
Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, size_t * pnCaps,
|
||||
uint64_t * pTypeCounts )
|
||||
static int Sn_ModuleCollectDistrib( const sn_design_t * pDesign, sn_module_id_t ModuleId, uint64_t Mult,
|
||||
Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, size_t * pnCaps,
|
||||
uint64_t * pTypeCounts )
|
||||
{
|
||||
const sn_module_t * pModule = sn_design_get_module_const( pDesign, ModuleId );
|
||||
sn_obj_id_t Obj;
|
||||
for ( Obj = 0; Obj < pModule->obj_types.size; Obj++ )
|
||||
{
|
||||
sn_obj_type_t Type = sn_obj_type( pModule, Obj );
|
||||
assert( UINT64_MAX - pTypeCounts[Type] >= Mult );
|
||||
if ( UINT64_MAX - pTypeCounts[Type] < Mult )
|
||||
return 0;
|
||||
pTypeCounts[Type] += Mult;
|
||||
Sn_DistribAdd( ppEntries + Type, pnEntries + Type, pnCaps + Type, pModule, Obj, Mult );
|
||||
if ( !Sn_DistribAdd(ppEntries + Type, pnEntries + Type, pnCaps + Type, pModule, Obj, Mult) )
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct Sn_DistribFrame_t_
|
||||
|
|
@ -467,19 +480,25 @@ typedef struct Sn_DistribFrame_t_
|
|||
size_t NextInst;
|
||||
} Sn_DistribFrame_t;
|
||||
|
||||
static void Sn_DesignPrintDistrib( FILE * pOut, const sn_design_t * pDesign, sn_module_id_t Top )
|
||||
// Returns the number of reachable occurrences of each module definition under Top. The hierarchy is a DAG, so a
|
||||
// reverse-postorder propagation accounts for repeated insts without expanding every hierarchical occurrence.
|
||||
static uint64_t * Sn_DesignCountModuleOccurrences( const sn_design_t * pDesign, sn_module_id_t Top )
|
||||
{
|
||||
Sn_DistribEntry_t * pEntries[SN_OBJ_TYPE_COUNT] = { NULL };
|
||||
size_t nEntries[SN_OBJ_TYPE_COUNT] = { 0 };
|
||||
size_t nCaps[SN_OBJ_TYPE_COUNT] = { 0 };
|
||||
uint64_t TypeCounts[SN_OBJ_TYPE_COUNT] = { 0 };
|
||||
unsigned char * pStates = ABC_CALLOC( unsigned char, pDesign->modules.size );
|
||||
uint64_t * pMults = ABC_CALLOC( uint64_t, pDesign->modules.size );
|
||||
unsigned char * pStates;
|
||||
uint64_t * pMults;
|
||||
sn_vec_t Stack, Postorder;
|
||||
Sn_DistribFrame_t * pFrame;
|
||||
size_t i;
|
||||
int Type;
|
||||
assert( pStates != NULL && pMults != NULL );
|
||||
if ( pDesign == NULL || Top >= pDesign->modules.size )
|
||||
return NULL;
|
||||
pStates = ABC_CALLOC( unsigned char, pDesign->modules.size );
|
||||
pMults = ABC_CALLOC( uint64_t, pDesign->modules.size );
|
||||
if ( pStates == NULL || pMults == NULL )
|
||||
{
|
||||
ABC_FREE( pStates );
|
||||
ABC_FREE( pMults );
|
||||
return NULL;
|
||||
}
|
||||
sn_vec_init( &Stack );
|
||||
sn_vec_init( &Postorder );
|
||||
pStates[Top] = 1;
|
||||
|
|
@ -494,7 +513,8 @@ static void Sn_DesignPrintDistrib( FILE * pOut, const sn_design_t * pDesign, sn_
|
|||
if ( pFrame->NextInst < pModule->inst_modules.size )
|
||||
{
|
||||
sn_module_id_t Child = sn_vec_at( sn_module_id_t, &pModule->inst_modules, pFrame->NextInst++ );
|
||||
assert( pStates[Child] != 1 );
|
||||
if ( Child >= pDesign->modules.size || pStates[Child] == 1 )
|
||||
goto fail;
|
||||
if ( pStates[Child] == 0 )
|
||||
{
|
||||
pStates[Child] = 1;
|
||||
|
|
@ -516,14 +536,117 @@ static void Sn_DesignPrintDistrib( FILE * pOut, const sn_design_t * pDesign, sn_
|
|||
uint64_t Mult = pMults[Module];
|
||||
if ( Mult == 0 )
|
||||
continue;
|
||||
Sn_ModuleCollectDistrib( pDesign, Module, Mult, pEntries, nEntries, nCaps, TypeCounts );
|
||||
for ( size_t k = 0; k < pModule->inst_modules.size; k++ )
|
||||
{
|
||||
sn_module_id_t Child = sn_vec_at( sn_module_id_t, &pModule->inst_modules, k );
|
||||
assert( UINT64_MAX - pMults[Child] >= Mult );
|
||||
if ( Child >= pDesign->modules.size || UINT64_MAX - pMults[Child] < Mult )
|
||||
goto fail;
|
||||
pMults[Child] += Mult;
|
||||
}
|
||||
}
|
||||
sn_vec_destroy( &Postorder );
|
||||
sn_vec_destroy( &Stack );
|
||||
ABC_FREE( pStates );
|
||||
return pMults;
|
||||
|
||||
fail:
|
||||
sn_vec_destroy( &Postorder );
|
||||
sn_vec_destroy( &Stack );
|
||||
ABC_FREE( pStates );
|
||||
ABC_FREE( pMults );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int Sn_ModulePortBits( const sn_module_t * pModule, sn_obj_type_t Type, uint64_t * pBits )
|
||||
{
|
||||
uint64_t Bits = 0;
|
||||
size_t i;
|
||||
assert( Type == SN_PI || Type == SN_PO );
|
||||
for ( i = 0; i < pModule->type_objects[Type].size; i++ )
|
||||
{
|
||||
uint32_t Width = sn_obj_width( pModule, sn_vec_at(sn_obj_id_t, &pModule->type_objects[Type], i) );
|
||||
if ( UINT64_MAX - Bits < Width )
|
||||
return 0;
|
||||
Bits += Width;
|
||||
}
|
||||
*pBits = Bits;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Sn_DesignPrintBlackboxes( FILE * pOut, const sn_design_t * pDesign, sn_module_id_t Top )
|
||||
{
|
||||
const sn_module_t * pTop = sn_design_get_module_const( pDesign, Top );
|
||||
uint64_t * pMults = Sn_DesignCountModuleOccurrences( pDesign, Top );
|
||||
uint64_t nOccurrences = 0, nAigInputs = 0, nAigOutputs = 0;
|
||||
size_t nTypes = 0, i;
|
||||
if ( pMults == NULL )
|
||||
{
|
||||
fprintf( pOut, "Cannot count black-box occurrences: hierarchy is invalid or the count overflows.\n" );
|
||||
return;
|
||||
}
|
||||
fprintf( pOut, "Black boxes reachable from \"%s\":\n", sn_name_get(&pDesign->names, pTop->name) );
|
||||
fprintf( pOut, "Module occurrences PI ports/bits PO ports/bits "
|
||||
"AIG inputs AIG outputs\n" );
|
||||
for ( i = 0; i < pDesign->modules.size; i++ )
|
||||
{
|
||||
const sn_module_t * pModule = sn_design_get_module_const( pDesign, (sn_module_id_t)i );
|
||||
uint64_t Mult = pMults[i], PiBits, PoBits, AigInputs, AigOutputs;
|
||||
if ( Mult == 0 || !sn_module_is_blackbox(pModule) )
|
||||
continue;
|
||||
if ( !Sn_ModulePortBits(pModule, SN_PI, &PiBits) || !Sn_ModulePortBits(pModule, SN_PO, &PoBits) ||
|
||||
(PoBits != 0 && Mult > UINT64_MAX / PoBits) ||
|
||||
(PiBits != 0 && Mult > UINT64_MAX / PiBits) )
|
||||
goto overflow;
|
||||
AigInputs = Mult * PoBits;
|
||||
AigOutputs = Mult * PiBits;
|
||||
if ( UINT64_MAX - nOccurrences < Mult || UINT64_MAX - nAigInputs < AigInputs ||
|
||||
UINT64_MAX - nAigOutputs < AigOutputs )
|
||||
goto overflow;
|
||||
nTypes++;
|
||||
nOccurrences += Mult;
|
||||
nAigInputs += AigInputs;
|
||||
nAigOutputs += AigOutputs;
|
||||
fprintf( pOut, "%-32s %10llu %6zu/%-6llu %6zu/%-6llu %10llu %11llu\n",
|
||||
sn_name_get(&pDesign->names, pModule->name), (unsigned long long)Mult,
|
||||
pModule->type_objects[SN_PI].size, (unsigned long long)PiBits,
|
||||
pModule->type_objects[SN_PO].size, (unsigned long long)PoBits,
|
||||
(unsigned long long)AigInputs, (unsigned long long)AigOutputs );
|
||||
}
|
||||
fprintf( pOut, "Black-box totals: types = %zu occurrences = %llu AIG inputs = %llu AIG outputs = %llu\n",
|
||||
nTypes, (unsigned long long)nOccurrences, (unsigned long long)nAigInputs,
|
||||
(unsigned long long)nAigOutputs );
|
||||
ABC_FREE( pMults );
|
||||
return;
|
||||
|
||||
overflow:
|
||||
fprintf( pOut, "Cannot print black-box statistics: a bit or occurrence total overflows 64 bits.\n" );
|
||||
ABC_FREE( pMults );
|
||||
}
|
||||
|
||||
static void Sn_DesignPrintDistrib( FILE * pOut, const sn_design_t * pDesign, sn_module_id_t Top )
|
||||
{
|
||||
Sn_DistribEntry_t * pEntries[SN_OBJ_TYPE_COUNT] = { NULL };
|
||||
size_t nEntries[SN_OBJ_TYPE_COUNT] = { 0 };
|
||||
size_t nCaps[SN_OBJ_TYPE_COUNT] = { 0 };
|
||||
uint64_t TypeCounts[SN_OBJ_TYPE_COUNT] = { 0 };
|
||||
uint64_t * pMults = Sn_DesignCountModuleOccurrences( pDesign, Top );
|
||||
size_t i;
|
||||
int Type;
|
||||
if ( pMults == NULL )
|
||||
{
|
||||
fprintf( pOut, "Cannot print object distribution: hierarchy is invalid or the occurrence count overflows.\n" );
|
||||
return;
|
||||
}
|
||||
for ( i = 0; i < pDesign->modules.size; i++ )
|
||||
if ( pMults[i] && !Sn_ModuleCollectDistrib(pDesign, (sn_module_id_t)i, pMults[i], pEntries,
|
||||
nEntries, nCaps, TypeCounts) )
|
||||
{
|
||||
fprintf( pOut, "Cannot print object distribution: a count overflows or allocation failed.\n" );
|
||||
for ( Type = 0; Type < SN_OBJ_TYPE_COUNT; Type++ )
|
||||
ABC_FREE( pEntries[Type] );
|
||||
ABC_FREE( pMults );
|
||||
return;
|
||||
}
|
||||
fprintf( pOut, "ID : name occurrence (occurrence)<output_width>=<input0_width>.<input1_width> ...\n" );
|
||||
for ( Type = 0; Type < SN_OBJ_TYPE_COUNT; Type++ )
|
||||
{
|
||||
|
|
@ -551,10 +674,7 @@ static void Sn_DesignPrintDistrib( FILE * pOut, const sn_design_t * pDesign, sn_
|
|||
fprintf( pOut, "\n" );
|
||||
ABC_FREE( pEntries[Type] );
|
||||
}
|
||||
sn_vec_destroy( &Postorder );
|
||||
sn_vec_destroy( &Stack );
|
||||
ABC_FREE( pMults );
|
||||
ABC_FREE( pStates );
|
||||
}
|
||||
|
||||
void Sn_Init( Abc_Frame_t * pAbc )
|
||||
|
|
@ -997,7 +1117,7 @@ static int Sn_CommandOptMux( Abc_Frame_t * pAbc, int argc, char ** argv )
|
|||
if ( !Sn_CommandCheckDesign(pAbc) )
|
||||
return 1;
|
||||
pCurrent = Sn_AbcGetMan( pAbc );
|
||||
if ( !sn_design_check(pCurrent->pDesign, pAbc->Err, 0) )
|
||||
if ( !sn_design_check(pCurrent->pDesign, Abc_FrameReadErr(pAbc), 0) )
|
||||
{
|
||||
Abc_Print( -1, "Cannot @opt_mux: the current SN design is inconsistent.\n" );
|
||||
return 1;
|
||||
|
|
@ -1009,7 +1129,7 @@ static int Sn_CommandOptMux( Abc_Frame_t * pAbc, int argc, char ** argv )
|
|||
return 1;
|
||||
}
|
||||
Stats = sn_design_share( p->pDesign, Options );
|
||||
if ( !sn_design_check(p->pDesign, pAbc->Err, 0) )
|
||||
if ( !sn_design_check(p->pDesign, Abc_FrameReadErr(pAbc), 0) )
|
||||
{
|
||||
Abc_Print( -1, "Cannot @opt_mux: the transformed SN design is inconsistent.\n" );
|
||||
Sn_ManFree( p );
|
||||
|
|
@ -1393,8 +1513,9 @@ static int Sn_CommandPut( Abc_Frame_t * pAbc, int argc, char ** argv )
|
|||
}
|
||||
pNtk = Abc_NtkFromCellMappedGia( pGia, 0 );
|
||||
vMapping = Abc_NtkWriteMiniMapping( pNtk );
|
||||
Top = sn_design_add_gate_module( p->pDesign, p->BlastModule, Vec_IntArray(vMapping), &p->Boundary,
|
||||
Sn_GateIdResolver, pLibrary, "__sn_gate_mapped" );
|
||||
Top = sn_design_add_gate_module( p->pDesign, p->BlastModule, Vec_IntArray(vMapping),
|
||||
(size_t)Vec_IntSize(vMapping), &p->Boundary, Sn_GateIdResolver,
|
||||
pLibrary, "__sn_gate_mapped" );
|
||||
if ( Top == SN_INVALID_ID )
|
||||
{
|
||||
Abc_Print( -1, "Cannot @put: the current genlib does not contain every gate used by the mapped GIA.\n" );
|
||||
|
|
@ -1826,7 +1947,6 @@ usage:
|
|||
static int Sn_CommandWrite( Abc_Frame_t * pAbc, int argc, char ** argv )
|
||||
{
|
||||
Sn_Man_t * p;
|
||||
const sn_module_t * pTop;
|
||||
char * pFileName;
|
||||
FILE * pFile;
|
||||
int c, Status = 0;
|
||||
|
|
@ -1838,7 +1958,6 @@ static int Sn_CommandWrite( Abc_Frame_t * pAbc, int argc, char ** argv )
|
|||
if ( !Sn_CommandCheckDesign(pAbc) )
|
||||
return 1;
|
||||
p = Sn_AbcGetMan( pAbc );
|
||||
pTop = sn_design_get_module_const( p->pDesign, p->Top );
|
||||
pFileName = argv[globalUtilOptind];
|
||||
if ( Sn_FileHasSuffix(pFileName, ".sn") )
|
||||
{
|
||||
|
|
@ -1887,17 +2006,27 @@ usage:
|
|||
static int Sn_CommandPs( Abc_Frame_t * pAbc, int argc, char ** argv )
|
||||
{
|
||||
Sn_Man_t * p;
|
||||
const sn_module_t * pTop;
|
||||
const sn_module_t * pReport;
|
||||
sn_design_mem_usage_t Mem;
|
||||
char * pModuleName = NULL;
|
||||
sn_module_id_t Report;
|
||||
size_t nObjects = 0;
|
||||
size_t i;
|
||||
int c, fDistrib = 0, fVerbose = 0, fMem, fLut, fGate;
|
||||
char UsedMemory[32], AllocatedMemory[32];
|
||||
Extra_UtilGetoptReset();
|
||||
while ( (c = Extra_UtilGetopt(argc, argv, "dvh")) != EOF )
|
||||
while ( (c = Extra_UtilGetopt(argc, argv, "Mdvh")) != EOF )
|
||||
{
|
||||
switch ( c )
|
||||
{
|
||||
case 'M':
|
||||
if ( globalUtilOptind >= argc )
|
||||
{
|
||||
Abc_Print( -1, "Command line switch \"-M\" should be followed by a module name.\n" );
|
||||
goto usage;
|
||||
}
|
||||
pModuleName = argv[globalUtilOptind++];
|
||||
break;
|
||||
case 'd':
|
||||
fDistrib ^= 1;
|
||||
break;
|
||||
|
|
@ -1914,7 +2043,13 @@ static int Sn_CommandPs( Abc_Frame_t * pAbc, int argc, char ** argv )
|
|||
if ( !Sn_CommandCheckDesign(pAbc) )
|
||||
return 1;
|
||||
p = Sn_AbcGetMan( pAbc );
|
||||
pTop = sn_design_get_module_const( p->pDesign, p->Top );
|
||||
Report = pModuleName ? sn_design_find_module( p->pDesign, pModuleName ) : p->Top;
|
||||
if ( Report == SN_INVALID_ID )
|
||||
{
|
||||
Abc_Print( -1, "Cannot find module \"%s\" in the current SN design.\n", pModuleName );
|
||||
return 1;
|
||||
}
|
||||
pReport = sn_design_get_module_const( p->pDesign, Report );
|
||||
for ( i = 0; i < p->pDesign->modules.size; i++ )
|
||||
nObjects += sn_design_get_module_const( p->pDesign, (sn_module_id_t)i )->obj_types.size;
|
||||
sn_design_get_mem_usage( p->pDesign, &Mem );
|
||||
|
|
@ -1925,27 +2060,35 @@ static int Sn_CommandPs( Abc_Frame_t * pAbc, int argc, char ** argv )
|
|||
Sn_FormatMemory( Mem.total.allocated_bytes, AllocatedMemory, sizeof(AllocatedMemory) );
|
||||
fprintf( pAbc->Out, "SN design: top = %s modules = %zu objects = %zu memory = %s/%s "
|
||||
"(used/allocated)\n",
|
||||
sn_name_get(&p->pDesign->names, pTop->name), p->pDesign->modules.size, nObjects,
|
||||
sn_name_get(&p->pDesign->names, pReport->name), p->pDesign->modules.size, nObjects,
|
||||
UsedMemory, AllocatedMemory );
|
||||
Sn_ModulePrintStats( pAbc->Out, pTop, fMem, fLut, fGate );
|
||||
if ( fVerbose )
|
||||
if ( pModuleName )
|
||||
Sn_ModulePrintStats( pAbc->Out, pReport, fMem, fLut, fGate );
|
||||
else
|
||||
{
|
||||
fprintf( pAbc->Out, "Hierarchy:\n" );
|
||||
sn_design_print_hierarchy( pAbc->Out, p->pDesign, p->Top );
|
||||
fprintf( pAbc->Out, "Modules:\n" );
|
||||
for ( i = 0; i < p->pDesign->modules.size; i++ )
|
||||
Sn_ModulePrintStats( pAbc->Out, sn_design_get_module_const(p->pDesign, (sn_module_id_t)i),
|
||||
fMem, fLut, fGate );
|
||||
}
|
||||
if ( fVerbose )
|
||||
{
|
||||
fprintf( pAbc->Out, "Hierarchy:\n" );
|
||||
sn_design_print_hierarchy( pAbc->Out, p->pDesign, Report );
|
||||
}
|
||||
if ( fDistrib )
|
||||
Sn_DesignPrintDistrib( pAbc->Out, p->pDesign, p->Top );
|
||||
{
|
||||
Sn_DesignPrintBlackboxes( pAbc->Out, p->pDesign, Report );
|
||||
Sn_DesignPrintDistrib( pAbc->Out, p->pDesign, Report );
|
||||
}
|
||||
return 0;
|
||||
|
||||
usage:
|
||||
Abc_Print( -2, "usage: @ps [-dvh]\n" );
|
||||
Abc_Print( -2, "usage: @ps [-M module] [-dvh]\n" );
|
||||
Abc_Print( -2, "\t prints statistics for the current SN design\n" );
|
||||
Abc_Print( -2, "\t-M name : select one module and its hierarchy [default = print all module definitions]\n" );
|
||||
Abc_Print( -2, "\t-d : print object-type and width distribution for the elaborated hierarchy\n" );
|
||||
Abc_Print( -2, "\t-v : print hierarchy and per-module statistics\n" );
|
||||
Abc_Print( -2, "\t-v : print the hierarchy rooted at the selected module\n" );
|
||||
Abc_Print( -2, "\t-h : print the command usage\n" );
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ static inline sn_module_id_t sn_design_add_aig_module(sn_design_t* design, sn_mo
|
|||
}
|
||||
assert(co_index <= boundary->cos.size);
|
||||
sn_boundary_regs_finish(®s, drivers);
|
||||
result = sn_design_get_module(design, result_id);
|
||||
|
||||
free(drivers);
|
||||
free(objects);
|
||||
|
|
|
|||
|
|
@ -31,38 +31,86 @@ typedef uint32_t (*sn_gate_id_resolver_t)(void* context, const char* gate_name);
|
|||
// afterward in topological order. Gate names stored at the end of the array are resolved into the current library's
|
||||
// stable gate IDs; the name is also retained as the SN object name for structural Verilog emission.
|
||||
static inline sn_module_id_t sn_design_add_gate_module(sn_design_t* design, sn_module_id_t source_top_id,
|
||||
const int* mapping, const sn_blast_boundary_t* boundary,
|
||||
const int* mapping, size_t mapping_count,
|
||||
const sn_blast_boundary_t* boundary,
|
||||
sn_gate_id_resolver_t resolver, void* resolver_context,
|
||||
const char* module_name)
|
||||
{
|
||||
assert(design && source_top_id < design->modules.size && mapping && boundary && resolver && module_name);
|
||||
if (mapping_count < 4 || mapping[0] < 0 || mapping[1] < 0 || mapping[2] < 0 || mapping[3] < 0)
|
||||
return SN_INVALID_ID;
|
||||
uint32_t ci_count = (uint32_t)mapping[0];
|
||||
uint32_t co_count = (uint32_t)mapping[1];
|
||||
uint32_t node_count = (uint32_t)mapping[2];
|
||||
uint32_t reg_count = (uint32_t)mapping[3];
|
||||
assert(reg_count == 0 && ci_count == boundary->cis.size && co_count == boundary->cos.size);
|
||||
if (reg_count != 0 || ci_count != boundary->cis.size || co_count != boundary->cos.size ||
|
||||
node_count > UINT32_MAX - ci_count)
|
||||
return SN_INVALID_ID;
|
||||
|
||||
// Resolve all gate names before mutating the design. A changed genlib can otherwise leave a partially constructed
|
||||
// module behind or turn a user-level @put error into an assertion failure.
|
||||
uint32_t position = 4;
|
||||
for (uint32_t i = 0; i < node_count; i++)
|
||||
// Validate the complete structural prefix and resolve all bounded gate-name strings before mutating the design.
|
||||
// A changed genlib or malformed mini-mapping can otherwise leave a partially constructed module behind.
|
||||
size_t position = 4;
|
||||
uint32_t* fanin_counts = node_count ? (uint32_t*)malloc(sizeof(uint32_t) * node_count) : NULL;
|
||||
const uint32_t** fanin_indices =
|
||||
node_count ? (const uint32_t**)malloc(sizeof(uint32_t*) * node_count) : NULL;
|
||||
uint32_t* gate_ids = node_count ? (uint32_t*)malloc(sizeof(uint32_t) * node_count) : NULL;
|
||||
bool valid = true;
|
||||
assert((fanin_counts && fanin_indices && gate_ids) || node_count == 0);
|
||||
for (uint32_t i = 0; valid && i < node_count; i++)
|
||||
{
|
||||
if (position >= mapping_count || mapping[position] < 0)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
uint32_t count = (uint32_t)mapping[position++];
|
||||
if (count > mapping_count - position)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
fanin_counts[i] = count;
|
||||
fanin_indices[i] = (const uint32_t*)(mapping + position);
|
||||
for (uint32_t k = 0; k < count; k++)
|
||||
if (mapping[position + k] < 0 || (uint32_t)mapping[position + k] >= ci_count + i)
|
||||
valid = false;
|
||||
position += count;
|
||||
}
|
||||
position += co_count;
|
||||
const char* gate_name = (const char*)(mapping + position);
|
||||
uint32_t* gate_ids = node_count ? (uint32_t*)malloc(sizeof(uint32_t) * node_count) : NULL;
|
||||
assert(gate_ids || node_count == 0);
|
||||
for (uint32_t i = 0; i < node_count; i++)
|
||||
if (valid && co_count > mapping_count - position)
|
||||
valid = false;
|
||||
const uint32_t* output_indices = valid ? (const uint32_t*)(mapping + position) : NULL;
|
||||
for (uint32_t i = 0; valid && i < co_count; i++)
|
||||
if (mapping[position + i] < 0 || (uint32_t)mapping[position + i] >= ci_count + node_count)
|
||||
valid = false;
|
||||
if (valid)
|
||||
position += co_count;
|
||||
const char* gate_names = valid ? (const char*)(mapping + position) : NULL;
|
||||
const char* gate_name = gate_names;
|
||||
size_t name_bytes = valid ? (mapping_count - position) * sizeof(int) : 0;
|
||||
for (uint32_t i = 0; valid && i < node_count; i++)
|
||||
{
|
||||
const char* end = (const char*)memchr(gate_name, '\0', name_bytes);
|
||||
if (!end || end == gate_name)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
gate_ids[i] = resolver(resolver_context, gate_name);
|
||||
if (gate_ids[i] == SN_INVALID_ID)
|
||||
{
|
||||
free(gate_ids);
|
||||
return SN_INVALID_ID;
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
gate_name += strlen(gate_name) + 1;
|
||||
size_t length = (size_t)(end - gate_name) + 1;
|
||||
gate_name += length;
|
||||
name_bytes -= length;
|
||||
}
|
||||
if (!valid)
|
||||
{
|
||||
free(gate_ids);
|
||||
free(fanin_indices);
|
||||
free(fanin_counts);
|
||||
return SN_INVALID_ID;
|
||||
}
|
||||
|
||||
const sn_module_t* source = sn_design_get_module_const(design, source_top_id);
|
||||
|
|
@ -103,19 +151,7 @@ static inline sn_module_id_t sn_design_add_gate_module(sn_design_t* design, sn_m
|
|||
assert(false);
|
||||
}
|
||||
|
||||
position = 4;
|
||||
uint32_t* fanin_counts = (uint32_t*)malloc(sizeof(uint32_t) * node_count);
|
||||
const uint32_t** fanin_indices = (const uint32_t**)malloc(sizeof(uint32_t*) * node_count);
|
||||
assert((fanin_counts && fanin_indices) || node_count == 0);
|
||||
for (uint32_t i = 0; i < node_count; i++)
|
||||
{
|
||||
fanin_counts[i] = (uint32_t)mapping[position++];
|
||||
fanin_indices[i] = (const uint32_t*)(mapping + position);
|
||||
position += fanin_counts[i];
|
||||
}
|
||||
const uint32_t* output_indices = (const uint32_t*)(mapping + position);
|
||||
position += co_count;
|
||||
gate_name = (const char*)(mapping + position);
|
||||
gate_name = gate_names;
|
||||
|
||||
for (uint32_t i = 0; i < node_count; i++)
|
||||
{
|
||||
|
|
@ -160,6 +196,7 @@ static inline sn_module_id_t sn_design_add_gate_module(sn_design_t* design, sn_m
|
|||
co_drivers[i] = objects[output_indices[i]];
|
||||
}
|
||||
sn_boundary_regs_finish(®s, co_drivers);
|
||||
result = sn_design_get_module(design, result_id);
|
||||
|
||||
free(co_drivers);
|
||||
free(fanin_indices);
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ static inline sn_module_id_t sn_design_add_lut_module(sn_design_t* design, sn_mo
|
|||
sn_module_add_po(result, width, sn_obj_is_signed(source, old_po), sn_obj_name(source, old_po), driver);
|
||||
}
|
||||
sn_boundary_regs_finish(®s, co_drivers);
|
||||
result = sn_design_get_module(design, result_id);
|
||||
|
||||
free(co_drivers);
|
||||
free(mini_objects);
|
||||
|
|
|
|||
|
|
@ -505,6 +505,8 @@ static inline bool sn_share_reg_mux_tree(sn_module_t* target, const sn_module_t*
|
|||
sn_vec_t stack, steps, paths, terms, term_hashes, term_links, data_terms, controls;
|
||||
sn_obj_id_t hold = SN_INVALID_ID, data = SN_INVALID_ID, new_reg = SN_INVALID_ID;
|
||||
uint32_t* term_buckets = NULL;
|
||||
uint32_t* group_heads = NULL;
|
||||
uint32_t* path_links = NULL;
|
||||
uint32_t term_bucket_count = 0;
|
||||
size_t hold_index = 0;
|
||||
bool exclusive = true, overflow = false;
|
||||
|
|
@ -553,6 +555,18 @@ static inline bool sn_share_reg_mux_tree(sn_module_t* target, const sn_module_t*
|
|||
}
|
||||
if (paths.size <= terms.size || paths.size - terms.size < options.min_saved_paths || paths.size < 2 * terms.size)
|
||||
goto unchanged;
|
||||
group_heads = (uint32_t*)malloc(terms.size * sizeof(uint32_t));
|
||||
path_links = (uint32_t*)malloc(paths.size * sizeof(uint32_t));
|
||||
assert(group_heads && path_links);
|
||||
for (size_t k = 0; k < terms.size; k++)
|
||||
group_heads[k] = SN_INVALID_ID;
|
||||
for (size_t i = 0; i < paths.size; i++)
|
||||
{
|
||||
uint32_t group = sn_vec_at(sn_share_path_t, &paths, i).group;
|
||||
assert(group < terms.size);
|
||||
path_links[i] = group_heads[group];
|
||||
group_heads[group] = (uint32_t)i;
|
||||
}
|
||||
|
||||
hold = sn_share_strip_value(source, old_reg);
|
||||
hold_index = terms.size;
|
||||
|
|
@ -566,11 +580,10 @@ static inline bool sn_share_reg_mux_tree(sn_module_t* target, const sn_module_t*
|
|||
continue;
|
||||
sn_vec_t cubes;
|
||||
sn_vec_init(&cubes);
|
||||
for (size_t i = 0; i < paths.size; i++)
|
||||
for (uint32_t i = group_heads[k]; i != SN_INVALID_ID; i = path_links[i])
|
||||
{
|
||||
sn_share_path_t* path = &sn_vec_at(sn_share_path_t, &paths, i);
|
||||
if (path->group == k)
|
||||
*sn_vec_push(sn_obj_id_t, &cubes) = sn_share_path_condition(target, source, path, &steps);
|
||||
*sn_vec_push(sn_obj_id_t, &cubes) = sn_share_path_condition(target, source, path, &steps);
|
||||
}
|
||||
*sn_vec_push(sn_obj_id_t, &controls) =
|
||||
sn_share_or(target, sn_vec_data(sn_obj_id_t, &cubes), (uint32_t)cubes.size);
|
||||
|
|
@ -615,6 +628,8 @@ static inline bool sn_share_reg_mux_tree(sn_module_t* target, const sn_module_t*
|
|||
sn_vec_destroy(&terms);
|
||||
sn_vec_destroy(&term_hashes);
|
||||
sn_vec_destroy(&term_links);
|
||||
free(path_links);
|
||||
free(group_heads);
|
||||
free(term_buckets);
|
||||
return true;
|
||||
|
||||
|
|
@ -625,6 +640,8 @@ unchanged:
|
|||
sn_vec_destroy(&terms);
|
||||
sn_vec_destroy(&term_hashes);
|
||||
sn_vec_destroy(&term_links);
|
||||
free(path_links);
|
||||
free(group_heads);
|
||||
free(term_buckets);
|
||||
sn_vec_destroy(&data_terms);
|
||||
sn_vec_destroy(&controls);
|
||||
|
|
@ -672,6 +689,8 @@ static inline bool sn_share_reg_pmux(sn_module_t* target, const sn_module_t* sou
|
|||
while (bucket_count < 2 * count)
|
||||
bucket_count <<= 1;
|
||||
uint32_t* buckets = (uint32_t*)malloc((size_t)bucket_count * sizeof(uint32_t));
|
||||
uint32_t* member_heads = NULL;
|
||||
uint32_t* member_links = NULL;
|
||||
assert(buckets);
|
||||
memset(buckets, 0xff, (size_t)bucket_count * sizeof(uint32_t));
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
|
|
@ -712,6 +731,18 @@ static inline bool sn_share_reg_pmux(sn_module_t* target, const sn_module_t* sou
|
|||
free(buckets);
|
||||
return false;
|
||||
}
|
||||
member_heads = (uint32_t*)malloc(unique.size * sizeof(uint32_t));
|
||||
member_links = (uint32_t*)malloc(members.size * sizeof(uint32_t));
|
||||
assert(member_heads && member_links);
|
||||
for (size_t k = 0; k < unique.size; k++)
|
||||
member_heads[k] = SN_INVALID_ID;
|
||||
for (size_t j = 0; j < members.size; j++)
|
||||
{
|
||||
uint32_t group = sn_vec_at(uint32_t, &members, j) >> 16;
|
||||
assert(group < unique.size);
|
||||
member_links[j] = member_heads[group];
|
||||
member_heads[group] = (uint32_t)j;
|
||||
}
|
||||
|
||||
sn_obj_id_t new_reg = sn_obj_dup(source, old_reg);
|
||||
sn_obj_id_t new_in = sn_obj_pair_in(target, new_reg);
|
||||
|
|
@ -720,11 +751,10 @@ static inline bool sn_share_reg_pmux(sn_module_t* target, const sn_module_t* sou
|
|||
{
|
||||
sn_vec_t bits;
|
||||
sn_vec_init(&bits);
|
||||
for (size_t j = 0; j < members.size; j++)
|
||||
for (uint32_t j = member_heads[k]; j != SN_INVALID_ID; j = member_links[j])
|
||||
{
|
||||
uint32_t member = sn_vec_at(uint32_t, &members, j);
|
||||
if ((member >> 16) == k)
|
||||
*sn_vec_push(sn_obj_id_t, &bits) = sn_share_select_bit(target, new_select, member & UINT16_MAX);
|
||||
*sn_vec_push(sn_obj_id_t, &bits) = sn_share_select_bit(target, new_select, member & UINT16_MAX);
|
||||
}
|
||||
*sn_vec_push(sn_obj_id_t, &conditions) =
|
||||
sn_share_or(target, sn_vec_data(sn_obj_id_t, &bits), (uint32_t)bits.size);
|
||||
|
|
@ -776,6 +806,8 @@ static inline bool sn_share_reg_pmux(sn_module_t* target, const sn_module_t* sou
|
|||
sn_vec_destroy(&unique_links);
|
||||
sn_vec_destroy(&conditions);
|
||||
sn_vec_destroy(&members);
|
||||
free(member_links);
|
||||
free(member_heads);
|
||||
free(buckets);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,10 +103,22 @@ static inline void sn_pth_process(void** jobs, size_t count, unsigned processes,
|
|||
pool.context = context;
|
||||
pool.function = function;
|
||||
int status = pthread_mutex_init(&pool.mutex, NULL);
|
||||
assert(status == 0);
|
||||
(void)status;
|
||||
if (status != 0)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
function(context, jobs[i]);
|
||||
return;
|
||||
}
|
||||
pthread_t* workers = (pthread_t*)malloc(sizeof(pthread_t) * worker_count);
|
||||
assert(workers);
|
||||
if (!workers)
|
||||
{
|
||||
status = pthread_mutex_destroy(&pool.mutex);
|
||||
assert(status == 0);
|
||||
(void)status;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
function(context, jobs[i]);
|
||||
return;
|
||||
}
|
||||
unsigned created = 0;
|
||||
for (; created < worker_count; created++)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue