mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-15 11:45:41 +00:00
Merge remote-tracking branch 'upstream/main' into silimate
This commit is contained in:
commit
e58125b605
834 changed files with 25281 additions and 8780 deletions
189
kernel/CMakeLists.txt
Normal file
189
kernel/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
yosys_version_file(version.cc.in version.cc)
|
||||
configure_file(yosys_config.h.in yosys_config.h @ONLY)
|
||||
|
||||
set(cellhelp_sources)
|
||||
foreach (library simlib simcells)
|
||||
add_custom_command(
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cellhelp.py ${CMAKE_SOURCE_DIR}/techlibs/common/${library}.v
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${library}_help.inc
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cellhelp.py
|
||||
${CMAKE_SOURCE_DIR}/techlibs/common/${library}.v
|
||||
> ${CMAKE_CURRENT_BINARY_DIR}/${library}_help.inc
|
||||
VERBATIM
|
||||
)
|
||||
list(APPEND cellhelp_sources ${CMAKE_CURRENT_BINARY_DIR}/${library}_help.inc)
|
||||
endforeach()
|
||||
|
||||
yosys_core(kernel
|
||||
binding.cc
|
||||
binding.h
|
||||
bitpattern.h
|
||||
calc.cc
|
||||
cellaigs.cc
|
||||
cellaigs.h
|
||||
celledges.cc
|
||||
celledges.h
|
||||
celltypes.h
|
||||
compute_graph.h
|
||||
consteval.h
|
||||
constids.inc
|
||||
cost.cc
|
||||
cost.h
|
||||
drivertools.cc
|
||||
drivertools.h
|
||||
ff.cc
|
||||
ff.h
|
||||
ffinit.h
|
||||
ffmerge.cc
|
||||
ffmerge.h
|
||||
fmt.cc
|
||||
fmt.h
|
||||
functional.cc
|
||||
functional.h
|
||||
gzip.cc
|
||||
gzip.h
|
||||
hashlib.h
|
||||
io.cc
|
||||
io.h
|
||||
json.cc
|
||||
json.h
|
||||
log.cc
|
||||
log.h
|
||||
$<${YOSYS_ENABLE_VERIFIC}:log_compat.cc>
|
||||
log_help.cc
|
||||
log_help.h
|
||||
macc.h
|
||||
mem.cc
|
||||
mem.h
|
||||
modtools.h
|
||||
newcelltypes.h
|
||||
pattern.h
|
||||
qcsat.cc
|
||||
qcsat.h
|
||||
register.cc
|
||||
register.h
|
||||
${cellhelp_sources}
|
||||
rtlil_bufnorm.cc
|
||||
rtlil.cc
|
||||
rtlil.h
|
||||
satgen.cc
|
||||
satgen.h
|
||||
scopeinfo.cc
|
||||
scopeinfo.h
|
||||
sexpr.cc
|
||||
sexpr.h
|
||||
sigtools.h
|
||||
tclapi.cc
|
||||
threading.cc
|
||||
threading.h
|
||||
timinginfo.h
|
||||
topo_scc.h
|
||||
utils.h
|
||||
version.cc
|
||||
wallace_tree.h
|
||||
yosys.cc
|
||||
yosys_common.h
|
||||
yosys_config.h
|
||||
yosys.h
|
||||
yw.cc
|
||||
yw.h
|
||||
INCLUDE_DIRS
|
||||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
cxxopts
|
||||
$<${YOSYS_ENABLE_PLUGINS}:${Dlfcn_LIBRARIES}>
|
||||
$<${YOSYS_ENABLE_ZLIB}:PkgConfig::zlib>
|
||||
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
|
||||
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
|
||||
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Module>
|
||||
REQUIRES
|
||||
bigint
|
||||
ezsat
|
||||
json11
|
||||
sha1
|
||||
PROVIDES
|
||||
help
|
||||
echo
|
||||
license
|
||||
tcl
|
||||
shell
|
||||
history
|
||||
script
|
||||
DATA_DIR
|
||||
include/kernel
|
||||
DATA_FILES
|
||||
binding.h
|
||||
bitpattern.h
|
||||
cellaigs.h
|
||||
celledges.h
|
||||
celltypes.h
|
||||
newcelltypes.h
|
||||
consteval.h
|
||||
constids.inc
|
||||
cost.h
|
||||
drivertools.h
|
||||
ff.h
|
||||
ffinit.h
|
||||
ffmerge.h
|
||||
fmt.h
|
||||
gzip.h
|
||||
hashlib.h
|
||||
io.h
|
||||
json.h
|
||||
log.h
|
||||
macc.h
|
||||
modtools.h
|
||||
mem.h
|
||||
qcsat.h
|
||||
register.h
|
||||
rtlil.h
|
||||
satgen.h
|
||||
scopeinfo.h
|
||||
sexpr.h
|
||||
sigtools.h
|
||||
threading.h
|
||||
timinginfo.h
|
||||
utils.h
|
||||
yosys.h
|
||||
yosys_common.h
|
||||
yw.h
|
||||
DATA_EXPLICIT
|
||||
yosys_config.h ${CMAKE_CURRENT_BINARY_DIR}/yosys_config.h
|
||||
ESSENTIAL
|
||||
)
|
||||
|
||||
set(yosys_cc_definitions
|
||||
"$<$<BOOL:${YOSYS_ABC_EXECUTABLE}>:ABCEXTERNAL=\"${YOSYS_ABC_EXECUTABLE}\">"
|
||||
$<$<BOOL:${MSYS}>:YOSYS_WIN32_UNIX_DIR>
|
||||
)
|
||||
set_source_files_properties(yosys.cc PROPERTIES
|
||||
COMPILE_DEFINITIONS "${yosys_cc_definitions}"
|
||||
)
|
||||
|
||||
yosys_core(fstdata
|
||||
fstdata.cc
|
||||
fstdata.h
|
||||
REQUIRES
|
||||
fst
|
||||
DATA_DIR
|
||||
include/kernel
|
||||
DATA_FILES
|
||||
fstdata.h
|
||||
)
|
||||
|
||||
if (NOT YOSYS_BUILD_PYTHON_ONLY)
|
||||
yosys_core(driver
|
||||
driver.cc
|
||||
INCLUDE_DIRS
|
||||
${pybind11_INCLUDE_DIR}
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
|
||||
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
|
||||
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
|
||||
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
|
||||
REQUIRES
|
||||
essentials
|
||||
BOOTSTRAP
|
||||
)
|
||||
endif()
|
||||
|
|
@ -291,7 +291,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
|
|||
if (pos < 0)
|
||||
result.set(i, vacant_bits);
|
||||
else if (pos >= BigInteger(GetSize(arg1)))
|
||||
result.set(i, sign_ext ? arg1.back() : vacant_bits);
|
||||
result.set(i, sign_ext && !arg1.empty() ? arg1.back() : vacant_bits);
|
||||
else
|
||||
result.set(i, arg1[pos.toInt()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,14 +66,7 @@ struct AigMaker
|
|||
Cell *cell;
|
||||
idict<AigNode> aig_indices;
|
||||
|
||||
int the_true_node;
|
||||
int the_false_node;
|
||||
|
||||
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
|
||||
{
|
||||
the_true_node = -1;
|
||||
the_false_node = -1;
|
||||
}
|
||||
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) {}
|
||||
|
||||
int node2index(const AigNode &node)
|
||||
{
|
||||
|
|
|
|||
100
kernel/cellhelp.py
Normal file
100
kernel/cellhelp.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
import fileinput
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
class SimHelper:
|
||||
name: str = ""
|
||||
title: str = ""
|
||||
ports: str = ""
|
||||
source: str = ""
|
||||
desc: list[str]
|
||||
code: list[str]
|
||||
group: str = ""
|
||||
ver: str = "1"
|
||||
tags: list[str]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.desc = []
|
||||
self.tags = []
|
||||
|
||||
def __str__(self) -> str:
|
||||
printed_fields = [
|
||||
"name", "title", "ports", "source", "desc", "code", "group", "ver",
|
||||
"tags",
|
||||
]
|
||||
# generate C++ struct
|
||||
val = f"cell_help[{json.dumps(self.name)}] = "
|
||||
val += "{\n"
|
||||
for field in printed_fields:
|
||||
field_val = getattr(self, field)
|
||||
if isinstance(field_val, list):
|
||||
field_val = "\n".join(field_val)
|
||||
field_val = field_val.strip()
|
||||
val += f' {json.dumps(field_val)},\n'
|
||||
val += "};\n"
|
||||
return val
|
||||
|
||||
def simcells_reparse(cell: SimHelper):
|
||||
# cut manual signature
|
||||
cell.desc = cell.desc[3:]
|
||||
|
||||
# code-block truth table
|
||||
new_desc = []
|
||||
indent = ""
|
||||
for line in cell.desc:
|
||||
if line.startswith("Truth table:"):
|
||||
indent = " "
|
||||
new_desc.pop()
|
||||
new_desc.extend(["::", ""])
|
||||
new_desc.append(indent + line)
|
||||
cell.desc = new_desc
|
||||
|
||||
# set version
|
||||
cell.ver = "2a"
|
||||
|
||||
simHelper = SimHelper()
|
||||
|
||||
for line in fileinput.input():
|
||||
line = line.rstrip()
|
||||
# special comments
|
||||
if line.startswith("//-"):
|
||||
simHelper.desc.append(line[4:] if len(line) > 4 else "")
|
||||
elif line.startswith("//* "):
|
||||
_, key, val = line.split(maxsplit=2)
|
||||
setattr(simHelper, key, val)
|
||||
|
||||
# code parsing
|
||||
if line.startswith("module "):
|
||||
clean_line = line[7:].replace("\\", "").replace(";", "")
|
||||
simHelper.name, simHelper.ports = clean_line.split(maxsplit=1)
|
||||
simHelper.code = []
|
||||
short_filename = Path(fileinput.filename()).name
|
||||
simHelper.source = f'{short_filename}:{fileinput.filelineno()}'
|
||||
elif not line.startswith("endmodule"):
|
||||
line = " " + line
|
||||
try:
|
||||
simHelper.code.append(line.replace("\t", " "))
|
||||
except AttributeError:
|
||||
# no module definition, ignore line
|
||||
pass
|
||||
if line.startswith("endmodule"):
|
||||
short_filename = Path(fileinput.filename()).name
|
||||
if simHelper.ver == "1" and short_filename == "simcells.v":
|
||||
# default simcells parsing
|
||||
simcells_reparse(simHelper)
|
||||
|
||||
# check help
|
||||
if simHelper.desc and simHelper.ver == "1" and short_filename == "simlib.v" and simHelper.desc[1].startswith(' '):
|
||||
simHelper.desc.pop(1)
|
||||
|
||||
# check group
|
||||
assert simHelper.group, f"techlibs/common/{simHelper.source}: {simHelper.name} cell missing group"
|
||||
|
||||
# dump
|
||||
print(simHelper)
|
||||
# new
|
||||
simHelper = SimHelper()
|
||||
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#define CELLTYPES_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -87,22 +88,22 @@ struct CellTypes
|
|||
{
|
||||
setup_internals_eval();
|
||||
|
||||
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true);
|
||||
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y});
|
||||
|
||||
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y}, true);
|
||||
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y}, true);
|
||||
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y}, true);
|
||||
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y}, true);
|
||||
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y}, true);
|
||||
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true);
|
||||
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y});
|
||||
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y});
|
||||
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y});
|
||||
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y});
|
||||
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y});
|
||||
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y});
|
||||
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
|
||||
|
|
@ -196,7 +197,7 @@ struct CellTypes
|
|||
{
|
||||
setup_stdcells_eval();
|
||||
|
||||
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true);
|
||||
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y});
|
||||
}
|
||||
|
||||
void setup_stdcells_eval()
|
||||
|
|
@ -549,9 +550,6 @@ struct CellTypes
|
|||
}
|
||||
};
|
||||
|
||||
// initialized by yosys_setup()
|
||||
extern CellTypes yosys_celltypes;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/macc.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -44,9 +45,8 @@ struct ConstEval
|
|||
|
||||
ConstEval(RTLIL::Module *module, RTLIL::State defaultval = RTLIL::State::Sm) : module(module), assign_map(module), defaultval(defaultval)
|
||||
{
|
||||
CellTypes ct;
|
||||
ct.setup_internals();
|
||||
ct.setup_stdcells();
|
||||
auto ct = NewCellTypes();
|
||||
ct.static_cell_types = StaticCellTypes::Compat::nomem_noff;
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
if (!ct.cell_known(it.second->type))
|
||||
|
|
|
|||
|
|
@ -460,9 +460,7 @@ X(EDGE_POL)
|
|||
X(EFX_ADD)
|
||||
X(EN)
|
||||
X(ENPOL)
|
||||
X(EN_DST)
|
||||
X(EN_POLARITY)
|
||||
X(EN_SRC)
|
||||
X(EQN)
|
||||
X(F)
|
||||
X(FDCE)
|
||||
|
|
@ -838,6 +836,7 @@ X(abcgroup)
|
|||
X(acc_fir)
|
||||
X(acc_fir_i)
|
||||
X(add_carry)
|
||||
X(aiger2_zbuf)
|
||||
X(allconst)
|
||||
X(allseq)
|
||||
X(always_comb)
|
||||
|
|
|
|||
|
|
@ -210,6 +210,6 @@ unsigned int CellCosts::get(RTLIL::Cell *cell)
|
|||
// TODO: $fsm
|
||||
// ignored: $pow $memrd $memwr $meminit (and v2 counterparts)
|
||||
|
||||
log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters));
|
||||
log_warning("Can't determine cost of %s cell (%d parameters).\n", cell->type.unescape(), GetSize(cell->parameters));
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#define CXXOPTS_VECTOR_DELIMITER '\0'
|
||||
#include "libs/cxxopts/include/cxxopts.hpp"
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef YOSYS_ENABLE_READLINE
|
||||
# include <readline/readline.h>
|
||||
|
|
@ -143,19 +144,6 @@ int yosys_history_offset = 0;
|
|||
std::string yosys_history_file;
|
||||
#endif
|
||||
|
||||
#if defined(__wasm)
|
||||
extern "C" {
|
||||
// FIXME: WASI does not currently support exceptions.
|
||||
void* __cxa_allocate_exception(size_t thrown_size) throw() {
|
||||
return malloc(thrown_size);
|
||||
}
|
||||
bool __cxa_uncaught_exception() throw();
|
||||
void __cxa_throw(void* thrown_exception, struct std::type_info * tinfo, void (*dest)(void*)) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void yosys_atexit()
|
||||
{
|
||||
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
|
||||
|
|
@ -195,6 +183,7 @@ namespace Yosys {
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
auto wall_clock_start = std::chrono::steady_clock::now();
|
||||
std::string frontend_command = "auto";
|
||||
std::string backend_command = "auto";
|
||||
std::vector<std::string> vlog_defines;
|
||||
|
|
@ -675,6 +664,7 @@ int main(int argc, char **argv)
|
|||
|
||||
#ifdef _WIN32
|
||||
log("End of script. Logfile hash: %s\n", hash);
|
||||
(void)wall_clock_start;
|
||||
#else
|
||||
std::string meminfo;
|
||||
std::string stats_divider = ", ";
|
||||
|
|
@ -700,8 +690,11 @@ int main(int argc, char **argv)
|
|||
meminfo = stringf(", MEM: %.2f MB peak",
|
||||
ru_buffer.ru_maxrss / (1024.0 * 1024.0));
|
||||
#endif
|
||||
log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash,
|
||||
stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
|
||||
double wall_seconds = std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - wall_clock_start).count();
|
||||
|
||||
log("End of script. Logfile hash: %s%stime: %.2fs, user: %.2fs, system: %.2fs%s\n", hash,
|
||||
stats_divider.c_str(), wall_seconds, ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
|
||||
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
|
||||
#endif
|
||||
log("%s\n", yosys_maybe_version());
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ DriveSpec DriverMap::operator()(DriveSpec spec)
|
|||
|
||||
std::string log_signal(DriveChunkWire const &chunk)
|
||||
{
|
||||
const char *id = log_id(chunk.wire->name);
|
||||
std::string id = chunk.wire->name.unescape();
|
||||
if (chunk.is_whole())
|
||||
return id;
|
||||
if (chunk.width == 1)
|
||||
|
|
@ -877,8 +877,8 @@ std::string log_signal(DriveChunkWire const &chunk)
|
|||
|
||||
std::string log_signal(DriveChunkPort const &chunk)
|
||||
{
|
||||
const char *cell_id = log_id(chunk.cell->name);
|
||||
const char *port_id = log_id(chunk.port);
|
||||
std::string cell_id = chunk.cell->name.unescape();
|
||||
std::string port_id = chunk.port.unescape();
|
||||
if (chunk.is_whole())
|
||||
return stringf("%s <%s>", cell_id, port_id);
|
||||
if (chunk.width == 1)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -1093,10 +1093,10 @@ private:
|
|||
|
||||
struct DriverMap
|
||||
{
|
||||
CellTypes celltypes;
|
||||
NewCellTypes celltypes;
|
||||
|
||||
DriverMap() { celltypes.setup(); }
|
||||
DriverMap(Design *design) { celltypes.setup(); celltypes.setup_design(design); }
|
||||
DriverMap(Design *design) { celltypes.setup(design); }
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
|||
|
|
@ -792,7 +792,7 @@ void FfData::flip_bits(const pool<int> &bits) {
|
|||
Wire *new_q = module->addWire(NEW_ID4_SUFFIX("new_q"), width); // SILIMATE: Improve the naming
|
||||
|
||||
if (has_sr && cell) {
|
||||
log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", log_id(module->name), log_id(cell->name), log_id(cell->type));
|
||||
log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", module->name.unescape(), cell->name.unescape(), cell->type.unescape());
|
||||
}
|
||||
|
||||
if (is_fine) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/threading.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -35,34 +36,55 @@ struct FfInitVals
|
|||
sigmap = sigmap_;
|
||||
initbits.clear();
|
||||
for (auto wire : module->wires())
|
||||
if (wire->attributes.count(ID::init))
|
||||
process_wire(wire);
|
||||
}
|
||||
|
||||
void process_wire(RTLIL::Wire *wire)
|
||||
{
|
||||
SigSpec wirebits = (*sigmap)(wire);
|
||||
Const initval = wire->attributes.at(ID::init);
|
||||
|
||||
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
|
||||
{
|
||||
if (wire->attributes.count(ID::init) == 0)
|
||||
SigBit bit = wirebits[i];
|
||||
State val = initval[i];
|
||||
|
||||
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
|
||||
continue;
|
||||
|
||||
SigSpec wirebits = (*sigmap)(wire);
|
||||
Const initval = wire->attributes.at(ID::init);
|
||||
|
||||
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
|
||||
{
|
||||
SigBit bit = wirebits[i];
|
||||
State val = initval[i];
|
||||
|
||||
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
|
||||
continue;
|
||||
|
||||
if (initbits.count(bit)) {
|
||||
if (initbits.at(bit).first != val)
|
||||
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
|
||||
log_signal(bit), log_signal(SigBit(wire, i)),
|
||||
log_signal(val), log_signal(initbits.at(bit).first));
|
||||
continue;
|
||||
}
|
||||
|
||||
initbits[bit] = std::make_pair(val,SigBit(wire,i));
|
||||
if (initbits.count(bit)) {
|
||||
if (initbits.at(bit).first != val)
|
||||
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
|
||||
log_signal(bit), log_signal(SigBit(wire, i)),
|
||||
log_signal(val), log_signal(initbits.at(bit).first));
|
||||
continue;
|
||||
}
|
||||
|
||||
initbits[bit] = std::make_pair(val,SigBit(wire,i));
|
||||
}
|
||||
}
|
||||
|
||||
void set_parallel(const SigMapView *sigmap_, ParallelDispatchThreadPool &thread_pool, RTLIL::Module *module)
|
||||
{
|
||||
sigmap = sigmap_;
|
||||
initbits.clear();
|
||||
|
||||
const RTLIL::Module *const_module = module;
|
||||
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->wires_size(), 1000));
|
||||
ShardedVector<RTLIL::Wire*> init_wires(subpool);
|
||||
subpool.run([const_module, &init_wires](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(const_module->wires_size())) {
|
||||
RTLIL::Wire *wire = const_module->wire_at(i);
|
||||
if (wire->attributes.count(ID::init))
|
||||
init_wires.insert(ctx, wire);
|
||||
}
|
||||
});
|
||||
|
||||
for (RTLIL::Wire *wire : init_wires)
|
||||
process_wire(wire);
|
||||
}
|
||||
|
||||
RTLIL::State operator()(RTLIL::SigBit bit) const
|
||||
{
|
||||
auto it = initbits.find((*sigmap)(bit));
|
||||
|
|
|
|||
|
|
@ -804,8 +804,10 @@ std::string Fmt::render() const
|
|||
buf += 'X';
|
||||
else if (has_z)
|
||||
buf += 'Z';
|
||||
else
|
||||
buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()];
|
||||
else {
|
||||
const char *digits = part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
buf += digits[subvalue.as_int()];
|
||||
}
|
||||
}
|
||||
} else if (part.base == 10) {
|
||||
if (part.show_base)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ static std::string file_base_name(std::string const & path)
|
|||
|
||||
FstData::FstData(std::string filename) : ctx(nullptr)
|
||||
{
|
||||
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(YOSYS_ENABLE_SPAWN)
|
||||
std::string filename_trim = file_base_name(filename);
|
||||
if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) {
|
||||
filename_trim.erase(filename_trim.size()-4);
|
||||
|
|
@ -45,8 +45,7 @@ FstData::FstData(std::string filename) : ctx(nullptr)
|
|||
ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
|
||||
if (!ctx)
|
||||
log_error("Error opening '%s' as FST file\n", filename);
|
||||
int scale = (int)fstReaderGetTimescale(ctx);
|
||||
timescale = pow(10.0, scale);
|
||||
scale = (int)fstReaderGetTimescale(ctx);
|
||||
timescale_str = "";
|
||||
int unit = 0;
|
||||
int zeros = 0;
|
||||
|
|
@ -88,11 +87,11 @@ static void normalize_brackets(std::string &str)
|
|||
}
|
||||
}
|
||||
|
||||
fstHandle FstData::getHandle(std::string name) {
|
||||
fstHandle FstData::getHandle(std::string name) {
|
||||
normalize_brackets(name);
|
||||
if (name_to_handle.find(name) != name_to_handle.end())
|
||||
return name_to_handle[name];
|
||||
else
|
||||
else
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
|
@ -253,7 +252,7 @@ void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_faci
|
|||
bool is_clock = false;
|
||||
if (!all_samples) {
|
||||
for(auto &s : clk_signals) {
|
||||
if (s==pnt_facidx) {
|
||||
if (s==pnt_facidx) {
|
||||
is_clock=true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -386,7 +385,7 @@ std::string FstData::autoScope(Module *topmod) {
|
|||
|
||||
// Logging results
|
||||
if (results.empty()) {
|
||||
log_warning("Could not auto-discover scope for module '%s'...\n",
|
||||
log_warning("Could not auto-discover scope for module '%s'...\n",
|
||||
top.c_str());
|
||||
return "";
|
||||
} else {
|
||||
|
|
@ -395,7 +394,7 @@ std::string FstData::autoScope(Module *topmod) {
|
|||
log(" %s\n", scope.c_str());
|
||||
}
|
||||
if (results.size() > 1) {
|
||||
log_warning("Multiple scopes found for module '%s'. Using the first one.\n",
|
||||
log_warning("Multiple scopes found for module '%s'. Using the first one.\n",
|
||||
top.c_str());
|
||||
}
|
||||
return results[0];
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class FstData
|
|||
std::string valueOf(fstHandle signal);
|
||||
fstHandle getHandle(std::string name);
|
||||
dict<int,fstHandle> getMemoryHandles(std::string name);
|
||||
double getTimescale() { return timescale; }
|
||||
int getScale() { return scale; }
|
||||
const char *getTimescaleString() { return timescale_str.c_str(); }
|
||||
int getWidth(fstHandle signal);
|
||||
std::string autoScope(Module *topmod);
|
||||
|
|
@ -72,7 +72,7 @@ private:
|
|||
uint64_t last_time;
|
||||
std::map<fstHandle, std::string> past_data;
|
||||
uint64_t past_time;
|
||||
double timescale;
|
||||
int scale; // exponent of 10, e.g. -6 = us, -9 = ns
|
||||
std::string timescale_str;
|
||||
uint64_t start_time;
|
||||
uint64_t end_time;
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ struct PrintVisitor : DefaultVisitor<std::string> {
|
|||
|
||||
std::string Node::to_string()
|
||||
{
|
||||
return to_string([](Node n) { return RTLIL::unescape_id(n.name()); });
|
||||
return to_string([](Node n) { return n.name().unescape(); });
|
||||
}
|
||||
|
||||
std::string Node::to_string(std::function<std::string(Node)> np)
|
||||
|
|
@ -572,7 +572,7 @@ private:
|
|||
const auto &wr = mem->wr_ports[i];
|
||||
if (wr.clk_enable)
|
||||
log_error("Write port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", i, log_id(mem->module), log_id(mem->memid));
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", i, mem->module, mem->memid.unescape());
|
||||
Node en = enqueue(driver_map(DriveSpec(wr.en)));
|
||||
Node addr = enqueue(driver_map(DriveSpec(wr.addr)));
|
||||
Node new_data = enqueue(driver_map(DriveSpec(wr.data)));
|
||||
|
|
@ -582,12 +582,12 @@ private:
|
|||
}
|
||||
if (mem->rd_ports.empty())
|
||||
log_error("Memory %s.%s has no read ports. This is not supported by the functional backend. "
|
||||
"Call opt_clean to remove it.", log_id(mem->module), log_id(mem->memid));
|
||||
"Call opt_clean to remove it.", mem->module, mem->memid.unescape());
|
||||
for (size_t i = 0; i < mem->rd_ports.size(); i++) {
|
||||
const auto &rd = mem->rd_ports[i];
|
||||
if (rd.clk_enable)
|
||||
log_error("Read port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
|
||||
"Call memory_nordff to avoid this error.\n", i, log_id(mem->module), log_id(mem->memid));
|
||||
"Call memory_nordff to avoid this error.\n", i, mem->module, mem->memid.unescape());
|
||||
Node addr = enqueue(driver_map(DriveSpec(rd.addr)));
|
||||
read_results.push_back(factory.memory_read(node, addr));
|
||||
}
|
||||
|
|
@ -609,7 +609,7 @@ private:
|
|||
FfData ff(&ff_initvals, cell);
|
||||
if (!ff.has_gclk)
|
||||
log_error("The design contains a %s flip-flop at %s. This is not supported by the functional backend. "
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", log_id(cell->type), log_id(cell));
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", cell->type.unescape(), cell);
|
||||
auto &state = factory.add_state(ff.name, ID($state), Sort(ff.width));
|
||||
Node q_value = factory.value(state);
|
||||
factory.suggest_name(q_value, ff.name);
|
||||
|
|
@ -677,7 +677,7 @@ public:
|
|||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(wire_chunk));
|
||||
check_undriven(driver, RTLIL::unescape_id(wire_chunk.wire->name));
|
||||
check_undriven(driver, wire_chunk.wire->name.unescape());
|
||||
Node node = enqueue(driver);
|
||||
factory.suggest_name(node, wire_chunk.wire->name);
|
||||
factory.update_pending(pending, node);
|
||||
|
|
@ -695,7 +695,7 @@ public:
|
|||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(port_chunk));
|
||||
check_undriven(driver, RTLIL::unescape_id(port_chunk.cell->name) + " port " + RTLIL::unescape_id(port_chunk.port));
|
||||
check_undriven(driver, port_chunk.cell->name.unescape() + " port " + port_chunk.port.unescape());
|
||||
factory.update_pending(pending, enqueue(driver));
|
||||
}
|
||||
} else {
|
||||
|
|
@ -744,7 +744,7 @@ void IR::topological_sort() {
|
|||
log_warning("Combinational loop:\n");
|
||||
for (int *i = begin; i != end; ++i) {
|
||||
Node node(_graph[*i]);
|
||||
log("- %s = %s\n", RTLIL::unescape_id(node.name()), node.to_string());
|
||||
log("- %s = %s\n", node.name().unescape(), node.to_string());
|
||||
}
|
||||
log("\n");
|
||||
scc = true;
|
||||
|
|
|
|||
|
|
@ -588,7 +588,7 @@ namespace Functional {
|
|||
_used_names.insert(std::move(name));
|
||||
}
|
||||
std::string unique_name(IdString suggestion) {
|
||||
std::string str = RTLIL::unescape_id(suggestion);
|
||||
std::string str = suggestion.unescape();
|
||||
for(size_t i = 0; i < str.size(); i++)
|
||||
if(!is_character_legal(str[i], i))
|
||||
str[i] = substitution_character;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ namespace hashlib {
|
|||
* instead of pointers.
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define HASHLIB_ATTRIBUTE_WARN_UNUSED __attribute__((warn_unused))
|
||||
#else
|
||||
# define HASHLIB_ATTRIBUTE_WARN_UNUSED
|
||||
#endif
|
||||
|
||||
const int hashtable_size_trigger = 2;
|
||||
const int hashtable_size_factor = 3;
|
||||
|
||||
|
|
@ -403,7 +409,7 @@ private:
|
|||
};
|
||||
|
||||
template<typename K, typename T, typename OPS>
|
||||
class dict {
|
||||
class HASHLIB_ATTRIBUTE_WARN_UNUSED dict {
|
||||
struct entry_t
|
||||
{
|
||||
std::pair<K, T> udata;
|
||||
|
|
@ -878,7 +884,7 @@ public:
|
|||
};
|
||||
|
||||
template<typename K, typename OPS>
|
||||
class pool
|
||||
class HASHLIB_ATTRIBUTE_WARN_UNUSED pool
|
||||
{
|
||||
template<typename, int, typename> friend class idict;
|
||||
|
||||
|
|
@ -1263,7 +1269,7 @@ public:
|
|||
};
|
||||
|
||||
template<typename K, int offset, typename OPS>
|
||||
class idict
|
||||
class HASHLIB_ATTRIBUTE_WARN_UNUSED idict
|
||||
{
|
||||
pool<K, OPS> database;
|
||||
|
||||
|
|
@ -1366,7 +1372,7 @@ public:
|
|||
* i-prefixed methods operate on indices in parents
|
||||
*/
|
||||
template<typename K, typename OPS>
|
||||
class mfp
|
||||
class HASHLIB_ATTRIBUTE_WARN_UNUSED mfp
|
||||
{
|
||||
idict<K, 0, OPS> database;
|
||||
class AtomicParent {
|
||||
|
|
|
|||
12
kernel/io.cc
12
kernel/io.cc
|
|
@ -155,19 +155,13 @@ std::string get_base_tmpdir()
|
|||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
# ifdef __MINGW32__
|
||||
char longpath[MAX_PATH + 1];
|
||||
char shortpath[MAX_PATH + 1];
|
||||
# else
|
||||
WCHAR longpath[MAX_PATH + 1];
|
||||
TCHAR shortpath[MAX_PATH + 1];
|
||||
# endif
|
||||
if (!GetTempPath(MAX_PATH+1, longpath))
|
||||
if (!GetTempPathA(MAX_PATH+1, longpath))
|
||||
log_error("GetTempPath() failed.\n");
|
||||
if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1))
|
||||
if (!GetShortPathNameA(longpath, shortpath, MAX_PATH + 1))
|
||||
log_error("GetShortPathName() failed.\n");
|
||||
for (int i = 0; shortpath[i]; i++)
|
||||
tmpdir += char(shortpath[i]);
|
||||
tmpdir += shortpath;
|
||||
#else
|
||||
char * var = std::getenv("TMPDIR");
|
||||
if (var && strlen(var)!=0) {
|
||||
|
|
|
|||
39
kernel/io.h
39
kernel/io.h
|
|
@ -197,6 +197,28 @@ check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormat
|
|||
ensure_no_format_spec(fmt, fmt_start, has_escapes);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static auto has_name_member_imp(int)
|
||||
-> decltype(static_cast<const RTLIL::IdString>(std::declval<T>().name), std::true_type{});
|
||||
|
||||
template <class T>
|
||||
static auto has_name_member_imp(long)
|
||||
-> std::false_type;
|
||||
|
||||
template <class T>
|
||||
struct has_name_member : decltype(has_name_member_imp<T>(0)){};
|
||||
|
||||
template <class T>
|
||||
static auto ptr_has_name_member_imp(int)
|
||||
-> decltype(static_cast<const RTLIL::IdString>(std::declval<T>()->name), std::true_type{});
|
||||
|
||||
template <class T>
|
||||
static auto ptr_has_name_member_imp(long)
|
||||
-> std::false_type;
|
||||
|
||||
template <class T>
|
||||
struct ptr_has_name_member : decltype(ptr_has_name_member_imp<T>(0)){};
|
||||
|
||||
// Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments.
|
||||
// Fills `specs` with the FoundFormatSpecs found in the format string.
|
||||
// `int_args_consumed` is the number of int arguments already consumed to satisfy the
|
||||
|
|
@ -245,7 +267,9 @@ constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escap
|
|||
if constexpr (!std::is_convertible_v<Arg, const char *> &&
|
||||
!std::is_convertible_v<Arg, const std::string &> &&
|
||||
!std::is_convertible_v<Arg, const std::string_view &> &&
|
||||
!std::is_convertible_v<Arg, const RTLIL::IdString &>) {
|
||||
!std::is_convertible_v<Arg, const RTLIL::IdString &> &&
|
||||
!has_name_member<Arg>() &&
|
||||
!ptr_has_name_member<Arg>()) {
|
||||
YOSYS_ABORT("Expected type convertible to char *");
|
||||
}
|
||||
*specs = found;
|
||||
|
|
@ -343,6 +367,16 @@ inline void format_emit_one(std::string &result, std::string_view fmt, const Fou
|
|||
format_emit_idstring(result, spec, dynamic_ints, num_dynamic_ints, s);
|
||||
return;
|
||||
}
|
||||
if constexpr (has_name_member<Arg>()) {
|
||||
const std::string &s = arg.name.unescape();
|
||||
format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
|
||||
return;
|
||||
}
|
||||
if constexpr (ptr_has_name_member<Arg>()) {
|
||||
const std::string &s = arg->name.unescape();
|
||||
format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case CONVSPEC_VOID_PTR:
|
||||
if constexpr (std::is_convertible_v<Arg, const void *>) {
|
||||
|
|
@ -441,7 +475,8 @@ public:
|
|||
private:
|
||||
std::string_view fmt;
|
||||
bool has_escapes = false;
|
||||
FoundFormatSpec specs[sizeof...(Args)] = {};
|
||||
// Making array at least size of one to make MSVC happy and strict to standards
|
||||
FoundFormatSpec specs[sizeof...(Args) ? sizeof...(Args) : 1] = {};
|
||||
};
|
||||
|
||||
template <typename T> struct WrapType { using type = T; };
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(YOSYS_ENABLE_DLOPEN)
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
|
|
@ -324,6 +324,14 @@ void log_formatted_file_info(std::string_view filename, int lineno, std::string
|
|||
log("%s:%d: Info: %s", filename, lineno, str);
|
||||
}
|
||||
|
||||
void log_suppressed() {
|
||||
if (log_debug_suppressed && !log_make_debug) {
|
||||
constexpr const char* format = "<suppressed ~%d debug messages>\n";
|
||||
logv_string(format, stringf(format, log_debug_suppressed));
|
||||
log_debug_suppressed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
static void log_error_with_prefix(std::string_view prefix, std::string str)
|
||||
{
|
||||
|
|
@ -345,7 +353,9 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
|
|||
}
|
||||
|
||||
log_last_error = std::move(str);
|
||||
log("%s%s", prefix, log_last_error);
|
||||
std::string message(prefix);
|
||||
message += log_last_error;
|
||||
logv_string("%s%s", message);
|
||||
log_flush();
|
||||
|
||||
log_make_debug = bak_log_make_debug;
|
||||
|
|
@ -355,7 +365,7 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
|
|||
item.current_count++;
|
||||
|
||||
for (auto &[_, item] : log_expect_prefix_error)
|
||||
if (std::regex_search(string(prefix) + string(log_last_error), item.pattern))
|
||||
if (std::regex_search(message, item.pattern))
|
||||
item.current_count++;
|
||||
|
||||
log_check_expected();
|
||||
|
|
@ -461,7 +471,7 @@ void log_pop()
|
|||
log_flush();
|
||||
}
|
||||
|
||||
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(YOSYS_ENABLE_PLUGINS)
|
||||
#if defined(YOSYS_ENABLE_DLOPEN)
|
||||
void log_backtrace(const char *prefix, int levels)
|
||||
{
|
||||
if (levels <= 0) return;
|
||||
|
|
@ -576,7 +586,7 @@ void log_flush()
|
|||
}
|
||||
|
||||
void log_dump_val_worker(RTLIL::IdString v) {
|
||||
log("%s", log_id(v));
|
||||
log("%s", v.unescape());
|
||||
}
|
||||
|
||||
void log_dump_val_worker(RTLIL::SigSpec v) {
|
||||
|
|
@ -604,7 +614,7 @@ std::string log_const(const RTLIL::Const &value, bool autoint)
|
|||
|
||||
const char *log_id(const RTLIL::IdString &str)
|
||||
{
|
||||
std::string unescaped = RTLIL::unescape_id(str);
|
||||
std::string unescaped = str.unescape();
|
||||
log_id_cache.push_back(strdup(unescaped.c_str()));
|
||||
return log_id_cache.back();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,12 +206,7 @@ template <typename... Args>
|
|||
log_formatted_cmd_error(fmt.format(args...));
|
||||
}
|
||||
|
||||
static inline void log_suppressed() {
|
||||
if (log_debug_suppressed && !log_make_debug) {
|
||||
log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
|
||||
log_debug_suppressed = 0;
|
||||
}
|
||||
}
|
||||
void log_suppressed();
|
||||
|
||||
struct LogMakeDebugHdl {
|
||||
bool status = false;
|
||||
|
|
|
|||
|
|
@ -663,15 +663,15 @@ namespace {
|
|||
auto addr = cell->getPort(ID::ADDR);
|
||||
auto data = cell->getPort(ID::DATA);
|
||||
if (!addr.is_fully_const())
|
||||
log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
|
||||
log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), cell);
|
||||
if (!data.is_fully_const())
|
||||
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
|
||||
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), cell);
|
||||
init.addr = addr.as_const();
|
||||
init.data = data.as_const();
|
||||
if (cell->type == ID($meminit_v2)) {
|
||||
auto en = cell->getPort(ID::EN);
|
||||
if (!en.is_fully_const())
|
||||
log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell));
|
||||
log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), cell);
|
||||
init.en = en.as_const();
|
||||
} else {
|
||||
init.en = RTLIL::Const(State::S1, mem->width);
|
||||
|
|
@ -1056,7 +1056,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
|||
|
||||
if (c)
|
||||
log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data",
|
||||
idx, log_id(module), log_id(memid), log_id(c));
|
||||
idx, module, memid.unescape(), c);
|
||||
|
||||
port.en = State::S1;
|
||||
port.clk = State::S0;
|
||||
|
|
|
|||
|
|
@ -23,11 +23,28 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct ModIndex : public RTLIL::Monitor
|
||||
{
|
||||
struct PointerOrderedSigBit : public RTLIL::SigBit {
|
||||
PointerOrderedSigBit(SigBit s) {
|
||||
wire = s.wire;
|
||||
if (wire)
|
||||
offset = s.offset;
|
||||
else
|
||||
data = s.data;
|
||||
}
|
||||
inline bool operator<(const RTLIL::SigBit &other) const {
|
||||
if (wire == other.wire)
|
||||
return wire ? (offset < other.offset) : (data < other.data);
|
||||
if (wire != nullptr && other.wire != nullptr)
|
||||
return wire < other.wire; // look here
|
||||
return (wire != nullptr) < (other.wire != nullptr);
|
||||
}
|
||||
};
|
||||
struct PortInfo {
|
||||
RTLIL::Cell* cell;
|
||||
RTLIL::IdString port;
|
||||
|
|
@ -77,7 +94,7 @@ struct ModIndex : public RTLIL::Monitor
|
|||
|
||||
SigMap sigmap;
|
||||
RTLIL::Module *module;
|
||||
std::map<RTLIL::SigBit, SigBitInfo> database;
|
||||
std::map<PointerOrderedSigBit, SigBitInfo> database;
|
||||
int auto_reload_counter;
|
||||
bool auto_reload_module;
|
||||
|
||||
|
|
@ -94,8 +111,11 @@ struct ModIndex : public RTLIL::Monitor
|
|||
{
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
RTLIL::SigBit bit = sigmap(sig[i]);
|
||||
if (bit.wire)
|
||||
if (bit.wire) {
|
||||
database[bit].ports.erase(PortInfo(cell, port, i));
|
||||
if (!database[bit].is_input && !database[bit].is_output && database[bit].ports.empty())
|
||||
database.erase(bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,11 +152,11 @@ struct ModIndex : public RTLIL::Monitor
|
|||
}
|
||||
}
|
||||
|
||||
void check()
|
||||
bool ok()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (auto_reload_module)
|
||||
return;
|
||||
return true;
|
||||
|
||||
for (auto it : database)
|
||||
log_assert(it.first == sigmap(it.first));
|
||||
|
|
@ -156,9 +176,15 @@ struct ModIndex : public RTLIL::Monitor
|
|||
else if (!(it.second == database_bak.at(it.first)))
|
||||
log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first));
|
||||
|
||||
log_assert(database == database_bak);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void check()
|
||||
{
|
||||
log_assert(ok());
|
||||
}
|
||||
|
||||
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
|
||||
|
|
@ -294,8 +320,8 @@ struct ModIndex : public RTLIL::Monitor
|
|||
if (it.second.is_output)
|
||||
log(" PRIMARY OUTPUT\n");
|
||||
for (auto &port : it.second.ports)
|
||||
log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell),
|
||||
log_id(port.port), port.offset, log_id(port.cell->type));
|
||||
log(" PORT: %s.%s[%d] (%s)\n", port.cell,
|
||||
port.port.unescape(), port.offset, port.cell->type.unescape());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -332,7 +358,7 @@ struct ModWalker
|
|||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
|
||||
CellTypes ct;
|
||||
NewCellTypes ct;
|
||||
SigMap sigmap;
|
||||
|
||||
dict<RTLIL::SigBit, pool<PortBit>> signal_drivers;
|
||||
|
|
|
|||
651
kernel/newcelltypes.h
Normal file
651
kernel/newcelltypes.h
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
#ifndef NEWCELLTYPES_H
|
||||
#define NEWCELLTYPES_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* This API is unstable.
|
||||
* It may change or be removed in future versions and break dependent code.
|
||||
*/
|
||||
|
||||
namespace StaticCellTypes {
|
||||
|
||||
// Given by last internal cell type IdString constids.inc, compilation error if too low
|
||||
constexpr int MAX_CELLS = 300;
|
||||
// Currently given by _MUX16_, compilation error if too low
|
||||
constexpr int MAX_PORTS = 20;
|
||||
struct CellTableBuilder {
|
||||
struct PortList {
|
||||
std::array<RTLIL::IdString, MAX_PORTS> ports{};
|
||||
size_t count = 0;
|
||||
constexpr PortList() = default;
|
||||
constexpr PortList(std::initializer_list<RTLIL::IdString> init) {
|
||||
for (auto p : init) {
|
||||
ports[count++] = p;
|
||||
}
|
||||
}
|
||||
constexpr auto begin() const { return ports.begin(); }
|
||||
constexpr auto end() const { return ports.begin() + count; }
|
||||
constexpr bool contains(RTLIL::IdString port) const {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (port == ports[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
constexpr size_t size() const { return count; }
|
||||
};
|
||||
struct Features {
|
||||
bool is_evaluable = false;
|
||||
bool is_combinatorial = false;
|
||||
bool is_synthesizable = false;
|
||||
bool is_stdcell = false;
|
||||
bool is_ff = false;
|
||||
bool is_mem_noff = false;
|
||||
bool is_anyinit = false;
|
||||
bool is_tristate = false;
|
||||
};
|
||||
struct CellInfo {
|
||||
RTLIL::IdString type;
|
||||
PortList inputs, outputs;
|
||||
Features features;
|
||||
};
|
||||
std::array<CellInfo, MAX_CELLS> cells{};
|
||||
size_t count = 0;
|
||||
|
||||
constexpr void setup_type(RTLIL::IdString type, std::initializer_list<RTLIL::IdString> inputs, std::initializer_list<RTLIL::IdString> outputs, const Features& features) {
|
||||
cells[count++] = {type, PortList(inputs), PortList(outputs), features};
|
||||
}
|
||||
constexpr void setup_internals_other()
|
||||
{
|
||||
Features features {};
|
||||
features.is_tristate = true;
|
||||
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, features);
|
||||
|
||||
features = {};
|
||||
setup_type(ID($assert), {ID::A, ID::EN}, {}, features);
|
||||
setup_type(ID($assume), {ID::A, ID::EN}, {}, features);
|
||||
setup_type(ID($live), {ID::A, ID::EN}, {}, features);
|
||||
setup_type(ID($fair), {ID::A, ID::EN}, {}, features);
|
||||
setup_type(ID($cover), {ID::A, ID::EN}, {}, features);
|
||||
setup_type(ID($initstate), {}, {ID::Y}, features);
|
||||
setup_type(ID($anyconst), {}, {ID::Y}, features);
|
||||
setup_type(ID($anyseq), {}, {ID::Y}, features);
|
||||
setup_type(ID($allconst), {}, {ID::Y}, features);
|
||||
setup_type(ID($allseq), {}, {ID::Y}, features);
|
||||
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, {}, features);
|
||||
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, {}, features);
|
||||
setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, {}, features);
|
||||
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, {}, features);
|
||||
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, {}, features);
|
||||
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}, features);
|
||||
setup_type(ID($get_tag), {ID::A}, {ID::Y}, features);
|
||||
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, {}, features);
|
||||
setup_type(ID($original_tag), {ID::A}, {ID::Y}, features);
|
||||
setup_type(ID($future_ff), {ID::A}, {ID::Y}, features);
|
||||
setup_type(ID($scopeinfo), {}, {}, features);
|
||||
setup_type(ID($input_port), {}, {ID::Y}, features);
|
||||
setup_type(ID($connect), {ID::A, ID::B}, {}, features);
|
||||
}
|
||||
constexpr void setup_internals_eval()
|
||||
{
|
||||
Features features {};
|
||||
features.is_evaluable = true;
|
||||
std::initializer_list<RTLIL::IdString> unary_ops = {
|
||||
ID($not), ID($pos), ID($buf), ID($neg),
|
||||
ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
|
||||
ID($logic_not), ID($slice), ID($lut), ID($sop)
|
||||
};
|
||||
|
||||
std::initializer_list<RTLIL::IdString> binary_ops = {
|
||||
ID($and), ID($or), ID($xor), ID($xnor),
|
||||
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
|
||||
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
|
||||
ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
|
||||
ID($logic_and), ID($logic_or), ID($concat), ID($macc),
|
||||
ID($bweqx)
|
||||
};
|
||||
|
||||
for (auto type : unary_ops)
|
||||
setup_type(type, {ID::A}, {ID::Y}, features);
|
||||
|
||||
for (auto type : binary_ops)
|
||||
setup_type(type, {ID::A, ID::B}, {ID::Y}, features);
|
||||
|
||||
for (auto type : {ID($mux), ID($pmux), ID($bwmux)})
|
||||
setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, features);
|
||||
|
||||
for (auto type : {ID($bmux), ID($demux)})
|
||||
setup_type(type, {ID::A, ID::S}, {ID::Y}, features);
|
||||
|
||||
setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, features);
|
||||
setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, features);
|
||||
setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, features);
|
||||
setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, features);
|
||||
}
|
||||
constexpr void setup_internals_ff()
|
||||
{
|
||||
Features features {};
|
||||
features.is_ff = true;
|
||||
setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}, features);
|
||||
setup_type(ID($ff), {ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}, features);
|
||||
setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}, features);
|
||||
setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}, features);
|
||||
setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
|
||||
setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
|
||||
setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}, features);
|
||||
setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
|
||||
}
|
||||
constexpr void setup_internals_anyinit()
|
||||
{
|
||||
Features features {};
|
||||
features.is_anyinit = true;
|
||||
setup_type(ID($anyinit), {ID::D}, {ID::Q}, features);
|
||||
}
|
||||
constexpr void setup_internals_mem_noff()
|
||||
{
|
||||
Features features {};
|
||||
features.is_mem_noff = true;
|
||||
// NOT setup_internals_ff()
|
||||
|
||||
setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}, features);
|
||||
setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}, features);
|
||||
setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
|
||||
setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
|
||||
setup_type(ID($meminit), {ID::ADDR, ID::DATA}, {}, features);
|
||||
setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, {}, features);
|
||||
setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
|
||||
setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
|
||||
|
||||
// What?
|
||||
setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}, features);
|
||||
}
|
||||
constexpr void setup_stdcells_tristate()
|
||||
{
|
||||
Features features {};
|
||||
features.is_stdcell = true;
|
||||
features.is_tristate = true;
|
||||
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, features);
|
||||
}
|
||||
|
||||
constexpr void setup_stdcells_eval()
|
||||
{
|
||||
Features features {};
|
||||
features.is_stdcell = true;
|
||||
features.is_evaluable = true;
|
||||
setup_type(ID($_BUF_), {ID::A}, {ID::Y}, features);
|
||||
setup_type(ID($_NOT_), {ID::A}, {ID::Y}, features);
|
||||
setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, features);
|
||||
setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
|
||||
setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
|
||||
setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, features);
|
||||
setup_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}, features);
|
||||
setup_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}, features);
|
||||
setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
|
||||
setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
|
||||
setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
|
||||
setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
|
||||
}
|
||||
|
||||
constexpr void setup_stdcells_ff() {
|
||||
Features features {};
|
||||
features.is_stdcell = true;
|
||||
features.is_ff = true;
|
||||
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// setup_type(std::string("$_SR_") + c1 + c2 + "_", {ID::S, ID::R}, {ID::Q}, features);
|
||||
setup_type(ID($_SR_NN_), {ID::S, ID::R}, {ID::Q}, features);
|
||||
setup_type(ID($_SR_NP_), {ID::S, ID::R}, {ID::Q}, features);
|
||||
setup_type(ID($_SR_PN_), {ID::S, ID::R}, {ID::Q}, features);
|
||||
setup_type(ID($_SR_PP_), {ID::S, ID::R}, {ID::Q}, features);
|
||||
|
||||
setup_type(ID($_FF_), {ID::D}, {ID::Q}, features);
|
||||
|
||||
// for (auto c1 : list_np)
|
||||
// setup_type(std::string("$_DFF_") + c1 + "_", {ID::C, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID::$_DFF_N_, {ID::C, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID::$_DFF_P_, {ID::C, ID::D}, {ID::Q}, features);
|
||||
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// setup_type(std::string("$_DFFE_") + c1 + c2 + "_", {ID::C, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID::$_DFFE_NN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID::$_DFFE_NP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID::$_DFFE_PN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID::$_DFFE_PP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_01)
|
||||
// setup_type(std::string("$_DFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_01)
|
||||
// for (auto c4 : list_np)
|
||||
// setup_type(std::string("$_DFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// setup_type(std::string("$_ALDFF_") + c1 + c2 + "_", {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFF_NN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFF_NP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFF_PN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFF_PP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_np)
|
||||
// setup_type(std::string("$_ALDFFE_") + c1 + c2 + c3 + "_", {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_NNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_NNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_NPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_NPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_PNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_PNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_PPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_ALDFFE_PPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_np)
|
||||
// setup_type(std::string("$_DFFSR_") + c1 + c2 + c3 + "_", {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_NNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_NNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_NPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_NPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_PNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_PNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_PPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSR_PPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_np)
|
||||
// for (auto c4 : list_np)
|
||||
// setup_type(std::string("$_DFFSRE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_NPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_DFFSRE_PPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_01)
|
||||
// setup_type(std::string("$_SDFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_01)
|
||||
// for (auto c4 : list_np)
|
||||
// setup_type(std::string("$_SDFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_01)
|
||||
// for (auto c4 : list_np)
|
||||
// setup_type(std::string("$_SDFFCE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
setup_type(ID($_SDFFCE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// setup_type(std::string("$_DLATCH_") + c1 + "_", {ID::E, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_N_), {ID::E, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_P_), {ID::E, ID::D}, {ID::Q}, features);
|
||||
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_01)
|
||||
// setup_type(std::string("$_DLATCH_") + c1 + c2 + c3 + "_", {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_NN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_NN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_NP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_NP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_PN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_PN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_PP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCH_PP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
|
||||
// for (auto c1 : list_np)
|
||||
// for (auto c2 : list_np)
|
||||
// for (auto c3 : list_np)
|
||||
// setup_type(std::string("$_DLATCHSR_") + c1 + c2 + c3 + "_", {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_NNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_NNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_NPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_NPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_PNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_PNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_PPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
setup_type(ID($_DLATCHSR_PPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
|
||||
}
|
||||
constexpr CellTableBuilder() {
|
||||
setup_internals_other();
|
||||
setup_internals_eval();
|
||||
setup_internals_ff();
|
||||
setup_internals_anyinit();
|
||||
setup_internals_mem_noff();
|
||||
setup_stdcells_tristate();
|
||||
setup_stdcells_eval();
|
||||
setup_stdcells_ff();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
constexpr CellTableBuilder builder{};
|
||||
|
||||
struct PortInfo {
|
||||
struct PortLists {
|
||||
std::array<CellTableBuilder::PortList, MAX_CELLS> data{};
|
||||
constexpr CellTableBuilder::PortList operator()(IdString type) const {
|
||||
return data[type.index_];
|
||||
}
|
||||
constexpr CellTableBuilder::PortList& operator[](size_t idx) {
|
||||
return data[idx];
|
||||
}
|
||||
constexpr size_t size() const { return data.size(); }
|
||||
};
|
||||
PortLists inputs {};
|
||||
PortLists outputs {};
|
||||
constexpr PortInfo() {
|
||||
for (size_t i = 0; i < builder.count; ++i) {
|
||||
auto& cell = builder.cells[i];
|
||||
size_t idx = cell.type.index_;
|
||||
inputs[idx] = cell.inputs;
|
||||
outputs[idx] = cell.outputs;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Categories {
|
||||
struct Category {
|
||||
std::array<bool, MAX_CELLS> data{};
|
||||
constexpr bool operator()(IdString type) const {
|
||||
size_t idx = type.index_;
|
||||
if (idx >= MAX_CELLS)
|
||||
return false;
|
||||
return data[idx];
|
||||
}
|
||||
constexpr bool operator[](size_t idx) {
|
||||
return data[idx];
|
||||
}
|
||||
constexpr void set_id(IdString type, bool val = true) {
|
||||
size_t idx = type.index_;
|
||||
if (idx >= MAX_CELLS)
|
||||
return; // TODO should be an assert but then it's not constexpr
|
||||
data[idx] = val;
|
||||
}
|
||||
constexpr void set(size_t idx, bool val = true) {
|
||||
data[idx] = val;
|
||||
}
|
||||
constexpr size_t size() const { return data.size(); }
|
||||
};
|
||||
Category empty {};
|
||||
Category is_known {};
|
||||
Category is_evaluable {};
|
||||
Category is_combinatorial {};
|
||||
Category is_synthesizable {};
|
||||
Category is_stdcell {};
|
||||
Category is_ff {};
|
||||
Category is_mem_noff {};
|
||||
Category is_anyinit {};
|
||||
Category is_tristate {};
|
||||
constexpr Categories() {
|
||||
for (size_t i = 0; i < builder.count; ++i) {
|
||||
auto& cell = builder.cells[i];
|
||||
size_t idx = cell.type.index_;
|
||||
is_known.set(idx);
|
||||
is_evaluable.set(idx, cell.features.is_evaluable);
|
||||
is_combinatorial.set(idx, cell.features.is_combinatorial);
|
||||
is_synthesizable.set(idx, cell.features.is_synthesizable);
|
||||
is_stdcell.set(idx, cell.features.is_stdcell);
|
||||
is_ff.set(idx, cell.features.is_ff);
|
||||
is_mem_noff.set(idx, cell.features.is_mem_noff);
|
||||
is_anyinit.set(idx, cell.features.is_anyinit);
|
||||
is_tristate.set(idx, cell.features.is_tristate);
|
||||
}
|
||||
}
|
||||
constexpr static Category join(Category left, Category right) {
|
||||
Category c {};
|
||||
for (size_t i = 0; i < MAX_CELLS; ++i) {
|
||||
c.set(i, left[i] || right[i]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
constexpr static Category meet(Category left, Category right) {
|
||||
Category c {};
|
||||
for (size_t i = 0; i < MAX_CELLS; ++i) {
|
||||
c.set(i, left[i] && right[i]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
// Sketchy! Make sure to always meet with only the known universe.
|
||||
// In other words, no modus tollens allowed
|
||||
constexpr static Category complement(Category arg) {
|
||||
Category c {};
|
||||
for (size_t i = 0; i < MAX_CELLS; ++i) {
|
||||
c.set(i, !arg[i]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
// Pure
|
||||
static constexpr PortInfo port_info;
|
||||
static constexpr Categories categories;
|
||||
|
||||
// Legacy
|
||||
namespace Compat {
|
||||
static constexpr auto internals_all = Categories::meet(categories.is_known, Categories::complement(categories.is_stdcell));
|
||||
static constexpr auto mem_ff = Categories::join(categories.is_ff, categories.is_mem_noff);
|
||||
// old setup_internals + setup_stdcells
|
||||
static constexpr auto nomem_noff = Categories::meet(categories.is_known, Categories::complement(mem_ff));
|
||||
static constexpr auto internals_mem_ff = Categories::meet(internals_all, mem_ff);
|
||||
// old setup_internals
|
||||
static constexpr auto internals_nomem_noff = Categories::meet(internals_all, nomem_noff);
|
||||
// old setup_stdcells
|
||||
static constexpr auto stdcells_nomem_noff = Categories::meet(categories.is_stdcell, nomem_noff);
|
||||
static constexpr auto stdcells_mem = Categories::meet(categories.is_stdcell, categories.is_mem_noff);
|
||||
// old setup_internals_eval
|
||||
// static constexpr auto internals_eval = Categories::meet(internals_all, categories.is_evaluable);
|
||||
};
|
||||
|
||||
namespace {
|
||||
static_assert(categories.is_evaluable(ID($and)));
|
||||
static_assert(!categories.is_ff(ID($and)));
|
||||
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($and)));
|
||||
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($dffsr)));
|
||||
static_assert(!Categories::join(categories.is_evaluable, categories.is_ff)(ID($anyinit)));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct NewCellType {
|
||||
RTLIL::IdString type;
|
||||
pool<RTLIL::IdString> inputs, outputs;
|
||||
bool is_evaluable;
|
||||
bool is_combinatorial;
|
||||
bool is_synthesizable;
|
||||
};
|
||||
|
||||
struct NewCellTypes {
|
||||
struct IdStringHash {
|
||||
std::size_t operator()(const IdString id) const {
|
||||
return static_cast<size_t>(id.hash_top().yield());
|
||||
}
|
||||
};
|
||||
StaticCellTypes::Categories::Category static_cell_types = StaticCellTypes::categories.empty;
|
||||
std::unordered_map<RTLIL::IdString, NewCellType, IdStringHash> custom_cell_types {};
|
||||
|
||||
NewCellTypes() {
|
||||
static_cell_types = StaticCellTypes::categories.empty;
|
||||
}
|
||||
|
||||
NewCellTypes(RTLIL::Design *design) {
|
||||
static_cell_types = StaticCellTypes::categories.empty;
|
||||
setup(design);
|
||||
}
|
||||
void setup(RTLIL::Design *design = NULL) {
|
||||
if (design)
|
||||
setup_design(design);
|
||||
static_cell_types = StaticCellTypes::categories.is_known;
|
||||
}
|
||||
void setup_design(RTLIL::Design *design) {
|
||||
for (auto module : design->modules())
|
||||
setup_module(module);
|
||||
}
|
||||
|
||||
void setup_module(RTLIL::Module *module) {
|
||||
pool<RTLIL::IdString> inputs, outputs;
|
||||
for (RTLIL::IdString wire_name : module->ports) {
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (wire->port_input)
|
||||
inputs.insert(wire->name);
|
||||
if (wire->port_output)
|
||||
outputs.insert(wire->name);
|
||||
}
|
||||
setup_type(module->name, inputs, outputs);
|
||||
}
|
||||
|
||||
void setup_type(RTLIL::IdString type, const pool<RTLIL::IdString> &inputs, const pool<RTLIL::IdString> &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false) {
|
||||
NewCellType ct = {type, inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable};
|
||||
custom_cell_types[ct.type] = ct;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
custom_cell_types.clear();
|
||||
static_cell_types = StaticCellTypes::categories.empty;
|
||||
}
|
||||
|
||||
bool cell_known(const RTLIL::IdString &type) const {
|
||||
return static_cell_types(type) || custom_cell_types.count(type) != 0;
|
||||
}
|
||||
|
||||
bool cell_output(const RTLIL::IdString &type, const RTLIL::IdString &port) const
|
||||
{
|
||||
if (static_cell_types(type) && StaticCellTypes::port_info.outputs(type).contains(port)) {
|
||||
return true;
|
||||
}
|
||||
auto it = custom_cell_types.find(type);
|
||||
return it != custom_cell_types.end() && it->second.outputs.count(port) != 0;
|
||||
}
|
||||
|
||||
bool cell_input(const RTLIL::IdString &type, const RTLIL::IdString &port) const
|
||||
{
|
||||
if (static_cell_types(type) && StaticCellTypes::port_info.inputs(type).contains(port)) {
|
||||
return true;
|
||||
}
|
||||
auto it = custom_cell_types.find(type);
|
||||
return it != custom_cell_types.end() && it->second.inputs.count(port) != 0;
|
||||
}
|
||||
|
||||
RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const
|
||||
{
|
||||
bool is_input, is_output;
|
||||
if (static_cell_types(type)) {
|
||||
is_input = StaticCellTypes::port_info.inputs(type).contains(port);
|
||||
is_output = StaticCellTypes::port_info.outputs(type).contains(port);
|
||||
} else {
|
||||
auto it = custom_cell_types.find(type);
|
||||
if (it == custom_cell_types.end())
|
||||
return RTLIL::PD_UNKNOWN;
|
||||
is_input = it->second.inputs.count(port);
|
||||
is_output = it->second.outputs.count(port);
|
||||
}
|
||||
return RTLIL::PortDir(is_input + is_output * 2);
|
||||
}
|
||||
bool cell_evaluable(const RTLIL::IdString &type) const
|
||||
{
|
||||
return static_cell_types(type) && StaticCellTypes::categories.is_evaluable(type);
|
||||
}
|
||||
};
|
||||
|
||||
extern NewCellTypes yosys_celltypes;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -22,11 +22,13 @@
|
|||
#include "kernel/json.h"
|
||||
#include "kernel/gzip.h"
|
||||
#include "kernel/log_help.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <filesystem>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -59,7 +61,7 @@ void try_collect_garbage()
|
|||
RTLIL::OwningIdString::collect_garbage();
|
||||
}
|
||||
|
||||
Pass::Pass(std::string name, std::string short_help, source_location location) :
|
||||
Pass::Pass(std::string name, std::string short_help, source_location location) :
|
||||
pass_name(name), short_help(short_help), location(location)
|
||||
{
|
||||
next_queued_pass = first_queued_pass;
|
||||
|
|
@ -216,7 +218,7 @@ void Pass::call(RTLIL::Design *design, std::string command)
|
|||
return;
|
||||
|
||||
if (tok[0] == '!') {
|
||||
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(YOSYS_ENABLE_SPAWN)
|
||||
cmd_buf = command.substr(command.find('!') + 1);
|
||||
while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' ||
|
||||
cmd_buf.back() == '\r' || cmd_buf.back() == '\n'))
|
||||
|
|
@ -740,8 +742,8 @@ static void log_warning_flags(Pass *pass) {
|
|||
static struct CellHelpMessages {
|
||||
dict<string, SimHelper> cell_help;
|
||||
CellHelpMessages() {
|
||||
#include "techlibs/common/simlib_help.inc"
|
||||
#include "techlibs/common/simcells_help.inc"
|
||||
#include "kernel/simlib_help.inc"
|
||||
#include "kernel/simcells_help.inc"
|
||||
cell_help.sort();
|
||||
}
|
||||
bool contains(string name) { return cell_help.count(get_cell_name(name)) > 0; }
|
||||
|
|
@ -771,6 +773,10 @@ struct HelpPass : public Pass {
|
|||
bool raise_error = false;
|
||||
std::map<string, vector<string>> groups;
|
||||
|
||||
// get root path
|
||||
auto this_path = std::filesystem::path(source_location::current().file_name());
|
||||
auto source_root = this_path.parent_path().parent_path();
|
||||
|
||||
json.name("cmds"); json.begin_object();
|
||||
// iterate over commands
|
||||
for (auto &it : pass_register) {
|
||||
|
|
@ -911,10 +917,29 @@ struct HelpPass : public Pass {
|
|||
}
|
||||
}
|
||||
|
||||
// fix path
|
||||
string source_file = pass->location.file_name();
|
||||
bool has_source = source_file.compare("unknown") != 0;
|
||||
std::filesystem::path source_path;
|
||||
auto no_source_group = false;
|
||||
if (has_source) {
|
||||
source_path = std::filesystem::path(pass->location.file_name());
|
||||
if (source_path.is_absolute()) {
|
||||
// using proximate instead of relative means that we
|
||||
// still get the source path if they aren't relative
|
||||
auto proximate_path = std::filesystem::proximate(source_path, source_root);
|
||||
if (proximate_path == std::filesystem::weakly_canonical(proximate_path))
|
||||
// we're only interested if it's a subpath of our root dir
|
||||
source_path = proximate_path;
|
||||
else
|
||||
// don't try to group external paths
|
||||
no_source_group = true;
|
||||
}
|
||||
source_file = source_path.string();
|
||||
}
|
||||
|
||||
// attempt auto group
|
||||
if (!cmd_help.has_group()) {
|
||||
string source_file = pass->location.file_name();
|
||||
bool has_source = source_file.compare("unknown") != 0;
|
||||
if (pass->internal_flag)
|
||||
cmd_help.group = "internal";
|
||||
else if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0))
|
||||
|
|
@ -922,11 +947,8 @@ struct HelpPass : public Pass {
|
|||
else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0))
|
||||
cmd_help.group = "frontends";
|
||||
else if (has_source) {
|
||||
auto last_slash = source_file.find_last_of('/');
|
||||
if (last_slash != string::npos) {
|
||||
auto parent_path = source_file.substr(0, last_slash);
|
||||
cmd_help.group = parent_path;
|
||||
}
|
||||
if (source_path.has_parent_path() && !no_source_group)
|
||||
cmd_help.group = source_path.parent_path().string();
|
||||
}
|
||||
// implicit !has_source
|
||||
else if (name.find("equiv") == 0)
|
||||
|
|
@ -954,7 +976,7 @@ struct HelpPass : public Pass {
|
|||
json.value(content.to_json());
|
||||
json.end_array();
|
||||
json.entry("group", cmd_help.group);
|
||||
json.entry("source_file", pass->location.file_name());
|
||||
json.entry("source_file", source_file);
|
||||
json.entry("source_line", pass->location.line());
|
||||
json.entry("source_func", pass->location.function_name());
|
||||
json.entry("experimental_flag", pass->experimental_flag);
|
||||
|
|
@ -975,16 +997,18 @@ struct HelpPass : public Pass {
|
|||
json.entry("generator", yosys_maybe_version());
|
||||
|
||||
dict<string, vector<string>> groups;
|
||||
dict<string, pair<SimHelper, CellType>> cells;
|
||||
dict<string, pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>> cells;
|
||||
|
||||
// iterate over cells
|
||||
bool raise_error = false;
|
||||
for (auto &it : yosys_celltypes.cell_types) {
|
||||
auto name = it.first.str();
|
||||
for (auto it : StaticCellTypes::builder.cells) {
|
||||
if (!StaticCellTypes::categories.is_known(it.type))
|
||||
continue;
|
||||
auto name = it.type.str();
|
||||
if (cell_help_messages.contains(name)) {
|
||||
auto cell_help = cell_help_messages.get(name);
|
||||
groups[cell_help.group].emplace_back(name);
|
||||
auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second);
|
||||
auto cell_pair = pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>(cell_help, it);
|
||||
cells.emplace(name, cell_pair);
|
||||
} else {
|
||||
log("ERROR: Missing cell help for cell '%s'.\n", name);
|
||||
|
|
@ -1025,9 +1049,9 @@ struct HelpPass : public Pass {
|
|||
json.name("outputs"); json.value(outputs);
|
||||
vector<string> properties;
|
||||
// CellType properties
|
||||
if (ct.is_evaluable) properties.push_back("is_evaluable");
|
||||
if (ct.is_combinatorial) properties.push_back("is_combinatorial");
|
||||
if (ct.is_synthesizable) properties.push_back("is_synthesizable");
|
||||
if (ct.features.is_evaluable) properties.push_back("is_evaluable");
|
||||
if (ct.features.is_combinatorial) properties.push_back("is_combinatorial");
|
||||
if (ct.features.is_synthesizable) properties.push_back("is_synthesizable");
|
||||
// SimHelper properties
|
||||
size_t last = 0; size_t next = 0;
|
||||
while ((next = ch.tags.find(", ", last)) != string::npos) {
|
||||
|
|
|
|||
|
|
@ -23,29 +23,13 @@
|
|||
#include "kernel/yosys_common.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
#ifdef YOSYS_ENABLE_HELP_SOURCE
|
||||
#include <version>
|
||||
# if __cpp_lib_source_location == 201907L
|
||||
#include <source_location>
|
||||
using std::source_location;
|
||||
#define HAS_SOURCE_LOCATION
|
||||
# elif defined(__has_include)
|
||||
# if __has_include(<experimental/source_location>)
|
||||
#include <experimental/source_location>
|
||||
using std::experimental::source_location;
|
||||
#define HAS_SOURCE_LOCATION
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef HAS_SOURCE_LOCATION
|
||||
struct source_location { // dummy placeholder
|
||||
int line() const { return 0; }
|
||||
int column() const { return 0; }
|
||||
const char* file_name() const { return "unknown"; }
|
||||
const char* function_name() const { return "unknown"; }
|
||||
static const source_location current(...) { return source_location(); }
|
||||
};
|
||||
#include <version>
|
||||
#if __cpp_lib_source_location >= 201907L
|
||||
#include <source_location>
|
||||
using std::source_location;
|
||||
#else
|
||||
#include <experimental/source_location>
|
||||
using std::experimental::source_location;
|
||||
#endif
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
|
|
|||
606
kernel/rtlil.cc
606
kernel/rtlil.cc
|
|
@ -19,9 +19,10 @@
|
|||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/macc.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/binding.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/threading.h"
|
||||
#include "frontends/verilog/verilog_frontend.h"
|
||||
#include "frontends/verilog/preproc.h"
|
||||
#include "backends/rtlil/rtlil_backend.h"
|
||||
|
|
@ -144,9 +145,17 @@ static constexpr bool check_well_known_id_order()
|
|||
// and in sorted ascii order, as required by the ID macro.
|
||||
static_assert(check_well_known_id_order());
|
||||
|
||||
constexpr int STATIC_ID_END = static_cast<int>(RTLIL::StaticId::STATIC_ID_END);
|
||||
|
||||
struct IdStringCollector {
|
||||
IdStringCollector(std::vector<MonotonicFlag> &live_ids)
|
||||
: live_ids(live_ids) {}
|
||||
|
||||
void trace(IdString id) {
|
||||
live.insert(id.index_);
|
||||
if (id.index_ >= STATIC_ID_END)
|
||||
live_ids[id.index_ - STATIC_ID_END].set();
|
||||
else if (id.index_ < 0)
|
||||
live_autoidx_ids.push_back(id.index_);
|
||||
}
|
||||
template <typename T> void trace(const T* v) {
|
||||
trace(*v);
|
||||
|
|
@ -180,10 +189,6 @@ struct IdStringCollector {
|
|||
trace(element);
|
||||
}
|
||||
|
||||
void trace(const RTLIL::Design &design) {
|
||||
trace_values(design.modules_);
|
||||
trace(design.selection_vars);
|
||||
}
|
||||
void trace(const RTLIL::Selection &selection_var) {
|
||||
trace(selection_var.selected_modules);
|
||||
trace(selection_var.selected_members);
|
||||
|
|
@ -192,15 +197,6 @@ struct IdStringCollector {
|
|||
trace_keys(named.attributes);
|
||||
trace(named.name);
|
||||
}
|
||||
void trace(const RTLIL::Module &module) {
|
||||
trace_named(module);
|
||||
trace_values(module.wires_);
|
||||
trace_values(module.cells_);
|
||||
trace(module.avail_parameters);
|
||||
trace_keys(module.parameter_default_values);
|
||||
trace_values(module.memories);
|
||||
trace_values(module.processes);
|
||||
}
|
||||
void trace(const RTLIL::Wire &wire) {
|
||||
trace_named(wire);
|
||||
if (wire.known_driver())
|
||||
|
|
@ -236,7 +232,8 @@ struct IdStringCollector {
|
|||
trace(action.memid);
|
||||
}
|
||||
|
||||
std::unordered_set<int> live;
|
||||
std::vector<MonotonicFlag> &live_ids;
|
||||
std::vector<int> live_autoidx_ids;
|
||||
};
|
||||
|
||||
int64_t RTLIL::OwningIdString::gc_ns;
|
||||
|
|
@ -245,20 +242,55 @@ int RTLIL::OwningIdString::gc_count;
|
|||
void RTLIL::OwningIdString::collect_garbage()
|
||||
{
|
||||
int64_t start = PerformanceTimer::query();
|
||||
IdStringCollector collector;
|
||||
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
|
||||
collector.trace(*design);
|
||||
}
|
||||
int size = GetSize(global_id_storage_);
|
||||
for (int i = static_cast<int>(StaticId::STATIC_ID_END); i < size; ++i) {
|
||||
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
|
||||
if (storage.buf == nullptr)
|
||||
continue;
|
||||
if (collector.live.find(i) != collector.live.end())
|
||||
continue;
|
||||
if (global_refcount_storage_.find(i) != global_refcount_storage_.end())
|
||||
continue;
|
||||
|
||||
int pool_size = 0;
|
||||
for (auto &[idx, design] : *RTLIL::Design::get_all_designs())
|
||||
for (RTLIL::Module *module : design->modules())
|
||||
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
|
||||
ParallelDispatchThreadPool thread_pool(pool_size);
|
||||
|
||||
int size = GetSize(global_id_storage_);
|
||||
std::vector<MonotonicFlag> live_ids(size - STATIC_ID_END);
|
||||
std::vector<IdStringCollector> collectors;
|
||||
int num_threads = thread_pool.num_threads();
|
||||
collectors.reserve(num_threads);
|
||||
for (int i = 0; i < num_threads; ++i)
|
||||
collectors.emplace_back(live_ids);
|
||||
|
||||
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
|
||||
for (RTLIL::Module *module : design->modules()) {
|
||||
collectors[0].trace_named(*module);
|
||||
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
|
||||
subpool.run([&collectors, module](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(module->cells_size()))
|
||||
collectors[ctx.thread_num].trace(module->cell_at(i));
|
||||
for (int i : ctx.item_range(module->wires_size()))
|
||||
collectors[ctx.thread_num].trace(module->wire_at(i));
|
||||
});
|
||||
collectors[0].trace(module->avail_parameters);
|
||||
collectors[0].trace_keys(module->parameter_default_values);
|
||||
collectors[0].trace_values(module->memories);
|
||||
collectors[0].trace_values(module->processes);
|
||||
}
|
||||
collectors[0].trace(design->selection_vars);
|
||||
}
|
||||
|
||||
ShardedVector<int> free_ids(thread_pool);
|
||||
thread_pool.run([&live_ids, size, &free_ids](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(size - STATIC_ID_END)) {
|
||||
int index = i + STATIC_ID_END;
|
||||
RTLIL::IdString::Storage &storage = global_id_storage_.at(index);
|
||||
if (storage.buf == nullptr)
|
||||
continue;
|
||||
if (live_ids[i].load())
|
||||
continue;
|
||||
if (global_refcount_storage_.find(index) != global_refcount_storage_.end())
|
||||
continue;
|
||||
free_ids.insert(ctx, index);
|
||||
}
|
||||
});
|
||||
for (int i : free_ids) {
|
||||
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Removed IdString '%s' with index %d.\n", storage.buf, i);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
|
|
@ -270,8 +302,13 @@ void RTLIL::OwningIdString::collect_garbage()
|
|||
global_free_idx_list_.push_back(i);
|
||||
}
|
||||
|
||||
std::unordered_set<int> live_autoidx_ids;
|
||||
for (IdStringCollector &collector : collectors)
|
||||
for (int id : collector.live_autoidx_ids)
|
||||
live_autoidx_ids.insert(id);
|
||||
|
||||
for (auto it = global_autoidx_id_storage_.begin(); it != global_autoidx_id_storage_.end();) {
|
||||
if (collector.live.find(it->first) != collector.live.end()) {
|
||||
if (live_autoidx_ids.find(it->first) != live_autoidx_ids.end()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -290,159 +327,17 @@ void RTLIL::OwningIdString::collect_garbage()
|
|||
|
||||
dict<std::string, std::string> RTLIL::constpad;
|
||||
|
||||
static const pool<IdString> &builtin_ff_cell_types_internal() {
|
||||
static const pool<IdString> res = {
|
||||
ID($sr),
|
||||
ID($ff),
|
||||
ID($dff),
|
||||
ID($dffe),
|
||||
ID($dffsr),
|
||||
ID($dffsre),
|
||||
ID($adff),
|
||||
ID($adffe),
|
||||
ID($aldff),
|
||||
ID($aldffe),
|
||||
ID($sdff),
|
||||
ID($sdffe),
|
||||
ID($sdffce),
|
||||
ID($dlatch),
|
||||
ID($adlatch),
|
||||
ID($dlatchsr),
|
||||
ID($_DFFE_NN_),
|
||||
ID($_DFFE_NP_),
|
||||
ID($_DFFE_PN_),
|
||||
ID($_DFFE_PP_),
|
||||
ID($_DFFSR_NNN_),
|
||||
ID($_DFFSR_NNP_),
|
||||
ID($_DFFSR_NPN_),
|
||||
ID($_DFFSR_NPP_),
|
||||
ID($_DFFSR_PNN_),
|
||||
ID($_DFFSR_PNP_),
|
||||
ID($_DFFSR_PPN_),
|
||||
ID($_DFFSR_PPP_),
|
||||
ID($_DFFSRE_NNNN_),
|
||||
ID($_DFFSRE_NNNP_),
|
||||
ID($_DFFSRE_NNPN_),
|
||||
ID($_DFFSRE_NNPP_),
|
||||
ID($_DFFSRE_NPNN_),
|
||||
ID($_DFFSRE_NPNP_),
|
||||
ID($_DFFSRE_NPPN_),
|
||||
ID($_DFFSRE_NPPP_),
|
||||
ID($_DFFSRE_PNNN_),
|
||||
ID($_DFFSRE_PNNP_),
|
||||
ID($_DFFSRE_PNPN_),
|
||||
ID($_DFFSRE_PNPP_),
|
||||
ID($_DFFSRE_PPNN_),
|
||||
ID($_DFFSRE_PPNP_),
|
||||
ID($_DFFSRE_PPPN_),
|
||||
ID($_DFFSRE_PPPP_),
|
||||
ID($_DFF_N_),
|
||||
ID($_DFF_P_),
|
||||
ID($_DFF_NN0_),
|
||||
ID($_DFF_NN1_),
|
||||
ID($_DFF_NP0_),
|
||||
ID($_DFF_NP1_),
|
||||
ID($_DFF_PN0_),
|
||||
ID($_DFF_PN1_),
|
||||
ID($_DFF_PP0_),
|
||||
ID($_DFF_PP1_),
|
||||
ID($_DFFE_NN0N_),
|
||||
ID($_DFFE_NN0P_),
|
||||
ID($_DFFE_NN1N_),
|
||||
ID($_DFFE_NN1P_),
|
||||
ID($_DFFE_NP0N_),
|
||||
ID($_DFFE_NP0P_),
|
||||
ID($_DFFE_NP1N_),
|
||||
ID($_DFFE_NP1P_),
|
||||
ID($_DFFE_PN0N_),
|
||||
ID($_DFFE_PN0P_),
|
||||
ID($_DFFE_PN1N_),
|
||||
ID($_DFFE_PN1P_),
|
||||
ID($_DFFE_PP0N_),
|
||||
ID($_DFFE_PP0P_),
|
||||
ID($_DFFE_PP1N_),
|
||||
ID($_DFFE_PP1P_),
|
||||
ID($_ALDFF_NN_),
|
||||
ID($_ALDFF_NP_),
|
||||
ID($_ALDFF_PN_),
|
||||
ID($_ALDFF_PP_),
|
||||
ID($_ALDFFE_NNN_),
|
||||
ID($_ALDFFE_NNP_),
|
||||
ID($_ALDFFE_NPN_),
|
||||
ID($_ALDFFE_NPP_),
|
||||
ID($_ALDFFE_PNN_),
|
||||
ID($_ALDFFE_PNP_),
|
||||
ID($_ALDFFE_PPN_),
|
||||
ID($_ALDFFE_PPP_),
|
||||
ID($_SDFF_NN0_),
|
||||
ID($_SDFF_NN1_),
|
||||
ID($_SDFF_NP0_),
|
||||
ID($_SDFF_NP1_),
|
||||
ID($_SDFF_PN0_),
|
||||
ID($_SDFF_PN1_),
|
||||
ID($_SDFF_PP0_),
|
||||
ID($_SDFF_PP1_),
|
||||
ID($_SDFFE_NN0N_),
|
||||
ID($_SDFFE_NN0P_),
|
||||
ID($_SDFFE_NN1N_),
|
||||
ID($_SDFFE_NN1P_),
|
||||
ID($_SDFFE_NP0N_),
|
||||
ID($_SDFFE_NP0P_),
|
||||
ID($_SDFFE_NP1N_),
|
||||
ID($_SDFFE_NP1P_),
|
||||
ID($_SDFFE_PN0N_),
|
||||
ID($_SDFFE_PN0P_),
|
||||
ID($_SDFFE_PN1N_),
|
||||
ID($_SDFFE_PN1P_),
|
||||
ID($_SDFFE_PP0N_),
|
||||
ID($_SDFFE_PP0P_),
|
||||
ID($_SDFFE_PP1N_),
|
||||
ID($_SDFFE_PP1P_),
|
||||
ID($_SDFFCE_NN0N_),
|
||||
ID($_SDFFCE_NN0P_),
|
||||
ID($_SDFFCE_NN1N_),
|
||||
ID($_SDFFCE_NN1P_),
|
||||
ID($_SDFFCE_NP0N_),
|
||||
ID($_SDFFCE_NP0P_),
|
||||
ID($_SDFFCE_NP1N_),
|
||||
ID($_SDFFCE_NP1P_),
|
||||
ID($_SDFFCE_PN0N_),
|
||||
ID($_SDFFCE_PN0P_),
|
||||
ID($_SDFFCE_PN1N_),
|
||||
ID($_SDFFCE_PN1P_),
|
||||
ID($_SDFFCE_PP0N_),
|
||||
ID($_SDFFCE_PP0P_),
|
||||
ID($_SDFFCE_PP1N_),
|
||||
ID($_SDFFCE_PP1P_),
|
||||
ID($_SR_NN_),
|
||||
ID($_SR_NP_),
|
||||
ID($_SR_PN_),
|
||||
ID($_SR_PP_),
|
||||
ID($_DLATCH_N_),
|
||||
ID($_DLATCH_P_),
|
||||
ID($_DLATCH_NN0_),
|
||||
ID($_DLATCH_NN1_),
|
||||
ID($_DLATCH_NP0_),
|
||||
ID($_DLATCH_NP1_),
|
||||
ID($_DLATCH_PN0_),
|
||||
ID($_DLATCH_PN1_),
|
||||
ID($_DLATCH_PP0_),
|
||||
ID($_DLATCH_PP1_),
|
||||
ID($_DLATCHSR_NNN_),
|
||||
ID($_DLATCHSR_NNP_),
|
||||
ID($_DLATCHSR_NPN_),
|
||||
ID($_DLATCHSR_NPP_),
|
||||
ID($_DLATCHSR_PNN_),
|
||||
ID($_DLATCHSR_PNP_),
|
||||
ID($_DLATCHSR_PPN_),
|
||||
ID($_DLATCHSR_PPP_),
|
||||
ID($_FF_),
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
const pool<IdString> &RTLIL::builtin_ff_cell_types() {
|
||||
return builtin_ff_cell_types_internal();
|
||||
static const pool<IdString> res = []() {
|
||||
pool<IdString> r;
|
||||
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
|
||||
auto &cell = StaticCellTypes::builder.cells[i];
|
||||
if (cell.features.is_ff)
|
||||
r.insert(cell.type);
|
||||
}
|
||||
return r;
|
||||
}();
|
||||
return res;
|
||||
}
|
||||
|
||||
#define check(condition) log_assert(condition && "malformed Const union")
|
||||
|
|
@ -1333,7 +1228,7 @@ void RTLIL::Design::add(RTLIL::Module *module)
|
|||
mon->notify_module_add(module);
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# New Module: %s\n", log_id(module));
|
||||
log("#X# New Module: %s\n", module);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1361,7 +1256,7 @@ RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
|
|||
mon->notify_module_add(module);
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# New Module: %s\n", log_id(module));
|
||||
log("#X# New Module: %s\n", module);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
|
|
@ -1439,7 +1334,7 @@ void RTLIL::Design::remove(RTLIL::Module *module)
|
|||
mon->notify_module_del(module);
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Remove Module: %s\n", log_id(module));
|
||||
log("#X# Remove Module: %s\n", module);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
|
|
@ -1470,15 +1365,21 @@ void RTLIL::Design::sort_modules()
|
|||
modules_.sort(sort_by_id_str());
|
||||
}
|
||||
|
||||
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool);
|
||||
|
||||
void RTLIL::Design::check()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
log_assert(!selection_stack.empty());
|
||||
int pool_size = 0;
|
||||
for (auto &it : modules_)
|
||||
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, it.second->cells_size(), 1000));
|
||||
ParallelDispatchThreadPool thread_pool(pool_size);
|
||||
for (auto &it : modules_) {
|
||||
log_assert(this == it.second->design);
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
it.second->check();
|
||||
check_module(it.second, thread_pool);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1575,22 +1476,22 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartial
|
|||
switch (boxes)
|
||||
{
|
||||
case RTLIL::SB_UNBOXED_WARN:
|
||||
log_warning("Ignoring boxed module %s.\n", log_id(it.first));
|
||||
log_warning("Ignoring boxed module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SB_EXCL_BB_WARN:
|
||||
log_warning("Ignoring blackbox module %s.\n", log_id(it.first));
|
||||
log_warning("Ignoring blackbox module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SB_UNBOXED_ERR:
|
||||
log_error("Unsupported boxed module %s.\n", log_id(it.first));
|
||||
log_error("Unsupported boxed module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SB_EXCL_BB_ERR:
|
||||
log_error("Unsupported blackbox module %s.\n", log_id(it.first));
|
||||
log_error("Unsupported blackbox module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SB_UNBOXED_CMDERR:
|
||||
log_cmd_error("Unsupported boxed module %s.\n", log_id(it.first));
|
||||
log_cmd_error("Unsupported boxed module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SB_EXCL_BB_CMDERR:
|
||||
log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.first));
|
||||
log_cmd_error("Unsupported blackbox module %s.\n", it.first.unescape());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1599,13 +1500,13 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartial
|
|||
switch(partials)
|
||||
{
|
||||
case RTLIL::SELECT_WHOLE_WARN:
|
||||
log_warning("Ignoring partially selected module %s.\n", log_id(it.first));
|
||||
log_warning("Ignoring partially selected module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SELECT_WHOLE_ERR:
|
||||
log_error("Unsupported partially selected module %s.\n", log_id(it.first));
|
||||
log_error("Unsupported partially selected module %s.\n", it.first.unescape());
|
||||
break;
|
||||
case RTLIL::SELECT_WHOLE_CMDERR:
|
||||
log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.first));
|
||||
log_cmd_error("Unsupported partially selected module %s.\n", it.first.unescape());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1682,7 +1583,7 @@ void RTLIL::Module::makeblackbox()
|
|||
|
||||
void RTLIL::Module::expand_interfaces(RTLIL::Design *, const dict<RTLIL::IdString, RTLIL::Module *> &)
|
||||
{
|
||||
log_error("Class doesn't support expand_interfaces (module: `%s')!\n", id2cstr(name));
|
||||
log_error("Class doesn't support expand_interfaces (module: `%s')!\n", name.unescape());
|
||||
}
|
||||
|
||||
bool RTLIL::Module::reprocess_if_necessary(RTLIL::Design *)
|
||||
|
|
@ -1694,7 +1595,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString
|
|||
{
|
||||
if (mayfail)
|
||||
return RTLIL::IdString();
|
||||
log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name));
|
||||
log_error("Module `%s' is used with parameters but is not parametric!\n", name.unescape());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1702,7 +1603,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString
|
|||
{
|
||||
if (mayfail)
|
||||
return RTLIL::IdString();
|
||||
log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name));
|
||||
log_error("Module `%s' is used with parameters but is not parametric!\n", name.unescape());
|
||||
}
|
||||
|
||||
size_t RTLIL::Module::count_id(RTLIL::IdString id)
|
||||
|
|
@ -1714,11 +1615,11 @@ size_t RTLIL::Module::count_id(RTLIL::IdString id)
|
|||
namespace {
|
||||
struct InternalCellChecker
|
||||
{
|
||||
RTLIL::Module *module;
|
||||
const RTLIL::Module *module;
|
||||
RTLIL::Cell *cell;
|
||||
pool<RTLIL::IdString> expected_params, expected_ports;
|
||||
|
||||
InternalCellChecker(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
|
||||
InternalCellChecker(const RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
|
||||
|
||||
void error(int linenr)
|
||||
{
|
||||
|
|
@ -2703,88 +2604,96 @@ void RTLIL::Module::sort()
|
|||
it.second->attributes.sort(sort_by_id_str());
|
||||
}
|
||||
|
||||
void RTLIL::Module::check()
|
||||
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::vector<bool> ports_declared;
|
||||
for (auto &it : wires_) {
|
||||
log_assert(this == it.second->module);
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
log_assert(it.second->width >= 0);
|
||||
log_assert(it.second->port_id >= 0);
|
||||
for (auto &it2 : it.second->attributes)
|
||||
log_assert(!it2.first.empty());
|
||||
if (it.second->port_id) {
|
||||
log_assert(GetSize(ports) >= it.second->port_id);
|
||||
log_assert(ports.at(it.second->port_id-1) == it.first);
|
||||
log_assert(it.second->port_input || it.second->port_output);
|
||||
if (GetSize(ports_declared) < it.second->port_id)
|
||||
ports_declared.resize(it.second->port_id);
|
||||
log_assert(ports_declared[it.second->port_id-1] == false);
|
||||
ports_declared[it.second->port_id-1] = true;
|
||||
} else
|
||||
log_assert(!it.second->port_input && !it.second->port_output);
|
||||
}
|
||||
for (auto port_declared : ports_declared)
|
||||
log_assert(port_declared == true);
|
||||
log_assert(GetSize(ports) == GetSize(ports_declared));
|
||||
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
|
||||
const RTLIL::Module *const_module = module;
|
||||
|
||||
for (auto &it : memories) {
|
||||
pool<std::string> memory_strings;
|
||||
for (auto &it : module->memories) {
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
log_assert(it.second->width >= 0);
|
||||
log_assert(it.second->size >= 0);
|
||||
for (auto &it2 : it.second->attributes)
|
||||
log_assert(!it2.first.empty());
|
||||
memory_strings.insert(it.second->name.str());
|
||||
}
|
||||
|
||||
pool<IdString> packed_memids;
|
||||
std::vector<MonotonicFlag> ports_declared(GetSize(module->ports));
|
||||
ShardedVector<std::string> memids(subpool);
|
||||
subpool.run([const_module, &ports_declared, &memory_strings, &memids](const ParallelDispatchThreadPool::RunCtx &ctx) {
|
||||
for (int i : ctx.item_range(const_module->cells_size())) {
|
||||
auto it = *const_module->cells_.element(i);
|
||||
log_assert(const_module == it.second->module);
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
log_assert(!it.second->type.empty());
|
||||
for (auto &it2 : it.second->connections()) {
|
||||
log_assert(!it2.first.empty());
|
||||
it2.second.check(const_module);
|
||||
}
|
||||
for (auto &it2 : it.second->attributes)
|
||||
log_assert(!it2.first.empty());
|
||||
for (auto &it2 : it.second->parameters)
|
||||
log_assert(!it2.first.empty());
|
||||
InternalCellChecker checker(const_module, it.second);
|
||||
checker.check();
|
||||
if (it.second->has_memid()) {
|
||||
log_assert(memory_strings.count(it.second->parameters.at(ID::MEMID).decode_string()));
|
||||
} else if (it.second->is_mem_cell()) {
|
||||
std::string memid = it.second->parameters.at(ID::MEMID).decode_string();
|
||||
log_assert(!memory_strings.count(memid));
|
||||
memids.insert(ctx, std::move(memid));
|
||||
}
|
||||
auto cell_mod = const_module->design->module(it.first);
|
||||
if (cell_mod != nullptr) {
|
||||
// assertion check below to make sure that there are no
|
||||
// cases where a cell has a blackbox attribute since
|
||||
// that is deprecated
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
log_assert(!it.second->get_blackbox_attribute());
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : cells_) {
|
||||
log_assert(this == it.second->module);
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
log_assert(!it.second->type.empty());
|
||||
for (auto &it2 : it.second->connections()) {
|
||||
log_assert(!it2.first.empty());
|
||||
it2.second.check(this);
|
||||
for (int i : ctx.item_range(const_module->wires_size())) {
|
||||
auto it = *const_module->wires_.element(i);
|
||||
log_assert(const_module == it.second->module);
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
log_assert(it.second->width >= 0);
|
||||
log_assert(it.second->port_id >= 0);
|
||||
for (auto &it2 : it.second->attributes)
|
||||
log_assert(!it2.first.empty());
|
||||
if (it.second->port_id) {
|
||||
log_assert(GetSize(const_module->ports) >= it.second->port_id);
|
||||
log_assert(const_module->ports.at(it.second->port_id-1) == it.first);
|
||||
log_assert(it.second->port_input || it.second->port_output);
|
||||
log_assert(it.second->port_id <= GetSize(ports_declared));
|
||||
bool previously_declared = ports_declared[it.second->port_id-1].set_and_return_old();
|
||||
log_assert(previously_declared == false);
|
||||
} else
|
||||
log_assert(!it.second->port_input && !it.second->port_output);
|
||||
}
|
||||
for (auto &it2 : it.second->attributes)
|
||||
log_assert(!it2.first.empty());
|
||||
for (auto &it2 : it.second->parameters)
|
||||
log_assert(!it2.first.empty());
|
||||
InternalCellChecker checker(this, it.second);
|
||||
checker.check();
|
||||
if (it.second->has_memid()) {
|
||||
log_assert(memories.count(it.second->parameters.at(ID::MEMID).decode_string()));
|
||||
} else if (it.second->is_mem_cell()) {
|
||||
IdString memid = it.second->parameters.at(ID::MEMID).decode_string();
|
||||
log_assert(!memories.count(memid));
|
||||
log_assert(!packed_memids.count(memid));
|
||||
packed_memids.insert(memid);
|
||||
}
|
||||
auto cell_mod = design->module(it.first);
|
||||
if (cell_mod != nullptr) {
|
||||
// assertion check below to make sure that there are no
|
||||
// cases where a cell has a blackbox attribute since
|
||||
// that is deprecated
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
log_assert(!it.second->get_blackbox_attribute());
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
for (const MonotonicFlag &port_declared : ports_declared)
|
||||
log_assert(port_declared.load() == true);
|
||||
pool<std::string> memids_pool;
|
||||
for (std::string &memid : memids)
|
||||
log_assert(memids_pool.insert(memid).second);
|
||||
|
||||
for (auto &it : processes) {
|
||||
for (auto &it : module->processes) {
|
||||
log_assert(it.first == it.second->name);
|
||||
log_assert(!it.first.empty());
|
||||
log_assert(it.second->root_case.compare.empty());
|
||||
std::vector<CaseRule*> all_cases = {&it.second->root_case};
|
||||
std::vector<RTLIL::CaseRule*> all_cases = {&it.second->root_case};
|
||||
for (size_t i = 0; i < all_cases.size(); i++) {
|
||||
for (auto &switch_it : all_cases[i]->switches) {
|
||||
for (auto &case_it : switch_it->cases) {
|
||||
|
|
@ -2797,34 +2706,41 @@ void RTLIL::Module::check()
|
|||
}
|
||||
for (auto &sync_it : it.second->syncs) {
|
||||
switch (sync_it->type) {
|
||||
case SyncType::ST0:
|
||||
case SyncType::ST1:
|
||||
case SyncType::STp:
|
||||
case SyncType::STn:
|
||||
case SyncType::STe:
|
||||
case RTLIL::SyncType::ST0:
|
||||
case RTLIL::SyncType::ST1:
|
||||
case RTLIL::SyncType::STp:
|
||||
case RTLIL::SyncType::STn:
|
||||
case RTLIL::SyncType::STe:
|
||||
log_assert(!sync_it->signal.empty());
|
||||
break;
|
||||
case SyncType::STa:
|
||||
case SyncType::STg:
|
||||
case SyncType::STi:
|
||||
case RTLIL::SyncType::STa:
|
||||
case RTLIL::SyncType::STg:
|
||||
case RTLIL::SyncType::STi:
|
||||
log_assert(sync_it->signal.empty());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : connections_) {
|
||||
for (auto &it : module->connections_) {
|
||||
log_assert(it.first.size() == it.second.size());
|
||||
log_assert(!it.first.has_const());
|
||||
it.first.check(this);
|
||||
it.second.check(this);
|
||||
it.first.check(module);
|
||||
it.second.check(module);
|
||||
}
|
||||
|
||||
for (auto &it : attributes)
|
||||
for (auto &it : module->attributes)
|
||||
log_assert(!it.first.empty());
|
||||
#endif
|
||||
}
|
||||
|
||||
void RTLIL::Module::check()
|
||||
{
|
||||
int pool_size = ThreadPool::work_pool_size(0, cells_size(), 1000);
|
||||
ParallelDispatchThreadPool thread_pool(pool_size);
|
||||
check_module(this, thread_pool);
|
||||
}
|
||||
|
||||
void RTLIL::Module::optimize()
|
||||
{
|
||||
}
|
||||
|
|
@ -2893,14 +2809,14 @@ bool RTLIL::Module::has_processes() const
|
|||
bool RTLIL::Module::has_memories_warn() const
|
||||
{
|
||||
if (!memories.empty())
|
||||
log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", log_id(this));
|
||||
log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", this);
|
||||
return !memories.empty();
|
||||
}
|
||||
|
||||
bool RTLIL::Module::has_processes_warn() const
|
||||
{
|
||||
if (!processes.empty())
|
||||
log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", log_id(this));
|
||||
log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", this);
|
||||
return !processes.empty();
|
||||
}
|
||||
|
||||
|
|
@ -3206,7 +3122,7 @@ void RTLIL::Module::connect(const RTLIL::SigSig &conn)
|
|||
}
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", log_id(this), log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
|
||||
log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", this, log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
|
|
@ -3229,7 +3145,7 @@ void RTLIL::Module::new_connections(const std::vector<RTLIL::SigSig> &new_conn)
|
|||
mon->notify_connect(this, new_conn);
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# New connections vector in %s:\n", log_id(this));
|
||||
log("#X# New connections vector in %s:\n", this);
|
||||
for (auto &conn: new_conn)
|
||||
log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
|
|
@ -4702,7 +4618,7 @@ bool RTLIL::Cell::is_mem_cell() const
|
|||
}
|
||||
|
||||
bool RTLIL::Cell::is_builtin_ff() const {
|
||||
return builtin_ff_cell_types_internal().count(type) > 0;
|
||||
return StaticCellTypes::categories.is_ff(type);
|
||||
}
|
||||
|
||||
RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
|
||||
|
|
@ -5166,31 +5082,35 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe
|
|||
other->unpack();
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
bool other_modified = false;
|
||||
for (int i = GetSize(bits_) - 1; i >= 0; i--)
|
||||
{
|
||||
if (bits_[i].wire == NULL) continue;
|
||||
// Convert pattern to pool for O(1) lookup, avoiding O(n*m) chunk iteration
|
||||
pool<SigBit> pattern_bits;
|
||||
pattern_bits.reserve(pattern.size());
|
||||
for (auto &bit : pattern)
|
||||
if (bit.wire != NULL)
|
||||
pattern_bits.insert(bit);
|
||||
|
||||
for (auto &pattern_chunk : pattern.chunks())
|
||||
if (bits_[i].wire == pattern_chunk.wire &&
|
||||
bits_[i].offset >= pattern_chunk.offset &&
|
||||
bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) {
|
||||
modified = true;
|
||||
bits_.erase(bits_.begin() + i);
|
||||
if (other != NULL) {
|
||||
other_modified = true;
|
||||
other->bits_.erase(other->bits_.begin() + i);
|
||||
}
|
||||
break;
|
||||
// Compact in-place to avoid O(n^2) erase operations
|
||||
size_t write_idx = 0;
|
||||
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++)
|
||||
{
|
||||
if (!(bits_[read_idx].wire != NULL && pattern_bits.count(bits_[read_idx]))) {
|
||||
if (write_idx != read_idx) {
|
||||
bits_[write_idx] = bits_[read_idx];
|
||||
if (other != NULL)
|
||||
other->bits_[write_idx] = other->bits_[read_idx];
|
||||
}
|
||||
write_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
bool modified = (write_idx < bits_.size());
|
||||
if (modified) {
|
||||
bits_.resize(write_idx);
|
||||
hash_.clear();
|
||||
try_repack();
|
||||
}
|
||||
if (other_modified) {
|
||||
if (other != NULL && modified) {
|
||||
other->bits_.resize(write_idx);
|
||||
other->hash_.clear();
|
||||
other->try_repack();
|
||||
}
|
||||
|
|
@ -5217,24 +5137,27 @@ void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec
|
|||
other->unpack();
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
bool other_modified = false;
|
||||
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
|
||||
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
|
||||
modified = true;
|
||||
bits_.erase(bits_.begin() + i);
|
||||
if (other != NULL) {
|
||||
other_modified = true;
|
||||
other->bits_.erase(other->bits_.begin() + i);
|
||||
// Avoid O(n^2) complexity by compacting in-place
|
||||
size_t write_idx = 0;
|
||||
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
|
||||
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
|
||||
if (write_idx != read_idx) {
|
||||
bits_[write_idx] = bits_[read_idx];
|
||||
if (other != NULL)
|
||||
other->bits_[write_idx] = other->bits_[read_idx];
|
||||
}
|
||||
write_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
bool modified = (write_idx < bits_.size());
|
||||
if (modified) {
|
||||
bits_.resize(write_idx);
|
||||
hash_.clear();
|
||||
try_repack();
|
||||
}
|
||||
if (other_modified) {
|
||||
if (other != NULL && modified) {
|
||||
other->bits_.resize(write_idx);
|
||||
other->hash_.clear();
|
||||
other->try_repack();
|
||||
}
|
||||
|
|
@ -5250,24 +5173,27 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
|
|||
other->unpack();
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
bool other_modified = false;
|
||||
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
|
||||
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
|
||||
modified = true;
|
||||
bits_.erase(bits_.begin() + i);
|
||||
if (other != NULL) {
|
||||
other_modified = true;
|
||||
other->bits_.erase(other->bits_.begin() + i);
|
||||
// Avoid O(n^2) complexity by compacting in-place
|
||||
size_t write_idx = 0;
|
||||
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
|
||||
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
|
||||
if (write_idx != read_idx) {
|
||||
bits_[write_idx] = bits_[read_idx];
|
||||
if (other != NULL)
|
||||
other->bits_[write_idx] = other->bits_[read_idx];
|
||||
}
|
||||
write_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
bool modified = (write_idx < bits_.size());
|
||||
if (modified) {
|
||||
bits_.resize(write_idx);
|
||||
hash_.clear();
|
||||
try_repack();
|
||||
}
|
||||
if (other_modified) {
|
||||
if (other != NULL && modified) {
|
||||
other->bits_.resize(write_idx);
|
||||
other->hash_.clear();
|
||||
other->try_repack();
|
||||
}
|
||||
|
|
@ -5438,26 +5364,32 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
|
|||
log_assert(length >= 0);
|
||||
log_assert(offset + length <= size());
|
||||
|
||||
SigSpec extracted;
|
||||
Chunks cs = chunks();
|
||||
auto it = cs.begin();
|
||||
for (; offset; offset -= it->width, ++it) {
|
||||
if (offset < it->width) {
|
||||
int chunk_length = min(it->width - offset, length);
|
||||
extracted.append(it->extract(offset, chunk_length));
|
||||
length -= chunk_length;
|
||||
++it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; length; length -= it->width, ++it) {
|
||||
if (length >= it->width) {
|
||||
extracted.append(*it);
|
||||
std::vector<SigBit> extracted;
|
||||
SigBit first;
|
||||
bool is_packing = true;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
bool was_packing_before = is_packing;
|
||||
SigBit bit = (*this)[i];
|
||||
if (i == offset) {
|
||||
first = bit;
|
||||
if (!bit.wire)
|
||||
is_packing = false;
|
||||
} else {
|
||||
extracted.append(it->extract(0, length));
|
||||
break;
|
||||
if (bit.wire != first.wire)
|
||||
is_packing = false;
|
||||
if (bit.wire)
|
||||
if (bit.offset != first.offset + (i - offset))
|
||||
is_packing = false;
|
||||
}
|
||||
if (was_packing_before && !is_packing)
|
||||
for (int j = offset; j < i; j++)
|
||||
extracted.push_back((*this)[j]);
|
||||
if (!is_packing)
|
||||
extracted.push_back((*this)[i]);
|
||||
}
|
||||
if (is_packing)
|
||||
return SigChunk(first.wire, first.offset, length);
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
|
|
@ -5562,7 +5494,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const
|
|||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void RTLIL::SigSpec::check(Module *mod) const
|
||||
void RTLIL::SigSpec::check(const Module *mod) const
|
||||
{
|
||||
if (rep_ == CHUNK)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -275,6 +275,17 @@ struct RTLIL::IdString
|
|||
*out += std::to_string(-index_);
|
||||
}
|
||||
|
||||
std::string unescape() const {
|
||||
if (index_ < 0) {
|
||||
// Must start with "$auto$" so no unescaping required.
|
||||
return str();
|
||||
}
|
||||
std::string_view str = global_id_storage_.at(index_).str_view();
|
||||
if (str.size() < 2 || str[0] != '\\' || str[1] == '$' || str[1] == '\\' || (str[1] >= '0' && str[1] <= '9'))
|
||||
return std::string(str);
|
||||
return std::string(str.substr(1));
|
||||
}
|
||||
|
||||
class Substrings {
|
||||
std::string_view first_;
|
||||
int suffix_number;
|
||||
|
|
@ -737,6 +748,7 @@ template <> struct IDMacroHelper<-1> {
|
|||
namespace RTLIL {
|
||||
extern dict<std::string, std::string> constpad;
|
||||
|
||||
[[deprecated("use StaticCellTypes::categories.is_ff() instead")]]
|
||||
const pool<IdString> &builtin_ff_cell_types();
|
||||
|
||||
static inline std::string escape_id(const std::string &str) {
|
||||
|
|
@ -758,7 +770,7 @@ namespace RTLIL {
|
|||
}
|
||||
|
||||
static inline std::string unescape_id(RTLIL::IdString str) {
|
||||
return unescape_id(str.str());
|
||||
return str.unescape();
|
||||
}
|
||||
|
||||
static inline const char *id2cstr(RTLIL::IdString str) {
|
||||
|
|
@ -1394,6 +1406,8 @@ struct RTLIL::SigSpecConstIterator
|
|||
struct RTLIL::SigSpec
|
||||
{
|
||||
private:
|
||||
friend class SigSpecRepTest;
|
||||
FRIEND_TEST(SigSpecRepTest, Extract);
|
||||
enum Representation : char {
|
||||
CHUNK,
|
||||
BITS,
|
||||
|
|
@ -1758,9 +1772,9 @@ public:
|
|||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void check(Module *mod = nullptr) const;
|
||||
void check(const Module *mod = nullptr) const;
|
||||
#else
|
||||
void check(Module *mod = nullptr) const { (void)mod; }
|
||||
void check(const Module *mod = nullptr) const { (void)mod; }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ void RTLIL::Module::bufNormalize()
|
|||
// already enqueued or becomes reachable when denormalizing $buf or
|
||||
// $connect cells.
|
||||
auto enqueue_cell_port = [&](Cell *cell, IdString port) {
|
||||
xlog("processing cell port %s.%s\n", log_id(cell), log_id(port));
|
||||
xlog("processing cell port %s.%s\n", cell, port.unescape());
|
||||
|
||||
// An empty cell type means the cell got removed
|
||||
if (cell->type.empty())
|
||||
|
|
@ -270,7 +270,7 @@ void RTLIL::Module::bufNormalize()
|
|||
// normalized mode).
|
||||
while (wire_queue_pos < GetSize(wire_queue_entries)) {
|
||||
auto wire = wire_queue_entries[wire_queue_pos++];
|
||||
xlog("processing wire %s\n", log_id(wire));
|
||||
xlog("processing wire %s\n", wire);
|
||||
|
||||
if (wire->driverCell_) {
|
||||
Cell *cell = wire->driverCell_;
|
||||
|
|
@ -287,7 +287,7 @@ void RTLIL::Module::bufNormalize()
|
|||
log_assert(connect_cell->type == ID($connect));
|
||||
SigSpec const &sig_a = connect_cell->getPort(ID::A);
|
||||
SigSpec const &sig_b = connect_cell->getPort(ID::B);
|
||||
xlog("found $connect cell %s: %s <-> %s\n", log_id(connect_cell), log_signal(sig_a), log_signal(sig_b));
|
||||
xlog("found $connect cell %s: %s <-> %s\n", connect_cell, log_signal(sig_a), log_signal(sig_b));
|
||||
for (auto &side : {sig_a, sig_b})
|
||||
for (auto chunk : side.chunks())
|
||||
if (chunk.wire)
|
||||
|
|
@ -452,7 +452,7 @@ void RTLIL::Module::bufNormalize()
|
|||
}
|
||||
|
||||
if (wire->driverCell_ == nullptr) {
|
||||
xlog("wire %s drivers %s\n", log_id(wire), log_signal(wire_drivers));
|
||||
xlog("wire %s drivers %s\n", wire, log_signal(wire_drivers));
|
||||
addBuf(NEW_ID, wire_drivers, wire);
|
||||
}
|
||||
}
|
||||
|
|
@ -541,7 +541,7 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname)
|
|||
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname));
|
||||
log("#X# Unconnect %s.%s.%s\n", this->module, this, portname.unescape());
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
|
|
@ -601,7 +601,7 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
|
|||
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal));
|
||||
log("#X# Connect %s.%s.%s = %s (%d)\n", this->module, this, portname.unescape(), log_signal(signal), GetSize(signal));
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/ff.h"
|
||||
#include "kernel/yosys_common.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
|
|
@ -1378,7 +1379,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == ID($scopeinfo))
|
||||
if (cell->type == ID($scopeinfo) || cell->type == ID($input_port))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1387,3 +1388,22 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
|
|||
// .. and all sequential cells with asynchronous inputs
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace Yosys {
|
||||
|
||||
void report_missing_model(bool warn_only, RTLIL::Cell* cell)
|
||||
{
|
||||
std::string s;
|
||||
if (cell->is_builtin_ff())
|
||||
s = stringf("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", cell, cell->type.unescape());
|
||||
else
|
||||
s = stringf("No SAT model available for cell %s (%s).\n", cell, cell->type.unescape());
|
||||
|
||||
if (warn_only) {
|
||||
log_formatted_warning_noprefix(s);
|
||||
} else {
|
||||
log_formatted_error(s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ struct SatGen
|
|||
else
|
||||
vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->CONST_TRUE : ez->CONST_FALSE);
|
||||
} else {
|
||||
std::string wire_name = RTLIL::unescape_id(bit.wire->name);
|
||||
std::string wire_name = bit.wire->name.unescape();
|
||||
std::string name = pf +
|
||||
(bit.wire->width == 1 ? wire_name : stringf("%s [%d]", wire_name, bit.offset));
|
||||
vec.push_back(ez->frozen_literal(name));
|
||||
|
|
@ -293,6 +293,8 @@ struct SatGen
|
|||
bool importCell(RTLIL::Cell *cell, int timestep = -1);
|
||||
};
|
||||
|
||||
void report_missing_model(bool warn_only, RTLIL::Cell* cell);
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -100,13 +100,13 @@ static const char *attr_prefix(ScopeinfoAttrs attrs)
|
|||
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
|
||||
{
|
||||
log_assert(scopeinfo->type == ID($scopeinfo));
|
||||
return scopeinfo->has_attribute(attr_prefix(attrs) + RTLIL::unescape_id(id));
|
||||
return scopeinfo->has_attribute(attr_prefix(attrs) + id.unescape());
|
||||
}
|
||||
|
||||
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
|
||||
{
|
||||
log_assert(scopeinfo->type == ID($scopeinfo));
|
||||
auto found = scopeinfo->attributes.find(attr_prefix(attrs) + RTLIL::unescape_id(id));
|
||||
auto found = scopeinfo->attributes.find(attr_prefix(attrs) + id.unescape());
|
||||
if (found == scopeinfo->attributes.end())
|
||||
return RTLIL::Const();
|
||||
return found->second;
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ struct ModuleItem {
|
|||
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(ptr); return h; }
|
||||
};
|
||||
|
||||
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }
|
||||
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, cursor.scope_name.unescape()); }
|
||||
|
||||
template<typename T>
|
||||
static inline void log_dump_val_worker(const typename std::unique_ptr<T> &cursor ) { log("unique %p", cursor.get()); }
|
||||
|
|
|
|||
|
|
@ -206,13 +206,16 @@ bool mp_int_to_const(mp_int *a, Const &b, bool is_signed)
|
|||
return false;
|
||||
|
||||
if (negative) {
|
||||
mp_neg(a, a);
|
||||
mp_sub_d(a, 1, a);
|
||||
if (mp_neg(a, a) != MP_OKAY)
|
||||
return false;
|
||||
if (mp_sub_d(a, 1, a) != MP_OKAY)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> buf;
|
||||
buf.resize(mp_unsigned_bin_size(a));
|
||||
mp_to_unsigned_bin(a, buf.data());
|
||||
if (mp_to_unsigned_bin(a, buf.data()) != MP_OKAY)
|
||||
return false;
|
||||
|
||||
Const::Builder b_bits(mp_count_bits(a) + is_signed);
|
||||
for (int i = 0; i < mp_count_bits(a);) {
|
||||
|
|
@ -279,7 +282,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
|||
ERROR("object not found")
|
||||
|
||||
if (string_flag) {
|
||||
Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->get_string_attribute(attr_id).c_str(), -1));
|
||||
} else if (int_flag || uint_flag || sint_flag) {
|
||||
if (!obj->has_attribute(attr_id))
|
||||
ERROR("attribute missing (required for -int)");
|
||||
|
|
@ -295,7 +298,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
|||
if (!obj->has_attribute(attr_id))
|
||||
ERROR("attribute missing (required unless -bool or -string)")
|
||||
|
||||
Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->attributes.at(attr_id).as_string().c_str(), -1));
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
|
|
@ -341,7 +344,7 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
|||
if (!obj)
|
||||
ERROR("object not found")
|
||||
|
||||
Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(std::to_string(obj->has_attribute(attr_id)).c_str(), -1));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
|
@ -465,14 +468,14 @@ static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *a
|
|||
const RTLIL::Const &value = cell->getParam(param_id);
|
||||
|
||||
if (string_flag) {
|
||||
Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.decode_string().c_str(), -1));
|
||||
} else if (int_flag || uint_flag || sint_flag) {
|
||||
mp_int value_mp;
|
||||
if (!const_to_mp_int(value, &value_mp, sint_flag, uint_flag))
|
||||
ERROR("bignum manipulation failed");
|
||||
Tcl_SetObjResult(interp, Tcl_NewBignumObj(&value_mp));
|
||||
} else {
|
||||
Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.as_string().c_str(), -1));
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,20 @@ static int get_max_threads()
|
|||
return max_threads;
|
||||
}
|
||||
|
||||
static int init_work_units_per_thread_override()
|
||||
{
|
||||
const char *v = getenv("YOSYS_WORK_UNITS_PER_THREAD");
|
||||
if (v == nullptr)
|
||||
return 0;
|
||||
return atoi(v);
|
||||
}
|
||||
|
||||
static int get_work_units_per_thread_override()
|
||||
{
|
||||
static int work_units_per_thread = init_work_units_per_thread_override();
|
||||
return work_units_per_thread;
|
||||
}
|
||||
|
||||
void DeferredLogs::flush()
|
||||
{
|
||||
for (auto &m : logs)
|
||||
|
|
@ -31,21 +45,32 @@ int ThreadPool::pool_size(int reserved_cores, int max_worker_threads)
|
|||
#ifdef YOSYS_ENABLE_THREADS
|
||||
int available_threads = std::min<int>(std::thread::hardware_concurrency(), get_max_threads());
|
||||
int num_threads = std::min(available_threads - reserved_cores, max_worker_threads);
|
||||
return std::max(0, num_threads);
|
||||
return std::max(0, num_threads);
|
||||
#else
|
||||
return 0;
|
||||
(void)reserved_cores;
|
||||
(void)max_worker_threads;
|
||||
(void)get_max_threads();
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ThreadPool::work_pool_size(int reserved_cores, int work_units, int work_units_per_thread)
|
||||
{
|
||||
int work_units_per_thread_override = get_work_units_per_thread_override();
|
||||
if (work_units_per_thread_override > 0)
|
||||
work_units_per_thread = work_units_per_thread_override;
|
||||
return pool_size(reserved_cores, work_units / work_units_per_thread);
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
|
||||
: body(std::move(b))
|
||||
{
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
threads.reserve(pool_size);
|
||||
for (int i = 0; i < pool_size; i++)
|
||||
threads.emplace_back([i, this]{ body(i); });
|
||||
threads.reserve(pool_size);
|
||||
for (int i = 0; i < pool_size; i++)
|
||||
threads.emplace_back([i, this]{ body(i); });
|
||||
#else
|
||||
log_assert(pool_size == 0);
|
||||
(void)pool_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -57,4 +82,74 @@ ThreadPool::~ThreadPool()
|
|||
#endif
|
||||
}
|
||||
|
||||
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads)
|
||||
{
|
||||
if (num_threads <= 1) {
|
||||
return {0, num_items};
|
||||
}
|
||||
int items_per_thread = num_items / num_threads;
|
||||
int extra_items = num_items % num_threads;
|
||||
// The first `extra_items` threads get one extra item.
|
||||
int start = thread_num * items_per_thread + std::min(thread_num, extra_items);
|
||||
int end = (thread_num + 1) * items_per_thread + std::min(thread_num + 1, extra_items);
|
||||
return {start, end};
|
||||
}
|
||||
|
||||
ParallelDispatchThreadPool::ParallelDispatchThreadPool(int pool_size)
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
: num_worker_threads_(std::max(1, pool_size) - 1)
|
||||
#else
|
||||
: num_worker_threads_(0)
|
||||
#endif
|
||||
{
|
||||
main_to_workers_signal.resize(num_worker_threads_, 0);
|
||||
// Don't start the threads until we've constructed all our data members.
|
||||
thread_pool = std::make_unique<ThreadPool>(num_worker_threads_, [this](int thread_num){
|
||||
run_worker(thread_num);
|
||||
});
|
||||
}
|
||||
|
||||
ParallelDispatchThreadPool::~ParallelDispatchThreadPool()
|
||||
{
|
||||
if (num_worker_threads_ == 0)
|
||||
return;
|
||||
current_work = nullptr;
|
||||
num_active_worker_threads_.store(num_worker_threads_, std::memory_order_relaxed);
|
||||
signal_workers_start();
|
||||
wait_for_workers_done();
|
||||
}
|
||||
|
||||
void ParallelDispatchThreadPool::run(std::function<void(const RunCtx &)> work, int max_threads)
|
||||
{
|
||||
Multithreading multithreading;
|
||||
int num_active_worker_threads = num_threads(max_threads) - 1;
|
||||
if (num_active_worker_threads == 0) {
|
||||
work({{0}, 1});
|
||||
return;
|
||||
}
|
||||
num_active_worker_threads_.store(num_active_worker_threads, std::memory_order_relaxed);
|
||||
current_work = &work;
|
||||
signal_workers_start();
|
||||
work({{0}, num_active_worker_threads + 1});
|
||||
wait_for_workers_done();
|
||||
}
|
||||
|
||||
void ParallelDispatchThreadPool::run_worker(int thread_num)
|
||||
{
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
while (true)
|
||||
{
|
||||
worker_wait_for_start(thread_num);
|
||||
if (current_work == nullptr)
|
||||
break;
|
||||
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
|
||||
(*current_work)({{thread_num + 1}, num_active_worker_threads + 1});
|
||||
signal_worker_done();
|
||||
}
|
||||
signal_worker_done();
|
||||
#else
|
||||
(void)current_work;
|
||||
#endif
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -1,19 +1,47 @@
|
|||
#include <deque>
|
||||
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/utils.h"
|
||||
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "kernel/log.h"
|
||||
|
||||
#ifndef YOSYS_THREADING_H
|
||||
#define YOSYS_THREADING_H
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
// Redirect to no-op to avoid dependence on <mutex>
|
||||
// and <condition_variable> in single-threaded builds
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
using Mutex = std::mutex;
|
||||
using CondVar = std::condition_variable;
|
||||
using UniqueLock = std::unique_lock<Mutex>;
|
||||
using LockGuard = std::lock_guard<Mutex>;
|
||||
#else
|
||||
struct Mutex {
|
||||
void lock() {}
|
||||
void unlock() {}
|
||||
bool try_lock() { return true; }
|
||||
};
|
||||
struct CondVar {
|
||||
template <class L> void wait(L &) {}
|
||||
template <class L, class P> void wait(L &, P) {}
|
||||
void notify_one() {}
|
||||
void notify_all() {}
|
||||
};
|
||||
struct UniqueLock {
|
||||
UniqueLock(Mutex &) {}
|
||||
};
|
||||
struct LockGuard {
|
||||
LockGuard(Mutex &) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Concurrent queue implementation. Not fast, but simple.
|
||||
// Multi-producer, multi-consumer, optionally bounded.
|
||||
// When YOSYS_ENABLE_THREADS is not defined, this is just a non-thread-safe non-blocking deque.
|
||||
|
|
@ -26,26 +54,20 @@ public:
|
|||
// Push an element into the queue. If it's at capacity, block until there is room.
|
||||
void push_back(T t)
|
||||
{
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
UniqueLock lock(mutex);
|
||||
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
|
||||
if (contents.empty())
|
||||
not_empty_condition.notify_one();
|
||||
#endif
|
||||
log_assert(!closed);
|
||||
contents.push_back(std::move(t));
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
if (static_cast<int>(contents.size()) < capacity)
|
||||
not_full_condition.notify_one();
|
||||
#endif
|
||||
}
|
||||
// Signal that no more elements will be produced. `pop_front()` will return nullopt.
|
||||
void close()
|
||||
{
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
UniqueLock lock(mutex);
|
||||
not_empty_condition.notify_all();
|
||||
#endif
|
||||
closed = true;
|
||||
}
|
||||
// Pop an element from the queue. Blocks until an element is available
|
||||
|
|
@ -61,39 +83,28 @@ public:
|
|||
return pop_front_internal(false);
|
||||
}
|
||||
private:
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::optional<T> pop_front_internal(bool wait)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
UniqueLock lock(mutex);
|
||||
if (wait) {
|
||||
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
|
||||
}
|
||||
#else
|
||||
std::optional<T> pop_front_internal(bool)
|
||||
{
|
||||
#endif
|
||||
if (contents.empty())
|
||||
return std::nullopt;
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
if (static_cast<int>(contents.size()) == capacity)
|
||||
not_full_condition.notify_one();
|
||||
#endif
|
||||
T result = std::move(contents.front());
|
||||
contents.pop_front();
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
if (!contents.empty())
|
||||
not_empty_condition.notify_one();
|
||||
#endif
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::mutex mutex;
|
||||
Mutex mutex;
|
||||
// Signals one waiter thread when the queue changes and is not full.
|
||||
std::condition_variable not_full_condition;
|
||||
CondVar not_full_condition;
|
||||
// Signals one waiter thread when the queue changes and is not empty.
|
||||
std::condition_variable not_empty_condition;
|
||||
#endif
|
||||
CondVar not_empty_condition;
|
||||
std::deque<T> contents;
|
||||
int capacity;
|
||||
bool closed = false;
|
||||
|
|
@ -131,6 +142,11 @@ public:
|
|||
// The result may be 0.
|
||||
static int pool_size(int reserved_cores, int max_worker_threads);
|
||||
|
||||
// Computes the number of worker threads to use, by dividing work_units among threads.
|
||||
// For testing purposes you can set YOSYS_WORK_UNITS_PER_THREAD to override `work_units_per_thread`.
|
||||
// The result may be 0.
|
||||
static int work_pool_size(int reserved_cores, int work_units, int work_units_per_thread);
|
||||
|
||||
// Create a pool of threads running the given closure (parameterized by thread number).
|
||||
// `pool_size` must be the result of a `pool_size()` call.
|
||||
ThreadPool(int pool_size, std::function<void(int)> b);
|
||||
|
|
@ -154,20 +170,150 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
// Divides some number of items into `num_threads` subranges and returns the
|
||||
// `thread_num`'th subrange. If `num_threads` is zero, returns the whole range.
|
||||
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads);
|
||||
|
||||
// A type that encapsulates the index of a thread in some list of threads. Useful for
|
||||
// stronger typechecking and code readability.
|
||||
struct ThreadIndex {
|
||||
int thread_num;
|
||||
};
|
||||
|
||||
// A set of threads with a `run()` API that runs a closure on all of the threads
|
||||
// and wait for all those closures to complete. This is a convenient way to implement
|
||||
// parallel algorithms that use barrier synchronization.
|
||||
class ParallelDispatchThreadPool
|
||||
{
|
||||
public:
|
||||
// Create a pool of threads running the given closure (parameterized by thread number).
|
||||
// `pool_size` must be the result of a `pool_size()` call.
|
||||
// `pool_size` can be zero, which we treat as 1.
|
||||
ParallelDispatchThreadPool(int pool_size);
|
||||
~ParallelDispatchThreadPool();
|
||||
|
||||
// For each thread running a closure, a `RunCtx` is passed to the closure. Currently
|
||||
// it contains the thread index and the total number of threads. It can be passed
|
||||
// directly to any APIs requiring a `ThreadIndex`.
|
||||
struct RunCtx : public ThreadIndex {
|
||||
int num_threads;
|
||||
IntRange item_range(int num_items) const {
|
||||
return item_range_for_worker(num_items, thread_num, num_threads);
|
||||
}
|
||||
};
|
||||
// Sometimes we only want to activate a subset of the threads in the pool. This
|
||||
// class provides a way to do that. It provides the same `num_threads()`
|
||||
// and `run()` APIs as a `ParallelDispatchThreadPool`.
|
||||
class Subpool {
|
||||
public:
|
||||
Subpool(ParallelDispatchThreadPool &parent, int max_threads)
|
||||
: parent(parent), max_threads(max_threads) {}
|
||||
// Returns the number of threads that will be used when calling `run()`.
|
||||
int num_threads() const {
|
||||
return parent.num_threads(max_threads);
|
||||
}
|
||||
void run(std::function<void(const RunCtx &)> work) {
|
||||
parent.run(std::move(work), max_threads);
|
||||
}
|
||||
ParallelDispatchThreadPool &thread_pool() { return parent; }
|
||||
private:
|
||||
ParallelDispatchThreadPool &parent;
|
||||
int max_threads;
|
||||
};
|
||||
|
||||
// Run the `work` function in parallel on each thread in the pool (parameterized by
|
||||
// thread number). Waits for all work functions to complete. Only one `run()` can be
|
||||
// active at a time.
|
||||
// Uses no more than `max_threads` threads (but at least one).
|
||||
void run(std::function<void(const RunCtx &)> work) {
|
||||
run(std::move(work), INT_MAX);
|
||||
}
|
||||
|
||||
// Returns the number of threads that will be used when calling `run()`.
|
||||
int num_threads() const {
|
||||
return num_threads(INT_MAX);
|
||||
}
|
||||
private:
|
||||
friend class Subpool;
|
||||
|
||||
void run(std::function<void(const RunCtx &)> work, int max_threads);
|
||||
int num_threads(int max_threads) const {
|
||||
return std::min(num_worker_threads_ + 1, std::max(1, max_threads));
|
||||
}
|
||||
void run_worker(int thread_num);
|
||||
|
||||
std::function<void(const RunCtx &)> *current_work = nullptr;
|
||||
// Keeps a correct count even when threads are exiting.
|
||||
int num_worker_threads_;
|
||||
// The count of active worker threads for the current `run()`.
|
||||
// This is only written by the main thread, and only written when
|
||||
// no other worker threads are running (i.e. all worker threads have
|
||||
// passed the increment of `done_workers` in `signal_worker_done()`
|
||||
// and not passed the release of the lock in `worker_wait_for_start()`.
|
||||
// Although there can't be any races, we still need to make it atomic
|
||||
// to prevent the compiler reordering accesses so the above invariant
|
||||
// is maintained.
|
||||
std::atomic<int> num_active_worker_threads_ = 0;
|
||||
|
||||
// Not especially efficient for large numbers of threads. Worker wakeup could scale
|
||||
// better by conceptually organising workers into a tree and having workers wake
|
||||
// up their children.
|
||||
Mutex main_to_workers_signal_mutex;
|
||||
CondVar main_to_workers_signal_cv;
|
||||
std::vector<uint8_t> main_to_workers_signal;
|
||||
void signal_workers_start() {
|
||||
UniqueLock lock(main_to_workers_signal_mutex);
|
||||
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
|
||||
std::fill(main_to_workers_signal.begin(), main_to_workers_signal.begin() + num_active_worker_threads, 1);
|
||||
// When `num_active_worker_threads_` is small compared to `num_worker_threads_`, we have a "thundering herd"
|
||||
// problem here. Fixing that would add complexity so don't worry about it for now.
|
||||
main_to_workers_signal_cv.notify_all();
|
||||
}
|
||||
void worker_wait_for_start(int thread_num) {
|
||||
UniqueLock lock(main_to_workers_signal_mutex);
|
||||
main_to_workers_signal_cv.wait(lock, [this, thread_num] { return main_to_workers_signal[thread_num] > 0; });
|
||||
main_to_workers_signal[thread_num] = 0;
|
||||
}
|
||||
|
||||
std::atomic<int> done_workers = 0;
|
||||
Mutex workers_to_main_signal_mutex;
|
||||
CondVar workers_to_main_signal_cv;
|
||||
void signal_worker_done() {
|
||||
// Must read `num_active_worker_threads_` before we increment `d`! Otherwise
|
||||
// it is possible we would increment `d`, and then another worker signals the
|
||||
// main thread that all workers are done, and the main thread writes to
|
||||
// `num_active_worker_threads_` before we check it.
|
||||
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
|
||||
int d = done_workers.fetch_add(1, std::memory_order_release);
|
||||
if (d + 1 == num_active_worker_threads) {
|
||||
UniqueLock lock(workers_to_main_signal_mutex);
|
||||
workers_to_main_signal_cv.notify_all();
|
||||
}
|
||||
}
|
||||
void wait_for_workers_done() {
|
||||
UniqueLock lock(workers_to_main_signal_mutex);
|
||||
workers_to_main_signal_cv.wait(lock, [this] {
|
||||
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
|
||||
return done_workers.load(std::memory_order_acquire) == num_active_worker_threads;
|
||||
});
|
||||
done_workers.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
// Ensure `thread_pool` is destroyed before any other members,
|
||||
// forcing all threads to be joined before destroying the
|
||||
// members (e.g. workers_to_main_signal_mutex) they might be using.
|
||||
std::unique_ptr<ThreadPool> thread_pool;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ConcurrentStack
|
||||
{
|
||||
public:
|
||||
void push_back(T &&t) {
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
#endif
|
||||
LockGuard lock(mutex);
|
||||
contents.push_back(std::move(t));
|
||||
}
|
||||
std::optional<T> try_pop_back() {
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
#endif
|
||||
LockGuard lock(mutex);
|
||||
if (contents.empty())
|
||||
return std::nullopt;
|
||||
T result = std::move(contents.back());
|
||||
|
|
@ -175,12 +321,387 @@ public:
|
|||
return result;
|
||||
}
|
||||
private:
|
||||
#ifdef YOSYS_ENABLE_THREADS
|
||||
std::mutex mutex;
|
||||
#endif
|
||||
Mutex mutex;
|
||||
std::vector<T> contents;
|
||||
};
|
||||
|
||||
// A vector that is sharded into buckets, one per thread. This lets multiple threads write
|
||||
// efficiently to the vector without synchronization overhead. After all writers have
|
||||
// finished writing, the vector can be iterated over. The iteration order is deterministic:
|
||||
// all the elements written by thread 0 in the order it inserted them, followed by all elements
|
||||
// written by thread 1, etc.
|
||||
template <typename T>
|
||||
class ShardedVector {
|
||||
public:
|
||||
ShardedVector(const ParallelDispatchThreadPool &thread_pool) {
|
||||
init(thread_pool.num_threads());
|
||||
}
|
||||
ShardedVector(const ParallelDispatchThreadPool::Subpool &thread_pool) {
|
||||
init(thread_pool.num_threads());
|
||||
}
|
||||
|
||||
// Insert a value, passing the `ThreadIndex` of the writer thread.
|
||||
// Parallel inserts with different `ThreadIndex` values are fine.
|
||||
// Inserts must not run concurrently with any other methods (e.g.
|
||||
// iteration or `empty()`.)
|
||||
void insert(const ThreadIndex &thread, T value) {
|
||||
buckets[thread.thread_num].emplace_back(std::move(value));
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
for (const std::vector<T> &bucket : buckets)
|
||||
if (!bucket.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
using Buckets = std::vector<std::vector<T>>;
|
||||
class iterator {
|
||||
public:
|
||||
iterator(typename Buckets::iterator bucket_it, typename Buckets::iterator bucket_end)
|
||||
: bucket_it(std::move(bucket_it)), bucket_end(std::move(bucket_end)) {
|
||||
if (bucket_it != bucket_end)
|
||||
inner_it = bucket_it->begin();
|
||||
normalize();
|
||||
}
|
||||
T& operator*() const { return *inner_it.value(); }
|
||||
iterator &operator++() {
|
||||
++*inner_it;
|
||||
normalize();
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator &other) const {
|
||||
return bucket_it != other.bucket_it || inner_it != other.inner_it;
|
||||
}
|
||||
private:
|
||||
void normalize() {
|
||||
if (bucket_it == bucket_end)
|
||||
return;
|
||||
while (inner_it == bucket_it->end()) {
|
||||
++bucket_it;
|
||||
if (bucket_it == bucket_end) {
|
||||
inner_it.reset();
|
||||
return;
|
||||
}
|
||||
inner_it = bucket_it->begin();
|
||||
}
|
||||
}
|
||||
std::optional<typename std::vector<T>::iterator> inner_it;
|
||||
typename Buckets::iterator bucket_it;
|
||||
typename Buckets::iterator bucket_end;
|
||||
};
|
||||
iterator begin() { return iterator(buckets.begin(), buckets.end()); }
|
||||
iterator end() { return iterator(buckets.end(), buckets.end()); }
|
||||
private:
|
||||
void init(int num_threads) {
|
||||
buckets.resize(num_threads);
|
||||
}
|
||||
Buckets buckets;
|
||||
};
|
||||
|
||||
// This collision handler for `ShardedHashtable` resolves collisions by keeping
|
||||
// the current value and discarding the other. This is correct when all values with the
|
||||
// same key are interchangeable, i.e. when the hashtable is being used as a set instead
|
||||
// of a map.
|
||||
template <typename V>
|
||||
struct SetCollisionHandler {
|
||||
void operator()(typename V::Accumulated &, typename V::Accumulated &) const {}
|
||||
};
|
||||
|
||||
// A hashtable that can be efficiently built in parallel and then looked up concurrently.
|
||||
// `V` is the type of elements that will be added to the hashtable. It must have a
|
||||
// member type `Accumulated` representing the combination of multiple `V` elements. This
|
||||
// can be the same as `V`, but for example `V` could contain a Wire* and `V::Accumulated`
|
||||
// could contain a `pool<Wire*>`. `KeyEquality` is a class containing an `operator()` that
|
||||
// returns true of two `V` elements have equal keys.
|
||||
// `CollisionHandler` is used to reduce two `V::Accumulated` values into a single value.
|
||||
//
|
||||
// To use this, first construct a `Builder` and fill it in (in parallel), then construct
|
||||
// a `ShardedHashtable` from the `Builder`.
|
||||
template <typename V, typename KeyEquality, typename CollisionHandler>
|
||||
class ShardedHashtable {
|
||||
public:
|
||||
// A combination of a `V` and its hash value.
|
||||
struct Value {
|
||||
Value(V value, unsigned int hash) : value(std::move(value)), hash(hash) {}
|
||||
Value(Value &&) = default;
|
||||
Value(const Value &) = delete;
|
||||
Value &operator=(const Value &) = delete;
|
||||
V value;
|
||||
unsigned int hash;
|
||||
};
|
||||
// A combination of a `V::Accumulated` and its hash value.
|
||||
struct AccumulatedValue {
|
||||
AccumulatedValue(typename V::Accumulated value, unsigned int hash) : value(std::move(value)), hash(hash) {}
|
||||
AccumulatedValue(AccumulatedValue &&) = default;
|
||||
#if defined(_MSC_VER)
|
||||
AccumulatedValue(const AccumulatedValue &) {
|
||||
log_error("Copy constructor called on AccumulatedValue");
|
||||
}
|
||||
AccumulatedValue &operator=(const AccumulatedValue &) {
|
||||
log_error("Copy assignment called on AccumulatedValue");
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
AccumulatedValue(const AccumulatedValue &) = delete;
|
||||
AccumulatedValue &operator=(const AccumulatedValue &) = delete;
|
||||
#endif
|
||||
typename V::Accumulated value;
|
||||
unsigned int hash;
|
||||
};
|
||||
// A class containing an `operator()` that returns true of two `AccumulatedValue`
|
||||
// elements have equal keys.
|
||||
// Required to insert `AccumulatedValue`s into an `std::unordered_set`.
|
||||
struct AccumulatedValueEquality {
|
||||
KeyEquality inner;
|
||||
AccumulatedValueEquality(const KeyEquality &inner) : inner(inner) {}
|
||||
bool operator()(const AccumulatedValue &v1, const AccumulatedValue &v2) const {
|
||||
return inner(v1.value, v2.value);
|
||||
}
|
||||
};
|
||||
// A class containing an `operator()` that returns the hash value of an `AccumulatedValue`.
|
||||
// Required to insert `AccumulatedValue`s into an `std::unordered_set`.
|
||||
struct AccumulatedValueHashOp {
|
||||
size_t operator()(const AccumulatedValue &v) const {
|
||||
return static_cast<size_t>(v.hash);
|
||||
}
|
||||
};
|
||||
using Shard = std::unordered_set<AccumulatedValue, AccumulatedValueHashOp, AccumulatedValueEquality>;
|
||||
|
||||
// First construct one of these. Then populate it in parallel by calling `insert()` from many threads.
|
||||
// Then do another parallel phase calling `process()` from many threads.
|
||||
class Builder {
|
||||
public:
|
||||
Builder(const ParallelDispatchThreadPool &thread_pool, KeyEquality equality = KeyEquality(), CollisionHandler collision_handler = CollisionHandler())
|
||||
: collision_handler(std::move(collision_handler)) {
|
||||
init(thread_pool.num_threads(), std::move(equality));
|
||||
}
|
||||
Builder(const ParallelDispatchThreadPool::Subpool &thread_pool, KeyEquality equality = KeyEquality(), CollisionHandler collision_handler = CollisionHandler())
|
||||
: collision_handler(std::move(collision_handler)) {
|
||||
init(thread_pool.num_threads(), std::move(equality));
|
||||
}
|
||||
// First call `insert` to insert all elements. All inserts must finish
|
||||
// before calling any `process()`.
|
||||
void insert(const ThreadIndex &thread, Value v) {
|
||||
// You might think that for the single-threaded case, we can optimize by
|
||||
// inserting directly into the `std::unordered_set` here. But that slows things down
|
||||
// a lot and I never got around to figuring out why.
|
||||
std::vector<std::vector<Value>> &buckets = all_buckets[thread.thread_num];
|
||||
size_t bucket = static_cast<size_t>(v.hash) % buckets.size();
|
||||
buckets[bucket].emplace_back(std::move(v));
|
||||
}
|
||||
// Then call `process` for each thread. All `process()`s must finish before using
|
||||
// the `Builder` to construct a `ShardedHashtable`.
|
||||
void process(const ThreadIndex &thread) {
|
||||
int size = 0;
|
||||
for (std::vector<std::vector<Value>> &buckets : all_buckets)
|
||||
size += GetSize(buckets[thread.thread_num]);
|
||||
Shard &shard = shards[thread.thread_num];
|
||||
shard.reserve(size);
|
||||
for (std::vector<std::vector<Value>> &buckets : all_buckets) {
|
||||
for (Value &value : buckets[thread.thread_num])
|
||||
accumulate(value, shard);
|
||||
// Free as much memory as we can during the parallel phase.
|
||||
std::vector<Value>().swap(buckets[thread.thread_num]);
|
||||
}
|
||||
}
|
||||
private:
|
||||
friend class ShardedHashtable<V, KeyEquality, CollisionHandler>;
|
||||
void accumulate(Value &value, Shard &shard) {
|
||||
// With C++20 we could make this more efficient using heterogenous lookup
|
||||
AccumulatedValue accumulated_value{std::move(value.value), value.hash};
|
||||
auto [it, inserted] = shard.insert(std::move(accumulated_value));
|
||||
if (!inserted)
|
||||
collision_handler(const_cast<typename V::Accumulated &>(it->value), accumulated_value.value);
|
||||
}
|
||||
void init(int num_threads, KeyEquality equality) {
|
||||
all_buckets.resize(num_threads);
|
||||
for (std::vector<std::vector<Value>> &buckets : all_buckets)
|
||||
buckets.resize(num_threads);
|
||||
for (int i = 0; i < num_threads; ++i)
|
||||
shards.emplace_back(0, AccumulatedValueHashOp(), AccumulatedValueEquality(equality));
|
||||
}
|
||||
const CollisionHandler collision_handler;
|
||||
// A num_threads x num_threads matrix of buckets.
|
||||
// In the first phase, each thread i gemerates elements and writes them to
|
||||
// bucket [i][j] where j = hash(element) % num_threads.
|
||||
// In the second phase, thread i reads from bucket [j][i] for all j, collecting
|
||||
// all elements where i = hash(element) % num_threads.
|
||||
std::vector<std::vector<std::vector<Value>>> all_buckets;
|
||||
std::vector<Shard> shards;
|
||||
};
|
||||
|
||||
// Then finally construct the hashtable:
|
||||
ShardedHashtable(Builder &builder) : shards(std::move(builder.shards)) {
|
||||
// Check that all necessary 'process()' calls were made.
|
||||
for (std::vector<std::vector<Value>> &buckets : builder.all_buckets)
|
||||
for (std::vector<Value> &bucket : buckets)
|
||||
log_assert(bucket.empty());
|
||||
// Free memory.
|
||||
std::vector<std::vector<std::vector<Value>>>().swap(builder.all_buckets);
|
||||
}
|
||||
ShardedHashtable(ShardedHashtable &&other) = default;
|
||||
ShardedHashtable() {}
|
||||
|
||||
ShardedHashtable &operator=(ShardedHashtable &&other) = default;
|
||||
|
||||
// Look up by `AccumulatedValue`. If we switch to C++20 then we could use
|
||||
// heterogenous lookup to support looking up by `Value` here. Returns nullptr
|
||||
// if the key is not found.
|
||||
const typename V::Accumulated *find(const AccumulatedValue &v) const {
|
||||
size_t num_shards = shards.size();
|
||||
if (num_shards == 0)
|
||||
return nullptr;
|
||||
size_t shard = static_cast<size_t>(v.hash) % num_shards;
|
||||
auto it = shards[shard].find(v);
|
||||
if (it == shards[shard].end())
|
||||
return nullptr;
|
||||
return &it->value;
|
||||
}
|
||||
|
||||
// Insert an element into the table. The caller is responsible for ensuring this does not
|
||||
// happen concurrently with any other method calls.
|
||||
void insert(AccumulatedValue v) {
|
||||
size_t num_shards = shards.size();
|
||||
if (num_shards == 0)
|
||||
return;
|
||||
size_t shard = static_cast<size_t>(v.hash) % num_shards;
|
||||
shards[shard].insert(v);
|
||||
}
|
||||
|
||||
// Call this for each shard to implement parallel destruction. For very large `ShardedHashtable`s,
|
||||
// deleting all elements of all shards on a single thread can be a performance bottleneck.
|
||||
void clear(const ThreadIndex &shard) {
|
||||
AccumulatedValueEquality equality = shards[shard.thread_num].key_eq();
|
||||
shards[shard.thread_num] = Shard(0, AccumulatedValueHashOp(), equality);
|
||||
}
|
||||
private:
|
||||
std::vector<Shard> shards;
|
||||
};
|
||||
|
||||
// A concurrent work-queue that can share batches of work across threads.
|
||||
// Uses a naive implementation of work-stealing.
|
||||
template <typename T>
|
||||
class ConcurrentWorkQueue {
|
||||
public:
|
||||
// Create a queue that supports the given number of threads and
|
||||
// groups work into `batch_size` units.
|
||||
ConcurrentWorkQueue(int num_threads, int batch_size = 100)
|
||||
: batch_size(batch_size), thread_states(num_threads) {}
|
||||
int num_threads() const { return GetSize(thread_states); }
|
||||
// Push some work to do. Pushes and pops with the same `thread` must
|
||||
// not happen concurrently.
|
||||
void push(const ThreadIndex &thread, T work) {
|
||||
ThreadState &thread_state = thread_states[thread.thread_num];
|
||||
thread_state.next_batch.emplace_back(std::move(work));
|
||||
if (GetSize(thread_state.next_batch) < batch_size)
|
||||
return;
|
||||
bool was_empty;
|
||||
{
|
||||
UniqueLock lock(thread_state.batches_lock);
|
||||
was_empty = thread_state.batches.empty();
|
||||
thread_state.batches.push_back(std::move(thread_state.next_batch));
|
||||
}
|
||||
if (was_empty) {
|
||||
UniqueLock lock(waiters_lock);
|
||||
if (num_waiters > 0) {
|
||||
waiters_cv.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Grab some work to do.
|
||||
// If all threads enter `pop_batch()`, then instead of deadlocking the
|
||||
// queue will return no work. That is the only case in which it will
|
||||
// return no work.
|
||||
std::vector<T> pop_batch(const ThreadIndex &thread) {
|
||||
ThreadState &thread_state = thread_states[thread.thread_num];
|
||||
if (!thread_state.next_batch.empty())
|
||||
return std::move(thread_state.next_batch);
|
||||
// Empty our own work queue first.
|
||||
{
|
||||
UniqueLock lock(thread_state.batches_lock);
|
||||
if (!thread_state.batches.empty()) {
|
||||
std::vector<T> batch = std::move(thread_state.batches.back());
|
||||
thread_state.batches.pop_back();
|
||||
return batch;
|
||||
}
|
||||
}
|
||||
// From here on in this function, our work queue is empty.
|
||||
while (true) {
|
||||
std::vector<T> batch = try_steal(thread);
|
||||
if (!batch.empty()) {
|
||||
return std::move(batch);
|
||||
}
|
||||
// Termination: if all threads run out of work, then all of
|
||||
// them will eventually enter this loop and there will be no further
|
||||
// notifications on waiters_cv, so all will eventually increment
|
||||
// num_waiters and wait, so num_waiters == num_threads()
|
||||
// will become true. In single-threaded builds, num_threads() is 1,
|
||||
// so we always terminate on the first iteration.
|
||||
UniqueLock lock(waiters_lock);
|
||||
++num_waiters;
|
||||
if (num_waiters == num_threads()) {
|
||||
waiters_cv.notify_all();
|
||||
return {};
|
||||
}
|
||||
// As above, it's possible that we'll wait here even when there
|
||||
// are work batches posted by other threads. That's OK.
|
||||
waiters_cv.wait(lock);
|
||||
if (num_waiters == num_threads())
|
||||
return {};
|
||||
--num_waiters;
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<T> try_steal(const ThreadIndex &thread) {
|
||||
for (int i = 1; i < num_threads(); i++) {
|
||||
int other_thread_num = (thread.thread_num + i) % num_threads();
|
||||
ThreadState &other_thread_state = thread_states[other_thread_num];
|
||||
UniqueLock lock(other_thread_state.batches_lock);
|
||||
if (!other_thread_state.batches.empty()) {
|
||||
std::vector<T> batch = std::move(other_thread_state.batches.front());
|
||||
other_thread_state.batches.pop_front();
|
||||
return batch;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int batch_size;
|
||||
|
||||
struct ThreadState {
|
||||
// Entirely thread-local.
|
||||
std::vector<T> next_batch;
|
||||
|
||||
Mutex batches_lock;
|
||||
// Only the associated thread ever adds to this, and only at the back.
|
||||
// Other threads can remove elements from the front.
|
||||
std::deque<std::vector<T>> batches;
|
||||
};
|
||||
std::vector<ThreadState> thread_states;
|
||||
|
||||
Mutex waiters_lock;
|
||||
CondVar waiters_cv;
|
||||
// Number of threads waiting for work. Their queues are empty.
|
||||
int num_waiters = 0;
|
||||
};
|
||||
|
||||
// A monotonic flag. Starts false, and can be set to true in a thread-safe way.
|
||||
// Once `load()` returns true, it will always return true.
|
||||
// Uses relaxed atomics so there are no memory ordering guarantees. Do not use this
|
||||
// to guard access to shared memory.
|
||||
class MonotonicFlag {
|
||||
public:
|
||||
MonotonicFlag() : value(false) {}
|
||||
bool load() const { return value.load(std::memory_order_relaxed); }
|
||||
void set() { value.store(true, std::memory_order_relaxed); }
|
||||
bool set_and_return_old() {
|
||||
return value.exchange(true, std::memory_order_relaxed);
|
||||
}
|
||||
private:
|
||||
std::atomic<bool> value;
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif // YOSYS_THREADING_H
|
||||
|
|
|
|||
|
|
@ -105,21 +105,21 @@ struct TimingInfo
|
|||
auto dst = cell->getPort(ID::DST);
|
||||
for (const auto &c : src.chunks())
|
||||
if (!c.wire || !c.wire->port_input)
|
||||
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
|
||||
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", module, cell, log_signal(src));
|
||||
for (const auto &c : dst.chunks())
|
||||
if (!c.wire || !c.wire->port_output)
|
||||
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
|
||||
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", module, cell, log_signal(dst));
|
||||
int rise_max = cell->getParam(ID::T_RISE_MAX).as_int();
|
||||
int fall_max = cell->getParam(ID::T_FALL_MAX).as_int();
|
||||
int max = std::max(rise_max,fall_max);
|
||||
if (max < 0)
|
||||
log_error("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0.\n", log_id(module), log_id(cell));
|
||||
log_error("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0.\n", module, cell);
|
||||
if (cell->getParam(ID::FULL).as_bool()) {
|
||||
for (const auto &s : src)
|
||||
for (const auto &d : dst) {
|
||||
auto r = t.comb.insert(BitBit(s,d));
|
||||
if (!r.second)
|
||||
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", log_id(module), log_signal(s), log_signal(d));
|
||||
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", module, log_signal(s), log_signal(d));
|
||||
r.first->second = max;
|
||||
}
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ struct TimingInfo
|
|||
const auto &d = dst[i];
|
||||
auto r = t.comb.insert(BitBit(s,d));
|
||||
if (!r.second)
|
||||
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", log_id(module), log_signal(s), log_signal(d));
|
||||
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", module, log_signal(s), log_signal(d));
|
||||
r.first->second = max;
|
||||
}
|
||||
}
|
||||
|
|
@ -139,15 +139,15 @@ struct TimingInfo
|
|||
auto src = cell->getPort(ID::SRC).as_bit();
|
||||
auto dst = cell->getPort(ID::DST);
|
||||
if (!src.wire || !src.wire->port_input)
|
||||
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
|
||||
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", module, cell, log_signal(src));
|
||||
for (const auto &c : dst.chunks())
|
||||
if (!c.wire->port_output)
|
||||
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
|
||||
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", module, cell, log_signal(dst));
|
||||
int rise_max = cell->getParam(ID::T_RISE_MAX).as_int();
|
||||
int fall_max = cell->getParam(ID::T_FALL_MAX).as_int();
|
||||
int max = std::max(rise_max,fall_max);
|
||||
if (max < 0) {
|
||||
log_warning("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell));
|
||||
log_warning("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0 which is currently unsupported. Clamping to 0.\n", module, cell);
|
||||
max = 0;
|
||||
}
|
||||
for (const auto &d : dst) {
|
||||
|
|
@ -167,12 +167,12 @@ struct TimingInfo
|
|||
auto dst = cell->getPort(ID::DST).as_bit();
|
||||
for (const auto &c : src.chunks())
|
||||
if (!c.wire || !c.wire->port_input)
|
||||
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
|
||||
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", module, cell, log_signal(src));
|
||||
if (!dst.wire || !dst.wire->port_input)
|
||||
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst));
|
||||
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", module, cell, log_signal(dst));
|
||||
int max = cell->getParam(ID::T_LIMIT_MAX).as_int();
|
||||
if (max < 0) {
|
||||
log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell));
|
||||
log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", module, cell);
|
||||
max = 0;
|
||||
}
|
||||
for (const auto &s : src) {
|
||||
|
|
|
|||
|
|
@ -125,20 +125,22 @@ public:
|
|||
};
|
||||
|
||||
|
||||
// ------------------------------------------------
|
||||
// A simple class for topological sorting
|
||||
// ------------------------------------------------
|
||||
// ---------------------------------------------------
|
||||
// Best-effort topological sorting with loop detection
|
||||
// ---------------------------------------------------
|
||||
|
||||
template <typename T, typename C = std::less<T>> class TopoSort
|
||||
{
|
||||
public:
|
||||
static_assert(!(std::is_pointer<T>::value && std::is_same<C, std::less<T>>::value),
|
||||
"std::less is run-to-run unstable for pointers");
|
||||
public:
|
||||
// We use this ordering of the edges in the adjacency matrix for
|
||||
// exact compatibility with an older implementation.
|
||||
struct IndirectCmp {
|
||||
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
|
||||
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
|
||||
bool operator()(int a, int b) const
|
||||
{
|
||||
log_assert(static_cast<size_t>(a) < nodes_.size());
|
||||
log_assert(static_cast<size_t>(a) < nodes_.size());
|
||||
log_assert(static_cast<size_t>(b) < nodes_.size());
|
||||
return node_cmp_(nodes_[a], nodes_[b]);
|
||||
}
|
||||
|
|
@ -147,7 +149,9 @@ template <typename T, typename C = std::less<T>> class TopoSort
|
|||
};
|
||||
|
||||
bool analyze_loops;
|
||||
// The stability doesn't rely on std::less of T, so pointers are safe
|
||||
std::map<T, int, C> node_to_index;
|
||||
// edges[i] is the set of nodes with an edge into node i
|
||||
std::vector<std::set<int, IndirectCmp>> edges;
|
||||
std::vector<T> sorted;
|
||||
std::set<std::vector<T>> loops;
|
||||
|
|
@ -160,10 +164,10 @@ template <typename T, typename C = std::less<T>> class TopoSort
|
|||
|
||||
int node(T n)
|
||||
{
|
||||
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
|
||||
if (rv.second) {
|
||||
nodes.push_back(n);
|
||||
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
|
||||
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
|
||||
if (rv.second) {
|
||||
nodes.push_back(n);
|
||||
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
|
||||
}
|
||||
return rv.first->second;
|
||||
}
|
||||
|
|
@ -183,13 +187,14 @@ template <typename T, typename C = std::less<T>> class TopoSort
|
|||
sorted.clear();
|
||||
found_loops = false;
|
||||
|
||||
std::vector<bool> marked_cells(edges.size(), false);
|
||||
std::vector<bool> active_cells(edges.size(), false);
|
||||
std::vector<int> active_stack;
|
||||
std::vector<bool> node_is_sorted(edges.size(), false);
|
||||
std::vector<bool> node_is_on_stack(edges.size(), false);
|
||||
// Only used with analyze_loops
|
||||
std::vector<int> stack;
|
||||
sorted.reserve(edges.size());
|
||||
|
||||
for (const auto &it : node_to_index)
|
||||
sort_worker(it.second, marked_cells, active_cells, active_stack);
|
||||
sort_worker(it.second, node_is_sorted, node_is_on_stack, stack);
|
||||
|
||||
log_assert(GetSize(sorted) == GetSize(nodes));
|
||||
|
||||
|
|
@ -211,19 +216,20 @@ template <typename T, typename C = std::less<T>> class TopoSort
|
|||
return database;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
bool found_loops;
|
||||
std::vector<T> nodes;
|
||||
const IndirectCmp indirect_cmp;
|
||||
|
||||
void sort_worker(const int root_index, std::vector<bool> &marked_cells, std::vector<bool> &active_cells, std::vector<int> &active_stack)
|
||||
void sort_worker(const int root_index, std::vector<bool> &node_is_sorted, std::vector<bool> &node_is_on_stack, std::vector<int> &stack)
|
||||
{
|
||||
if (active_cells[root_index]) {
|
||||
if (node_is_on_stack[root_index]) {
|
||||
// We've been here before, meaning we have a loop
|
||||
found_loops = true;
|
||||
if (analyze_loops) {
|
||||
std::vector<T> loop;
|
||||
for (int i = GetSize(active_stack) - 1; i >= 0; i--) {
|
||||
const int index = active_stack[i];
|
||||
for (int i = GetSize(stack) - 1; i >= 0; i--) {
|
||||
const int index = stack[i];
|
||||
loop.push_back(nodes[index]);
|
||||
if (index == root_index)
|
||||
break;
|
||||
|
|
@ -233,23 +239,24 @@ template <typename T, typename C = std::less<T>> class TopoSort
|
|||
return;
|
||||
}
|
||||
|
||||
if (marked_cells[root_index])
|
||||
// We're done if we've already sorted this subgraph
|
||||
if (node_is_sorted[root_index])
|
||||
return;
|
||||
|
||||
if (!edges[root_index].empty()) {
|
||||
if (analyze_loops)
|
||||
active_stack.push_back(root_index);
|
||||
active_cells[root_index] = true;
|
||||
stack.push_back(root_index);
|
||||
node_is_on_stack[root_index] = true;
|
||||
|
||||
for (int left_n : edges[root_index])
|
||||
sort_worker(left_n, marked_cells, active_cells, active_stack);
|
||||
sort_worker(left_n, node_is_sorted, node_is_on_stack, stack);
|
||||
|
||||
if (analyze_loops)
|
||||
active_stack.pop_back();
|
||||
active_cells[root_index] = false;
|
||||
stack.pop_back();
|
||||
node_is_on_stack[root_index] = false;
|
||||
}
|
||||
|
||||
marked_cells[root_index] = true;
|
||||
node_is_sorted[root_index] = true;
|
||||
sorted.push_back(nodes[root_index]);
|
||||
}
|
||||
};
|
||||
|
|
@ -299,6 +306,24 @@ auto reversed(const T& container) {
|
|||
return reverse_view{container};
|
||||
}
|
||||
|
||||
// A range of integers [start_, end_) that can be iterated over with a
|
||||
// C++ range-based for loop.
|
||||
struct IntRange {
|
||||
int start_;
|
||||
int end_;
|
||||
struct Int {
|
||||
int v;
|
||||
int operator*() const { return v; }
|
||||
Int &operator++() { ++v; return *this; }
|
||||
bool operator!=(const Int &other) const { return v != other.v; }
|
||||
};
|
||||
Int begin() const { return {start_}; }
|
||||
Int end() const { return {end_}; }
|
||||
|
||||
bool operator==(const IntRange &other) const { return start_ == other.start_ && end_ == other.end_; }
|
||||
bool operator!=(const IntRange &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
|||
4
kernel/version.cc.in
Normal file
4
kernel/version.cc.in
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace Yosys {
|
||||
const char *yosys_version_str = "@YOSYS_BUILD_INFO@";
|
||||
const char *yosys_git_hash_str = "@YOSYS_CHECKOUT_INFO@";
|
||||
}
|
||||
112
kernel/wallace_tree.h
Normal file
112
kernel/wallace_tree.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* Wallace tree utilities for multi-operand addition using carry-save adders
|
||||
*
|
||||
* Terminology:
|
||||
* - compressor: $fa viewed as reducing 3 inputs to 2 outputs (sum + shifted carry) (3:2 compressor)
|
||||
* - level: A stage of parallel compression operations
|
||||
* - depth: Maximum number of 3:2 compressor levels from any input to a signal
|
||||
*
|
||||
* References:
|
||||
* - "Binary Adder Architectures for Cell-Based VLSI and their Synthesis" (https://iis-people.ee.ethz.ch/~zimmi/publications/adder_arch.pdf)
|
||||
* - "A Suggestion for a Fast Multiplier" (https://www.ece.ucdavis.edu/~vojin/CLASSES/EEC280/Web-page/papers/Arithmetic/Wallace_mult.pdf)
|
||||
*/
|
||||
|
||||
#ifndef WALLACE_TREE_H
|
||||
#define WALLACE_TREE_H
|
||||
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
inline std::pair<SigSpec, SigSpec> emit_fa(Module *module, SigSpec a, SigSpec b, SigSpec c, int width)
|
||||
{
|
||||
SigSpec sum = module->addWire(NEW_ID, width);
|
||||
SigSpec cout = module->addWire(NEW_ID, width);
|
||||
|
||||
module->addFa(NEW_ID, a, b, c, cout, sum);
|
||||
|
||||
SigSpec carry;
|
||||
carry.append(State::S0);
|
||||
carry.append(cout.extract(0, width - 1));
|
||||
return {sum, carry};
|
||||
}
|
||||
|
||||
/**
|
||||
* wallace_reduce_scheduled() - Reduce multiple operands to two using a Wallace tree
|
||||
* @module: The Yosys module to which the compressors will be added
|
||||
* @sigs: Vector of input signals (operands) to be reduced
|
||||
* @width: Target bit-width to which all operands will be zero-extended
|
||||
* @compressor_count: Optional pointer to return the number of $fa cells emitted
|
||||
*
|
||||
* Return: The final two reduced operands, that are to be fed into an adder
|
||||
*/
|
||||
inline std::pair<SigSpec, SigSpec> wallace_reduce_scheduled(Module *module, std::vector<SigSpec> &sigs, int width, int *compressor_count = nullptr)
|
||||
{
|
||||
struct DepthSig {
|
||||
SigSpec sig;
|
||||
int depth;
|
||||
};
|
||||
|
||||
for (auto &s : sigs)
|
||||
s.extend_u0(width);
|
||||
|
||||
std::vector<DepthSig> operands;
|
||||
operands.reserve(sigs.size());
|
||||
for (auto &s : sigs)
|
||||
operands.push_back({s, 0});
|
||||
|
||||
// Number of $fa's emitted
|
||||
if (compressor_count)
|
||||
*compressor_count = 0;
|
||||
|
||||
// Only compress operands ready at current level
|
||||
for (int level = 0; operands.size() > 2; level++) {
|
||||
// Partition operands into ready and waiting
|
||||
std::vector<DepthSig> ready, waiting;
|
||||
for (auto &op : operands) {
|
||||
if (op.depth <= level)
|
||||
ready.push_back(op);
|
||||
else
|
||||
waiting.push_back(op);
|
||||
}
|
||||
|
||||
if (ready.size() < 3)
|
||||
continue;
|
||||
|
||||
// Apply compressors to ready operands
|
||||
std::vector<DepthSig> compressed;
|
||||
size_t i = 0;
|
||||
while (i + 2 < ready.size()) {
|
||||
auto [sum, carry] = emit_fa(module, ready[i].sig, ready[i + 1].sig, ready[i + 2].sig, width);
|
||||
int new_depth = std::max({ready[i].depth, ready[i + 1].depth, ready[i + 2].depth}) + 1;
|
||||
compressed.push_back({sum, new_depth});
|
||||
compressed.push_back({carry, new_depth});
|
||||
if (compressor_count)
|
||||
(*compressor_count)++;
|
||||
i += 3;
|
||||
}
|
||||
// Uncompressed operands pass through to next level
|
||||
for (; i < ready.size(); i++)
|
||||
compressed.push_back(ready[i]);
|
||||
// Merge compressed with waiting operands
|
||||
for (auto &op : waiting)
|
||||
compressed.push_back(op);
|
||||
|
||||
operands = std::move(compressed);
|
||||
}
|
||||
|
||||
if (operands.size() == 0)
|
||||
return {SigSpec(State::S0, width), SigSpec(State::S0, width)};
|
||||
else if (operands.size() == 1)
|
||||
return {operands[0].sig, SigSpec(State::S0, width)};
|
||||
else {
|
||||
log_assert(operands.size() == 2);
|
||||
log(" Wallace tree depth: %d levels of $fa + 1 final $add\n", std::max(operands[0].depth, operands[1].depth));
|
||||
return {operands[0].sig, operands[1].sig};
|
||||
}
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
#include "libs/backward-cpp/backward.hpp"
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ namespace py = pybind11;
|
|||
# include <dirent.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# if !defined(YOSYS_DISABLE_SPAWN)
|
||||
# if defined(YOSYS_ENABLE_SPAWN)
|
||||
# include <sys/wait.h>
|
||||
# endif
|
||||
#endif
|
||||
|
|
@ -94,7 +94,7 @@ const char* yosys_maybe_version() {
|
|||
}
|
||||
|
||||
RTLIL::Design *yosys_design = NULL;
|
||||
CellTypes yosys_celltypes;
|
||||
NewCellTypes yosys_celltypes;
|
||||
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
Tcl_Interp *yosys_tcl_interp = NULL;
|
||||
|
|
@ -181,7 +181,7 @@ void yosys_banner()
|
|||
log(" %s\n", yosys_maybe_version());
|
||||
}
|
||||
|
||||
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||
#if defined(YOSYS_ENABLE_SPAWN)
|
||||
int run_command(const std::string &command, std::function<void(const std::string&)> process_line)
|
||||
{
|
||||
if (!process_line)
|
||||
|
|
@ -230,6 +230,7 @@ PYBIND11_MODULE(pyosys, m) {
|
|||
// This should not affect using wheels as the dylib has to actually be called
|
||||
// libyosys_dummy.so for this function to be interacted with at all.
|
||||
PYBIND11_MODULE(libyosys_dummy, _) {
|
||||
(void)_;
|
||||
throw py::import_error("Change your import from 'import libyosys' to 'from pyosys import libyosys'.");
|
||||
}
|
||||
#endif
|
||||
|
|
@ -265,7 +266,7 @@ void yosys_setup()
|
|||
|
||||
Pass::init_register();
|
||||
yosys_design = new RTLIL::Design;
|
||||
yosys_celltypes.setup();
|
||||
yosys_celltypes.static_cell_types = StaticCellTypes::categories.is_known;
|
||||
log_push();
|
||||
}
|
||||
|
||||
|
|
@ -294,8 +295,6 @@ void yosys_shutdown()
|
|||
log_errfile = NULL;
|
||||
log_files.clear();
|
||||
|
||||
yosys_celltypes.clear();
|
||||
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
if (yosys_tcl_interp != NULL) {
|
||||
if (!Tcl_InterpDeleted(yosys_tcl_interp)) {
|
||||
|
|
@ -476,17 +475,30 @@ struct TclPass : public Pass {
|
|||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
#if defined(__linux__) || defined(__CYGWIN__) || defined(__gnu_hurd__)
|
||||
std::string proc_self_dirname()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
|
||||
std::string path(4096, '\0');
|
||||
ssize_t buflen = -1;
|
||||
// Double until sucess, while avoiding endless loop. Give up
|
||||
// when symlink is longer than 4096*(2^30) = 4398046511104
|
||||
// bytes.
|
||||
for (int tries = 30; 0 < tries; tries--) {
|
||||
buflen = readlink("/proc/self/exe", path.data(), path.size());
|
||||
if (buflen < (ssize_t)path.size())
|
||||
break;
|
||||
else
|
||||
path.resize(path.size() * 2);
|
||||
}
|
||||
if (buflen < 0) {
|
||||
log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
|
||||
path.resize(0);
|
||||
} else {
|
||||
while (buflen > 0 && path[buflen-1] != '/')
|
||||
buflen--;
|
||||
path.resize(buflen);
|
||||
}
|
||||
while (buflen > 0 && path[buflen-1] != '/')
|
||||
buflen--;
|
||||
return std::string(path, buflen);
|
||||
return path;
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
std::string proc_self_dirname()
|
||||
|
|
@ -529,25 +541,17 @@ std::string proc_self_dirname()
|
|||
std::string proc_self_dirname()
|
||||
{
|
||||
int i = 0;
|
||||
# ifdef __MINGW32__
|
||||
char longpath[MAX_PATH + 1];
|
||||
char shortpath[MAX_PATH + 1];
|
||||
# else
|
||||
WCHAR longpath[MAX_PATH + 1];
|
||||
TCHAR shortpath[MAX_PATH + 1];
|
||||
# endif
|
||||
if (!GetModuleFileName(0, longpath, MAX_PATH+1))
|
||||
if (!GetModuleFileNameA(0, longpath, MAX_PATH+1))
|
||||
log_error("GetModuleFileName() failed.\n");
|
||||
if (!GetShortPathName(longpath, shortpath, MAX_PATH+1))
|
||||
if (!GetShortPathNameA(longpath, shortpath, MAX_PATH+1))
|
||||
log_error("GetShortPathName() failed.\n");
|
||||
while (shortpath[i] != 0)
|
||||
i++;
|
||||
while (i > 0 && shortpath[i-1] != '/' && shortpath[i-1] != '\\')
|
||||
shortpath[--i] = 0;
|
||||
std::string path;
|
||||
for (i = 0; shortpath[i]; i++)
|
||||
path += char(shortpath[i]);
|
||||
return path;
|
||||
return shortpath;
|
||||
}
|
||||
#elif defined(EMSCRIPTEN) || defined(__wasm)
|
||||
std::string proc_self_dirname()
|
||||
|
|
@ -628,13 +632,11 @@ void init_share_dirname()
|
|||
yosys_share_dirname = proc_share_path;
|
||||
return;
|
||||
}
|
||||
# ifdef YOSYS_DATDIR
|
||||
proc_share_path = YOSYS_DATDIR "/";
|
||||
if (check_directory_exists(proc_share_path, true)) {
|
||||
yosys_share_dirname = proc_share_path;
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
|
@ -676,11 +678,7 @@ std::string proc_share_dirname()
|
|||
|
||||
std::string proc_program_prefix()
|
||||
{
|
||||
std::string program_prefix;
|
||||
#ifdef YOSYS_PROGRAM_PREFIX
|
||||
program_prefix = YOSYS_PROGRAM_PREFIX;
|
||||
#endif
|
||||
return program_prefix;
|
||||
return YOSYS_PROGRAM_PREFIX;
|
||||
}
|
||||
|
||||
bool fgetline(FILE *f, std::string &buffer)
|
||||
|
|
@ -940,28 +938,28 @@ static char *readline_obj_generator(const char *text, int state)
|
|||
if (design->selected_active_module.empty())
|
||||
{
|
||||
for (auto mod : design->modules())
|
||||
if (RTLIL::unescape_id(mod->name).compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(log_id(mod->name)));
|
||||
if (mod->name.unescape().compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(mod->name.unescape().c_str()));
|
||||
}
|
||||
else if (design->module(design->selected_active_module) != nullptr)
|
||||
{
|
||||
RTLIL::Module *module = design->module(design->selected_active_module);
|
||||
|
||||
for (auto w : module->wires())
|
||||
if (RTLIL::unescape_id(w->name).compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(log_id(w->name)));
|
||||
if (w->name.unescape().compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(w->name.unescape().c_str()));
|
||||
|
||||
for (auto &it : module->memories)
|
||||
if (RTLIL::unescape_id(it.first).compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(log_id(it.first)));
|
||||
if (it.first.unescape().compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(it.first.unescape().c_str()));
|
||||
|
||||
for (auto cell : module->cells())
|
||||
if (RTLIL::unescape_id(cell->name).compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(log_id(cell->name)));
|
||||
if (cell->name.unescape().compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(cell->name.unescape().c_str()));
|
||||
|
||||
for (auto &it : module->processes)
|
||||
if (RTLIL::unescape_id(it.first).compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(log_id(it.first)));
|
||||
if (it.first.unescape().compare(0, len, text) == 0)
|
||||
obj_names.push_back(strdup(it.first.unescape().c_str()));
|
||||
}
|
||||
|
||||
std::sort(obj_names.begin(), obj_names.end());
|
||||
|
|
@ -1166,7 +1164,7 @@ struct ScriptCmdPass : public Pass {
|
|||
if (!mod->selected(w))
|
||||
continue;
|
||||
if (!c.second.is_fully_const())
|
||||
log_error("RHS of selected wire %s.%s is not constant.\n", log_id(mod), log_id(w));
|
||||
log_error("RHS of selected wire %s.%s is not constant.\n", mod, w);
|
||||
auto v = c.second.as_const();
|
||||
Pass::call_on_module(design, mod, v.decode_string());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@
|
|||
defines the Yosys Makefile would set for your build configuration.
|
||||
#endif
|
||||
|
||||
#include "kernel/yosys_config.h"
|
||||
|
||||
#define FRIEND_TEST(test_case_name, test_name) \
|
||||
friend class test_case_name##_##test_name##_Test
|
||||
|
||||
|
|
@ -91,6 +93,8 @@
|
|||
# undef CONST
|
||||
// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc
|
||||
# undef TRANSPARENT
|
||||
// `wingdi.h` defines an ERROR macro that conflicts with `ERROR()` macro in kernel/tclapi.cc
|
||||
# undef ERROR
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
|
|
@ -120,10 +124,10 @@
|
|||
# define YS_MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#if __cplusplus >= 202002L
|
||||
# define YS_FALLTHROUGH [[fallthrough]];
|
||||
#else
|
||||
# error "C++17 or later compatible compiler is required"
|
||||
# error "C++20 or later compatible compiler is required"
|
||||
#endif
|
||||
|
||||
#if defined(__has_cpp_attribute) && __has_cpp_attribute(gnu::cold)
|
||||
|
|
|
|||
21
kernel/yosys_config.h.in
Normal file
21
kernel/yosys_config.h.in
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef YOSYS_CONFIG_H
|
||||
#define YOSYS_CONFIG_H
|
||||
|
||||
// Installation parameters
|
||||
#define YOSYS_PROGRAM_PREFIX "@YOSYS_PROGRAM_PREFIX@"
|
||||
#define YOSYS_DATDIR "@YOSYS_INSTALL_DATADIR@"
|
||||
|
||||
// Feature toggles
|
||||
#cmakedefine YOSYS_ENABLE_GLOB
|
||||
#cmakedefine YOSYS_ENABLE_SPAWN
|
||||
#cmakedefine YOSYS_ENABLE_THREADS
|
||||
#cmakedefine YOSYS_ENABLE_DLOPEN
|
||||
#cmakedefine YOSYS_ENABLE_ZLIB
|
||||
#cmakedefine YOSYS_ENABLE_PLUGINS
|
||||
#cmakedefine YOSYS_ENABLE_READLINE
|
||||
#cmakedefine YOSYS_ENABLE_EDITLINE
|
||||
#cmakedefine YOSYS_ENABLE_TCL
|
||||
#cmakedefine YOSYS_ENABLE_PYTHON
|
||||
#cmakedefine YOSYS_ENABLE_VERIFIC
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue