3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-07 01:54:10 +00:00

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,12 +2,26 @@
List of major changes and improvements between releases 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 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 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 ability on MacOS and Windows to pass options after arguments on cli.
- Added option "-cmp2softlogic" to synth_lattice. - Added option "-cmp2softlogic" to synth_lattice.

View file

@ -141,7 +141,7 @@ LDLIBS += -lrt
endif endif
endif endif
YOSYS_VER := 0.36+85 YOSYS_VER := 0.37+52
# Note: We arrange for .gitcommit to contain the (short) commit hash in # Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo # tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -157,7 +157,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o OBJS = kernel/version_$(GIT_REV).o
bumpversion: 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 # 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 OBJS += libs/sha1/sha1.o
ifneq ($(SMALL),1)
OBJS += libs/json11/json11.o OBJS += libs/json11/json11.o
OBJS += libs/subcircuit/subcircuit.o
OBJS += libs/ezsat/ezsat.o OBJS += libs/ezsat/ezsat.o
OBJS += libs/ezsat/ezminisat.o OBJS += libs/ezsat/ezminisat.o
@ -699,6 +695,10 @@ OBJS += libs/fst/fastlz.o
OBJS += libs/fst/lz4.o OBJS += libs/fst/lz4.o
endif endif
ifneq ($(SMALL),1)
OBJS += libs/subcircuit/subcircuit.o
include $(YOSYS_SRC)/frontends/*/Makefile.inc include $(YOSYS_SRC)/frontends/*/Makefile.inc
include $(YOSYS_SRC)/passes/*/Makefile.inc include $(YOSYS_SRC)/passes/*/Makefile.inc
include $(YOSYS_SRC)/backends/*/Makefile.inc include $(YOSYS_SRC)/backends/*/Makefile.inc
@ -707,6 +707,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc
else else
include $(YOSYS_SRC)/frontends/verilog/Makefile.inc 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/rtlil/Makefile.inc
include $(YOSYS_SRC)/frontends/ast/Makefile.inc include $(YOSYS_SRC)/frontends/ast/Makefile.inc
include $(YOSYS_SRC)/frontends/blif/Makefile.inc include $(YOSYS_SRC)/frontends/blif/Makefile.inc

View file

@ -54,6 +54,8 @@ struct AigerWriter
vector<pair<int, int>> aig_gates; vector<pair<int, int>> aig_gates;
vector<int> aig_latchin, aig_latchinit, aig_outputs; 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_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; int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
@ -81,6 +83,23 @@ struct AigerWriter
return it->second; 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() // NB: Cannot use iterator returned from aig_map.insert()
// since this function is called recursively // since this function is called recursively
@ -101,6 +120,8 @@ struct AigerWriter
a = initstate_ff; a = initstate_ff;
} }
bit2aig_stack.pop_back();
if (bit == State::Sx || bit == State::Sz) if (bit == State::Sx || bit == State::Sz)
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); 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)); dump_sigspec_rhs(cell->getPort(ID::EN));
f << " == value<1>{1u}) {\n"; f << " == value<1>{1u}) {\n";
inc_indent(); inc_indent();
f << indent << print_output; dict<std::string, RTLIL::SigSpec> fmt_args;
fmt.emit_cxxrtl(f, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); f << indent << "struct : public lazy_fmt {\n";
f << ";\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(); dec_indent();
f << indent << "}\n"; f << indent << "}\n";
} }
@ -1291,20 +1327,29 @@ struct CxxrtlWorker {
log_assert(!for_debug); log_assert(!for_debug);
// Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph. // 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 = "; if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell
dump_sigspec_rhs(cell->getPort(ID::EN)); f << indent << "auto " << mangle(cell) << "_curr = ";
f << ".concat("; dump_sigspec_rhs(cell->getPort(ID::EN));
dump_sigspec_rhs(cell->getPort(ID::ARGS)); f << ".concat(";
f << ").val();\n"; dump_sigspec_rhs(cell->getPort(ID::ARGS));
f << ").val();\n";
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n"; f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
inc_indent(); inc_indent();
dump_print(cell); dump_print(cell);
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n"; f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
dec_indent(); dec_indent();
f << indent << "}\n"; 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 // Flip-flops
} else if (is_ff_cell(cell->type)) { } else if (is_ff_cell(cell->type)) {
log_assert(!for_debug); log_assert(!for_debug);
@ -1401,7 +1446,7 @@ struct CxxrtlWorker {
f << indent; f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = "; f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update("; f << ".update(";
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
f << ", "; f << ", ";
@ -1413,7 +1458,7 @@ struct CxxrtlWorker {
f << indent; f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = "; f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update("; f << ".update(";
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int())); dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
f << ", "; f << ", ";
@ -1485,11 +1530,11 @@ struct CxxrtlWorker {
}; };
if (buffered_inputs) { if (buffered_inputs) {
// If we have any buffered inputs, there's no chance of converging immediately. // 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"; f << indent << "converged = false;\n";
assign_from_outputs(/*cell_converged=*/false); assign_from_outputs(/*cell_converged=*/false);
} else { } else {
f << indent << "if (" << mangle(cell) << access << "eval()) {\n"; f << indent << "if (" << mangle(cell) << access << "eval(performer)) {\n";
inc_indent(); inc_indent();
assign_from_outputs(/*cell_converged=*/true); assign_from_outputs(/*cell_converged=*/true);
dec_indent(); dec_indent();
@ -2002,6 +2047,11 @@ struct CxxrtlWorker {
} }
} }
for (auto cell : module->cells()) { 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)) if (is_internal_cell(cell->type))
continue; continue;
f << indent << mangle(cell); f << indent << mangle(cell);
@ -2367,7 +2417,8 @@ struct CxxrtlWorker {
dump_reset_method(module); dump_reset_method(module);
f << indent << "}\n"; f << indent << "}\n";
f << "\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); dump_eval_method(module);
f << indent << "}\n"; f << indent << "}\n";
f << "\n"; f << "\n";
@ -2377,7 +2428,7 @@ struct CxxrtlWorker {
f << indent << "}\n"; f << indent << "}\n";
f << "\n"; f << "\n";
f << indent << "bool commit() override {\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 << indent << "return commit<>(observer);\n";
f << indent << "}\n"; f << indent << "}\n";
if (debug_info) { if (debug_info) {
@ -2430,11 +2481,11 @@ struct CxxrtlWorker {
f << "\n"; f << "\n";
bool has_cells = false; bool has_cells = false;
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) { // Certain $print cells have additional state, which requires storage.
// comb $print cell -- store the last EN/ARGS values to know when they change. if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
dump_attrs(cell);
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; 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)) if (is_internal_cell(cell->type))
continue; continue;
dump_attrs(cell); dump_attrs(cell);
@ -2464,7 +2515,7 @@ struct CxxrtlWorker {
f << "\n"; f << "\n";
f << indent << "void reset() override;\n"; f << indent << "void reset() override;\n";
f << "\n"; f << "\n";
f << indent << "bool eval() override;\n"; f << indent << "bool eval(performer *performer = nullptr) override;\n";
f << "\n"; f << "\n";
f << indent << "template<class ObserverT>\n"; f << indent << "template<class ObserverT>\n";
f << indent << "bool commit(ObserverT &observer) {\n"; f << indent << "bool commit(ObserverT &observer) {\n";
@ -2472,7 +2523,7 @@ struct CxxrtlWorker {
f << indent << "}\n"; f << indent << "}\n";
f << "\n"; f << "\n";
f << indent << "bool commit() override {\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 << indent << "return commit<>(observer);\n";
f << indent << "}\n"; f << indent << "}\n";
if (debug_info) { if (debug_info) {
@ -2503,7 +2554,7 @@ struct CxxrtlWorker {
dump_reset_method(module); dump_reset_method(module);
f << indent << "}\n"; f << indent << "}\n";
f << "\n"; f << "\n";
f << indent << "bool " << mangle(module) << "::eval() {\n"; f << indent << "bool " << mangle(module) << "::eval(performer *performer) {\n";
dump_eval_method(module); dump_eval_method(module);
f << indent << "}\n"; f << indent << "}\n";
if (debug_info) { if (debug_info) {
@ -2527,7 +2578,6 @@ struct CxxrtlWorker {
RTLIL::Module *top_module = nullptr; RTLIL::Module *top_module = nullptr;
std::vector<RTLIL::Module*> modules; std::vector<RTLIL::Module*> modules;
TopoSort<RTLIL::Module*> topo_design; TopoSort<RTLIL::Module*> topo_design;
bool has_prints = false;
for (auto module : design->modules()) { for (auto module : design->modules()) {
if (!design->selected_module(module)) if (!design->selected_module(module))
continue; continue;
@ -2540,8 +2590,6 @@ struct CxxrtlWorker {
topo_design.node(module); topo_design.node(module);
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
if (cell->type == ID($print))
has_prints = true;
if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell)) if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell))
continue; continue;
RTLIL::Module *cell_module = design->module(cell->type); RTLIL::Module *cell_module = design->module(cell->type);
@ -2600,8 +2648,6 @@ struct CxxrtlWorker {
f << "#include \"" << basename(intf_filename) << "\"\n"; f << "#include \"" << basename(intf_filename) << "\"\n";
else else
f << "#include <cxxrtl/cxxrtl.h>\n"; f << "#include <cxxrtl/cxxrtl.h>\n";
if (has_prints)
f << "#include <iostream>\n";
f << "\n"; f << "\n";
f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n"; f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
@ -2964,8 +3010,9 @@ struct CxxrtlWorker {
for (auto node : node_order) for (auto node : node_order)
if (live_nodes[node]) { if (live_nodes[node]) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
node->cell->type == ID($print) && node->cell->type == ID($print) &&
node->cell->getParam(ID::TRG_ENABLE).as_bool()) 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); sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
else else
schedule[module].push_back(*node); schedule[module].push_back(*node);
@ -3278,7 +3325,7 @@ struct CxxrtlBackend : public Backend {
log(" value<8> p_i_data;\n"); log(" value<8> p_i_data;\n");
log(" wire<8> p_o_data;\n"); log(" wire<8> p_o_data;\n");
log("\n"); log("\n");
log(" bool eval() override;\n"); log(" bool eval(performer *performer) override;\n");
log(" template<class ObserverT>\n"); log(" template<class ObserverT>\n");
log(" bool commit(ObserverT &observer);\n"); log(" bool commit(ObserverT &observer);\n");
log(" bool commit() override;\n"); log(" bool commit() override;\n");
@ -3293,11 +3340,11 @@ struct CxxrtlBackend : public Backend {
log(" namespace cxxrtl_design {\n"); log(" namespace cxxrtl_design {\n");
log("\n"); log("\n");
log(" struct stderr_debug : public bb_p_debug {\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(" if (posedge_p_clk() && p_en)\n");
log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n"); log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n");
log(" p_o_data.next = p_i_data;\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"); log(" };\n");
log("\n"); log("\n");
@ -3398,7 +3445,7 @@ struct CxxrtlBackend : public Backend {
log(" -print-output <stream>\n"); log(" -print-output <stream>\n");
log(" $print cells in the generated code direct their output to <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(" 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("\n");
log(" -nohierarchy\n"); log(" -nohierarchy\n");
log(" use design hierarchy as-is. in most designs, a top module should be\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 <memory>
#include <functional> #include <functional>
#include <sstream> #include <sstream>
#include <iostream>
// `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements. // `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements.
#include <cxxrtl/capi/cxxrtl_capi.h> #include <cxxrtl/capi/cxxrtl_capi.h>
@ -565,7 +566,7 @@ struct value : public expr_base<value<Bits>> {
} }
value<Bits> neg() const { value<Bits> neg() const {
return value<Bits> { 0u }.sub(*this); return value<Bits>().sub(*this);
} }
bool ucmp(const value<Bits> &other) const { bool ucmp(const value<Bits> &other) const {
@ -763,123 +764,6 @@ std::ostream &operator<<(std::ostream &os, const value<Bits> &val) {
return os; 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> template<size_t Bits>
struct wire { struct wire {
static constexpr size_t bits = Bits; 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 // 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 // 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 // to allow the `on_update` method to be non-virtual.
// a single implementation for both this method and the one in `memory`.
template<class ObserverT> template<class ObserverT>
bool commit(ObserverT &observer) { bool commit(ObserverT &observer) {
if (curr != next) { if (curr != next) {
observer.on_commit(curr.chunks, curr.data, next.data); observer.on_update(curr.chunks, curr.data, next.data);
curr = next; curr = next;
return true; return true;
} }
@ -1003,7 +886,7 @@ struct memory {
value<Width> elem = data[entry.index]; value<Width> elem = data[entry.index];
elem = elem.update(entry.val, entry.mask); elem = elem.update(entry.val, entry.mask);
if (data[entry.index] != elem) { 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; changed |= true;
} }
data[entry.index] = elem; data[entry.index] = elem;
@ -1062,6 +945,174 @@ struct metadata {
typedef std::map<std::string, metadata> metadata_map; 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. // Tag class to disambiguate values/wires and their aliases.
struct debug_alias {}; struct debug_alias {};
@ -1304,17 +1355,14 @@ struct module {
virtual void reset() = 0; virtual void reset() = 0;
virtual bool eval() = 0; virtual bool eval(performer *performer = nullptr) = 0;
virtual bool commit() = 0; virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls
unsigned int steps = 0; size_t step(performer *performer = nullptr) {
size_t step() {
++steps;
size_t deltas = 0; size_t deltas = 0;
bool converged = false; bool converged = false;
do { do {
converged = eval(); converged = eval(performer);
deltas++; deltas++;
} while (commit() && !converged); } while (commit() && !converged);
return deltas; return deltas;

View file

@ -556,22 +556,20 @@ public:
bool record_incremental(ModuleT &module) { bool record_incremental(ModuleT &module) {
assert(streaming); assert(streaming);
struct : public observer { struct {
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup; std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
spool::writer *writer; spool::writer *writer;
CXXRTL_ALWAYS_INLINE 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); writer->write_change(ident_lookup->at(base), chunks, value);
} }
CXXRTL_ALWAYS_INLINE 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); writer->write_change(ident_lookup->at(base), chunks, value, index);
} }
} record_observer; } record_observer = { &ident_lookup, &writer };
record_observer.ident_lookup = &ident_lookup;
record_observer.writer = &writer;
writer.write_sample(/*incremental=*/true, pointer++, timestamp); writer.write_sample(/*incremental=*/true, pointer++, timestamp);
for (auto input_index : inputs) { 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"); f << stringf(";\n");
return true; 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_))) { 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()); 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()) if (it != cell->parameters.begin())
f << stringf(","); f << stringf(",");
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); 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(")");
} }
f << stringf("\n%s" ")", indent.c_str()); 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) 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()); if (trg.size() == 0) {
for (int i = 0; i < trg.size(); i++) { f << stringf("%s" "initial begin\n", indent.c_str());
if (i != 0) } else {
f << " or "; f << stringf("%s" "always @(", indent.c_str());
if (polarity[i]) for (int i = 0; i < trg.size(); i++) {
f << "posedge "; if (i != 0)
else f << " or ";
f << "negedge "; if (polarity[i])
dump_sigspec(f, trg[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) { 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(); 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_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) { for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
if (it->first.size() == 0) if (it->first.size() == 0)
continue; continue;
@ -1960,15 +1969,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo
dump_sigspec(f, it->second); dump_sigspec(f, it->second);
f << stringf(";\n"); 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) 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; f << indent;
auto sig_it = sw->signal.begin(); auto sig_it = sw->signal.begin();
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) { for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
bool had_newline = true;
if (it != sw->cases.begin()) { if (it != sw->cases.begin()) {
if ((*it)->compare.empty()) { if ((*it)->compare.empty())
f << indent << "else\n"; f << " else begin\n";
had_newline = true; else
} else { f << " else ";
f << indent << "else ";
had_newline = false;
}
} }
if (!(*it)->compare.empty()) { if (!(*it)->compare.empty()) {
if (!(*it)->attributes.empty()) {
if (!had_newline)
f << "\n" << indent;
dump_attributes(f, "", (*it)->attributes, "\n" + indent);
}
f << stringf("if ("); f << stringf("if (");
dump_sigspec(f, *sig_it); 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()) if ((*it)->compare.empty())
break; break;
} }
f << "\n";
return true; 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) void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
{ {
if (sw->signal.size() == 0) { 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` $sshr :verilog:`Y = A - B` $sub
:verilog:`Y = A && B` $logic_and :verilog:`Y = A * B` $mul :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` $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` $nex ``N/A`` $divfloor
:verilog:`Y = A ** B` $pow ``N/A`` $modfoor :verilog:`Y = A ** B` $pow ``N/A`` $modfoor
======================= ============= ======================= ========= ======================= ============= ======================= =========
@ -661,6 +661,8 @@ Ports:
``\TRG`` ``\TRG``
The signals that control when this ``$print`` cell is triggered. 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`` ``\EN``
Enable signal for the whole cell. Enable signal for the whole cell.

View file

@ -45,7 +45,7 @@ namespace AST {
// instantiate global variables (private API) // instantiate global variables (private API)
namespace AST_INTERNAL { 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; 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; AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope; 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' // 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) 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 = ast;
current_ast_mod = nullptr; current_ast_mod = nullptr;
flag_nodisplay = nodisplay;
flag_dump_ast1 = dump_ast1; flag_dump_ast1 = dump_ast1;
flag_dump_ast2 = dump_ast2; flag_dump_ast2 = dump_ast2;
flag_no_dump_ptr = no_dump_ptr; flag_no_dump_ptr = no_dump_ptr;

View file

@ -287,7 +287,7 @@ namespace AST
bool is_simple_const_expr(); bool is_simple_const_expr();
// helper for parsing format strings // 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; bool is_recursive_function() const;
std::pair<AstNode*, AstNode*> get_tern_choice(); 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 // 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); 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 // parametric modules are supported directly by the AST library
@ -432,7 +432,7 @@ namespace AST
namespace AST_INTERNAL namespace AST_INTERNAL
{ {
// internal state variables // 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 bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire;
extern AST::AstNode *current_ast, *current_ast_mod; extern AST::AstNode *current_ast, *current_ast_mod;
extern std::map<std::string, AST::AstNode*> current_scope; 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_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::TRG_POLARITY] = polarity;
cell->parameters[ID::PRIORITY] = --last_print_priority; cell->parameters[ID::PRIORITY] = --last_print_priority;
cell->setPort(ID::TRG, triggers); 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 // 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; std::vector<VerilogFmtArg> args;
for (size_t index = first_arg_at; index < children.size(); index++) { for (size_t index = first_arg_at; index < children.size(); index++) {
AstNode *node_arg = children[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.type = VerilogFmtArg::INTEGER;
arg.sig = node_arg->bitsAsConst(); arg.sig = node_arg->bitsAsConst();
arg.signed_ = node_arg->is_signed; 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 { } 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); 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); log_assert(enum_item->children[1]->type == AST_RANGE);
is_signed = enum_item->children[1]->is_signed; is_signed = enum_item->children[1]->is_signed;
} else { } else {
log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n", log_error("enum_item children size==%zu, expected 1 or 2 for %s (%s)\n",
enum_item->children.size(), (size_t) enum_item->children.size(),
enum_item->str.c_str(), enum_node->str.c_str() 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) { if (!current_always) {
log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str()); 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) { delete_children();
int default_base = 10; str = std::string();
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());
} else { } else {
// when $display()/$write() functions are used in an always block, simplify the expressions and // simplify the expressions and convert them to a special cell later in genrtlil
// convert them to a special cell later in genrtlil
for (auto node : children) for (auto node : children)
while (node->simplify(true, stage, -1, false)) {} 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; return false;
} }
delete_children();
str = std::string();
} }
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) // 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 { } else {
// mask and shift operations // mask and shift operations
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
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);
AstNode *lvalue = children[0]->clone(); AstNode *lvalue = children[0]->clone();
lvalue->delete_children(); lvalue->delete_children();
if (member_node) if (member_node)
lvalue->set_attribute(ID::wiretype, member_node->clone()); 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(); AstNode *old_data = lvalue->clone();
if (type == AST_ASSIGN_LE) if (type == AST_ASSIGN_LE)
old_data->lookahead = true; old_data->lookahead = true;
AstNode *s = new AstNode(AST_ASSIGN_EQ, ref_sel->clone(), shift_expr); int shift_width_hint;
newNode->children.push_back(s); 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 // Temporary register holding the result of the bit- or part-select position expression.
shamt = new AstNode(AST_CAST_SIZE, mkconst_int(shamt_width_hint + 1, true), shamt); AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint);
shamt = new AstNode(AST_TO_SIGNED, shamt); 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 // offset the shift amount by the lower bound of the dimension
int start_bit = wire_offset; if (wire_offset != 0)
shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true)); shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true));
// reflect the shift amount if the dimension is swapped // reflect the shift amount if the dimension is swapped
if (children[0]->id2ast->range_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 // AST_SHIFT uses negative amounts for shifting left
shamt = new AstNode(AST_NEG, shamt); shift_val = new AstNode(AST_NEG, shift_val);
AstNode *t; // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
did_something = true;
t = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false); AstNode *bitmask = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
t = new AstNode(AST_SHIFT, t, shamt->clone()); newNode->children.push_back(
t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t); new AstNode(type,
newNode->children.push_back(t); lvalue,
new AstNode(AST_BIT_OR,
t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone()); new AstNode(AST_BIT_AND,
t = new AstNode(AST_SHIFT, t, shamt); old_data,
t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t); new AstNode(AST_BIT_NOT,
newNode->children.push_back(t); new AstNode(AST_SHIFT,
bitmask,
t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)); shift_val->clone()))),
t = new AstNode(AST_BIT_OR, t, ref_data); new AstNode(AST_SHIFT,
t = new AstNode(type, lvalue, t); new AstNode(AST_TO_UNSIGNED,
newNode->children.push_back(t); new AstNode(AST_CAST_SIZE,
mkconst_int(result_width, true),
children[1]->clone())),
shift_val))));
newNode->fixup_hierarchy_flags(true); newNode->fixup_hierarchy_flags(true);
} }

View file

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

View file

@ -100,6 +100,10 @@ struct VerilogFrontend : public Frontend {
log(" -assert-assumes\n"); log(" -assert-assumes\n");
log(" treat all assume() statements like assert() statements\n"); log(" treat all assume() statements like assert() statements\n");
log("\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(" -debug\n");
log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n"); log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n");
log("\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 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_ast1 = false;
bool flag_dump_ast2 = false; bool flag_dump_ast2 = false;
bool flag_no_dump_ptr = false; bool flag_no_dump_ptr = false;
@ -308,6 +313,10 @@ struct VerilogFrontend : public Frontend {
assert_assumes_mode = true; assert_assumes_mode = true;
continue; continue;
} }
if (arg == "-nodisplay") {
flag_nodisplay = true;
continue;
}
if (arg == "-debug") { if (arg == "-debug") {
flag_dump_ast1 = true; flag_dump_ast1 = true;
flag_dump_ast2 = true; flag_dump_ast2 = true;
@ -510,7 +519,7 @@ struct VerilogFrontend : public Frontend {
if (flag_nodpi) if (flag_nodpi)
error_on_dpi_function(current_ast); 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); 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') { } else if (fmt[i] == 'c') {
part.type = FmtPart::CHARACTER; part.type = FmtPart::CHARACTER;
} else if (fmt[i] == 't') { } else if (fmt[i] == 't') {
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
} else if (fmt[i] == 'r') { } else if (fmt[i] == 'r') {
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
part.realtime = true; part.realtime = true;
} else { } else {
log_assert(false && "Unexpected character in format substitution"); log_assert(false && "Unexpected character in format substitution");
@ -172,7 +172,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
} }
break; break;
case FmtPart::TIME: case FmtPart::VLOG_TIME:
log_assert(part.sig.size() == 0); log_assert(part.sig.size() == 0);
YS_FALLTHROUGH YS_FALLTHROUGH
case FmtPart::CHARACTER: case FmtPart::CHARACTER:
@ -205,7 +205,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
fmt += part.signed_ ? 's' : 'u'; fmt += part.signed_ ? 's' : 'u';
} else if (part.type == FmtPart::CHARACTER) { } else if (part.type == FmtPart::CHARACTER) {
fmt += 'c'; fmt += 'c';
} else if (part.type == FmtPart::TIME) { } else if (part.type == FmtPart::VLOG_TIME) {
if (part.realtime) if (part.realtime)
fmt += 'r'; fmt += 'r';
else else
@ -328,7 +328,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
case VerilogFmtArg::TIME: { case VerilogFmtArg::TIME: {
FmtPart part = {}; FmtPart part = {};
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
part.realtime = arg->realtime; part.realtime = arg->realtime;
part.padding = ' '; part.padding = ' ';
part.width = 20; part.width = 20;
@ -419,7 +419,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.padding = ' '; part.padding = ' ';
} else if (fmt[i] == 't' || fmt[i] == 'T') { } else if (fmt[i] == 't' || fmt[i] == 'T') {
if (arg->type == VerilogFmtArg::TIME) { if (arg->type == VerilogFmtArg::TIME) {
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
part.realtime = arg->realtime; part.realtime = arg->realtime;
if (!has_width && !has_leading_zero) if (!has_width && !has_leading_zero)
part.width = 20; part.width = 20;
@ -541,7 +541,7 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
break; break;
} }
case FmtPart::TIME: { case FmtPart::VLOG_TIME: {
VerilogFmtArg arg; VerilogFmtArg arg;
arg.type = VerilogFmtArg::TIME; arg.type = VerilogFmtArg::TIME;
if (part.realtime) if (part.realtime)
@ -569,82 +569,60 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
return args; 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) { std::string output = "\"";
switch (part.type) { for (auto c : input) {
case FmtPart::STRING: if (::isprint(c)) {
f << " << \""; if (c == '\\')
for (char c : part.str) { output.push_back('\\');
switch (c) { output.push_back(c);
case '\\': } else {
YS_FALLTHROUGH char l = c & 0xf, h = (c >> 4) & 0xf;
case '"': output.append("\\x");
f << '\\' << c; output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
break; output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
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();
} }
} }
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 std::string Fmt::render() const
@ -658,8 +636,8 @@ std::string Fmt::render() const
break; break;
case FmtPart::INTEGER: case FmtPart::INTEGER:
case FmtPart::TIME: case FmtPart::CHARACTER:
case FmtPart::CHARACTER: { case FmtPart::VLOG_TIME: {
std::string buf; std::string buf;
if (part.type == FmtPart::INTEGER) { if (part.type == FmtPart::INTEGER) {
RTLIL::Const value = part.sig.as_const(); RTLIL::Const value = part.sig.as_const();
@ -742,7 +720,7 @@ std::string Fmt::render() const
} else log_abort(); } else log_abort();
} else if (part.type == FmtPart::CHARACTER) { } else if (part.type == FmtPart::CHARACTER) {
buf = part.sig.as_const().decode_string(); 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. // We only render() during initial, so time is always zero.
buf = "0"; buf = "0";
} }

View file

@ -50,12 +50,13 @@ struct VerilogFmtArg {
// RTLIL format part, such as the substitutions in: // RTLIL format part, such as the substitutions in:
// "foo {4:> 4du} bar {2:<01hs}" // "foo {4:> 4du} bar {2:<01hs}"
// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h!
struct FmtPart { struct FmtPart {
enum { enum {
STRING = 0, STRING = 0,
INTEGER = 1, INTEGER = 1,
CHARACTER = 2, CHARACTER = 2,
TIME = 3, VLOG_TIME = 3,
} type; } type;
// STRING type // STRING type
@ -64,20 +65,20 @@ struct FmtPart {
// INTEGER/CHARACTER types // INTEGER/CHARACTER types
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
// INTEGER/CHARACTER/TIME types // INTEGER/CHARACTER/VLOG_TIME types
enum { enum {
RIGHT = 0, RIGHT = 0,
LEFT = 1, LEFT = 1,
} justify = RIGHT; } justify = RIGHT;
char padding = '\0'; char padding = '\0';
size_t width = 0; size_t width = 0;
// INTEGER type // INTEGER type
unsigned base = 10; unsigned base = 10;
bool signed_ = false; bool signed_ = false;
bool plus = false; bool plus = false;
// TIME type // VLOG_TIME type
bool realtime = false; 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); 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; 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; 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) { void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
log_assert(GetSize(lhs) == GetSize(rhs)); // If a deleted wire occurs on the lhs or rhs we just remove that part
lhs.unpack(); // of the assignment
rhs.unpack(); lhs.remove2(*wires_p, &rhs);
for (int i = 0; i < GetSize(lhs); i++) { rhs.remove2(*wires_p, &lhs);
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;
}
}
} }
}; };
@ -3693,6 +3686,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const 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; RTLIL::SigChunk ret;
if (wire) { if (wire) {
ret.wire = wire; ret.wire = wire;
@ -4238,6 +4234,34 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
check(); 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 RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
{ {
if (other) if (other)
@ -4377,6 +4401,9 @@ void RTLIL::SigSpec::remove(int offset, int length)
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
{ {
log_assert(offset >= 0);
log_assert(length >= 0);
log_assert(offset + length <= width_);
unpack(); unpack();
cover("kernel.rtlil.sigspec.extract_pos"); cover("kernel.rtlil.sigspec.extract_pos");
return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length); 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 remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other); void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
void remove2(const std::set<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(int offset, int length = 1);
void remove_const(); void remove_const();

View file

@ -539,7 +539,7 @@ struct ShowWorker
std::string proc_src = RTLIL::unescape_id(proc->name); std::string proc_src = RTLIL::unescape_id(proc->name);
if (proc->attributes.count(ID::src) > 0) if (proc->attributes.count(ID::src) > 0)
proc_src = proc->attributes.at(ID::src).decode_string(); 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()) 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(" use cell area information from the provided liberty file\n");
log("\n"); log("\n");
log(" -tech <technology>\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(" values for <technology>: xilinx, cmos\n");
log("\n"); log("\n");
log(" -width\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); 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) bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
{ {
if (cache.count(mod) == 0) if (cache.count(mod) == 0)
@ -762,6 +773,11 @@ struct HierarchyPass : public Pass {
log(" -nodefaults\n"); log(" -nodefaults\n");
log(" do not resolve input port default values\n"); log(" do not resolve input port default values\n");
log("\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(" -nokeep_asserts\n");
log(" per default this pass sets the \"keep\" attribute on all modules\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"); 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_positionals = false;
bool keep_portwidths = false; bool keep_portwidths = false;
bool nodefaults = false; bool nodefaults = false;
bool nokeep_prints = false;
bool nokeep_asserts = false; bool nokeep_asserts = false;
std::vector<std::string> generate_cells; std::vector<std::string> generate_cells;
std::vector<generate_port_decl_t> generate_ports; std::vector<generate_port_decl_t> generate_ports;
@ -893,6 +910,10 @@ struct HierarchyPass : public Pass {
nodefaults = true; nodefaults = true;
continue; continue;
} }
if (args[argidx] == "-nokeep_prints") {
nokeep_prints = true;
continue;
}
if (args[argidx] == "-nokeep_asserts") { if (args[argidx] == "-nokeep_asserts") {
nokeep_asserts = true; nokeep_asserts = true;
continue; 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) { if (!nokeep_asserts) {
std::map<RTLIL::Module*, bool> cache; std::map<RTLIL::Module*, bool> cache;
for (auto mod : design->modules()) 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. // Perform write port assignment, validating clock options as we go.
void MemMapping::assign_wr_ports() { 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) { for (auto &port: mem.wr_ports) {
if (!port.clk_enable) { if (!port.clk_enable) {
// Async write ports not supported. // 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. // Perform read port assignment, validating clock and rden options as we go.
void MemMapping::assign_rd_ports() { 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++) { for (int pidx = 0; pidx < GetSize(mem.rd_ports); pidx++) {
auto &port = mem.rd_ports[pidx]; auto &port = mem.rd_ports[pidx];
MemConfigs new_cfgs; MemConfigs new_cfgs;
@ -900,7 +900,7 @@ void MemMapping::assign_rd_ports() {
// Validate transparency restrictions, determine where to add soft transparency logic. // Validate transparency restrictions, determine where to add soft transparency logic.
void MemMapping::handle_trans() { 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()) { if (mem.emulate_read_first_ok()) {
MemConfigs new_cfgs; MemConfigs new_cfgs;
for (auto &cfg: cfgs) { for (auto &cfg: cfgs) {

View file

@ -24,6 +24,10 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN 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 { struct dlogic_t {
IdString cell_type; IdString cell_type;
// LUT input idx -> hard cell's port name // 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 { struct OptLutPass : public Pass {
OptLutPass() : Pass("opt_lut", "optimize LUT cells") { } OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
void help() override 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 case where both LUT and dedicated logic input are connected to\n");
log(" the same constant.\n"); log(" the same constant.\n");
log("\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(" -limit N\n");
log(" only perform the first N combines, then stop. useful for debugging.\n"); log(" only perform the first N combines, then stop. useful for debugging.\n");
log("\n"); log("\n");
@ -555,28 +553,28 @@ struct OptLutPass : public Pass {
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); 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; std::string tech = args[++argidx];
split(tokens, args[++argidx], ':'); if (tech != "ice40")
if (tokens.size() < 2) log_cmd_error("Unsupported -tech argument: %s\n", tech.c_str());
log_cmd_error("The -dlogic option requires at least one connection.\n");
dlogic_t entry; dlogic = {{
entry.cell_type = "\\" + tokens[0]; ID(SB_CARRY),
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { dict<int, IdString>{
std::vector<std::string> conn_tokens; std::make_pair(1, ID(I0)),
split(conn_tokens, *it, '='); std::make_pair(2, ID(I1)),
if (conn_tokens.size() != 2) std::make_pair(3, ID(CI))
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()); ID(SB_CARRY),
entry.lut_input_port[lut_input] = logic_port; dict<int, IdString>{
} std::make_pair(3, ID(CO))
dlogic.push_back(entry); }
}};
continue; continue;
} }
if (args[argidx] == "-limit" && argidx + 1 < args.size()) if (args[argidx] == "-limit" && argidx + 1 < args.size()) {
{
limit = atoi(args[++argidx].c_str()); limit = atoi(args[++argidx].c_str());
continue; continue;
} }

View file

@ -26,6 +26,7 @@
#include <algorithm> #include <algorithm>
#include <queue> #include <queue>
#include <cinttypes>
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
@ -623,7 +624,7 @@ struct RecoverNamesWorker {
if (pop == 1 || pop == (8*sizeof(equiv_cls_t) - 1)) if (pop == 1 || pop == (8*sizeof(equiv_cls_t) - 1))
continue; 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<IdBit> &gold_bits = cls2bits.at(cls.first).first;
const pool<InvBit> &gate_bits = cls2bits.at(cls.first).second; const pool<InvBit> &gate_bits = cls2bits.at(cls.first).second;
if (gold_bits.empty() || gate_bits.empty()) 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 struct SimShared
{ {
bool debug = false; bool debug = false;
@ -110,6 +121,7 @@ struct SimShared
int next_output_id = 0; int next_output_id = 0;
int step = 0; int step = 0;
std::vector<TriggeredAssertion> triggered_assertions; std::vector<TriggeredAssertion> triggered_assertions;
std::vector<DisplayOutput> display_output;
bool serious_asserts = false; bool serious_asserts = false;
bool initstate = true; bool initstate = true;
}; };
@ -173,6 +185,7 @@ struct SimInstance
struct print_state_t struct print_state_t
{ {
bool initial_done;
Const past_trg; Const past_trg;
Const past_en; Const past_en;
Const past_args; Const past_args;
@ -338,6 +351,7 @@ struct SimInstance
print.past_trg = Const(State::Sx, cell->getPort(ID::TRG).size()); print.past_trg = Const(State::Sx, cell->getPort(ID::TRG).size());
print.past_args = Const(State::Sx, cell->getPort(ID::ARGS).size()); print.past_args = Const(State::Sx, cell->getPort(ID::ARGS).size());
print.past_en = State::Sx; 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) for (auto &it : ff_database)
{ {
@ -840,45 +854,57 @@ struct SimInstance
bool triggered = false; bool triggered = false;
Const trg = get_state(cell->getPort(ID::TRG)); 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 en = get_state(cell->getPort(ID::EN));
Const args = get_state(cell->getPort(ID::ARGS)); Const args = get_state(cell->getPort(ID::ARGS));
if (!en.as_bool()) bool sampled = trg_en && trg.size() > 0;
goto update_print;
if (cell->getParam(ID::TRG_ENABLE).as_bool()) { if (sampled ? print.past_en.as_bool() : en.as_bool()) {
Const trg_pol = cell->getParam(ID::TRG_POLARITY); if (sampled) {
for (int i = 0; i < trg.size(); i++) { sampled = true;
bool pol = trg_pol[i] == State::S1; Const trg_pol = cell->getParam(ID::TRG_POLARITY);
State curr = trg[i], past = print.past_trg[i]; for (int i = 0; i < trg.size(); i++) {
if (pol && curr == State::S1 && past == State::S0) 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; 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; triggered = true;
} }
} else {
if (args != print.past_args || en != print.past_en)
triggered = true;
}
if (triggered) { if (triggered) {
int pos = 0; int pos = 0;
for (auto &part : print.fmt.parts) { for (auto &part : print.fmt.parts) {
part.sig = args.extract(pos, part.sig.size()); part.sig = (sampled ? print.past_args : args).extract(pos, part.sig.size());
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_trg = trg;
print.past_en = en; print.past_en = en;
print.past_args = args; print.past_args = args;
print.initial_done = true;
} }
if (check_assertions) if (gclk_trigger)
{ {
for (auto cell : formal_database) for (auto cell : formal_database)
{ {
@ -910,7 +936,7 @@ struct SimInstance
} }
for (auto it : children) for (auto it : children)
it.second->update_ph3(check_assertions); it.second->update_ph3(gclk_trigger);
} }
void set_initstate_outputs(State state) void set_initstate_outputs(State state)
@ -2055,6 +2081,20 @@ struct SimWorker : SimShared
json.end_object(); json.end_object();
} }
json.end_array(); 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(); json.end_object();
} }

View file

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

View file

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

View file

@ -432,7 +432,7 @@ struct SynthIce40Pass : public ScriptPass
run("ice40_wrapcarry -unwrap"); run("ice40_wrapcarry -unwrap");
run("techmap -map +/ice40/ff_map.v"); run("techmap -map +/ice40/ff_map.v");
run("clean"); 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")) if (check_label("map_cells"))

View file

@ -2,8 +2,13 @@
int main() 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; cxxrtl_design::p_always__full uut;
uut.p_clk.set(!uut.p_clk); uut.p_clk.set(!uut.p_clk);
uut.step(); uut.step(&performer);
return 0; 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