Merge branch 'YosysHQ:master' into master

This commit is contained in:
hakan-demirli 2024-01-31 01:03:59 +03:00 committed by GitHub
commit 8c731658c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 868 additions and 559 deletions

View File

@ -14,10 +14,10 @@ jobs:
run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: cpp
queries: security-extended,security-and-quality
@ -26,4 +26,4 @@ jobs:
run: make yosys -j6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View File

@ -6,13 +6,13 @@ jobs:
emcc:
runs-on: ubuntu-latest
steps:
- uses: mymindstorm/setup-emsdk@v11
- uses: actions/checkout@v3
- uses: mymindstorm/setup-emsdk@v14
- uses: actions/checkout@v4
- name: Build
run: |
make config-emcc
make YOSYS_VER=latest
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: yosysjs
path: yosysjs-latest.zip

View File

@ -79,19 +79,22 @@ jobs:
$CXX --version
- name: Checkout Yosys
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Get iverilog
shell: bash
run: |
git clone https://github.com/steveicarus/iverilog.git
cd iverilog
git checkout 192b6aec96fde982e6ddcb28b346d5893aa8e874
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache iverilog
id: cache-iverilog
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: .local/
key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }}
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
- name: Build iverilog
if: steps.cache-iverilog.outputs.cache-hit != 'true'

View File

@ -35,19 +35,22 @@ jobs:
cc --version
- name: Checkout Yosys
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Get iverilog
shell: bash
run: |
git clone https://github.com/steveicarus/iverilog.git
cd iverilog
git checkout 192b6aec96fde982e6ddcb28b346d5893aa8e874
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache iverilog
id: cache-iverilog
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: .local/
key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }}
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
- name: Build iverilog
if: steps.cache-iverilog.outputs.cache-hit != 'true'

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Take last commit

View File

@ -6,10 +6,10 @@ jobs:
yosys-vcxsrc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build
run: make vcxsrc YOSYS_VER=latest
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: vcxsrc
path: yosys-win32-vcxsrc-latest.zip
@ -18,7 +18,7 @@ jobs:
runs-on: windows-2019
needs: yosys-vcxsrc
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: vcxsrc
path: .

View File

@ -6,7 +6,7 @@ jobs:
wasi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build
run: |
WASI_SDK=wasi-sdk-19.0

View File

@ -2,12 +2,26 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.36 .. Yosys 0.37-dev
Yosys 0.37 .. Yosys 0.38-dev
--------------------------
Yosys 0.36 .. Yosys 0.37
--------------------------
* New commands and options
- Added option "-nodisplay" to read_verilog.
* SystemVerilog
- Correct hierarchical path names for structs and unions.
* Various
- Print hierarchy for failed assertions in "sim" pass.
- Add "--present-only" option to "yosys-witness" to omit unused signals.
- Implement a generic record/replay interface for CXXRTL.
- Improved readability of emitted code with "write_verilog".
Yosys 0.35 .. Yosys 0.36
--------------------------
* New commands and options
* New commands and options
- Added option "--" to pass arguments down to tcl when using -c option.
- Added ability on MacOS and Windows to pass options after arguments on cli.
- Added option "-cmp2softlogic" to synth_lattice.

View File

@ -141,7 +141,7 @@ LDLIBS += -lrt
endif
endif
YOSYS_VER := 0.36+85
YOSYS_VER := 0.37+52
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -157,7 +157,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o
bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8f07a0d.. | wc -l`/;" Makefile
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a5c7f69.. | wc -l`/;" Makefile
# set 'ABCREV = default' to use abc/ as it is
#
@ -679,12 +679,8 @@ OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
OBJS += libs/sha1/sha1.o
ifneq ($(SMALL),1)
OBJS += libs/json11/json11.o
OBJS += libs/subcircuit/subcircuit.o
OBJS += libs/ezsat/ezsat.o
OBJS += libs/ezsat/ezminisat.o
@ -699,6 +695,10 @@ OBJS += libs/fst/fastlz.o
OBJS += libs/fst/lz4.o
endif
ifneq ($(SMALL),1)
OBJS += libs/subcircuit/subcircuit.o
include $(YOSYS_SRC)/frontends/*/Makefile.inc
include $(YOSYS_SRC)/passes/*/Makefile.inc
include $(YOSYS_SRC)/backends/*/Makefile.inc
@ -707,6 +707,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc
else
include $(YOSYS_SRC)/frontends/verilog/Makefile.inc
ifeq ($(ENABLE_VERIFIC),1)
include $(YOSYS_SRC)/frontends/verific/Makefile.inc
endif
include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc
include $(YOSYS_SRC)/frontends/ast/Makefile.inc
include $(YOSYS_SRC)/frontends/blif/Makefile.inc

View File

@ -54,6 +54,8 @@ struct AigerWriter
vector<pair<int, int>> aig_gates;
vector<int> aig_latchin, aig_latchinit, aig_outputs;
vector<SigBit> bit2aig_stack;
size_t next_loop_check = 1024;
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
@ -81,6 +83,23 @@ struct AigerWriter
return it->second;
}
if (bit2aig_stack.size() == next_loop_check) {
for (size_t i = 0; i < next_loop_check; ++i)
{
SigBit report_bit = bit2aig_stack[i];
if (report_bit != bit)
continue;
for (size_t j = i; j < next_loop_check; ++j) {
report_bit = bit2aig_stack[j];
if (report_bit.is_wire() && report_bit.wire->name.isPublic())
break;
}
log_error("Found combinational logic loop while processing signal %s.\n", log_signal(report_bit));
}
next_loop_check *= 2;
}
bit2aig_stack.push_back(bit);
// NB: Cannot use iterator returned from aig_map.insert()
// since this function is called recursively
@ -101,6 +120,8 @@ struct AigerWriter
a = initstate_ff;
}
bit2aig_stack.pop_back();
if (bit == State::Sx || bit == State::Sz)
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");

View File

@ -1072,9 +1072,45 @@ struct CxxrtlWorker {
dump_sigspec_rhs(cell->getPort(ID::EN));
f << " == value<1>{1u}) {\n";
inc_indent();
f << indent << print_output;
fmt.emit_cxxrtl(f, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); });
f << ";\n";
dict<std::string, RTLIL::SigSpec> fmt_args;
f << indent << "struct : public lazy_fmt {\n";
inc_indent();
f << indent << "std::string operator() () const override {\n";
inc_indent();
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
if (sig.size() == 0)
f << "value<0>()";
else {
std::string arg_name = "arg" + std::to_string(fmt_args.size());
fmt_args[arg_name] = sig;
f << arg_name;
}
}, "performer");
dec_indent();
f << indent << "}\n";
f << indent << "struct performer *performer;\n";
for (auto arg : fmt_args)
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
dec_indent();
f << indent << "} formatter;\n";
f << indent << "formatter.performer = performer;\n";
for (auto arg : fmt_args) {
f << indent << "formatter." << arg.first << " = ";
dump_sigspec_rhs(arg.second);
f << ";\n";
}
f << indent << "if (performer) {\n";
inc_indent();
f << indent << "static const metadata_map attributes = ";
dump_metadata_map(cell->attributes);
f << ";\n";
f << indent << "performer->on_print(formatter, attributes);\n";
dec_indent();
f << indent << "} else {\n";
inc_indent();
f << indent << print_output << " << formatter();\n";
dec_indent();
f << indent << "}\n";
dec_indent();
f << indent << "}\n";
}
@ -1291,20 +1327,29 @@ struct CxxrtlWorker {
log_assert(!for_debug);
// Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph.
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool());
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0));
f << indent << "auto " << mangle(cell) << "_curr = ";
dump_sigspec_rhs(cell->getPort(ID::EN));
f << ".concat(";
dump_sigspec_rhs(cell->getPort(ID::ARGS));
f << ").val();\n";
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell
f << indent << "auto " << mangle(cell) << "_curr = ";
dump_sigspec_rhs(cell->getPort(ID::EN));
f << ".concat(";
dump_sigspec_rhs(cell->getPort(ID::ARGS));
f << ").val();\n";
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
inc_indent();
dump_print(cell);
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
dec_indent();
f << indent << "}\n";
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
inc_indent();
dump_print(cell);
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
dec_indent();
f << indent << "}\n";
} else { // initial $print cell
f << indent << "if (!" << mangle(cell) << ") {\n";
inc_indent();
dump_print(cell);
f << indent << mangle(cell) << " = value<1>{1u};\n";
dec_indent();
f << indent << "}\n";
}
// Flip-flops
} else if (is_ff_cell(cell->type)) {
log_assert(!for_debug);
@ -1401,7 +1446,7 @@ struct CxxrtlWorker {
f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q));
dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update(";
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
f << ", ";
@ -1413,7 +1458,7 @@ struct CxxrtlWorker {
f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q));
dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update(";
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
f << ", ";
@ -1485,11 +1530,11 @@ struct CxxrtlWorker {
};
if (buffered_inputs) {
// If we have any buffered inputs, there's no chance of converging immediately.
f << indent << mangle(cell) << access << "eval();\n";
f << indent << mangle(cell) << access << "eval(performer);\n";
f << indent << "converged = false;\n";
assign_from_outputs(/*cell_converged=*/false);
} else {
f << indent << "if (" << mangle(cell) << access << "eval()) {\n";
f << indent << "if (" << mangle(cell) << access << "eval(performer)) {\n";
inc_indent();
assign_from_outputs(/*cell_converged=*/true);
dec_indent();
@ -2002,6 +2047,11 @@ struct CxxrtlWorker {
}
}
for (auto cell : module->cells()) {
// Certain $print cells have additional state, which must be reset as well.
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n";
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
f << indent << mangle(cell) << " = value<1>();\n";
if (is_internal_cell(cell->type))
continue;
f << indent << mangle(cell);
@ -2367,7 +2417,8 @@ struct CxxrtlWorker {
dump_reset_method(module);
f << indent << "}\n";
f << "\n";
f << indent << "bool eval() override {\n";
// No default argument, to prevent unintentional `return bb_foo::eval();` calls that drop performer.
f << indent << "bool eval(performer *performer) override {\n";
dump_eval_method(module);
f << indent << "}\n";
f << "\n";
@ -2377,7 +2428,7 @@ struct CxxrtlWorker {
f << indent << "}\n";
f << "\n";
f << indent << "bool commit() override {\n";
f << indent << indent << "null_observer observer;\n";
f << indent << indent << "observer observer;\n";
f << indent << indent << "return commit<>(observer);\n";
f << indent << "}\n";
if (debug_info) {
@ -2430,11 +2481,11 @@ struct CxxrtlWorker {
f << "\n";
bool has_cells = false;
for (auto cell : module->cells()) {
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) {
// comb $print cell -- store the last EN/ARGS values to know when they change.
dump_attrs(cell);
// Certain $print cells have additional state, which requires storage.
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n";
}
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
f << indent << "value<1> " << mangle(cell) << ";\n";
if (is_internal_cell(cell->type))
continue;
dump_attrs(cell);
@ -2464,7 +2515,7 @@ struct CxxrtlWorker {
f << "\n";
f << indent << "void reset() override;\n";
f << "\n";
f << indent << "bool eval() override;\n";
f << indent << "bool eval(performer *performer = nullptr) override;\n";
f << "\n";
f << indent << "template<class ObserverT>\n";
f << indent << "bool commit(ObserverT &observer) {\n";
@ -2472,7 +2523,7 @@ struct CxxrtlWorker {
f << indent << "}\n";
f << "\n";
f << indent << "bool commit() override {\n";
f << indent << indent << "null_observer observer;\n";
f << indent << indent << "observer observer;\n";
f << indent << indent << "return commit<>(observer);\n";
f << indent << "}\n";
if (debug_info) {
@ -2503,7 +2554,7 @@ struct CxxrtlWorker {
dump_reset_method(module);
f << indent << "}\n";
f << "\n";
f << indent << "bool " << mangle(module) << "::eval() {\n";
f << indent << "bool " << mangle(module) << "::eval(performer *performer) {\n";
dump_eval_method(module);
f << indent << "}\n";
if (debug_info) {
@ -2527,7 +2578,6 @@ struct CxxrtlWorker {
RTLIL::Module *top_module = nullptr;
std::vector<RTLIL::Module*> modules;
TopoSort<RTLIL::Module*> topo_design;
bool has_prints = false;
for (auto module : design->modules()) {
if (!design->selected_module(module))
continue;
@ -2540,8 +2590,6 @@ struct CxxrtlWorker {
topo_design.node(module);
for (auto cell : module->cells()) {
if (cell->type == ID($print))
has_prints = true;
if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell))
continue;
RTLIL::Module *cell_module = design->module(cell->type);
@ -2600,8 +2648,6 @@ struct CxxrtlWorker {
f << "#include \"" << basename(intf_filename) << "\"\n";
else
f << "#include <cxxrtl/cxxrtl.h>\n";
if (has_prints)
f << "#include <iostream>\n";
f << "\n";
f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
@ -2964,8 +3010,9 @@ struct CxxrtlWorker {
for (auto node : node_order)
if (live_nodes[node]) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
node->cell->type == ID($print) &&
node->cell->getParam(ID::TRG_ENABLE).as_bool())
node->cell->type == ID($print) &&
node->cell->getParam(ID::TRG_ENABLE).as_bool() &&
node->cell->getParam(ID::TRG_WIDTH).as_int() != 0)
sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
else
schedule[module].push_back(*node);
@ -3278,7 +3325,7 @@ struct CxxrtlBackend : public Backend {
log(" value<8> p_i_data;\n");
log(" wire<8> p_o_data;\n");
log("\n");
log(" bool eval() override;\n");
log(" bool eval(performer *performer) override;\n");
log(" template<class ObserverT>\n");
log(" bool commit(ObserverT &observer);\n");
log(" bool commit() override;\n");
@ -3293,11 +3340,11 @@ struct CxxrtlBackend : public Backend {
log(" namespace cxxrtl_design {\n");
log("\n");
log(" struct stderr_debug : public bb_p_debug {\n");
log(" bool eval() override {\n");
log(" bool eval(performer *performer) override {\n");
log(" if (posedge_p_clk() && p_en)\n");
log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n");
log(" p_o_data.next = p_i_data;\n");
log(" return bb_p_debug::eval();\n");
log(" return bb_p_debug::eval(performer);\n");
log(" }\n");
log(" };\n");
log("\n");
@ -3398,7 +3445,7 @@ struct CxxrtlBackend : public Backend {
log(" -print-output <stream>\n");
log(" $print cells in the generated code direct their output to <stream>.\n");
log(" must be one of \"std::cout\", \"std::cerr\". if not specified,\n");
log(" \"std::cout\" is used.\n");
log(" \"std::cout\" is used. explicitly provided performer overrides this.\n");
log("\n");
log(" -nohierarchy\n");
log(" use design hierarchy as-is. in most designs, a top module should be\n");

View File

@ -39,6 +39,7 @@
#include <memory>
#include <functional>
#include <sstream>
#include <iostream>
// `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements.
#include <cxxrtl/capi/cxxrtl_capi.h>
@ -565,7 +566,7 @@ struct value : public expr_base<value<Bits>> {
}
value<Bits> neg() const {
return value<Bits> { 0u }.sub(*this);
return value<Bits>().sub(*this);
}
bool ucmp(const value<Bits> &other) const {
@ -763,123 +764,6 @@ std::ostream &operator<<(std::ostream &os, const value<Bits> &val) {
return os;
}
template<size_t Bits>
struct value_formatted {
const value<Bits> &val;
bool character;
bool justify_left;
char padding;
int width;
int base;
bool signed_;
bool plus;
value_formatted(const value<Bits> &val, bool character, bool justify_left, char padding, int width, int base, bool signed_, bool plus) :
val(val), character(character), justify_left(justify_left), padding(padding), width(width), base(base), signed_(signed_), plus(plus) {}
value_formatted(const value_formatted<Bits> &) = delete;
value_formatted<Bits> &operator=(const value_formatted<Bits> &rhs) = delete;
};
template<size_t Bits>
std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf)
{
value<Bits> val = vf.val;
std::string buf;
// We might want to replace some of these bit() calls with direct
// chunk access if it turns out to be slow enough to matter.
if (!vf.character) {
size_t width = Bits;
if (vf.base != 10) {
width = 0;
for (size_t index = 0; index < Bits; index++)
if (val.bit(index))
width = index + 1;
}
if (vf.base == 2) {
for (size_t i = width; i > 0; i--)
buf += (val.bit(i - 1) ? '1' : '0');
} else if (vf.base == 8 || vf.base == 16) {
size_t step = (vf.base == 16) ? 4 : 3;
for (size_t index = 0; index < width; index += step) {
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
if (step == 4)
value |= val.bit(index + 3) << 3;
buf += "0123456789abcdef"[value];
}
std::reverse(buf.begin(), buf.end());
} else if (vf.base == 10) {
bool negative = vf.signed_ && val.is_neg();
if (negative)
val = val.neg();
if (val.is_zero())
buf += '0';
while (!val.is_zero()) {
value<Bits> quotient, remainder;
if (Bits >= 4)
std::tie(quotient, remainder) = val.udivmod(value<Bits>{10u});
else
std::tie(quotient, remainder) = std::make_pair(value<Bits>{0u}, val);
buf += '0' + remainder.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get<uint8_t>();
val = quotient;
}
if (negative || vf.plus)
buf += negative ? '-' : '+';
std::reverse(buf.begin(), buf.end());
} else assert(false);
} else {
buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) {
char ch = 0;
for (int j = 0; j < 8 && i + j < int(Bits); j++)
if (val.bit(i + j))
ch |= 1 << j;
if (ch != 0)
buf.append({ch});
}
std::reverse(buf.begin(), buf.end());
}
assert(vf.width == 0 || vf.padding != '\0');
if (!vf.justify_left && buf.size() < vf.width) {
size_t pad_width = vf.width - buf.size();
if (vf.padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
os << buf.front();
buf.erase(0, 1);
}
os << std::string(pad_width, vf.padding);
}
os << buf;
if (vf.justify_left && buf.size() < vf.width)
os << std::string(vf.width - buf.size(), vf.padding);
return os;
}
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
// the simulation.
struct observer {
// Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks
// at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and
// `base` points to the first chunk.
virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) = 0;
// Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]`
// with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to
// the memory element chunk count and `base` points to the first chunk of the first element of the memory.
virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) = 0;
};
// The `null_observer` class has the same interface as `observer`, but has no invocation overhead, since its methods
// are final and have no implementation. This allows the observer feature to be zero-cost when not in use.
struct null_observer final: observer {
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override {}
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {}
};
template<size_t Bits>
struct wire {
static constexpr size_t bits = Bits;
@ -916,12 +800,11 @@ struct wire {
// This method intentionally takes a mandatory argument (to make it more difficult to misuse in
// black box implementations, leading to missed observer events). It is generic over its argument
// to make sure the `on_commit` call is devirtualized. This is somewhat awkward but lets us keep
// a single implementation for both this method and the one in `memory`.
// to allow the `on_update` method to be non-virtual.
template<class ObserverT>
bool commit(ObserverT &observer) {
if (curr != next) {
observer.on_commit(curr.chunks, curr.data, next.data);
observer.on_update(curr.chunks, curr.data, next.data);
curr = next;
return true;
}
@ -1003,7 +886,7 @@ struct memory {
value<Width> elem = data[entry.index];
elem = elem.update(entry.val, entry.mask);
if (data[entry.index] != elem) {
observer.on_commit(value<Width>::chunks, data[0].data, elem.data, entry.index);
observer.on_update(value<Width>::chunks, data[0].data, elem.data, entry.index);
changed |= true;
}
data[entry.index] = elem;
@ -1062,6 +945,174 @@ struct metadata {
typedef std::map<std::string, metadata> metadata_map;
struct performer;
// An object that allows formatting a string lazily.
struct lazy_fmt {
virtual std::string operator() () const = 0;
};
// An object that can be passed to a `eval()` method in order to act on side effects.
struct performer {
// Called by generated formatting code to evaluate a Verilog `$time` expression.
virtual int64_t vlog_time() const { return 0; }
// Called by generated formatting code to evaluate a Verilog `$realtime` expression.
virtual double vlog_realtime() const { return vlog_time(); }
// Called when a `$print` cell is triggered.
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
std::cout << formatter();
}
};
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and
// a comparatively heavyweight template-based solution is justified.
struct observer {
// Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks
// at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and
// `base` points to the first chunk.
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {}
// Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]`
// with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to
// the memory element chunk count and `base` points to the first chunk of the first element of the memory.
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {}
};
// Must be kept in sync with `struct FmtPart` in kernel/fmt.h!
// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
struct fmt_part {
enum {
STRING = 0,
INTEGER = 1,
CHARACTER = 2,
VLOG_TIME = 3,
} type;
// STRING type
std::string str;
// INTEGER/CHARACTER types
// + value<Bits> val;
// INTEGER/CHARACTER/VLOG_TIME types
enum {
RIGHT = 0,
LEFT = 1,
} justify; // = RIGHT;
char padding; // = '\0';
size_t width; // = 0;
// INTEGER type
unsigned base; // = 10;
bool signed_; // = false;
bool plus; // = false;
// VLOG_TIME type
bool realtime; // = false;
// + int64_t itime;
// + double ftime;
// Format the part as a string.
//
// The values of `vlog_time` and `vlog_realtime` are used for Verilog `$time` and `$realtime`, correspondingly.
template<size_t Bits>
std::string render(value<Bits> val, performer *performer = nullptr)
{
// We might want to replace some of these bit() calls with direct
// chunk access if it turns out to be slow enough to matter.
std::string buf;
switch (type) {
case STRING:
return str;
case CHARACTER: {
buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) {
char ch = 0;
for (int j = 0; j < 8 && i + j < int(Bits); j++)
if (val.bit(i + j))
ch |= 1 << j;
if (ch != 0)
buf.append({ch});
}
std::reverse(buf.begin(), buf.end());
break;
}
case INTEGER: {
size_t width = Bits;
if (base != 10) {
width = 0;
for (size_t index = 0; index < Bits; index++)
if (val.bit(index))
width = index + 1;
}
if (base == 2) {
for (size_t i = width; i > 0; i--)
buf += (val.bit(i - 1) ? '1' : '0');
} else if (base == 8 || base == 16) {
size_t step = (base == 16) ? 4 : 3;
for (size_t index = 0; index < width; index += step) {
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
if (step == 4)
value |= val.bit(index + 3) << 3;
buf += "0123456789abcdef"[value];
}
std::reverse(buf.begin(), buf.end());
} else if (base == 10) {
bool negative = signed_ && val.is_neg();
if (negative)
val = val.neg();
if (val.is_zero())
buf += '0';
value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>();
while (!xval.is_zero()) {
value<(Bits > 4 ? Bits : 4)> quotient, remainder;
if (Bits >= 4)
std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u});
else
std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval);
buf += '0' + remainder.template trunc<4>().template get<uint8_t>();
xval = quotient;
}
if (negative || plus)
buf += negative ? '-' : '+';
std::reverse(buf.begin(), buf.end());
} else assert(false && "Unsupported base for fmt_part");
break;
}
case VLOG_TIME: {
if (performer) {
buf = realtime ? std::to_string(performer->vlog_realtime()) : std::to_string(performer->vlog_time());
} else {
buf = realtime ? std::to_string(0.0) : std::to_string(0);
}
break;
}
}
std::string str;
assert(width == 0 || padding != '\0');
if (justify == RIGHT && buf.size() < width) {
size_t pad_width = width - buf.size();
if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
str += buf.front();
buf.erase(0, 1);
}
str += std::string(pad_width, padding);
}
str += buf;
if (justify == LEFT && buf.size() < width)
str += std::string(width - buf.size(), padding);
return str;
}
};
// Tag class to disambiguate values/wires and their aliases.
struct debug_alias {};
@ -1304,17 +1355,14 @@ struct module {
virtual void reset() = 0;
virtual bool eval() = 0;
virtual bool commit() = 0;
virtual bool eval(performer *performer = nullptr) = 0;
virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls
unsigned int steps = 0;
size_t step() {
++steps;
size_t step(performer *performer = nullptr) {
size_t deltas = 0;
bool converged = false;
do {
converged = eval();
converged = eval(performer);
deltas++;
} while (commit() && !converged);
return deltas;

View File

@ -556,22 +556,20 @@ public:
bool record_incremental(ModuleT &module) {
assert(streaming);
struct : public observer {
struct {
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
spool::writer *writer;
CXXRTL_ALWAYS_INLINE
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override {
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {
writer->write_change(ident_lookup->at(base), chunks, value);
}
CXXRTL_ALWAYS_INLINE
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {
writer->write_change(ident_lookup->at(base), chunks, value, index);
}
} record_observer;
record_observer.ident_lookup = &ident_lookup;
record_observer.writer = &writer;
} record_observer = { &ident_lookup, &writer };
writer.write_sample(/*incremental=*/true, pointer++, timestamp);
for (auto input_index : inputs) {

View File

@ -1053,6 +1053,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(";\n");
return true;
}
if (cell->type == ID($_BUF_)) {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort(ID::Y));
f << stringf(" = ");
dump_cell_expr_port(f, cell, "A", false);
f << stringf(";\n");
return true;
}
if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
f << stringf("%s" "assign ", indent.c_str());
@ -1830,7 +1839,8 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (it != cell->parameters.begin())
f << stringf(",");
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
dump_const(f, it->second);
if (it->second.size() > 0)
dump_const(f, it->second);
f << stringf(")");
}
f << stringf("\n%s" ")", indent.c_str());
@ -1895,17 +1905,21 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
{
f << stringf("%s" "always @(", indent.c_str());
for (int i = 0; i < trg.size(); i++) {
if (i != 0)
f << " or ";
if (polarity[i])
f << "posedge ";
else
f << "negedge ";
dump_sigspec(f, trg[i]);
if (trg.size() == 0) {
f << stringf("%s" "initial begin\n", indent.c_str());
} else {
f << stringf("%s" "always @(", indent.c_str());
for (int i = 0; i < trg.size(); i++) {
if (i != 0)
f << " or ";
if (polarity[i])
f << "posedge ";
else
f << "negedge ";
dump_sigspec(f, trg[i]);
}
f << ") begin\n";
}
f << ") begin\n";
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
@ -1944,13 +1958,8 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left,
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
{
int number_of_stmts = cs->switches.size() + cs->actions.size();
if (!omit_trailing_begin && number_of_stmts >= 2)
f << stringf("%s" "begin\n", indent.c_str());
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
if (it->first.size() == 0)
continue;
@ -1960,15 +1969,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo
dump_sigspec(f, it->second);
f << stringf(";\n");
}
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
dump_proc_switch(f, indent + " ", *it);
if (!omit_trailing_begin && number_of_stmts == 0)
f << stringf("%s /* empty */;\n", indent.c_str());
if (omit_trailing_begin || number_of_stmts >= 2)
f << stringf("%s" "end\n", indent.c_str());
}
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
@ -1991,36 +1991,52 @@ bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchR
}
}
dump_attributes(f, indent, sw->attributes);
f << indent;
auto sig_it = sw->signal.begin();
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
bool had_newline = true;
if (it != sw->cases.begin()) {
if ((*it)->compare.empty()) {
f << indent << "else\n";
had_newline = true;
} else {
f << indent << "else ";
had_newline = false;
}
if ((*it)->compare.empty())
f << " else begin\n";
else
f << " else ";
}
if (!(*it)->compare.empty()) {
if (!(*it)->attributes.empty()) {
if (!had_newline)
f << "\n" << indent;
dump_attributes(f, "", (*it)->attributes, "\n" + indent);
}
f << stringf("if (");
dump_sigspec(f, *sig_it);
f << stringf(")\n");
f << stringf(") begin\n");
}
dump_case_body(f, indent, *it);
dump_case_actions(f, indent, (*it));
for (auto it2 = (*it)->switches.begin(); it2 != (*it)->switches.end(); ++it2)
dump_proc_switch(f, indent + " ", *it2);
f << indent << "end";
if ((*it)->compare.empty())
break;
}
f << "\n";
return true;
}
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
{
int number_of_stmts = cs->switches.size() + cs->actions.size();
if (!omit_trailing_begin && number_of_stmts >= 2)
f << stringf("%s" "begin\n", indent.c_str());
dump_case_actions(f, indent, cs);
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
dump_proc_switch(f, indent + " ", *it);
if (!omit_trailing_begin && number_of_stmts == 0)
f << stringf("%s /* empty */;\n", indent.c_str());
if (omit_trailing_begin || number_of_stmts >= 2)
f << stringf("%s" "end\n", indent.c_str());
}
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
{
if (sw->signal.size() == 0) {

View File

@ -120,7 +120,7 @@ All binary RTL cells have two input ports ``\A`` and ``\B`` and one output port
:verilog:`Y = A >>> B` $sshr :verilog:`Y = A - B` $sub
:verilog:`Y = A && B` $logic_and :verilog:`Y = A * B` $mul
:verilog:`Y = A || B` $logic_or :verilog:`Y = A / B` $div
:verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod
:verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod
:verilog:`Y = A !== B` $nex ``N/A`` $divfloor
:verilog:`Y = A ** B` $pow ``N/A`` $modfoor
======================= ============= ======================= =========
@ -661,6 +661,8 @@ Ports:
``\TRG``
The signals that control when this ``$print`` cell is triggered.
If the width of this port is zero and ``\TRG_ENABLE`` is true, the cell is
triggered during initial evaluation (time zero) only.
``\EN``
Enable signal for the whole cell.

View File

@ -45,7 +45,7 @@ namespace AST {
// instantiate global variables (private API)
namespace AST_INTERNAL {
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire;
AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope;
@ -1320,11 +1320,12 @@ static void rename_in_package_stmts(AstNode *pkg)
}
// create AstModule instances for all modules in the AST tree and add them to 'design'
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire)
{
current_ast = ast;
current_ast_mod = nullptr;
flag_nodisplay = nodisplay;
flag_dump_ast1 = dump_ast1;
flag_dump_ast2 = dump_ast2;
flag_no_dump_ptr = no_dump_ptr;

View File

@ -287,7 +287,7 @@ namespace AST
bool is_simple_const_expr();
// helper for parsing format strings
Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0);
Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0, bool may_fail = false);
bool is_recursive_function() const;
std::pair<AstNode*, AstNode*> get_tern_choice();
@ -376,7 +376,7 @@ namespace AST
};
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
void process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire);
// parametric modules are supported directly by the AST library
@ -432,7 +432,7 @@ namespace AST
namespace AST_INTERNAL
{
// internal state variables
extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
extern bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire;
extern AST::AstNode *current_ast, *current_ast_mod;
extern std::map<std::string, AST::AstNode*> current_scope;

View File

@ -718,7 +718,7 @@ struct AST_INTERNAL::ProcessGenerator
}
}
cell->parameters[ID::TRG_WIDTH] = triggers.size();
cell->parameters[ID::TRG_ENABLE] = !triggers.empty();
cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty();
cell->parameters[ID::TRG_POLARITY] = polarity;
cell->parameters[ID::PRIORITY] = --last_print_priority;
cell->setPort(ID::TRG, triggers);

View File

@ -145,7 +145,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend)
// Process a format string and arguments for $display, $write, $sprintf, etc
Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at) {
Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at, bool may_fail) {
std::vector<VerilogFmtArg> args;
for (size_t index = first_arg_at; index < children.size(); index++) {
AstNode *node_arg = children[index];
@ -169,6 +169,9 @@ Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_
arg.type = VerilogFmtArg::INTEGER;
arg.sig = node_arg->bitsAsConst();
arg.signed_ = node_arg->is_signed;
} else if (may_fail) {
log_file_info(filename, location.first_line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1);
return Fmt();
} else {
log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1);
}
@ -217,8 +220,8 @@ void AstNode::annotateTypedEnums(AstNode *template_node)
log_assert(enum_item->children[1]->type == AST_RANGE);
is_signed = enum_item->children[1]->is_signed;
} else {
log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n",
enum_item->children.size(),
log_error("enum_item children size==%zu, expected 1 or 2 for %s (%s)\n",
(size_t) enum_item->children.size(),
enum_item->str.c_str(), enum_node->str.c_str()
);
}
@ -1055,30 +1058,31 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
{
if (!current_always) {
log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str());
} else if (current_always->type == AST_INITIAL) {
int default_base = 10;
if (str.back() == 'b')
default_base = 2;
else if (str.back() == 'o')
default_base = 8;
else if (str.back() == 'h')
default_base = 16;
// when $display()/$write() functions are used in an initial block, print them during synthesis
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base);
if (str.substr(0, 8) == "$display")
fmt.append_string("\n");
log("%s", fmt.render().c_str());
delete_children();
str = std::string();
} else {
// when $display()/$write() functions are used in an always block, simplify the expressions and
// convert them to a special cell later in genrtlil
// simplify the expressions and convert them to a special cell later in genrtlil
for (auto node : children)
while (node->simplify(true, stage, -1, false)) {}
if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) {
int default_base = 10;
if (str.back() == 'b')
default_base = 2;
else if (str.back() == 'o')
default_base = 8;
else if (str.back() == 'h')
default_base = 16;
// when $display()/$write() functions are used in an initial block, print them during synthesis
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true);
if (str.substr(0, 8) == "$display")
fmt.append_string("\n");
log("%s", fmt.render().c_str());
}
return false;
}
delete_children();
str = std::string();
}
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
@ -2966,96 +2970,67 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
}
} else {
// mask and shift operations
AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true)));
wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_mask->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire_mask->is_logic = true;
while (wire_mask->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire_mask);
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true)));
wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire_data->is_logic = true;
while (wire_data->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire_data);
int shamt_width_hint = -1;
bool shamt_sign_hint = true;
shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint);
AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true)));
wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_sel->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire_sel->is_logic = true;
wire_sel->is_signed = shamt_sign_hint;
while (wire_sel->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire_sel);
did_something = true;
newNode = new AstNode(AST_BLOCK);
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
AstNode *lvalue = children[0]->clone();
lvalue->delete_children();
if (member_node)
lvalue->set_attribute(ID::wiretype, member_node->clone());
AstNode *ref_mask = new AstNode(AST_IDENTIFIER);
ref_mask->str = wire_mask->str;
ref_mask->id2ast = wire_mask;
ref_mask->was_checked = true;
AstNode *ref_data = new AstNode(AST_IDENTIFIER);
ref_data->str = wire_data->str;
ref_data->id2ast = wire_data;
ref_data->was_checked = true;
AstNode *ref_sel = new AstNode(AST_IDENTIFIER);
ref_sel->str = wire_sel->str;
ref_sel->id2ast = wire_sel;
ref_sel->was_checked = true;
AstNode *old_data = lvalue->clone();
if (type == AST_ASSIGN_LE)
old_data->lookahead = true;
AstNode *s = new AstNode(AST_ASSIGN_EQ, ref_sel->clone(), shift_expr);
newNode->children.push_back(s);
int shift_width_hint;
bool shift_sign_hint;
shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint);
AstNode *shamt = ref_sel;
// All operations are carried out in a new block.
newNode = new AstNode(AST_BLOCK);
// convert to signed while preserving the sign and value
shamt = new AstNode(AST_CAST_SIZE, mkconst_int(shamt_width_hint + 1, true), shamt);
shamt = new AstNode(AST_TO_SIGNED, shamt);
// Temporary register holding the result of the bit- or part-select position expression.
AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint);
newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, pos, shift_expr));
// Calculate lsb from position.
AstNode *shift_val = pos->clone();
// If the expression is signed, we must add an extra bit for possible negation of the most negative number.
// If the expression is unsigned, we must add an extra bit for sign.
shift_val = new AstNode(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), shift_val);
if (!shift_sign_hint)
shift_val = new AstNode(AST_TO_SIGNED, shift_val);
// offset the shift amount by the lower bound of the dimension
int start_bit = wire_offset;
shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true));
if (wire_offset != 0)
shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true));
// reflect the shift amount if the dimension is swapped
if (children[0]->id2ast->range_swapped)
shamt = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shamt);
shift_val = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shift_val);
// AST_SHIFT uses negative amounts for shifting left
shamt = new AstNode(AST_NEG, shamt);
shift_val = new AstNode(AST_NEG, shift_val);
AstNode *t;
t = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
t = new AstNode(AST_SHIFT, t, shamt->clone());
t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t);
newNode->children.push_back(t);
t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone());
t = new AstNode(AST_SHIFT, t, shamt);
t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t);
newNode->children.push_back(t);
t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask));
t = new AstNode(AST_BIT_OR, t, ref_data);
t = new AstNode(type, lvalue, t);
newNode->children.push_back(t);
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
did_something = true;
AstNode *bitmask = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
newNode->children.push_back(
new AstNode(type,
lvalue,
new AstNode(AST_BIT_OR,
new AstNode(AST_BIT_AND,
old_data,
new AstNode(AST_BIT_NOT,
new AstNode(AST_SHIFT,
bitmask,
shift_val->clone()))),
new AstNode(AST_SHIFT,
new AstNode(AST_TO_UNSIGNED,
new AstNode(AST_CAST_SIZE,
mkconst_int(result_width, true),
children[1]->clone())),
shift_val))));
newNode->fixup_hierarchy_flags(true);
}

View File

@ -2110,7 +2110,7 @@ VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_a
if (sva_at_only)
do {
Instance *inst_mux = net->Driver();
if (inst_mux->Type() != PRIM_MUX)
if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX)
break;
bool pwr1 = inst_mux->GetInput1()->IsPwr();

View File

@ -100,6 +100,10 @@ struct VerilogFrontend : public Frontend {
log(" -assert-assumes\n");
log(" treat all assume() statements like assert() statements\n");
log("\n");
log(" -nodisplay\n");
log(" suppress output from display system tasks ($display et. al).\n");
log(" This does not affect the output from a later 'sim' command.\n");
log("\n");
log(" -debug\n");
log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n");
log("\n");
@ -235,6 +239,7 @@ struct VerilogFrontend : public Frontend {
}
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool flag_nodisplay = false;
bool flag_dump_ast1 = false;
bool flag_dump_ast2 = false;
bool flag_no_dump_ptr = false;
@ -308,6 +313,10 @@ struct VerilogFrontend : public Frontend {
assert_assumes_mode = true;
continue;
}
if (arg == "-nodisplay") {
flag_nodisplay = true;
continue;
}
if (arg == "-debug") {
flag_dump_ast1 = true;
flag_dump_ast2 = true;
@ -510,7 +519,7 @@ struct VerilogFrontend : public Frontend {
if (flag_nodpi)
error_on_dpi_function(current_ast);
AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
AST::process(design, current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);

View File

@ -110,9 +110,9 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
} else if (fmt[i] == 'c') {
part.type = FmtPart::CHARACTER;
} else if (fmt[i] == 't') {
part.type = FmtPart::TIME;
part.type = FmtPart::VLOG_TIME;
} else if (fmt[i] == 'r') {
part.type = FmtPart::TIME;
part.type = FmtPart::VLOG_TIME;
part.realtime = true;
} else {
log_assert(false && "Unexpected character in format substitution");
@ -172,7 +172,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
}
break;
case FmtPart::TIME:
case FmtPart::VLOG_TIME:
log_assert(part.sig.size() == 0);
YS_FALLTHROUGH
case FmtPart::CHARACTER:
@ -205,7 +205,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
fmt += part.signed_ ? 's' : 'u';
} else if (part.type == FmtPart::CHARACTER) {
fmt += 'c';
} else if (part.type == FmtPart::TIME) {
} else if (part.type == FmtPart::VLOG_TIME) {
if (part.realtime)
fmt += 'r';
else
@ -328,7 +328,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
case VerilogFmtArg::TIME: {
FmtPart part = {};
part.type = FmtPart::TIME;
part.type = FmtPart::VLOG_TIME;
part.realtime = arg->realtime;
part.padding = ' ';
part.width = 20;
@ -419,7 +419,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.padding = ' ';
} else if (fmt[i] == 't' || fmt[i] == 'T') {
if (arg->type == VerilogFmtArg::TIME) {
part.type = FmtPart::TIME;
part.type = FmtPart::VLOG_TIME;
part.realtime = arg->realtime;
if (!has_width && !has_leading_zero)
part.width = 20;
@ -541,7 +541,7 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
break;
}
case FmtPart::TIME: {
case FmtPart::VLOG_TIME: {
VerilogFmtArg arg;
arg.type = VerilogFmtArg::TIME;
if (part.realtime)
@ -569,82 +569,60 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
return args;
}
void Fmt::emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const
std::string escape_cxx_string(const std::string &input)
{
for (auto &part : parts) {
switch (part.type) {
case FmtPart::STRING:
f << " << \"";
for (char c : part.str) {
switch (c) {
case '\\':
YS_FALLTHROUGH
case '"':
f << '\\' << c;
break;
case '\a':
f << "\\a";
break;
case '\b':
f << "\\b";
break;
case '\f':
f << "\\f";
break;
case '\n':
f << "\\n";
break;
case '\r':
f << "\\r";
break;
case '\t':
f << "\\t";
break;
case '\v':
f << "\\v";
break;
default:
f << c;
break;
}
}
f << '"';
break;
case FmtPart::INTEGER:
case FmtPart::CHARACTER: {
f << " << value_formatted<" << part.sig.size() << ">(";
emit_sig(part.sig);
f << ", " << (part.type == FmtPart::CHARACTER);
f << ", " << (part.justify == FmtPart::LEFT);
f << ", (char)" << (int)part.padding;
f << ", " << part.width;
f << ", " << part.base;
f << ", " << part.signed_;
f << ", " << part.plus;
f << ')';
break;
}
case FmtPart::TIME: {
// CXXRTL only records steps taken, so there's no difference between
// the values taken by $time and $realtime.
f << " << value_formatted<64>(";
f << "value<64>{steps}";
f << ", " << (part.type == FmtPart::CHARACTER);
f << ", " << (part.justify == FmtPart::LEFT);
f << ", (char)" << (int)part.padding;
f << ", " << part.width;
f << ", " << part.base;
f << ", " << part.signed_;
f << ", " << part.plus;
f << ')';
break;
}
default: log_abort();
std::string output = "\"";
for (auto c : input) {
if (::isprint(c)) {
if (c == '\\')
output.push_back('\\');
output.push_back(c);
} else {
char l = c & 0xf, h = (c >> 4) & 0xf;
output.append("\\x");
output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
}
}
output.push_back('"');
if (output.find('\0') != std::string::npos) {
output.insert(0, "std::string {");
output.append(stringf(", %zu}", input.size()));
}
return output;
}
void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig, const std::string &context) const
{
os << indent << "std::string buf;\n";
for (auto &part : parts) {
os << indent << "buf += fmt_part { ";
os << "fmt_part::";
switch (part.type) {
case FmtPart::STRING: os << "STRING"; break;
case FmtPart::INTEGER: os << "INTEGER"; break;
case FmtPart::CHARACTER: os << "CHARACTER"; break;
case FmtPart::VLOG_TIME: os << "VLOG_TIME"; break;
}
os << ", ";
os << escape_cxx_string(part.str) << ", ";
os << "fmt_part::";
switch (part.justify) {
case FmtPart::LEFT: os << "LEFT"; break;
case FmtPart::RIGHT: os << "RIGHT"; break;
}
os << ", ";
os << "(char)" << (int)part.padding << ", ";
os << part.width << ", ";
os << part.base << ", ";
os << part.signed_ << ", ";
os << part.plus << ", ";
os << part.realtime;
os << " }.render(";
emit_sig(part.sig);
os << ", " << context << ");\n";
}
os << indent << "return buf;\n";
}
std::string Fmt::render() const
@ -658,8 +636,8 @@ std::string Fmt::render() const
break;
case FmtPart::INTEGER:
case FmtPart::TIME:
case FmtPart::CHARACTER: {
case FmtPart::CHARACTER:
case FmtPart::VLOG_TIME: {
std::string buf;
if (part.type == FmtPart::INTEGER) {
RTLIL::Const value = part.sig.as_const();
@ -742,7 +720,7 @@ std::string Fmt::render() const
} else log_abort();
} else if (part.type == FmtPart::CHARACTER) {
buf = part.sig.as_const().decode_string();
} else if (part.type == FmtPart::TIME) {
} else if (part.type == FmtPart::VLOG_TIME) {
// We only render() during initial, so time is always zero.
buf = "0";
}

View File

@ -50,12 +50,13 @@ struct VerilogFmtArg {
// RTLIL format part, such as the substitutions in:
// "foo {4:> 4du} bar {2:<01hs}"
// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h!
struct FmtPart {
enum {
STRING = 0,
INTEGER = 1,
CHARACTER = 2,
TIME = 3,
VLOG_TIME = 3,
} type;
// STRING type
@ -64,20 +65,20 @@ struct FmtPart {
// INTEGER/CHARACTER types
RTLIL::SigSpec sig;
// INTEGER/CHARACTER/TIME types
// INTEGER/CHARACTER/VLOG_TIME types
enum {
RIGHT = 0,
LEFT = 1,
} justify = RIGHT;
char padding = '\0';
size_t width = 0;
// INTEGER type
unsigned base = 10;
bool signed_ = false;
bool plus = false;
// TIME type
// VLOG_TIME type
bool realtime = false;
};
@ -93,7 +94,7 @@ public:
void parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name);
std::vector<VerilogFmtArg> emit_verilog() const;
void emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const;
void emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig, const std::string &context) const;
std::string render() const;

View File

@ -2157,17 +2157,10 @@ void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
}
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
log_assert(GetSize(lhs) == GetSize(rhs));
lhs.unpack();
rhs.unpack();
for (int i = 0; i < GetSize(lhs); i++) {
RTLIL::SigBit &lhs_bit = lhs.bits_[i];
RTLIL::SigBit &rhs_bit = rhs.bits_[i];
if ((lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) || (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))) {
lhs_bit = State::Sx;
rhs_bit = State::Sx;
}
}
// If a deleted wire occurs on the lhs or rhs we just remove that part
// of the assignment
lhs.remove2(*wires_p, &rhs);
rhs.remove2(*wires_p, &lhs);
}
};
@ -3693,6 +3686,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
{
log_assert(offset >= 0);
log_assert(length >= 0);
log_assert(offset + length <= width);
RTLIL::SigChunk ret;
if (wire) {
ret.wire = wire;
@ -4238,6 +4234,34 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
check();
}
void RTLIL::SigSpec::remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other)
{
if (other)
cover("kernel.rtlil.sigspec.remove_other");
else
cover("kernel.rtlil.sigspec.remove");
unpack();
if (other != NULL) {
log_assert(width_ == other->width_);
other->unpack();
}
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) {
bits_.erase(bits_.begin() + i);
width_--;
if (other != NULL) {
other->bits_.erase(other->bits_.begin() + i);
other->width_--;
}
}
}
check();
}
RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
{
if (other)
@ -4377,6 +4401,9 @@ void RTLIL::SigSpec::remove(int offset, int length)
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
{
log_assert(offset >= 0);
log_assert(length >= 0);
log_assert(offset + length <= width_);
unpack();
cover("kernel.rtlil.sigspec.extract_pos");
return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);

View File

@ -924,6 +924,7 @@ public:
void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
void remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other);
void remove(int offset, int length = 1);
void remove_const();

View File

@ -539,7 +539,7 @@ struct ShowWorker
std::string proc_src = RTLIL::unescape_id(proc->name);
if (proc->attributes.count(ID::src) > 0)
proc_src = proc->attributes.at(ID::src).decode_string();
fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\"];\n", pidx, findLabel(proc->name.str()), proc_src.c_str());
fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\", %s];\n", pidx, findLabel(proc->name.str()), proc_src.c_str(), findColor(proc->name).c_str());
}
for (auto &conn : module->connections())

View File

@ -366,7 +366,7 @@ struct StatPass : public Pass {
log(" use cell area information from the provided liberty file\n");
log("\n");
log(" -tech <technology>\n");
log(" print area estemate for the specified technology. Currently supported\n");
log(" print area estimate for the specified technology. Currently supported\n");
log(" values for <technology>: xilinx, cmos\n");
log("\n");
log(" -width\n");

View File

@ -655,6 +655,17 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
log("Removed %d unused modules.\n", del_counter);
}
bool set_keep_print(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
{
if (cache.count(mod) == 0)
for (auto c : mod->cells()) {
RTLIL::Module *m = mod->design->module(c->type);
if ((m != nullptr && set_keep_print(cache, m)) || c->type == ID($print))
return cache[mod] = true;
}
return cache[mod];
}
bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
{
if (cache.count(mod) == 0)
@ -762,6 +773,11 @@ struct HierarchyPass : public Pass {
log(" -nodefaults\n");
log(" do not resolve input port default values\n");
log("\n");
log(" -nokeep_prints\n");
log(" per default this pass sets the \"keep\" attribute on all modules\n");
log(" that directly or indirectly display text on the terminal.\n");
log(" This option disables this behavior.\n");
log("\n");
log(" -nokeep_asserts\n");
log(" per default this pass sets the \"keep\" attribute on all modules\n");
log(" that directly or indirectly contain one or more formal properties.\n");
@ -818,6 +834,7 @@ struct HierarchyPass : public Pass {
bool keep_positionals = false;
bool keep_portwidths = false;
bool nodefaults = false;
bool nokeep_prints = false;
bool nokeep_asserts = false;
std::vector<std::string> generate_cells;
std::vector<generate_port_decl_t> generate_ports;
@ -893,6 +910,10 @@ struct HierarchyPass : public Pass {
nodefaults = true;
continue;
}
if (args[argidx] == "-nokeep_prints") {
nokeep_prints = true;
continue;
}
if (args[argidx] == "-nokeep_asserts") {
nokeep_asserts = true;
continue;
@ -1091,6 +1112,15 @@ struct HierarchyPass : public Pass {
}
}
if (!nokeep_prints) {
std::map<RTLIL::Module*, bool> cache;
for (auto mod : design->modules())
if (set_keep_print(cache, mod)) {
log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", log_id(mod));
mod->set_bool_attribute(ID::keep);
}
}
if (!nokeep_asserts) {
std::map<RTLIL::Module*, bool> cache;
for (auto mod : design->modules())

View File

@ -690,7 +690,7 @@ bool apply_clock(MemConfig &cfg, const PortVariant &def, SigBit clk, bool clk_po
// Perform write port assignment, validating clock options as we go.
void MemMapping::assign_wr_ports() {
log_reject(stringf("Assigning write ports... (candidate configs: %lu)", cfgs.size()));
log_reject(stringf("Assigning write ports... (candidate configs: %zu)", (size_t) cfgs.size()));
for (auto &port: mem.wr_ports) {
if (!port.clk_enable) {
// Async write ports not supported.
@ -739,7 +739,7 @@ void MemMapping::assign_wr_ports() {
// Perform read port assignment, validating clock and rden options as we go.
void MemMapping::assign_rd_ports() {
log_reject(stringf("Assigning read ports... (candidate configs: %lu)", cfgs.size()));
log_reject(stringf("Assigning read ports... (candidate configs: %zu)", (size_t) cfgs.size()));
for (int pidx = 0; pidx < GetSize(mem.rd_ports); pidx++) {
auto &port = mem.rd_ports[pidx];
MemConfigs new_cfgs;
@ -900,7 +900,7 @@ void MemMapping::assign_rd_ports() {
// Validate transparency restrictions, determine where to add soft transparency logic.
void MemMapping::handle_trans() {
log_reject(stringf("Handling transparency... (candidate configs: %lu)", cfgs.size()));
log_reject(stringf("Handling transparency... (candidate configs: %zu)", (size_t) cfgs.size()));
if (mem.emulate_read_first_ok()) {
MemConfigs new_cfgs;
for (auto &cfg: cfgs) {

View File

@ -24,6 +24,10 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
// Type represents the following constraint: Preserve connections to dedicated
// logic cell <cell_type> that has ports connected to LUT inputs. This includes
// the case where both LUT and dedicated logic input are connected to the same
// constant.
struct dlogic_t {
IdString cell_type;
// LUT input idx -> hard cell's port name
@ -515,16 +519,6 @@ struct OptLutWorker
}
};
static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
{
size_t start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
}
struct OptLutPass : public Pass {
OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
void help() override
@ -541,6 +535,10 @@ struct OptLutPass : public Pass {
log(" the case where both LUT and dedicated logic input are connected to\n");
log(" the same constant.\n");
log("\n");
log(" -tech ice40\n");
log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n");
log(" and preserve connections to SB_CARRY as appropriate\n");
log("\n");
log(" -limit N\n");
log(" only perform the first N combines, then stop. useful for debugging.\n");
log("\n");
@ -555,28 +553,28 @@ struct OptLutPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-dlogic" && argidx+1 < args.size())
if (args[argidx] == "-tech" && argidx+1 < args.size())
{
std::vector<std::string> tokens;
split(tokens, args[++argidx], ':');
if (tokens.size() < 2)
log_cmd_error("The -dlogic option requires at least one connection.\n");
dlogic_t entry;
entry.cell_type = "\\" + tokens[0];
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
std::vector<std::string> conn_tokens;
split(conn_tokens, *it, '=');
if (conn_tokens.size() != 2)
log_cmd_error("Invalid format of -dlogic signal mapping.\n");
IdString logic_port = "\\" + conn_tokens[0];
int lut_input = atoi(conn_tokens[1].c_str());
entry.lut_input_port[lut_input] = logic_port;
}
dlogic.push_back(entry);
std::string tech = args[++argidx];
if (tech != "ice40")
log_cmd_error("Unsupported -tech argument: %s\n", tech.c_str());
dlogic = {{
ID(SB_CARRY),
dict<int, IdString>{
std::make_pair(1, ID(I0)),
std::make_pair(2, ID(I1)),
std::make_pair(3, ID(CI))
}
}, {
ID(SB_CARRY),
dict<int, IdString>{
std::make_pair(3, ID(CO))
}
}};
continue;
}
if (args[argidx] == "-limit" && argidx + 1 < args.size())
{
if (args[argidx] == "-limit" && argidx + 1 < args.size()) {
limit = atoi(args[++argidx].c_str());
continue;
}

View File

@ -26,6 +26,7 @@
#include <algorithm>
#include <queue>
#include <cinttypes>
USING_YOSYS_NAMESPACE
@ -623,7 +624,7 @@ struct RecoverNamesWorker {
if (pop == 1 || pop == (8*sizeof(equiv_cls_t) - 1))
continue;
log_debug("equivalence class: %016lx\n", cls.first);
log_debug("equivalence class: %016" PRIx64 "\n", cls.first);
const pool<IdBit> &gold_bits = cls2bits.at(cls.first).first;
const pool<InvBit> &gate_bits = cls2bits.at(cls.first).second;
if (gold_bits.empty() || gate_bits.empty())

View File

@ -88,6 +88,17 @@ struct TriggeredAssertion {
{ }
};
struct DisplayOutput {
int step;
SimInstance *instance;
Cell *cell;
std::string output;
DisplayOutput(int step, SimInstance *instance, Cell *cell, std::string output) :
step(step), instance(instance), cell(cell), output(output)
{ }
};
struct SimShared
{
bool debug = false;
@ -110,6 +121,7 @@ struct SimShared
int next_output_id = 0;
int step = 0;
std::vector<TriggeredAssertion> triggered_assertions;
std::vector<DisplayOutput> display_output;
bool serious_asserts = false;
bool initstate = true;
};
@ -173,6 +185,7 @@ struct SimInstance
struct print_state_t
{
bool initial_done;
Const past_trg;
Const past_en;
Const past_args;
@ -338,6 +351,7 @@ struct SimInstance
print.past_trg = Const(State::Sx, cell->getPort(ID::TRG).size());
print.past_args = Const(State::Sx, cell->getPort(ID::ARGS).size());
print.past_en = State::Sx;
print.initial_done = false;
}
}
@ -799,7 +813,7 @@ struct SimInstance
}
}
void update_ph3(bool check_assertions)
void update_ph3(bool gclk_trigger)
{
for (auto &it : ff_database)
{
@ -840,45 +854,57 @@ struct SimInstance
bool triggered = false;
Const trg = get_state(cell->getPort(ID::TRG));
bool trg_en = cell->getParam(ID::TRG_ENABLE).as_bool();
Const en = get_state(cell->getPort(ID::EN));
Const args = get_state(cell->getPort(ID::ARGS));
if (!en.as_bool())
goto update_print;
bool sampled = trg_en && trg.size() > 0;
if (cell->getParam(ID::TRG_ENABLE).as_bool()) {
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
for (int i = 0; i < trg.size(); i++) {
bool pol = trg_pol[i] == State::S1;
State curr = trg[i], past = print.past_trg[i];
if (pol && curr == State::S1 && past == State::S0)
if (sampled ? print.past_en.as_bool() : en.as_bool()) {
if (sampled) {
sampled = true;
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
for (int i = 0; i < trg.size(); i++) {
bool pol = trg_pol[i] == State::S1;
State curr = trg[i], past = print.past_trg[i];
if (pol && curr == State::S1 && past == State::S0)
triggered = true;
if (!pol && curr == State::S0 && past == State::S1)
triggered = true;
}
} else if (trg_en) {
// initial $print (TRG width = 0, TRG_ENABLE = true)
if (!print.initial_done && en != print.past_en)
triggered = true;
if (!pol && curr == State::S0 && past == State::S1)
} else if (cell->get_bool_attribute(ID(trg_on_gclk))) {
// unified $print for cycle based FV semantics
triggered = gclk_trigger;
} else {
// always @(*) $print
if (args != print.past_args || en != print.past_en)
triggered = true;
}
} else {
if (args != print.past_args || en != print.past_en)
triggered = true;
}
if (triggered) {
int pos = 0;
for (auto &part : print.fmt.parts) {
part.sig = args.extract(pos, part.sig.size());
pos += part.sig.size();
if (triggered) {
int pos = 0;
for (auto &part : print.fmt.parts) {
part.sig = (sampled ? print.past_args : args).extract(pos, part.sig.size());
pos += part.sig.size();
}
std::string rendered = print.fmt.render();
log("%s", rendered.c_str());
shared->display_output.emplace_back(shared->step, this, cell, rendered);
}
std::string rendered = print.fmt.render();
log("%s", rendered.c_str());
}
update_print:
print.past_trg = trg;
print.past_en = en;
print.past_args = args;
print.initial_done = true;
}
if (check_assertions)
if (gclk_trigger)
{
for (auto cell : formal_database)
{
@ -910,7 +936,7 @@ struct SimInstance
}
for (auto it : children)
it.second->update_ph3(check_assertions);
it.second->update_ph3(gclk_trigger);
}
void set_initstate_outputs(State state)
@ -2055,6 +2081,20 @@ struct SimWorker : SimShared
json.end_object();
}
json.end_array();
json.name("display_output");
json.begin_array();
for (auto &output : display_output) {
json.begin_object();
json.entry("step", output.step);
json.entry("path", output.instance->witness_full_path(output.cell));
auto src = output.cell->get_string_attribute(ID::src);
if (!src.empty()) {
json.entry("src", src);
}
json.entry("output", output.output);
json.end_object();
}
json.end_array();
json.end_object();
}

View File

@ -1,13 +1,11 @@
ram block $__GOWIN_SP_ {
abits 14;
widths 1 2 4 9 18 36 per_port;
byte 9;
cost 128;
init no_undef;
port srsw "A" {
clock posedge;
clken;
wrbe_separate;
option "RESET_MODE" "SYNC" {
rdsrst zero ungated;
}
@ -30,13 +28,11 @@ ram block $__GOWIN_SP_ {
ram block $__GOWIN_DP_ {
abits 14;
widths 1 2 4 9 18 per_port;
byte 9;
cost 128;
init no_undef;
port srsw "A" "B" {
clock posedge;
clken;
wrbe_separate;
option "RESET_MODE" "SYNC" {
rdsrst zero ungated;
}
@ -59,7 +55,6 @@ ram block $__GOWIN_DP_ {
ram block $__GOWIN_SDP_ {
abits 14;
widths 1 2 4 9 18 36 per_port;
byte 9;
cost 128;
init no_undef;
port sr "R" {
@ -76,6 +71,5 @@ ram block $__GOWIN_SDP_ {
port sw "W" {
clock posedge;
clken;
wrbe_separate;
}
}

View File

@ -14,8 +14,7 @@
`define x8_width(width) (width / 9 * 8 + width % 9)
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
`define wre(width, wr_en, wr_be) (width < 18 ? wr_en | wr_be[0] : wr_en)
`define addrbe(width, addr, wr_be) (width < 18 ? addr : {addr[13:4], wr_be})
`define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111})
`define INIT(func) \
@ -90,7 +89,6 @@ parameter INIT = 0;
parameter OPTION_RESET_MODE = "SYNC";
parameter PORT_A_WIDTH = 36;
parameter PORT_A_WR_BE_WIDTH = 4;
parameter PORT_A_OPTION_WRITE_MODE = 0;
input PORT_A_CLK;
@ -99,15 +97,13 @@ input PORT_A_WR_EN;
input PORT_A_RD_SRST;
input PORT_A_RD_ARST;
input [13:0] PORT_A_ADDR;
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
`DEF_FUNCS
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
wire WRE = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE);
wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
generate
@ -129,9 +125,9 @@ if (PORT_A_WIDTH < 9) begin
.BLKSEL(3'b000),
.CLK(PORT_A_CLK),
.CE(PORT_A_CLK_EN),
.WRE(WRE),
.WRE(PORT_A_WR_EN),
.RESET(RST),
.OCE(1'b0),
.OCE(1'b1),
.AD(AD),
.DI(DI),
.DO(DO),
@ -155,9 +151,9 @@ end else begin
.BLKSEL(3'b000),
.CLK(PORT_A_CLK),
.CE(PORT_A_CLK_EN),
.WRE(WRE),
.WRE(PORT_A_WR_EN),
.RESET(RST),
.OCE(1'b0),
.OCE(1'b1),
.AD(AD),
.DI(DI),
.DO(DO),
@ -176,11 +172,9 @@ parameter INIT = 0;
parameter OPTION_RESET_MODE = "SYNC";
parameter PORT_A_WIDTH = 18;
parameter PORT_A_WR_BE_WIDTH = 2;
parameter PORT_A_OPTION_WRITE_MODE = 0;
parameter PORT_B_WIDTH = 18;
parameter PORT_B_WR_BE_WIDTH = 2;
parameter PORT_B_OPTION_WRITE_MODE = 0;
input PORT_A_CLK;
@ -189,7 +183,6 @@ input PORT_A_WR_EN;
input PORT_A_RD_SRST;
input PORT_A_RD_ARST;
input [13:0] PORT_A_ADDR;
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
@ -199,7 +192,6 @@ input PORT_B_WR_EN;
input PORT_B_RD_SRST;
input PORT_B_RD_ARST;
input [13:0] PORT_B_ADDR;
input [PORT_A_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
@ -207,10 +199,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
wire WREA = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE);
wire WREB = `wre(PORT_B_WIDTH, PORT_B_WR_EN, PORT_B_WR_BE);
wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE);
wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR);
generate
@ -224,7 +214,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
assign PORT_A_RD_DATA = `x8_rd_data(DOA);
assign PORT_B_RD_DATA = `x8_rd_data(DOB);
DP #(
DPB #(
`INIT(init_slice_x8)
.READ_MODE0(1'b0),
.READ_MODE1(1'b0),
@ -232,25 +222,27 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
.WRITE_MODE1(PORT_B_OPTION_WRITE_MODE),
.BIT_WIDTH_0(`x8_width(PORT_A_WIDTH)),
.BIT_WIDTH_1(`x8_width(PORT_B_WIDTH)),
.BLK_SEL(3'b000),
.BLK_SEL_0(3'b000),
.BLK_SEL_1(3'b000),
.RESET_MODE(OPTION_RESET_MODE),
) _TECHMAP_REPLACE_ (
.BLKSEL(3'b000),
.BLKSELA(3'b000),
.BLKSELB(3'b000),
.CLKA(PORT_A_CLK),
.CEA(PORT_A_CLK_EN),
.WREA(WREA),
.WREA(PORT_A_WR_EN),
.RESETA(RSTA),
.OCEA(1'b0),
.OCEA(1'b1),
.ADA(ADA),
.DIA(DIA),
.DOA(DOA),
.CLKB(PORT_B_CLK),
.CEB(PORT_B_CLK_EN),
.WREB(WREB),
.WREB(PORT_B_WR_EN),
.RESETB(RSTB),
.OCEB(1'b0),
.OCEB(1'b1),
.ADB(ADB),
.DIB(DIB),
.DOB(DOB),
@ -266,7 +258,7 @@ end else begin
assign PORT_A_RD_DATA = DOA;
assign PORT_B_RD_DATA = DOB;
DPX9 #(
DPX9B #(
`INIT(init_slice_x9)
.READ_MODE0(1'b0),
.READ_MODE1(1'b0),
@ -274,25 +266,27 @@ end else begin
.WRITE_MODE1(PORT_B_OPTION_WRITE_MODE),
.BIT_WIDTH_0(PORT_A_WIDTH),
.BIT_WIDTH_1(PORT_B_WIDTH),
.BLK_SEL(3'b000),
.BLK_SEL_0(3'b000),
.BLK_SEL_1(3'b000),
.RESET_MODE(OPTION_RESET_MODE),
) _TECHMAP_REPLACE_ (
.BLKSEL(3'b000),
.BLKSELA(3'b000),
.BLKSELB(3'b000),
.CLKA(PORT_A_CLK),
.CEA(PORT_A_CLK_EN),
.WREA(WREA),
.WREA(PORT_A_WR_EN),
.RESETA(RSTA),
.OCEA(1'b0),
.OCEA(1'b1),
.ADA(ADA),
.DIA(DIA),
.DOA(DOA),
.CLKB(PORT_B_CLK),
.CEB(PORT_B_CLK_EN),
.WREB(WREB),
.WREB(PORT_B_WR_EN),
.RESETB(RSTB),
.OCEB(1'b0),
.OCEB(1'b1),
.ADB(ADB),
.DIB(DIB),
.DOB(DOB),
@ -311,9 +305,7 @@ parameter INIT = 0;
parameter OPTION_RESET_MODE = "SYNC";
parameter PORT_R_WIDTH = 18;
parameter PORT_W_WIDTH = 18;
parameter PORT_W_WR_BE_WIDTH = 2;
input PORT_R_CLK;
input PORT_R_CLK_EN;
@ -326,14 +318,13 @@ input PORT_W_CLK;
input PORT_W_CLK_EN;
input PORT_W_WR_EN;
input [13:0] PORT_W_ADDR;
input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE;
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
`DEF_FUNCS
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
wire WRE = `wre(PORT_W_WIDTH, PORT_W_WR_EN, PORT_W_WR_BE);
wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE);
wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR);
wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN;
generate
@ -344,28 +335,28 @@ if (PORT_W_WIDTH < 9 || PORT_R_WIDTH < 9) begin
assign PORT_R_RD_DATA = `x8_rd_data(DO);
SDP #(
SDPB #(
`INIT(init_slice_x8)
.READ_MODE(1'b0),
.BIT_WIDTH_0(`x8_width(PORT_W_WIDTH)),
.BIT_WIDTH_1(`x8_width(PORT_R_WIDTH)),
.BLK_SEL(3'b000),
.BLK_SEL_0(3'b000),
.BLK_SEL_1(3'b000),
.RESET_MODE(OPTION_RESET_MODE),
) _TECHMAP_REPLACE_ (
.BLKSEL(3'b000),
.BLKSELA(3'b000),
.BLKSELB(3'b000),
.CLKA(PORT_W_CLK),
.CEA(PORT_W_CLK_EN),
.WREA(WRE),
.CEA(WRE),
.RESETA(1'b0),
.ADA(ADW),
.DI(DI),
.CLKB(PORT_R_CLK),
.CEB(PORT_R_CLK_EN),
.WREB(1'b0),
.RESETB(RST),
.OCE(1'b0),
.OCE(1'b1),
.ADB(PORT_R_ADDR),
.DO(DO),
);
@ -377,28 +368,28 @@ end else begin
assign PORT_R_RD_DATA = DO;
SDPX9 #(
SDPX9B #(
`INIT(init_slice_x9)
.READ_MODE(1'b0),
.BIT_WIDTH_0(PORT_W_WIDTH),
.BIT_WIDTH_1(PORT_R_WIDTH),
.BLK_SEL(3'b000),
.BLK_SEL_0(3'b000),
.BLK_SEL_1(3'b000),
.RESET_MODE(OPTION_RESET_MODE),
) _TECHMAP_REPLACE_ (
.BLKSEL(3'b000),
.BLKSELA(3'b000),
.BLKSELB(3'b000),
.CLKA(PORT_W_CLK),
.CEA(PORT_W_CLK_EN),
.WREA(WRE),
.CEA(WRE),
.RESETA(1'b0),
.ADA(ADW),
.DI(DI),
.CLKB(PORT_R_CLK),
.CEB(PORT_R_CLK_EN),
.WREB(1'b0),
.RESETB(RST),
.OCE(1'b0),
.OCE(1'b1),
.ADB(PORT_R_ADDR),
.DO(DO),
);

View File

@ -432,7 +432,7 @@ struct SynthIce40Pass : public ScriptPass
run("ice40_wrapcarry -unwrap");
run("techmap -map +/ice40/ff_map.v");
run("clean");
run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3 -dlogic SB_CARRY:CO=3");
run("opt_lut -tech ice40");
}
if (check_label("map_cells"))

View File

@ -2,8 +2,13 @@
int main()
{
struct : public performer {
int64_t vlog_time() const override { return 1; }
void on_print(const lazy_fmt &output, const cxxrtl::metadata_map &) override { std::cerr << output(); }
} performer;
cxxrtl_design::p_always__full uut;
uut.p_clk.set(!uut.p_clk);
uut.step();
uut.step(&performer);
return 0;
}

View File

@ -0,0 +1,20 @@
module test (
offset_i,
data_o,
data_ref_o
);
input wire [ 2:0] offset_i;
output reg [15:0] data_o;
output reg [15:0] data_ref_o;
always @(*) begin
// defaults
data_o = '0;
data_ref_o = '0;
// partial assigns
data_ref_o[offset_i+:4] = 4'b1111; // unsigned
data_o[offset_i+:4] = 1'sb1; // sign extension to 4'b1111
end
endmodule

8
tests/various/bug4082.ys Normal file
View File

@ -0,0 +1,8 @@
read_verilog <<EOF
module top;
wire a;
wire b;
assign a = b;
endmodule
EOF
delete w:a

10
tests/verific/clocking.ys Normal file
View File

@ -0,0 +1,10 @@
read -sv <<EOT
module test(input foo);
always @(*) assert(foo);
endmodule
EOT
verific -import test
prep
select -assert-count 1 t:$assert

View File

@ -0,0 +1,65 @@
# Test "casez to if/elif/else conversion" in backend
read_verilog <<EOT
module top(a, b, c, out);
input wire a, b, c;
output reg [3:0] out;
always @(*) begin
casez ({c, b, a})
3'b??1: begin
out = 0;
end
3'b?1?: begin
out = 1;
end
3'b1??: begin
out = 2;
end
default: begin
out = 3;
end
endcase
end
endmodule
EOT
write_verilog roundtrip_proc_1.v
design -stash gold
read_verilog roundtrip_proc_1.v
design -stash gate
design -copy-from gold -as gold top
design -copy-from gate -as gate top
prep
miter -equiv -flatten -make_assert gold gate miter
hierarchy -top miter
sat -prove-asserts -verify
design -reset
read_verilog <<EOT
module top(a, b, c, out);
input wire a, b, c;
output reg [3:0] out;
always @(*) begin
out <= 0;
if (a) begin
if (b)
out <= 1;
end else begin
if (c)
out <= 2;
else
out <= 3;
end
end
endmodule
EOT
proc_clean
write_verilog roundtrip_proc_2.v
design -stash gold
read_verilog roundtrip_proc_2.v
design -stash gate
design -copy-from gold -as gold top
design -copy-from gate -as gate top
prep
miter -equiv -flatten -make_assert gold gate miter
hierarchy -top miter
sat -prove-asserts -verify