mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'YosysHQ:master' into master
This commit is contained in:
commit
8c731658c2
|
@ -14,10 +14,10 @@ jobs:
|
|||
run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: cpp
|
||||
queries: security-extended,security-and-quality
|
||||
|
@ -26,4 +26,4 @@ jobs:
|
|||
run: make yosys -j6
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
|
|
@ -6,13 +6,13 @@ jobs:
|
|||
emcc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: mymindstorm/setup-emsdk@v11
|
||||
- uses: actions/checkout@v3
|
||||
- uses: mymindstorm/setup-emsdk@v14
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
make config-emcc
|
||||
make YOSYS_VER=latest
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: yosysjs
|
||||
path: yosysjs-latest.zip
|
||||
|
|
|
@ -79,19 +79,22 @@ jobs:
|
|||
$CXX --version
|
||||
|
||||
- name: Checkout Yosys
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get iverilog
|
||||
shell: bash
|
||||
run: |
|
||||
git clone https://github.com/steveicarus/iverilog.git
|
||||
cd iverilog
|
||||
git checkout 192b6aec96fde982e6ddcb28b346d5893aa8e874
|
||||
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Cache iverilog
|
||||
id: cache-iverilog
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .local/
|
||||
key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }}
|
||||
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
|
||||
|
||||
- name: Build iverilog
|
||||
if: steps.cache-iverilog.outputs.cache-hit != 'true'
|
||||
|
|
|
@ -35,19 +35,22 @@ jobs:
|
|||
cc --version
|
||||
|
||||
- name: Checkout Yosys
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get iverilog
|
||||
shell: bash
|
||||
run: |
|
||||
git clone https://github.com/steveicarus/iverilog.git
|
||||
cd iverilog
|
||||
git checkout 192b6aec96fde982e6ddcb28b346d5893aa8e874
|
||||
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Cache iverilog
|
||||
id: cache-iverilog
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .local/
|
||||
key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }}
|
||||
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
|
||||
|
||||
- name: Build iverilog
|
||||
if: steps.cache-iverilog.outputs.cache-hit != 'true'
|
||||
|
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Take last commit
|
||||
|
|
|
@ -6,10 +6,10 @@ jobs:
|
|||
yosys-vcxsrc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: make vcxsrc YOSYS_VER=latest
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vcxsrc
|
||||
path: yosys-win32-vcxsrc-latest.zip
|
||||
|
@ -18,7 +18,7 @@ jobs:
|
|||
runs-on: windows-2019
|
||||
needs: yosys-vcxsrc
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: vcxsrc
|
||||
path: .
|
||||
|
|
|
@ -6,7 +6,7 @@ jobs:
|
|||
wasi:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
WASI_SDK=wasi-sdk-19.0
|
||||
|
|
18
CHANGELOG
18
CHANGELOG
|
@ -2,12 +2,26 @@
|
|||
List of major changes and improvements between releases
|
||||
=======================================================
|
||||
|
||||
Yosys 0.36 .. Yosys 0.37-dev
|
||||
Yosys 0.37 .. Yosys 0.38-dev
|
||||
--------------------------
|
||||
|
||||
Yosys 0.36 .. Yosys 0.37
|
||||
--------------------------
|
||||
* New commands and options
|
||||
- Added option "-nodisplay" to read_verilog.
|
||||
|
||||
* SystemVerilog
|
||||
- Correct hierarchical path names for structs and unions.
|
||||
|
||||
* Various
|
||||
- Print hierarchy for failed assertions in "sim" pass.
|
||||
- Add "--present-only" option to "yosys-witness" to omit unused signals.
|
||||
- Implement a generic record/replay interface for CXXRTL.
|
||||
- Improved readability of emitted code with "write_verilog".
|
||||
|
||||
Yosys 0.35 .. Yosys 0.36
|
||||
--------------------------
|
||||
* New commands and options
|
||||
* New commands and options
|
||||
- Added option "--" to pass arguments down to tcl when using -c option.
|
||||
- Added ability on MacOS and Windows to pass options after arguments on cli.
|
||||
- Added option "-cmp2softlogic" to synth_lattice.
|
||||
|
|
15
Makefile
15
Makefile
|
@ -141,7 +141,7 @@ LDLIBS += -lrt
|
|||
endif
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.36+85
|
||||
YOSYS_VER := 0.37+52
|
||||
|
||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||
|
@ -157,7 +157,7 @@ endif
|
|||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
bumpversion:
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8f07a0d.. | wc -l`/;" Makefile
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a5c7f69.. | wc -l`/;" Makefile
|
||||
|
||||
# set 'ABCREV = default' to use abc/ as it is
|
||||
#
|
||||
|
@ -679,12 +679,8 @@ OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
|
|||
|
||||
OBJS += libs/sha1/sha1.o
|
||||
|
||||
ifneq ($(SMALL),1)
|
||||
|
||||
OBJS += libs/json11/json11.o
|
||||
|
||||
OBJS += libs/subcircuit/subcircuit.o
|
||||
|
||||
OBJS += libs/ezsat/ezsat.o
|
||||
OBJS += libs/ezsat/ezminisat.o
|
||||
|
||||
|
@ -699,6 +695,10 @@ OBJS += libs/fst/fastlz.o
|
|||
OBJS += libs/fst/lz4.o
|
||||
endif
|
||||
|
||||
ifneq ($(SMALL),1)
|
||||
|
||||
OBJS += libs/subcircuit/subcircuit.o
|
||||
|
||||
include $(YOSYS_SRC)/frontends/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/passes/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/backends/*/Makefile.inc
|
||||
|
@ -707,6 +707,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc
|
|||
else
|
||||
|
||||
include $(YOSYS_SRC)/frontends/verilog/Makefile.inc
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
include $(YOSYS_SRC)/frontends/verific/Makefile.inc
|
||||
endif
|
||||
include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc
|
||||
include $(YOSYS_SRC)/frontends/ast/Makefile.inc
|
||||
include $(YOSYS_SRC)/frontends/blif/Makefile.inc
|
||||
|
|
|
@ -54,6 +54,8 @@ struct AigerWriter
|
|||
|
||||
vector<pair<int, int>> aig_gates;
|
||||
vector<int> aig_latchin, aig_latchinit, aig_outputs;
|
||||
vector<SigBit> bit2aig_stack;
|
||||
size_t next_loop_check = 1024;
|
||||
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
|
||||
int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
|
||||
|
||||
|
@ -81,6 +83,23 @@ struct AigerWriter
|
|||
return it->second;
|
||||
}
|
||||
|
||||
if (bit2aig_stack.size() == next_loop_check) {
|
||||
for (size_t i = 0; i < next_loop_check; ++i)
|
||||
{
|
||||
SigBit report_bit = bit2aig_stack[i];
|
||||
if (report_bit != bit)
|
||||
continue;
|
||||
for (size_t j = i; j < next_loop_check; ++j) {
|
||||
report_bit = bit2aig_stack[j];
|
||||
if (report_bit.is_wire() && report_bit.wire->name.isPublic())
|
||||
break;
|
||||
}
|
||||
log_error("Found combinational logic loop while processing signal %s.\n", log_signal(report_bit));
|
||||
}
|
||||
next_loop_check *= 2;
|
||||
}
|
||||
bit2aig_stack.push_back(bit);
|
||||
|
||||
// NB: Cannot use iterator returned from aig_map.insert()
|
||||
// since this function is called recursively
|
||||
|
||||
|
@ -101,6 +120,8 @@ struct AigerWriter
|
|||
a = initstate_ff;
|
||||
}
|
||||
|
||||
bit2aig_stack.pop_back();
|
||||
|
||||
if (bit == State::Sx || bit == State::Sz)
|
||||
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
|
||||
|
||||
|
|
|
@ -1072,9 +1072,45 @@ struct CxxrtlWorker {
|
|||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << " == value<1>{1u}) {\n";
|
||||
inc_indent();
|
||||
f << indent << print_output;
|
||||
fmt.emit_cxxrtl(f, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); });
|
||||
f << ";\n";
|
||||
dict<std::string, RTLIL::SigSpec> fmt_args;
|
||||
f << indent << "struct : public lazy_fmt {\n";
|
||||
inc_indent();
|
||||
f << indent << "std::string operator() () const override {\n";
|
||||
inc_indent();
|
||||
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
||||
if (sig.size() == 0)
|
||||
f << "value<0>()";
|
||||
else {
|
||||
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
||||
fmt_args[arg_name] = sig;
|
||||
f << arg_name;
|
||||
}
|
||||
}, "performer");
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
f << indent << "struct performer *performer;\n";
|
||||
for (auto arg : fmt_args)
|
||||
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
||||
dec_indent();
|
||||
f << indent << "} formatter;\n";
|
||||
f << indent << "formatter.performer = performer;\n";
|
||||
for (auto arg : fmt_args) {
|
||||
f << indent << "formatter." << arg.first << " = ";
|
||||
dump_sigspec_rhs(arg.second);
|
||||
f << ";\n";
|
||||
}
|
||||
f << indent << "if (performer) {\n";
|
||||
inc_indent();
|
||||
f << indent << "static const metadata_map attributes = ";
|
||||
dump_metadata_map(cell->attributes);
|
||||
f << ";\n";
|
||||
f << indent << "performer->on_print(formatter, attributes);\n";
|
||||
dec_indent();
|
||||
f << indent << "} else {\n";
|
||||
inc_indent();
|
||||
f << indent << print_output << " << formatter();\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
}
|
||||
|
@ -1291,20 +1327,29 @@ struct CxxrtlWorker {
|
|||
log_assert(!for_debug);
|
||||
|
||||
// Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph.
|
||||
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool());
|
||||
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0));
|
||||
|
||||
f << indent << "auto " << mangle(cell) << "_curr = ";
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << ".concat(";
|
||||
dump_sigspec_rhs(cell->getPort(ID::ARGS));
|
||||
f << ").val();\n";
|
||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell
|
||||
f << indent << "auto " << mangle(cell) << "_curr = ";
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << ".concat(";
|
||||
dump_sigspec_rhs(cell->getPort(ID::ARGS));
|
||||
f << ").val();\n";
|
||||
|
||||
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
|
||||
inc_indent();
|
||||
dump_print(cell);
|
||||
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
|
||||
inc_indent();
|
||||
dump_print(cell);
|
||||
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
} else { // initial $print cell
|
||||
f << indent << "if (!" << mangle(cell) << ") {\n";
|
||||
inc_indent();
|
||||
dump_print(cell);
|
||||
f << indent << mangle(cell) << " = value<1>{1u};\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
}
|
||||
// Flip-flops
|
||||
} else if (is_ff_cell(cell->type)) {
|
||||
log_assert(!for_debug);
|
||||
|
@ -1401,7 +1446,7 @@ struct CxxrtlWorker {
|
|||
f << indent;
|
||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
||||
f << " = ";
|
||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
||||
dump_sigspec_rhs(cell->getPort(ID::Q));
|
||||
f << ".update(";
|
||||
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
|
||||
f << ", ";
|
||||
|
@ -1413,7 +1458,7 @@ struct CxxrtlWorker {
|
|||
f << indent;
|
||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
||||
f << " = ";
|
||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
||||
dump_sigspec_rhs(cell->getPort(ID::Q));
|
||||
f << ".update(";
|
||||
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
|
||||
f << ", ";
|
||||
|
@ -1485,11 +1530,11 @@ struct CxxrtlWorker {
|
|||
};
|
||||
if (buffered_inputs) {
|
||||
// If we have any buffered inputs, there's no chance of converging immediately.
|
||||
f << indent << mangle(cell) << access << "eval();\n";
|
||||
f << indent << mangle(cell) << access << "eval(performer);\n";
|
||||
f << indent << "converged = false;\n";
|
||||
assign_from_outputs(/*cell_converged=*/false);
|
||||
} else {
|
||||
f << indent << "if (" << mangle(cell) << access << "eval()) {\n";
|
||||
f << indent << "if (" << mangle(cell) << access << "eval(performer)) {\n";
|
||||
inc_indent();
|
||||
assign_from_outputs(/*cell_converged=*/true);
|
||||
dec_indent();
|
||||
|
@ -2002,6 +2047,11 @@ struct CxxrtlWorker {
|
|||
}
|
||||
}
|
||||
for (auto cell : module->cells()) {
|
||||
// Certain $print cells have additional state, which must be reset as well.
|
||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
|
||||
f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n";
|
||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||
f << indent << mangle(cell) << " = value<1>();\n";
|
||||
if (is_internal_cell(cell->type))
|
||||
continue;
|
||||
f << indent << mangle(cell);
|
||||
|
@ -2367,7 +2417,8 @@ struct CxxrtlWorker {
|
|||
dump_reset_method(module);
|
||||
f << indent << "}\n";
|
||||
f << "\n";
|
||||
f << indent << "bool eval() override {\n";
|
||||
// No default argument, to prevent unintentional `return bb_foo::eval();` calls that drop performer.
|
||||
f << indent << "bool eval(performer *performer) override {\n";
|
||||
dump_eval_method(module);
|
||||
f << indent << "}\n";
|
||||
f << "\n";
|
||||
|
@ -2377,7 +2428,7 @@ struct CxxrtlWorker {
|
|||
f << indent << "}\n";
|
||||
f << "\n";
|
||||
f << indent << "bool commit() override {\n";
|
||||
f << indent << indent << "null_observer observer;\n";
|
||||
f << indent << indent << "observer observer;\n";
|
||||
f << indent << indent << "return commit<>(observer);\n";
|
||||
f << indent << "}\n";
|
||||
if (debug_info) {
|
||||
|
@ -2430,11 +2481,11 @@ struct CxxrtlWorker {
|
|||
f << "\n";
|
||||
bool has_cells = false;
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||
// comb $print cell -- store the last EN/ARGS values to know when they change.
|
||||
dump_attrs(cell);
|
||||
// Certain $print cells have additional state, which requires storage.
|
||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
|
||||
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n";
|
||||
}
|
||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||
f << indent << "value<1> " << mangle(cell) << ";\n";
|
||||
if (is_internal_cell(cell->type))
|
||||
continue;
|
||||
dump_attrs(cell);
|
||||
|
@ -2464,7 +2515,7 @@ struct CxxrtlWorker {
|
|||
f << "\n";
|
||||
f << indent << "void reset() override;\n";
|
||||
f << "\n";
|
||||
f << indent << "bool eval() override;\n";
|
||||
f << indent << "bool eval(performer *performer = nullptr) override;\n";
|
||||
f << "\n";
|
||||
f << indent << "template<class ObserverT>\n";
|
||||
f << indent << "bool commit(ObserverT &observer) {\n";
|
||||
|
@ -2472,7 +2523,7 @@ struct CxxrtlWorker {
|
|||
f << indent << "}\n";
|
||||
f << "\n";
|
||||
f << indent << "bool commit() override {\n";
|
||||
f << indent << indent << "null_observer observer;\n";
|
||||
f << indent << indent << "observer observer;\n";
|
||||
f << indent << indent << "return commit<>(observer);\n";
|
||||
f << indent << "}\n";
|
||||
if (debug_info) {
|
||||
|
@ -2503,7 +2554,7 @@ struct CxxrtlWorker {
|
|||
dump_reset_method(module);
|
||||
f << indent << "}\n";
|
||||
f << "\n";
|
||||
f << indent << "bool " << mangle(module) << "::eval() {\n";
|
||||
f << indent << "bool " << mangle(module) << "::eval(performer *performer) {\n";
|
||||
dump_eval_method(module);
|
||||
f << indent << "}\n";
|
||||
if (debug_info) {
|
||||
|
@ -2527,7 +2578,6 @@ struct CxxrtlWorker {
|
|||
RTLIL::Module *top_module = nullptr;
|
||||
std::vector<RTLIL::Module*> modules;
|
||||
TopoSort<RTLIL::Module*> topo_design;
|
||||
bool has_prints = false;
|
||||
for (auto module : design->modules()) {
|
||||
if (!design->selected_module(module))
|
||||
continue;
|
||||
|
@ -2540,8 +2590,6 @@ struct CxxrtlWorker {
|
|||
|
||||
topo_design.node(module);
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type == ID($print))
|
||||
has_prints = true;
|
||||
if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell))
|
||||
continue;
|
||||
RTLIL::Module *cell_module = design->module(cell->type);
|
||||
|
@ -2600,8 +2648,6 @@ struct CxxrtlWorker {
|
|||
f << "#include \"" << basename(intf_filename) << "\"\n";
|
||||
else
|
||||
f << "#include <cxxrtl/cxxrtl.h>\n";
|
||||
if (has_prints)
|
||||
f << "#include <iostream>\n";
|
||||
f << "\n";
|
||||
f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
|
||||
f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
|
||||
|
@ -2964,8 +3010,9 @@ struct CxxrtlWorker {
|
|||
for (auto node : node_order)
|
||||
if (live_nodes[node]) {
|
||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
|
||||
node->cell->type == ID($print) &&
|
||||
node->cell->getParam(ID::TRG_ENABLE).as_bool())
|
||||
node->cell->type == ID($print) &&
|
||||
node->cell->getParam(ID::TRG_ENABLE).as_bool() &&
|
||||
node->cell->getParam(ID::TRG_WIDTH).as_int() != 0)
|
||||
sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
|
||||
else
|
||||
schedule[module].push_back(*node);
|
||||
|
@ -3278,7 +3325,7 @@ struct CxxrtlBackend : public Backend {
|
|||
log(" value<8> p_i_data;\n");
|
||||
log(" wire<8> p_o_data;\n");
|
||||
log("\n");
|
||||
log(" bool eval() override;\n");
|
||||
log(" bool eval(performer *performer) override;\n");
|
||||
log(" template<class ObserverT>\n");
|
||||
log(" bool commit(ObserverT &observer);\n");
|
||||
log(" bool commit() override;\n");
|
||||
|
@ -3293,11 +3340,11 @@ struct CxxrtlBackend : public Backend {
|
|||
log(" namespace cxxrtl_design {\n");
|
||||
log("\n");
|
||||
log(" struct stderr_debug : public bb_p_debug {\n");
|
||||
log(" bool eval() override {\n");
|
||||
log(" bool eval(performer *performer) override {\n");
|
||||
log(" if (posedge_p_clk() && p_en)\n");
|
||||
log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n");
|
||||
log(" p_o_data.next = p_i_data;\n");
|
||||
log(" return bb_p_debug::eval();\n");
|
||||
log(" return bb_p_debug::eval(performer);\n");
|
||||
log(" }\n");
|
||||
log(" };\n");
|
||||
log("\n");
|
||||
|
@ -3398,7 +3445,7 @@ struct CxxrtlBackend : public Backend {
|
|||
log(" -print-output <stream>\n");
|
||||
log(" $print cells in the generated code direct their output to <stream>.\n");
|
||||
log(" must be one of \"std::cout\", \"std::cerr\". if not specified,\n");
|
||||
log(" \"std::cout\" is used.\n");
|
||||
log(" \"std::cout\" is used. explicitly provided performer overrides this.\n");
|
||||
log("\n");
|
||||
log(" -nohierarchy\n");
|
||||
log(" use design hierarchy as-is. in most designs, a top module should be\n");
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <memory>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
// `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements.
|
||||
#include <cxxrtl/capi/cxxrtl_capi.h>
|
||||
|
@ -565,7 +566,7 @@ struct value : public expr_base<value<Bits>> {
|
|||
}
|
||||
|
||||
value<Bits> neg() const {
|
||||
return value<Bits> { 0u }.sub(*this);
|
||||
return value<Bits>().sub(*this);
|
||||
}
|
||||
|
||||
bool ucmp(const value<Bits> &other) const {
|
||||
|
@ -763,123 +764,6 @@ std::ostream &operator<<(std::ostream &os, const value<Bits> &val) {
|
|||
return os;
|
||||
}
|
||||
|
||||
template<size_t Bits>
|
||||
struct value_formatted {
|
||||
const value<Bits> &val;
|
||||
bool character;
|
||||
bool justify_left;
|
||||
char padding;
|
||||
int width;
|
||||
int base;
|
||||
bool signed_;
|
||||
bool plus;
|
||||
|
||||
value_formatted(const value<Bits> &val, bool character, bool justify_left, char padding, int width, int base, bool signed_, bool plus) :
|
||||
val(val), character(character), justify_left(justify_left), padding(padding), width(width), base(base), signed_(signed_), plus(plus) {}
|
||||
value_formatted(const value_formatted<Bits> &) = delete;
|
||||
value_formatted<Bits> &operator=(const value_formatted<Bits> &rhs) = delete;
|
||||
};
|
||||
|
||||
template<size_t Bits>
|
||||
std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf)
|
||||
{
|
||||
value<Bits> val = vf.val;
|
||||
|
||||
std::string buf;
|
||||
|
||||
// We might want to replace some of these bit() calls with direct
|
||||
// chunk access if it turns out to be slow enough to matter.
|
||||
|
||||
if (!vf.character) {
|
||||
size_t width = Bits;
|
||||
if (vf.base != 10) {
|
||||
width = 0;
|
||||
for (size_t index = 0; index < Bits; index++)
|
||||
if (val.bit(index))
|
||||
width = index + 1;
|
||||
}
|
||||
|
||||
if (vf.base == 2) {
|
||||
for (size_t i = width; i > 0; i--)
|
||||
buf += (val.bit(i - 1) ? '1' : '0');
|
||||
} else if (vf.base == 8 || vf.base == 16) {
|
||||
size_t step = (vf.base == 16) ? 4 : 3;
|
||||
for (size_t index = 0; index < width; index += step) {
|
||||
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
|
||||
if (step == 4)
|
||||
value |= val.bit(index + 3) << 3;
|
||||
buf += "0123456789abcdef"[value];
|
||||
}
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
} else if (vf.base == 10) {
|
||||
bool negative = vf.signed_ && val.is_neg();
|
||||
if (negative)
|
||||
val = val.neg();
|
||||
if (val.is_zero())
|
||||
buf += '0';
|
||||
while (!val.is_zero()) {
|
||||
value<Bits> quotient, remainder;
|
||||
if (Bits >= 4)
|
||||
std::tie(quotient, remainder) = val.udivmod(value<Bits>{10u});
|
||||
else
|
||||
std::tie(quotient, remainder) = std::make_pair(value<Bits>{0u}, val);
|
||||
buf += '0' + remainder.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get<uint8_t>();
|
||||
val = quotient;
|
||||
}
|
||||
if (negative || vf.plus)
|
||||
buf += negative ? '-' : '+';
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
} else assert(false);
|
||||
} else {
|
||||
buf.reserve(Bits/8);
|
||||
for (int i = 0; i < Bits; i += 8) {
|
||||
char ch = 0;
|
||||
for (int j = 0; j < 8 && i + j < int(Bits); j++)
|
||||
if (val.bit(i + j))
|
||||
ch |= 1 << j;
|
||||
if (ch != 0)
|
||||
buf.append({ch});
|
||||
}
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
}
|
||||
|
||||
assert(vf.width == 0 || vf.padding != '\0');
|
||||
if (!vf.justify_left && buf.size() < vf.width) {
|
||||
size_t pad_width = vf.width - buf.size();
|
||||
if (vf.padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
|
||||
os << buf.front();
|
||||
buf.erase(0, 1);
|
||||
}
|
||||
os << std::string(pad_width, vf.padding);
|
||||
}
|
||||
os << buf;
|
||||
if (vf.justify_left && buf.size() < vf.width)
|
||||
os << std::string(vf.width - buf.size(), vf.padding);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
|
||||
// the simulation.
|
||||
struct observer {
|
||||
// Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks
|
||||
// at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and
|
||||
// `base` points to the first chunk.
|
||||
virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) = 0;
|
||||
|
||||
// Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]`
|
||||
// with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to
|
||||
// the memory element chunk count and `base` points to the first chunk of the first element of the memory.
|
||||
virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) = 0;
|
||||
};
|
||||
|
||||
// The `null_observer` class has the same interface as `observer`, but has no invocation overhead, since its methods
|
||||
// are final and have no implementation. This allows the observer feature to be zero-cost when not in use.
|
||||
struct null_observer final: observer {
|
||||
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override {}
|
||||
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {}
|
||||
};
|
||||
|
||||
template<size_t Bits>
|
||||
struct wire {
|
||||
static constexpr size_t bits = Bits;
|
||||
|
@ -916,12 +800,11 @@ struct wire {
|
|||
|
||||
// This method intentionally takes a mandatory argument (to make it more difficult to misuse in
|
||||
// black box implementations, leading to missed observer events). It is generic over its argument
|
||||
// to make sure the `on_commit` call is devirtualized. This is somewhat awkward but lets us keep
|
||||
// a single implementation for both this method and the one in `memory`.
|
||||
// to allow the `on_update` method to be non-virtual.
|
||||
template<class ObserverT>
|
||||
bool commit(ObserverT &observer) {
|
||||
if (curr != next) {
|
||||
observer.on_commit(curr.chunks, curr.data, next.data);
|
||||
observer.on_update(curr.chunks, curr.data, next.data);
|
||||
curr = next;
|
||||
return true;
|
||||
}
|
||||
|
@ -1003,7 +886,7 @@ struct memory {
|
|||
value<Width> elem = data[entry.index];
|
||||
elem = elem.update(entry.val, entry.mask);
|
||||
if (data[entry.index] != elem) {
|
||||
observer.on_commit(value<Width>::chunks, data[0].data, elem.data, entry.index);
|
||||
observer.on_update(value<Width>::chunks, data[0].data, elem.data, entry.index);
|
||||
changed |= true;
|
||||
}
|
||||
data[entry.index] = elem;
|
||||
|
@ -1062,6 +945,174 @@ struct metadata {
|
|||
|
||||
typedef std::map<std::string, metadata> metadata_map;
|
||||
|
||||
struct performer;
|
||||
|
||||
// An object that allows formatting a string lazily.
|
||||
struct lazy_fmt {
|
||||
virtual std::string operator() () const = 0;
|
||||
};
|
||||
|
||||
// An object that can be passed to a `eval()` method in order to act on side effects.
|
||||
struct performer {
|
||||
// Called by generated formatting code to evaluate a Verilog `$time` expression.
|
||||
virtual int64_t vlog_time() const { return 0; }
|
||||
|
||||
// Called by generated formatting code to evaluate a Verilog `$realtime` expression.
|
||||
virtual double vlog_realtime() const { return vlog_time(); }
|
||||
|
||||
// Called when a `$print` cell is triggered.
|
||||
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
|
||||
std::cout << formatter();
|
||||
}
|
||||
};
|
||||
|
||||
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
|
||||
// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and
|
||||
// a comparatively heavyweight template-based solution is justified.
|
||||
struct observer {
|
||||
// Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks
|
||||
// at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and
|
||||
// `base` points to the first chunk.
|
||||
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {}
|
||||
|
||||
// Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]`
|
||||
// with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to
|
||||
// the memory element chunk count and `base` points to the first chunk of the first element of the memory.
|
||||
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {}
|
||||
};
|
||||
|
||||
// Must be kept in sync with `struct FmtPart` in kernel/fmt.h!
|
||||
// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
|
||||
struct fmt_part {
|
||||
enum {
|
||||
STRING = 0,
|
||||
INTEGER = 1,
|
||||
CHARACTER = 2,
|
||||
VLOG_TIME = 3,
|
||||
} type;
|
||||
|
||||
// STRING type
|
||||
std::string str;
|
||||
|
||||
// INTEGER/CHARACTER types
|
||||
// + value<Bits> val;
|
||||
|
||||
// INTEGER/CHARACTER/VLOG_TIME types
|
||||
enum {
|
||||
RIGHT = 0,
|
||||
LEFT = 1,
|
||||
} justify; // = RIGHT;
|
||||
char padding; // = '\0';
|
||||
size_t width; // = 0;
|
||||
|
||||
// INTEGER type
|
||||
unsigned base; // = 10;
|
||||
bool signed_; // = false;
|
||||
bool plus; // = false;
|
||||
|
||||
// VLOG_TIME type
|
||||
bool realtime; // = false;
|
||||
// + int64_t itime;
|
||||
// + double ftime;
|
||||
|
||||
// Format the part as a string.
|
||||
//
|
||||
// The values of `vlog_time` and `vlog_realtime` are used for Verilog `$time` and `$realtime`, correspondingly.
|
||||
template<size_t Bits>
|
||||
std::string render(value<Bits> val, performer *performer = nullptr)
|
||||
{
|
||||
// We might want to replace some of these bit() calls with direct
|
||||
// chunk access if it turns out to be slow enough to matter.
|
||||
std::string buf;
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return str;
|
||||
|
||||
case CHARACTER: {
|
||||
buf.reserve(Bits/8);
|
||||
for (int i = 0; i < Bits; i += 8) {
|
||||
char ch = 0;
|
||||
for (int j = 0; j < 8 && i + j < int(Bits); j++)
|
||||
if (val.bit(i + j))
|
||||
ch |= 1 << j;
|
||||
if (ch != 0)
|
||||
buf.append({ch});
|
||||
}
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
break;
|
||||
}
|
||||
|
||||
case INTEGER: {
|
||||
size_t width = Bits;
|
||||
if (base != 10) {
|
||||
width = 0;
|
||||
for (size_t index = 0; index < Bits; index++)
|
||||
if (val.bit(index))
|
||||
width = index + 1;
|
||||
}
|
||||
|
||||
if (base == 2) {
|
||||
for (size_t i = width; i > 0; i--)
|
||||
buf += (val.bit(i - 1) ? '1' : '0');
|
||||
} else if (base == 8 || base == 16) {
|
||||
size_t step = (base == 16) ? 4 : 3;
|
||||
for (size_t index = 0; index < width; index += step) {
|
||||
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
|
||||
if (step == 4)
|
||||
value |= val.bit(index + 3) << 3;
|
||||
buf += "0123456789abcdef"[value];
|
||||
}
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
} else if (base == 10) {
|
||||
bool negative = signed_ && val.is_neg();
|
||||
if (negative)
|
||||
val = val.neg();
|
||||
if (val.is_zero())
|
||||
buf += '0';
|
||||
value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>();
|
||||
while (!xval.is_zero()) {
|
||||
value<(Bits > 4 ? Bits : 4)> quotient, remainder;
|
||||
if (Bits >= 4)
|
||||
std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u});
|
||||
else
|
||||
std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval);
|
||||
buf += '0' + remainder.template trunc<4>().template get<uint8_t>();
|
||||
xval = quotient;
|
||||
}
|
||||
if (negative || plus)
|
||||
buf += negative ? '-' : '+';
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
} else assert(false && "Unsupported base for fmt_part");
|
||||
break;
|
||||
}
|
||||
|
||||
case VLOG_TIME: {
|
||||
if (performer) {
|
||||
buf = realtime ? std::to_string(performer->vlog_realtime()) : std::to_string(performer->vlog_time());
|
||||
} else {
|
||||
buf = realtime ? std::to_string(0.0) : std::to_string(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string str;
|
||||
assert(width == 0 || padding != '\0');
|
||||
if (justify == RIGHT && buf.size() < width) {
|
||||
size_t pad_width = width - buf.size();
|
||||
if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
|
||||
str += buf.front();
|
||||
buf.erase(0, 1);
|
||||
}
|
||||
str += std::string(pad_width, padding);
|
||||
}
|
||||
str += buf;
|
||||
if (justify == LEFT && buf.size() < width)
|
||||
str += std::string(width - buf.size(), padding);
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
// Tag class to disambiguate values/wires and their aliases.
|
||||
struct debug_alias {};
|
||||
|
||||
|
@ -1304,17 +1355,14 @@ struct module {
|
|||
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual bool eval() = 0;
|
||||
virtual bool commit() = 0;
|
||||
virtual bool eval(performer *performer = nullptr) = 0;
|
||||
virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls
|
||||
|
||||
unsigned int steps = 0;
|
||||
|
||||
size_t step() {
|
||||
++steps;
|
||||
size_t step(performer *performer = nullptr) {
|
||||
size_t deltas = 0;
|
||||
bool converged = false;
|
||||
do {
|
||||
converged = eval();
|
||||
converged = eval(performer);
|
||||
deltas++;
|
||||
} while (commit() && !converged);
|
||||
return deltas;
|
||||
|
|
|
@ -556,22 +556,20 @@ public:
|
|||
bool record_incremental(ModuleT &module) {
|
||||
assert(streaming);
|
||||
|
||||
struct : public observer {
|
||||
struct {
|
||||
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
|
||||
spool::writer *writer;
|
||||
|
||||
CXXRTL_ALWAYS_INLINE
|
||||
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override {
|
||||
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {
|
||||
writer->write_change(ident_lookup->at(base), chunks, value);
|
||||
}
|
||||
|
||||
CXXRTL_ALWAYS_INLINE
|
||||
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {
|
||||
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {
|
||||
writer->write_change(ident_lookup->at(base), chunks, value, index);
|
||||
}
|
||||
} record_observer;
|
||||
record_observer.ident_lookup = &ident_lookup;
|
||||
record_observer.writer = &writer;
|
||||
} record_observer = { &ident_lookup, &writer };
|
||||
|
||||
writer.write_sample(/*incremental=*/true, pointer++, timestamp);
|
||||
for (auto input_index : inputs) {
|
||||
|
|
|
@ -1053,6 +1053,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
f << stringf(";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == ID($_BUF_)) {
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort(ID::Y));
|
||||
f << stringf(" = ");
|
||||
dump_cell_expr_port(f, cell, "A", false);
|
||||
f << stringf(";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
|
@ -1830,7 +1839,8 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
if (it != cell->parameters.begin())
|
||||
f << stringf(",");
|
||||
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
|
||||
dump_const(f, it->second);
|
||||
if (it->second.size() > 0)
|
||||
dump_const(f, it->second);
|
||||
f << stringf(")");
|
||||
}
|
||||
f << stringf("\n%s" ")", indent.c_str());
|
||||
|
@ -1895,17 +1905,21 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
|
||||
void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
|
||||
{
|
||||
f << stringf("%s" "always @(", indent.c_str());
|
||||
for (int i = 0; i < trg.size(); i++) {
|
||||
if (i != 0)
|
||||
f << " or ";
|
||||
if (polarity[i])
|
||||
f << "posedge ";
|
||||
else
|
||||
f << "negedge ";
|
||||
dump_sigspec(f, trg[i]);
|
||||
if (trg.size() == 0) {
|
||||
f << stringf("%s" "initial begin\n", indent.c_str());
|
||||
} else {
|
||||
f << stringf("%s" "always @(", indent.c_str());
|
||||
for (int i = 0; i < trg.size(); i++) {
|
||||
if (i != 0)
|
||||
f << " or ";
|
||||
if (polarity[i])
|
||||
f << "posedge ";
|
||||
else
|
||||
f << "negedge ";
|
||||
dump_sigspec(f, trg[i]);
|
||||
}
|
||||
f << ") begin\n";
|
||||
}
|
||||
f << ") begin\n";
|
||||
|
||||
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
|
||||
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
|
||||
|
@ -1944,13 +1958,8 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left,
|
|||
|
||||
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
|
||||
|
||||
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
|
||||
void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
|
||||
{
|
||||
int number_of_stmts = cs->switches.size() + cs->actions.size();
|
||||
|
||||
if (!omit_trailing_begin && number_of_stmts >= 2)
|
||||
f << stringf("%s" "begin\n", indent.c_str());
|
||||
|
||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
|
||||
if (it->first.size() == 0)
|
||||
continue;
|
||||
|
@ -1960,15 +1969,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo
|
|||
dump_sigspec(f, it->second);
|
||||
f << stringf(";\n");
|
||||
}
|
||||
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
||||
dump_proc_switch(f, indent + " ", *it);
|
||||
|
||||
if (!omit_trailing_begin && number_of_stmts == 0)
|
||||
f << stringf("%s /* empty */;\n", indent.c_str());
|
||||
|
||||
if (omit_trailing_begin || number_of_stmts >= 2)
|
||||
f << stringf("%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
||||
|
@ -1991,36 +1991,52 @@ bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchR
|
|||
}
|
||||
}
|
||||
|
||||
dump_attributes(f, indent, sw->attributes);
|
||||
f << indent;
|
||||
auto sig_it = sw->signal.begin();
|
||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
|
||||
bool had_newline = true;
|
||||
if (it != sw->cases.begin()) {
|
||||
if ((*it)->compare.empty()) {
|
||||
f << indent << "else\n";
|
||||
had_newline = true;
|
||||
} else {
|
||||
f << indent << "else ";
|
||||
had_newline = false;
|
||||
}
|
||||
if ((*it)->compare.empty())
|
||||
f << " else begin\n";
|
||||
else
|
||||
f << " else ";
|
||||
}
|
||||
if (!(*it)->compare.empty()) {
|
||||
if (!(*it)->attributes.empty()) {
|
||||
if (!had_newline)
|
||||
f << "\n" << indent;
|
||||
dump_attributes(f, "", (*it)->attributes, "\n" + indent);
|
||||
}
|
||||
f << stringf("if (");
|
||||
dump_sigspec(f, *sig_it);
|
||||
f << stringf(")\n");
|
||||
f << stringf(") begin\n");
|
||||
}
|
||||
dump_case_body(f, indent, *it);
|
||||
|
||||
dump_case_actions(f, indent, (*it));
|
||||
for (auto it2 = (*it)->switches.begin(); it2 != (*it)->switches.end(); ++it2)
|
||||
dump_proc_switch(f, indent + " ", *it2);
|
||||
|
||||
f << indent << "end";
|
||||
if ((*it)->compare.empty())
|
||||
break;
|
||||
}
|
||||
f << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
|
||||
{
|
||||
int number_of_stmts = cs->switches.size() + cs->actions.size();
|
||||
|
||||
if (!omit_trailing_begin && number_of_stmts >= 2)
|
||||
f << stringf("%s" "begin\n", indent.c_str());
|
||||
|
||||
dump_case_actions(f, indent, cs);
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
||||
dump_proc_switch(f, indent + " ", *it);
|
||||
|
||||
if (!omit_trailing_begin && number_of_stmts == 0)
|
||||
f << stringf("%s /* empty */;\n", indent.c_str());
|
||||
|
||||
if (omit_trailing_begin || number_of_stmts >= 2)
|
||||
f << stringf("%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
||||
{
|
||||
if (sw->signal.size() == 0) {
|
||||
|
|
|
@ -120,7 +120,7 @@ All binary RTL cells have two input ports ``\A`` and ``\B`` and one output port
|
|||
:verilog:`Y = A >>> B` $sshr :verilog:`Y = A - B` $sub
|
||||
:verilog:`Y = A && B` $logic_and :verilog:`Y = A * B` $mul
|
||||
:verilog:`Y = A || B` $logic_or :verilog:`Y = A / B` $div
|
||||
:verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod
|
||||
:verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod
|
||||
:verilog:`Y = A !== B` $nex ``N/A`` $divfloor
|
||||
:verilog:`Y = A ** B` $pow ``N/A`` $modfoor
|
||||
======================= ============= ======================= =========
|
||||
|
@ -661,6 +661,8 @@ Ports:
|
|||
|
||||
``\TRG``
|
||||
The signals that control when this ``$print`` cell is triggered.
|
||||
If the width of this port is zero and ``\TRG_ENABLE`` is true, the cell is
|
||||
triggered during initial evaluation (time zero) only.
|
||||
|
||||
``\EN``
|
||||
Enable signal for the whole cell.
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace AST {
|
|||
|
||||
// instantiate global variables (private API)
|
||||
namespace AST_INTERNAL {
|
||||
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire;
|
||||
AstNode *current_ast, *current_ast_mod;
|
||||
std::map<std::string, AstNode*> current_scope;
|
||||
|
@ -1320,11 +1320,12 @@ static void rename_in_package_stmts(AstNode *pkg)
|
|||
}
|
||||
|
||||
// create AstModule instances for all modules in the AST tree and add them to 'design'
|
||||
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
|
||||
void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
|
||||
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire)
|
||||
{
|
||||
current_ast = ast;
|
||||
current_ast_mod = nullptr;
|
||||
flag_nodisplay = nodisplay;
|
||||
flag_dump_ast1 = dump_ast1;
|
||||
flag_dump_ast2 = dump_ast2;
|
||||
flag_no_dump_ptr = no_dump_ptr;
|
||||
|
|
|
@ -287,7 +287,7 @@ namespace AST
|
|||
bool is_simple_const_expr();
|
||||
|
||||
// helper for parsing format strings
|
||||
Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0);
|
||||
Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0, bool may_fail = false);
|
||||
|
||||
bool is_recursive_function() const;
|
||||
std::pair<AstNode*, AstNode*> get_tern_choice();
|
||||
|
@ -376,7 +376,7 @@ namespace AST
|
|||
};
|
||||
|
||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire);
|
||||
|
||||
// parametric modules are supported directly by the AST library
|
||||
|
@ -432,7 +432,7 @@ namespace AST
|
|||
namespace AST_INTERNAL
|
||||
{
|
||||
// internal state variables
|
||||
extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire;
|
||||
extern AST::AstNode *current_ast, *current_ast_mod;
|
||||
extern std::map<std::string, AST::AstNode*> current_scope;
|
||||
|
|
|
@ -718,7 +718,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
}
|
||||
}
|
||||
cell->parameters[ID::TRG_WIDTH] = triggers.size();
|
||||
cell->parameters[ID::TRG_ENABLE] = !triggers.empty();
|
||||
cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty();
|
||||
cell->parameters[ID::TRG_POLARITY] = polarity;
|
||||
cell->parameters[ID::PRIORITY] = --last_print_priority;
|
||||
cell->setPort(ID::TRG, triggers);
|
||||
|
|
|
@ -145,7 +145,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend)
|
|||
|
||||
// Process a format string and arguments for $display, $write, $sprintf, etc
|
||||
|
||||
Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at) {
|
||||
Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at, bool may_fail) {
|
||||
std::vector<VerilogFmtArg> args;
|
||||
for (size_t index = first_arg_at; index < children.size(); index++) {
|
||||
AstNode *node_arg = children[index];
|
||||
|
@ -169,6 +169,9 @@ Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_
|
|||
arg.type = VerilogFmtArg::INTEGER;
|
||||
arg.sig = node_arg->bitsAsConst();
|
||||
arg.signed_ = node_arg->is_signed;
|
||||
} else if (may_fail) {
|
||||
log_file_info(filename, location.first_line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1);
|
||||
return Fmt();
|
||||
} else {
|
||||
log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1);
|
||||
}
|
||||
|
@ -217,8 +220,8 @@ void AstNode::annotateTypedEnums(AstNode *template_node)
|
|||
log_assert(enum_item->children[1]->type == AST_RANGE);
|
||||
is_signed = enum_item->children[1]->is_signed;
|
||||
} else {
|
||||
log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n",
|
||||
enum_item->children.size(),
|
||||
log_error("enum_item children size==%zu, expected 1 or 2 for %s (%s)\n",
|
||||
(size_t) enum_item->children.size(),
|
||||
enum_item->str.c_str(), enum_node->str.c_str()
|
||||
);
|
||||
}
|
||||
|
@ -1055,30 +1058,31 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
{
|
||||
if (!current_always) {
|
||||
log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str());
|
||||
} else if (current_always->type == AST_INITIAL) {
|
||||
int default_base = 10;
|
||||
if (str.back() == 'b')
|
||||
default_base = 2;
|
||||
else if (str.back() == 'o')
|
||||
default_base = 8;
|
||||
else if (str.back() == 'h')
|
||||
default_base = 16;
|
||||
|
||||
// when $display()/$write() functions are used in an initial block, print them during synthesis
|
||||
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base);
|
||||
if (str.substr(0, 8) == "$display")
|
||||
fmt.append_string("\n");
|
||||
log("%s", fmt.render().c_str());
|
||||
delete_children();
|
||||
str = std::string();
|
||||
} else {
|
||||
// when $display()/$write() functions are used in an always block, simplify the expressions and
|
||||
// convert them to a special cell later in genrtlil
|
||||
// simplify the expressions and convert them to a special cell later in genrtlil
|
||||
for (auto node : children)
|
||||
while (node->simplify(true, stage, -1, false)) {}
|
||||
|
||||
if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) {
|
||||
int default_base = 10;
|
||||
if (str.back() == 'b')
|
||||
default_base = 2;
|
||||
else if (str.back() == 'o')
|
||||
default_base = 8;
|
||||
else if (str.back() == 'h')
|
||||
default_base = 16;
|
||||
|
||||
// when $display()/$write() functions are used in an initial block, print them during synthesis
|
||||
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true);
|
||||
if (str.substr(0, 8) == "$display")
|
||||
fmt.append_string("\n");
|
||||
log("%s", fmt.render().c_str());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
delete_children();
|
||||
str = std::string();
|
||||
}
|
||||
|
||||
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
|
||||
|
@ -2966,96 +2970,67 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
}
|
||||
} else {
|
||||
// mask and shift operations
|
||||
|
||||
AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true)));
|
||||
wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
||||
wire_mask->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
||||
wire_mask->is_logic = true;
|
||||
while (wire_mask->simplify(true, 1, -1, false)) { }
|
||||
current_ast_mod->children.push_back(wire_mask);
|
||||
|
||||
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true)));
|
||||
wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
||||
wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
||||
wire_data->is_logic = true;
|
||||
while (wire_data->simplify(true, 1, -1, false)) { }
|
||||
current_ast_mod->children.push_back(wire_data);
|
||||
|
||||
int shamt_width_hint = -1;
|
||||
bool shamt_sign_hint = true;
|
||||
shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint);
|
||||
|
||||
AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true)));
|
||||
wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
||||
wire_sel->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
|
||||
wire_sel->is_logic = true;
|
||||
wire_sel->is_signed = shamt_sign_hint;
|
||||
while (wire_sel->simplify(true, 1, -1, false)) { }
|
||||
current_ast_mod->children.push_back(wire_sel);
|
||||
|
||||
did_something = true;
|
||||
newNode = new AstNode(AST_BLOCK);
|
||||
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
|
||||
|
||||
AstNode *lvalue = children[0]->clone();
|
||||
lvalue->delete_children();
|
||||
if (member_node)
|
||||
lvalue->set_attribute(ID::wiretype, member_node->clone());
|
||||
|
||||
AstNode *ref_mask = new AstNode(AST_IDENTIFIER);
|
||||
ref_mask->str = wire_mask->str;
|
||||
ref_mask->id2ast = wire_mask;
|
||||
ref_mask->was_checked = true;
|
||||
|
||||
AstNode *ref_data = new AstNode(AST_IDENTIFIER);
|
||||
ref_data->str = wire_data->str;
|
||||
ref_data->id2ast = wire_data;
|
||||
ref_data->was_checked = true;
|
||||
|
||||
AstNode *ref_sel = new AstNode(AST_IDENTIFIER);
|
||||
ref_sel->str = wire_sel->str;
|
||||
ref_sel->id2ast = wire_sel;
|
||||
ref_sel->was_checked = true;
|
||||
|
||||
AstNode *old_data = lvalue->clone();
|
||||
if (type == AST_ASSIGN_LE)
|
||||
old_data->lookahead = true;
|
||||
|
||||
AstNode *s = new AstNode(AST_ASSIGN_EQ, ref_sel->clone(), shift_expr);
|
||||
newNode->children.push_back(s);
|
||||
int shift_width_hint;
|
||||
bool shift_sign_hint;
|
||||
shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint);
|
||||
|
||||
AstNode *shamt = ref_sel;
|
||||
// All operations are carried out in a new block.
|
||||
newNode = new AstNode(AST_BLOCK);
|
||||
|
||||
// convert to signed while preserving the sign and value
|
||||
shamt = new AstNode(AST_CAST_SIZE, mkconst_int(shamt_width_hint + 1, true), shamt);
|
||||
shamt = new AstNode(AST_TO_SIGNED, shamt);
|
||||
// Temporary register holding the result of the bit- or part-select position expression.
|
||||
AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint);
|
||||
newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, pos, shift_expr));
|
||||
|
||||
// Calculate lsb from position.
|
||||
AstNode *shift_val = pos->clone();
|
||||
|
||||
// If the expression is signed, we must add an extra bit for possible negation of the most negative number.
|
||||
// If the expression is unsigned, we must add an extra bit for sign.
|
||||
shift_val = new AstNode(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), shift_val);
|
||||
if (!shift_sign_hint)
|
||||
shift_val = new AstNode(AST_TO_SIGNED, shift_val);
|
||||
|
||||
// offset the shift amount by the lower bound of the dimension
|
||||
int start_bit = wire_offset;
|
||||
shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true));
|
||||
if (wire_offset != 0)
|
||||
shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true));
|
||||
|
||||
// reflect the shift amount if the dimension is swapped
|
||||
if (children[0]->id2ast->range_swapped)
|
||||
shamt = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shamt);
|
||||
shift_val = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shift_val);
|
||||
|
||||
// AST_SHIFT uses negative amounts for shifting left
|
||||
shamt = new AstNode(AST_NEG, shamt);
|
||||
shift_val = new AstNode(AST_NEG, shift_val);
|
||||
|
||||
AstNode *t;
|
||||
|
||||
t = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
|
||||
t = new AstNode(AST_SHIFT, t, shamt->clone());
|
||||
t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t);
|
||||
newNode->children.push_back(t);
|
||||
|
||||
t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone());
|
||||
t = new AstNode(AST_SHIFT, t, shamt);
|
||||
t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t);
|
||||
newNode->children.push_back(t);
|
||||
|
||||
t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask));
|
||||
t = new AstNode(AST_BIT_OR, t, ref_data);
|
||||
t = new AstNode(type, lvalue, t);
|
||||
newNode->children.push_back(t);
|
||||
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
|
||||
did_something = true;
|
||||
AstNode *bitmask = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
|
||||
newNode->children.push_back(
|
||||
new AstNode(type,
|
||||
lvalue,
|
||||
new AstNode(AST_BIT_OR,
|
||||
new AstNode(AST_BIT_AND,
|
||||
old_data,
|
||||
new AstNode(AST_BIT_NOT,
|
||||
new AstNode(AST_SHIFT,
|
||||
bitmask,
|
||||
shift_val->clone()))),
|
||||
new AstNode(AST_SHIFT,
|
||||
new AstNode(AST_TO_UNSIGNED,
|
||||
new AstNode(AST_CAST_SIZE,
|
||||
mkconst_int(result_width, true),
|
||||
children[1]->clone())),
|
||||
shift_val))));
|
||||
|
||||
newNode->fixup_hierarchy_flags(true);
|
||||
}
|
||||
|
|
|
@ -2110,7 +2110,7 @@ VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_a
|
|||
if (sva_at_only)
|
||||
do {
|
||||
Instance *inst_mux = net->Driver();
|
||||
if (inst_mux->Type() != PRIM_MUX)
|
||||
if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX)
|
||||
break;
|
||||
|
||||
bool pwr1 = inst_mux->GetInput1()->IsPwr();
|
||||
|
|
|
@ -100,6 +100,10 @@ struct VerilogFrontend : public Frontend {
|
|||
log(" -assert-assumes\n");
|
||||
log(" treat all assume() statements like assert() statements\n");
|
||||
log("\n");
|
||||
log(" -nodisplay\n");
|
||||
log(" suppress output from display system tasks ($display et. al).\n");
|
||||
log(" This does not affect the output from a later 'sim' command.\n");
|
||||
log("\n");
|
||||
log(" -debug\n");
|
||||
log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n");
|
||||
log("\n");
|
||||
|
@ -235,6 +239,7 @@ struct VerilogFrontend : public Frontend {
|
|||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool flag_nodisplay = false;
|
||||
bool flag_dump_ast1 = false;
|
||||
bool flag_dump_ast2 = false;
|
||||
bool flag_no_dump_ptr = false;
|
||||
|
@ -308,6 +313,10 @@ struct VerilogFrontend : public Frontend {
|
|||
assert_assumes_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nodisplay") {
|
||||
flag_nodisplay = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-debug") {
|
||||
flag_dump_ast1 = true;
|
||||
flag_dump_ast2 = true;
|
||||
|
@ -510,7 +519,7 @@ struct VerilogFrontend : public Frontend {
|
|||
if (flag_nodpi)
|
||||
error_on_dpi_function(current_ast);
|
||||
|
||||
AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
|
||||
AST::process(design, current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
|
||||
flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
|
||||
|
||||
|
||||
|
|
144
kernel/fmt.cc
144
kernel/fmt.cc
|
@ -110,9 +110,9 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
|
|||
} else if (fmt[i] == 'c') {
|
||||
part.type = FmtPart::CHARACTER;
|
||||
} else if (fmt[i] == 't') {
|
||||
part.type = FmtPart::TIME;
|
||||
part.type = FmtPart::VLOG_TIME;
|
||||
} else if (fmt[i] == 'r') {
|
||||
part.type = FmtPart::TIME;
|
||||
part.type = FmtPart::VLOG_TIME;
|
||||
part.realtime = true;
|
||||
} else {
|
||||
log_assert(false && "Unexpected character in format substitution");
|
||||
|
@ -172,7 +172,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
|
|||
}
|
||||
break;
|
||||
|
||||
case FmtPart::TIME:
|
||||
case FmtPart::VLOG_TIME:
|
||||
log_assert(part.sig.size() == 0);
|
||||
YS_FALLTHROUGH
|
||||
case FmtPart::CHARACTER:
|
||||
|
@ -205,7 +205,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
|
|||
fmt += part.signed_ ? 's' : 'u';
|
||||
} else if (part.type == FmtPart::CHARACTER) {
|
||||
fmt += 'c';
|
||||
} else if (part.type == FmtPart::TIME) {
|
||||
} else if (part.type == FmtPart::VLOG_TIME) {
|
||||
if (part.realtime)
|
||||
fmt += 'r';
|
||||
else
|
||||
|
@ -328,7 +328,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
|
|||
|
||||
case VerilogFmtArg::TIME: {
|
||||
FmtPart part = {};
|
||||
part.type = FmtPart::TIME;
|
||||
part.type = FmtPart::VLOG_TIME;
|
||||
part.realtime = arg->realtime;
|
||||
part.padding = ' ';
|
||||
part.width = 20;
|
||||
|
@ -419,7 +419,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
|
|||
part.padding = ' ';
|
||||
} else if (fmt[i] == 't' || fmt[i] == 'T') {
|
||||
if (arg->type == VerilogFmtArg::TIME) {
|
||||
part.type = FmtPart::TIME;
|
||||
part.type = FmtPart::VLOG_TIME;
|
||||
part.realtime = arg->realtime;
|
||||
if (!has_width && !has_leading_zero)
|
||||
part.width = 20;
|
||||
|
@ -541,7 +541,7 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
|
|||
break;
|
||||
}
|
||||
|
||||
case FmtPart::TIME: {
|
||||
case FmtPart::VLOG_TIME: {
|
||||
VerilogFmtArg arg;
|
||||
arg.type = VerilogFmtArg::TIME;
|
||||
if (part.realtime)
|
||||
|
@ -569,82 +569,60 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
|
|||
return args;
|
||||
}
|
||||
|
||||
void Fmt::emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const
|
||||
std::string escape_cxx_string(const std::string &input)
|
||||
{
|
||||
for (auto &part : parts) {
|
||||
switch (part.type) {
|
||||
case FmtPart::STRING:
|
||||
f << " << \"";
|
||||
for (char c : part.str) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
YS_FALLTHROUGH
|
||||
case '"':
|
||||
f << '\\' << c;
|
||||
break;
|
||||
case '\a':
|
||||
f << "\\a";
|
||||
break;
|
||||
case '\b':
|
||||
f << "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
f << "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
f << "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
f << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
f << "\\t";
|
||||
break;
|
||||
case '\v':
|
||||
f << "\\v";
|
||||
break;
|
||||
default:
|
||||
f << c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
f << '"';
|
||||
break;
|
||||
|
||||
case FmtPart::INTEGER:
|
||||
case FmtPart::CHARACTER: {
|
||||
f << " << value_formatted<" << part.sig.size() << ">(";
|
||||
emit_sig(part.sig);
|
||||
f << ", " << (part.type == FmtPart::CHARACTER);
|
||||
f << ", " << (part.justify == FmtPart::LEFT);
|
||||
f << ", (char)" << (int)part.padding;
|
||||
f << ", " << part.width;
|
||||
f << ", " << part.base;
|
||||
f << ", " << part.signed_;
|
||||
f << ", " << part.plus;
|
||||
f << ')';
|
||||
break;
|
||||
}
|
||||
|
||||
case FmtPart::TIME: {
|
||||
// CXXRTL only records steps taken, so there's no difference between
|
||||
// the values taken by $time and $realtime.
|
||||
f << " << value_formatted<64>(";
|
||||
f << "value<64>{steps}";
|
||||
f << ", " << (part.type == FmtPart::CHARACTER);
|
||||
f << ", " << (part.justify == FmtPart::LEFT);
|
||||
f << ", (char)" << (int)part.padding;
|
||||
f << ", " << part.width;
|
||||
f << ", " << part.base;
|
||||
f << ", " << part.signed_;
|
||||
f << ", " << part.plus;
|
||||
f << ')';
|
||||
break;
|
||||
}
|
||||
|
||||
default: log_abort();
|
||||
std::string output = "\"";
|
||||
for (auto c : input) {
|
||||
if (::isprint(c)) {
|
||||
if (c == '\\')
|
||||
output.push_back('\\');
|
||||
output.push_back(c);
|
||||
} else {
|
||||
char l = c & 0xf, h = (c >> 4) & 0xf;
|
||||
output.append("\\x");
|
||||
output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
|
||||
output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
|
||||
}
|
||||
}
|
||||
output.push_back('"');
|
||||
if (output.find('\0') != std::string::npos) {
|
||||
output.insert(0, "std::string {");
|
||||
output.append(stringf(", %zu}", input.size()));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig, const std::string &context) const
|
||||
{
|
||||
os << indent << "std::string buf;\n";
|
||||
for (auto &part : parts) {
|
||||
os << indent << "buf += fmt_part { ";
|
||||
os << "fmt_part::";
|
||||
switch (part.type) {
|
||||
case FmtPart::STRING: os << "STRING"; break;
|
||||
case FmtPart::INTEGER: os << "INTEGER"; break;
|
||||
case FmtPart::CHARACTER: os << "CHARACTER"; break;
|
||||
case FmtPart::VLOG_TIME: os << "VLOG_TIME"; break;
|
||||
}
|
||||
os << ", ";
|
||||
os << escape_cxx_string(part.str) << ", ";
|
||||
os << "fmt_part::";
|
||||
switch (part.justify) {
|
||||
case FmtPart::LEFT: os << "LEFT"; break;
|
||||
case FmtPart::RIGHT: os << "RIGHT"; break;
|
||||
}
|
||||
os << ", ";
|
||||
os << "(char)" << (int)part.padding << ", ";
|
||||
os << part.width << ", ";
|
||||
os << part.base << ", ";
|
||||
os << part.signed_ << ", ";
|
||||
os << part.plus << ", ";
|
||||
os << part.realtime;
|
||||
os << " }.render(";
|
||||
emit_sig(part.sig);
|
||||
os << ", " << context << ");\n";
|
||||
}
|
||||
os << indent << "return buf;\n";
|
||||
}
|
||||
|
||||
std::string Fmt::render() const
|
||||
|
@ -658,8 +636,8 @@ std::string Fmt::render() const
|
|||
break;
|
||||
|
||||
case FmtPart::INTEGER:
|
||||
case FmtPart::TIME:
|
||||
case FmtPart::CHARACTER: {
|
||||
case FmtPart::CHARACTER:
|
||||
case FmtPart::VLOG_TIME: {
|
||||
std::string buf;
|
||||
if (part.type == FmtPart::INTEGER) {
|
||||
RTLIL::Const value = part.sig.as_const();
|
||||
|
@ -742,7 +720,7 @@ std::string Fmt::render() const
|
|||
} else log_abort();
|
||||
} else if (part.type == FmtPart::CHARACTER) {
|
||||
buf = part.sig.as_const().decode_string();
|
||||
} else if (part.type == FmtPart::TIME) {
|
||||
} else if (part.type == FmtPart::VLOG_TIME) {
|
||||
// We only render() during initial, so time is always zero.
|
||||
buf = "0";
|
||||
}
|
||||
|
|
11
kernel/fmt.h
11
kernel/fmt.h
|
@ -50,12 +50,13 @@ struct VerilogFmtArg {
|
|||
|
||||
// RTLIL format part, such as the substitutions in:
|
||||
// "foo {4:> 4du} bar {2:<01hs}"
|
||||
// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h!
|
||||
struct FmtPart {
|
||||
enum {
|
||||
STRING = 0,
|
||||
INTEGER = 1,
|
||||
CHARACTER = 2,
|
||||
TIME = 3,
|
||||
VLOG_TIME = 3,
|
||||
} type;
|
||||
|
||||
// STRING type
|
||||
|
@ -64,20 +65,20 @@ struct FmtPart {
|
|||
// INTEGER/CHARACTER types
|
||||
RTLIL::SigSpec sig;
|
||||
|
||||
// INTEGER/CHARACTER/TIME types
|
||||
// INTEGER/CHARACTER/VLOG_TIME types
|
||||
enum {
|
||||
RIGHT = 0,
|
||||
LEFT = 1,
|
||||
} justify = RIGHT;
|
||||
char padding = '\0';
|
||||
size_t width = 0;
|
||||
|
||||
|
||||
// INTEGER type
|
||||
unsigned base = 10;
|
||||
bool signed_ = false;
|
||||
bool plus = false;
|
||||
|
||||
// TIME type
|
||||
// VLOG_TIME type
|
||||
bool realtime = false;
|
||||
};
|
||||
|
||||
|
@ -93,7 +94,7 @@ public:
|
|||
void parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name);
|
||||
std::vector<VerilogFmtArg> emit_verilog() const;
|
||||
|
||||
void emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const;
|
||||
void emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig, const std::string &context) const;
|
||||
|
||||
std::string render() const;
|
||||
|
||||
|
|
|
@ -2157,17 +2157,10 @@ void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
|
|||
}
|
||||
|
||||
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
|
||||
log_assert(GetSize(lhs) == GetSize(rhs));
|
||||
lhs.unpack();
|
||||
rhs.unpack();
|
||||
for (int i = 0; i < GetSize(lhs); i++) {
|
||||
RTLIL::SigBit &lhs_bit = lhs.bits_[i];
|
||||
RTLIL::SigBit &rhs_bit = rhs.bits_[i];
|
||||
if ((lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) || (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))) {
|
||||
lhs_bit = State::Sx;
|
||||
rhs_bit = State::Sx;
|
||||
}
|
||||
}
|
||||
// If a deleted wire occurs on the lhs or rhs we just remove that part
|
||||
// of the assignment
|
||||
lhs.remove2(*wires_p, &rhs);
|
||||
rhs.remove2(*wires_p, &lhs);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3693,6 +3686,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
|
|||
|
||||
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
|
||||
{
|
||||
log_assert(offset >= 0);
|
||||
log_assert(length >= 0);
|
||||
log_assert(offset + length <= width);
|
||||
RTLIL::SigChunk ret;
|
||||
if (wire) {
|
||||
ret.wire = wire;
|
||||
|
@ -4238,6 +4234,34 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
|
|||
check();
|
||||
}
|
||||
|
||||
void RTLIL::SigSpec::remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other)
|
||||
{
|
||||
if (other)
|
||||
cover("kernel.rtlil.sigspec.remove_other");
|
||||
else
|
||||
cover("kernel.rtlil.sigspec.remove");
|
||||
|
||||
unpack();
|
||||
|
||||
if (other != NULL) {
|
||||
log_assert(width_ == other->width_);
|
||||
other->unpack();
|
||||
}
|
||||
|
||||
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
|
||||
if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) {
|
||||
bits_.erase(bits_.begin() + i);
|
||||
width_--;
|
||||
if (other != NULL) {
|
||||
other->bits_.erase(other->bits_.begin() + i);
|
||||
other->width_--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
|
||||
{
|
||||
if (other)
|
||||
|
@ -4377,6 +4401,9 @@ void RTLIL::SigSpec::remove(int offset, int length)
|
|||
|
||||
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
|
||||
{
|
||||
log_assert(offset >= 0);
|
||||
log_assert(length >= 0);
|
||||
log_assert(offset + length <= width_);
|
||||
unpack();
|
||||
cover("kernel.rtlil.sigspec.extract_pos");
|
||||
return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);
|
||||
|
|
|
@ -924,6 +924,7 @@ public:
|
|||
void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
|
||||
void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
|
||||
void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
|
||||
void remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other);
|
||||
|
||||
void remove(int offset, int length = 1);
|
||||
void remove_const();
|
||||
|
|
|
@ -539,7 +539,7 @@ struct ShowWorker
|
|||
std::string proc_src = RTLIL::unescape_id(proc->name);
|
||||
if (proc->attributes.count(ID::src) > 0)
|
||||
proc_src = proc->attributes.at(ID::src).decode_string();
|
||||
fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\"];\n", pidx, findLabel(proc->name.str()), proc_src.c_str());
|
||||
fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\", %s];\n", pidx, findLabel(proc->name.str()), proc_src.c_str(), findColor(proc->name).c_str());
|
||||
}
|
||||
|
||||
for (auto &conn : module->connections())
|
||||
|
|
|
@ -366,7 +366,7 @@ struct StatPass : public Pass {
|
|||
log(" use cell area information from the provided liberty file\n");
|
||||
log("\n");
|
||||
log(" -tech <technology>\n");
|
||||
log(" print area estemate for the specified technology. Currently supported\n");
|
||||
log(" print area estimate for the specified technology. Currently supported\n");
|
||||
log(" values for <technology>: xilinx, cmos\n");
|
||||
log("\n");
|
||||
log(" -width\n");
|
||||
|
|
|
@ -655,6 +655,17 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
|
|||
log("Removed %d unused modules.\n", del_counter);
|
||||
}
|
||||
|
||||
bool set_keep_print(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
|
||||
{
|
||||
if (cache.count(mod) == 0)
|
||||
for (auto c : mod->cells()) {
|
||||
RTLIL::Module *m = mod->design->module(c->type);
|
||||
if ((m != nullptr && set_keep_print(cache, m)) || c->type == ID($print))
|
||||
return cache[mod] = true;
|
||||
}
|
||||
return cache[mod];
|
||||
}
|
||||
|
||||
bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
|
||||
{
|
||||
if (cache.count(mod) == 0)
|
||||
|
@ -762,6 +773,11 @@ struct HierarchyPass : public Pass {
|
|||
log(" -nodefaults\n");
|
||||
log(" do not resolve input port default values\n");
|
||||
log("\n");
|
||||
log(" -nokeep_prints\n");
|
||||
log(" per default this pass sets the \"keep\" attribute on all modules\n");
|
||||
log(" that directly or indirectly display text on the terminal.\n");
|
||||
log(" This option disables this behavior.\n");
|
||||
log("\n");
|
||||
log(" -nokeep_asserts\n");
|
||||
log(" per default this pass sets the \"keep\" attribute on all modules\n");
|
||||
log(" that directly or indirectly contain one or more formal properties.\n");
|
||||
|
@ -818,6 +834,7 @@ struct HierarchyPass : public Pass {
|
|||
bool keep_positionals = false;
|
||||
bool keep_portwidths = false;
|
||||
bool nodefaults = false;
|
||||
bool nokeep_prints = false;
|
||||
bool nokeep_asserts = false;
|
||||
std::vector<std::string> generate_cells;
|
||||
std::vector<generate_port_decl_t> generate_ports;
|
||||
|
@ -893,6 +910,10 @@ struct HierarchyPass : public Pass {
|
|||
nodefaults = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nokeep_prints") {
|
||||
nokeep_prints = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nokeep_asserts") {
|
||||
nokeep_asserts = true;
|
||||
continue;
|
||||
|
@ -1091,6 +1112,15 @@ struct HierarchyPass : public Pass {
|
|||
}
|
||||
}
|
||||
|
||||
if (!nokeep_prints) {
|
||||
std::map<RTLIL::Module*, bool> cache;
|
||||
for (auto mod : design->modules())
|
||||
if (set_keep_print(cache, mod)) {
|
||||
log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", log_id(mod));
|
||||
mod->set_bool_attribute(ID::keep);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nokeep_asserts) {
|
||||
std::map<RTLIL::Module*, bool> cache;
|
||||
for (auto mod : design->modules())
|
||||
|
|
|
@ -690,7 +690,7 @@ bool apply_clock(MemConfig &cfg, const PortVariant &def, SigBit clk, bool clk_po
|
|||
|
||||
// Perform write port assignment, validating clock options as we go.
|
||||
void MemMapping::assign_wr_ports() {
|
||||
log_reject(stringf("Assigning write ports... (candidate configs: %lu)", cfgs.size()));
|
||||
log_reject(stringf("Assigning write ports... (candidate configs: %zu)", (size_t) cfgs.size()));
|
||||
for (auto &port: mem.wr_ports) {
|
||||
if (!port.clk_enable) {
|
||||
// Async write ports not supported.
|
||||
|
@ -739,7 +739,7 @@ void MemMapping::assign_wr_ports() {
|
|||
|
||||
// Perform read port assignment, validating clock and rden options as we go.
|
||||
void MemMapping::assign_rd_ports() {
|
||||
log_reject(stringf("Assigning read ports... (candidate configs: %lu)", cfgs.size()));
|
||||
log_reject(stringf("Assigning read ports... (candidate configs: %zu)", (size_t) cfgs.size()));
|
||||
for (int pidx = 0; pidx < GetSize(mem.rd_ports); pidx++) {
|
||||
auto &port = mem.rd_ports[pidx];
|
||||
MemConfigs new_cfgs;
|
||||
|
@ -900,7 +900,7 @@ void MemMapping::assign_rd_ports() {
|
|||
|
||||
// Validate transparency restrictions, determine where to add soft transparency logic.
|
||||
void MemMapping::handle_trans() {
|
||||
log_reject(stringf("Handling transparency... (candidate configs: %lu)", cfgs.size()));
|
||||
log_reject(stringf("Handling transparency... (candidate configs: %zu)", (size_t) cfgs.size()));
|
||||
if (mem.emulate_read_first_ok()) {
|
||||
MemConfigs new_cfgs;
|
||||
for (auto &cfg: cfgs) {
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
// Type represents the following constraint: Preserve connections to dedicated
|
||||
// logic cell <cell_type> that has ports connected to LUT inputs. This includes
|
||||
// the case where both LUT and dedicated logic input are connected to the same
|
||||
// constant.
|
||||
struct dlogic_t {
|
||||
IdString cell_type;
|
||||
// LUT input idx -> hard cell's port name
|
||||
|
@ -515,16 +519,6 @@ struct OptLutWorker
|
|||
}
|
||||
};
|
||||
|
||||
static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
|
||||
{
|
||||
size_t start = 0, end = 0;
|
||||
while ((end = text.find(sep, start)) != std::string::npos) {
|
||||
tokens.push_back(text.substr(start, end - start));
|
||||
start = end + 1;
|
||||
}
|
||||
tokens.push_back(text.substr(start));
|
||||
}
|
||||
|
||||
struct OptLutPass : public Pass {
|
||||
OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
|
||||
void help() override
|
||||
|
@ -541,6 +535,10 @@ struct OptLutPass : public Pass {
|
|||
log(" the case where both LUT and dedicated logic input are connected to\n");
|
||||
log(" the same constant.\n");
|
||||
log("\n");
|
||||
log(" -tech ice40\n");
|
||||
log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n");
|
||||
log(" and preserve connections to SB_CARRY as appropriate\n");
|
||||
log("\n");
|
||||
log(" -limit N\n");
|
||||
log(" only perform the first N combines, then stop. useful for debugging.\n");
|
||||
log("\n");
|
||||
|
@ -555,28 +553,28 @@ struct OptLutPass : public Pass {
|
|||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-dlogic" && argidx+1 < args.size())
|
||||
if (args[argidx] == "-tech" && argidx+1 < args.size())
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
split(tokens, args[++argidx], ':');
|
||||
if (tokens.size() < 2)
|
||||
log_cmd_error("The -dlogic option requires at least one connection.\n");
|
||||
dlogic_t entry;
|
||||
entry.cell_type = "\\" + tokens[0];
|
||||
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
|
||||
std::vector<std::string> conn_tokens;
|
||||
split(conn_tokens, *it, '=');
|
||||
if (conn_tokens.size() != 2)
|
||||
log_cmd_error("Invalid format of -dlogic signal mapping.\n");
|
||||
IdString logic_port = "\\" + conn_tokens[0];
|
||||
int lut_input = atoi(conn_tokens[1].c_str());
|
||||
entry.lut_input_port[lut_input] = logic_port;
|
||||
}
|
||||
dlogic.push_back(entry);
|
||||
std::string tech = args[++argidx];
|
||||
if (tech != "ice40")
|
||||
log_cmd_error("Unsupported -tech argument: %s\n", tech.c_str());
|
||||
|
||||
dlogic = {{
|
||||
ID(SB_CARRY),
|
||||
dict<int, IdString>{
|
||||
std::make_pair(1, ID(I0)),
|
||||
std::make_pair(2, ID(I1)),
|
||||
std::make_pair(3, ID(CI))
|
||||
}
|
||||
}, {
|
||||
ID(SB_CARRY),
|
||||
dict<int, IdString>{
|
||||
std::make_pair(3, ID(CO))
|
||||
}
|
||||
}};
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-limit" && argidx + 1 < args.size())
|
||||
{
|
||||
if (args[argidx] == "-limit" && argidx + 1 < args.size()) {
|
||||
limit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <cinttypes>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
|
@ -623,7 +624,7 @@ struct RecoverNamesWorker {
|
|||
if (pop == 1 || pop == (8*sizeof(equiv_cls_t) - 1))
|
||||
continue;
|
||||
|
||||
log_debug("equivalence class: %016lx\n", cls.first);
|
||||
log_debug("equivalence class: %016" PRIx64 "\n", cls.first);
|
||||
const pool<IdBit> &gold_bits = cls2bits.at(cls.first).first;
|
||||
const pool<InvBit> &gate_bits = cls2bits.at(cls.first).second;
|
||||
if (gold_bits.empty() || gate_bits.empty())
|
||||
|
|
|
@ -88,6 +88,17 @@ struct TriggeredAssertion {
|
|||
{ }
|
||||
};
|
||||
|
||||
struct DisplayOutput {
|
||||
int step;
|
||||
SimInstance *instance;
|
||||
Cell *cell;
|
||||
std::string output;
|
||||
|
||||
DisplayOutput(int step, SimInstance *instance, Cell *cell, std::string output) :
|
||||
step(step), instance(instance), cell(cell), output(output)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct SimShared
|
||||
{
|
||||
bool debug = false;
|
||||
|
@ -110,6 +121,7 @@ struct SimShared
|
|||
int next_output_id = 0;
|
||||
int step = 0;
|
||||
std::vector<TriggeredAssertion> triggered_assertions;
|
||||
std::vector<DisplayOutput> display_output;
|
||||
bool serious_asserts = false;
|
||||
bool initstate = true;
|
||||
};
|
||||
|
@ -173,6 +185,7 @@ struct SimInstance
|
|||
|
||||
struct print_state_t
|
||||
{
|
||||
bool initial_done;
|
||||
Const past_trg;
|
||||
Const past_en;
|
||||
Const past_args;
|
||||
|
@ -338,6 +351,7 @@ struct SimInstance
|
|||
print.past_trg = Const(State::Sx, cell->getPort(ID::TRG).size());
|
||||
print.past_args = Const(State::Sx, cell->getPort(ID::ARGS).size());
|
||||
print.past_en = State::Sx;
|
||||
print.initial_done = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -799,7 +813,7 @@ struct SimInstance
|
|||
}
|
||||
}
|
||||
|
||||
void update_ph3(bool check_assertions)
|
||||
void update_ph3(bool gclk_trigger)
|
||||
{
|
||||
for (auto &it : ff_database)
|
||||
{
|
||||
|
@ -840,45 +854,57 @@ struct SimInstance
|
|||
bool triggered = false;
|
||||
|
||||
Const trg = get_state(cell->getPort(ID::TRG));
|
||||
bool trg_en = cell->getParam(ID::TRG_ENABLE).as_bool();
|
||||
Const en = get_state(cell->getPort(ID::EN));
|
||||
Const args = get_state(cell->getPort(ID::ARGS));
|
||||
|
||||
if (!en.as_bool())
|
||||
goto update_print;
|
||||
bool sampled = trg_en && trg.size() > 0;
|
||||
|
||||
if (cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
|
||||
for (int i = 0; i < trg.size(); i++) {
|
||||
bool pol = trg_pol[i] == State::S1;
|
||||
State curr = trg[i], past = print.past_trg[i];
|
||||
if (pol && curr == State::S1 && past == State::S0)
|
||||
if (sampled ? print.past_en.as_bool() : en.as_bool()) {
|
||||
if (sampled) {
|
||||
sampled = true;
|
||||
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
|
||||
for (int i = 0; i < trg.size(); i++) {
|
||||
bool pol = trg_pol[i] == State::S1;
|
||||
State curr = trg[i], past = print.past_trg[i];
|
||||
if (pol && curr == State::S1 && past == State::S0)
|
||||
triggered = true;
|
||||
if (!pol && curr == State::S0 && past == State::S1)
|
||||
triggered = true;
|
||||
}
|
||||
} else if (trg_en) {
|
||||
// initial $print (TRG width = 0, TRG_ENABLE = true)
|
||||
if (!print.initial_done && en != print.past_en)
|
||||
triggered = true;
|
||||
if (!pol && curr == State::S0 && past == State::S1)
|
||||
} else if (cell->get_bool_attribute(ID(trg_on_gclk))) {
|
||||
// unified $print for cycle based FV semantics
|
||||
triggered = gclk_trigger;
|
||||
} else {
|
||||
// always @(*) $print
|
||||
if (args != print.past_args || en != print.past_en)
|
||||
triggered = true;
|
||||
}
|
||||
} else {
|
||||
if (args != print.past_args || en != print.past_en)
|
||||
triggered = true;
|
||||
}
|
||||
|
||||
if (triggered) {
|
||||
int pos = 0;
|
||||
for (auto &part : print.fmt.parts) {
|
||||
part.sig = args.extract(pos, part.sig.size());
|
||||
pos += part.sig.size();
|
||||
if (triggered) {
|
||||
int pos = 0;
|
||||
for (auto &part : print.fmt.parts) {
|
||||
part.sig = (sampled ? print.past_args : args).extract(pos, part.sig.size());
|
||||
pos += part.sig.size();
|
||||
}
|
||||
|
||||
std::string rendered = print.fmt.render();
|
||||
log("%s", rendered.c_str());
|
||||
shared->display_output.emplace_back(shared->step, this, cell, rendered);
|
||||
}
|
||||
|
||||
std::string rendered = print.fmt.render();
|
||||
log("%s", rendered.c_str());
|
||||
}
|
||||
|
||||
update_print:
|
||||
print.past_trg = trg;
|
||||
print.past_en = en;
|
||||
print.past_args = args;
|
||||
print.initial_done = true;
|
||||
}
|
||||
|
||||
if (check_assertions)
|
||||
if (gclk_trigger)
|
||||
{
|
||||
for (auto cell : formal_database)
|
||||
{
|
||||
|
@ -910,7 +936,7 @@ struct SimInstance
|
|||
}
|
||||
|
||||
for (auto it : children)
|
||||
it.second->update_ph3(check_assertions);
|
||||
it.second->update_ph3(gclk_trigger);
|
||||
}
|
||||
|
||||
void set_initstate_outputs(State state)
|
||||
|
@ -2055,6 +2081,20 @@ struct SimWorker : SimShared
|
|||
json.end_object();
|
||||
}
|
||||
json.end_array();
|
||||
json.name("display_output");
|
||||
json.begin_array();
|
||||
for (auto &output : display_output) {
|
||||
json.begin_object();
|
||||
json.entry("step", output.step);
|
||||
json.entry("path", output.instance->witness_full_path(output.cell));
|
||||
auto src = output.cell->get_string_attribute(ID::src);
|
||||
if (!src.empty()) {
|
||||
json.entry("src", src);
|
||||
}
|
||||
json.entry("output", output.output);
|
||||
json.end_object();
|
||||
}
|
||||
json.end_array();
|
||||
json.end_object();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
ram block $__GOWIN_SP_ {
|
||||
abits 14;
|
||||
widths 1 2 4 9 18 36 per_port;
|
||||
byte 9;
|
||||
cost 128;
|
||||
init no_undef;
|
||||
port srsw "A" {
|
||||
clock posedge;
|
||||
clken;
|
||||
wrbe_separate;
|
||||
option "RESET_MODE" "SYNC" {
|
||||
rdsrst zero ungated;
|
||||
}
|
||||
|
@ -30,13 +28,11 @@ ram block $__GOWIN_SP_ {
|
|||
ram block $__GOWIN_DP_ {
|
||||
abits 14;
|
||||
widths 1 2 4 9 18 per_port;
|
||||
byte 9;
|
||||
cost 128;
|
||||
init no_undef;
|
||||
port srsw "A" "B" {
|
||||
clock posedge;
|
||||
clken;
|
||||
wrbe_separate;
|
||||
option "RESET_MODE" "SYNC" {
|
||||
rdsrst zero ungated;
|
||||
}
|
||||
|
@ -59,7 +55,6 @@ ram block $__GOWIN_DP_ {
|
|||
ram block $__GOWIN_SDP_ {
|
||||
abits 14;
|
||||
widths 1 2 4 9 18 36 per_port;
|
||||
byte 9;
|
||||
cost 128;
|
||||
init no_undef;
|
||||
port sr "R" {
|
||||
|
@ -76,6 +71,5 @@ ram block $__GOWIN_SDP_ {
|
|||
port sw "W" {
|
||||
clock posedge;
|
||||
clken;
|
||||
wrbe_separate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
`define x8_width(width) (width / 9 * 8 + width % 9)
|
||||
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
|
||||
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
|
||||
`define wre(width, wr_en, wr_be) (width < 18 ? wr_en | wr_be[0] : wr_en)
|
||||
`define addrbe(width, addr, wr_be) (width < 18 ? addr : {addr[13:4], wr_be})
|
||||
`define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111})
|
||||
|
||||
|
||||
`define INIT(func) \
|
||||
|
@ -90,7 +89,6 @@ parameter INIT = 0;
|
|||
parameter OPTION_RESET_MODE = "SYNC";
|
||||
|
||||
parameter PORT_A_WIDTH = 36;
|
||||
parameter PORT_A_WR_BE_WIDTH = 4;
|
||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||
|
||||
input PORT_A_CLK;
|
||||
|
@ -99,15 +97,13 @@ input PORT_A_WR_EN;
|
|||
input PORT_A_RD_SRST;
|
||||
input PORT_A_RD_ARST;
|
||||
input [13:0] PORT_A_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
`DEF_FUNCS
|
||||
|
||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||
wire WRE = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE);
|
||||
wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
|
||||
wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||
|
||||
generate
|
||||
|
||||
|
@ -129,9 +125,9 @@ if (PORT_A_WIDTH < 9) begin
|
|||
.BLKSEL(3'b000),
|
||||
.CLK(PORT_A_CLK),
|
||||
.CE(PORT_A_CLK_EN),
|
||||
.WRE(WRE),
|
||||
.WRE(PORT_A_WR_EN),
|
||||
.RESET(RST),
|
||||
.OCE(1'b0),
|
||||
.OCE(1'b1),
|
||||
.AD(AD),
|
||||
.DI(DI),
|
||||
.DO(DO),
|
||||
|
@ -155,9 +151,9 @@ end else begin
|
|||
.BLKSEL(3'b000),
|
||||
.CLK(PORT_A_CLK),
|
||||
.CE(PORT_A_CLK_EN),
|
||||
.WRE(WRE),
|
||||
.WRE(PORT_A_WR_EN),
|
||||
.RESET(RST),
|
||||
.OCE(1'b0),
|
||||
.OCE(1'b1),
|
||||
.AD(AD),
|
||||
.DI(DI),
|
||||
.DO(DO),
|
||||
|
@ -176,11 +172,9 @@ parameter INIT = 0;
|
|||
parameter OPTION_RESET_MODE = "SYNC";
|
||||
|
||||
parameter PORT_A_WIDTH = 18;
|
||||
parameter PORT_A_WR_BE_WIDTH = 2;
|
||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||
|
||||
parameter PORT_B_WIDTH = 18;
|
||||
parameter PORT_B_WR_BE_WIDTH = 2;
|
||||
parameter PORT_B_OPTION_WRITE_MODE = 0;
|
||||
|
||||
input PORT_A_CLK;
|
||||
|
@ -189,7 +183,6 @@ input PORT_A_WR_EN;
|
|||
input PORT_A_RD_SRST;
|
||||
input PORT_A_RD_ARST;
|
||||
input [13:0] PORT_A_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
|
@ -199,7 +192,6 @@ input PORT_B_WR_EN;
|
|||
input PORT_B_RD_SRST;
|
||||
input PORT_B_RD_ARST;
|
||||
input [13:0] PORT_B_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
||||
|
||||
|
@ -207,10 +199,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
|||
|
||||
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
|
||||
wire WREA = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE);
|
||||
wire WREB = `wre(PORT_B_WIDTH, PORT_B_WR_EN, PORT_B_WR_BE);
|
||||
wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
|
||||
wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE);
|
||||
wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||
wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR);
|
||||
|
||||
generate
|
||||
|
||||
|
@ -224,7 +214,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
|
|||
assign PORT_A_RD_DATA = `x8_rd_data(DOA);
|
||||
assign PORT_B_RD_DATA = `x8_rd_data(DOB);
|
||||
|
||||
DP #(
|
||||
DPB #(
|
||||
`INIT(init_slice_x8)
|
||||
.READ_MODE0(1'b0),
|
||||
.READ_MODE1(1'b0),
|
||||
|
@ -232,25 +222,27 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
|
|||
.WRITE_MODE1(PORT_B_OPTION_WRITE_MODE),
|
||||
.BIT_WIDTH_0(`x8_width(PORT_A_WIDTH)),
|
||||
.BIT_WIDTH_1(`x8_width(PORT_B_WIDTH)),
|
||||
.BLK_SEL(3'b000),
|
||||
.BLK_SEL_0(3'b000),
|
||||
.BLK_SEL_1(3'b000),
|
||||
.RESET_MODE(OPTION_RESET_MODE),
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.BLKSEL(3'b000),
|
||||
.BLKSELA(3'b000),
|
||||
.BLKSELB(3'b000),
|
||||
|
||||
.CLKA(PORT_A_CLK),
|
||||
.CEA(PORT_A_CLK_EN),
|
||||
.WREA(WREA),
|
||||
.WREA(PORT_A_WR_EN),
|
||||
.RESETA(RSTA),
|
||||
.OCEA(1'b0),
|
||||
.OCEA(1'b1),
|
||||
.ADA(ADA),
|
||||
.DIA(DIA),
|
||||
.DOA(DOA),
|
||||
|
||||
.CLKB(PORT_B_CLK),
|
||||
.CEB(PORT_B_CLK_EN),
|
||||
.WREB(WREB),
|
||||
.WREB(PORT_B_WR_EN),
|
||||
.RESETB(RSTB),
|
||||
.OCEB(1'b0),
|
||||
.OCEB(1'b1),
|
||||
.ADB(ADB),
|
||||
.DIB(DIB),
|
||||
.DOB(DOB),
|
||||
|
@ -266,7 +258,7 @@ end else begin
|
|||
assign PORT_A_RD_DATA = DOA;
|
||||
assign PORT_B_RD_DATA = DOB;
|
||||
|
||||
DPX9 #(
|
||||
DPX9B #(
|
||||
`INIT(init_slice_x9)
|
||||
.READ_MODE0(1'b0),
|
||||
.READ_MODE1(1'b0),
|
||||
|
@ -274,25 +266,27 @@ end else begin
|
|||
.WRITE_MODE1(PORT_B_OPTION_WRITE_MODE),
|
||||
.BIT_WIDTH_0(PORT_A_WIDTH),
|
||||
.BIT_WIDTH_1(PORT_B_WIDTH),
|
||||
.BLK_SEL(3'b000),
|
||||
.BLK_SEL_0(3'b000),
|
||||
.BLK_SEL_1(3'b000),
|
||||
.RESET_MODE(OPTION_RESET_MODE),
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.BLKSEL(3'b000),
|
||||
.BLKSELA(3'b000),
|
||||
.BLKSELB(3'b000),
|
||||
|
||||
.CLKA(PORT_A_CLK),
|
||||
.CEA(PORT_A_CLK_EN),
|
||||
.WREA(WREA),
|
||||
.WREA(PORT_A_WR_EN),
|
||||
.RESETA(RSTA),
|
||||
.OCEA(1'b0),
|
||||
.OCEA(1'b1),
|
||||
.ADA(ADA),
|
||||
.DIA(DIA),
|
||||
.DOA(DOA),
|
||||
|
||||
.CLKB(PORT_B_CLK),
|
||||
.CEB(PORT_B_CLK_EN),
|
||||
.WREB(WREB),
|
||||
.WREB(PORT_B_WR_EN),
|
||||
.RESETB(RSTB),
|
||||
.OCEB(1'b0),
|
||||
.OCEB(1'b1),
|
||||
.ADB(ADB),
|
||||
.DIB(DIB),
|
||||
.DOB(DOB),
|
||||
|
@ -311,9 +305,7 @@ parameter INIT = 0;
|
|||
parameter OPTION_RESET_MODE = "SYNC";
|
||||
|
||||
parameter PORT_R_WIDTH = 18;
|
||||
|
||||
parameter PORT_W_WIDTH = 18;
|
||||
parameter PORT_W_WR_BE_WIDTH = 2;
|
||||
|
||||
input PORT_R_CLK;
|
||||
input PORT_R_CLK_EN;
|
||||
|
@ -326,14 +318,13 @@ input PORT_W_CLK;
|
|||
input PORT_W_CLK_EN;
|
||||
input PORT_W_WR_EN;
|
||||
input [13:0] PORT_W_ADDR;
|
||||
input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE;
|
||||
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
|
||||
|
||||
`DEF_FUNCS
|
||||
|
||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
|
||||
wire WRE = `wre(PORT_W_WIDTH, PORT_W_WR_EN, PORT_W_WR_BE);
|
||||
wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE);
|
||||
wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR);
|
||||
wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN;
|
||||
|
||||
generate
|
||||
|
||||
|
@ -344,28 +335,28 @@ if (PORT_W_WIDTH < 9 || PORT_R_WIDTH < 9) begin
|
|||
|
||||
assign PORT_R_RD_DATA = `x8_rd_data(DO);
|
||||
|
||||
SDP #(
|
||||
SDPB #(
|
||||
`INIT(init_slice_x8)
|
||||
.READ_MODE(1'b0),
|
||||
.BIT_WIDTH_0(`x8_width(PORT_W_WIDTH)),
|
||||
.BIT_WIDTH_1(`x8_width(PORT_R_WIDTH)),
|
||||
.BLK_SEL(3'b000),
|
||||
.BLK_SEL_0(3'b000),
|
||||
.BLK_SEL_1(3'b000),
|
||||
.RESET_MODE(OPTION_RESET_MODE),
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.BLKSEL(3'b000),
|
||||
.BLKSELA(3'b000),
|
||||
.BLKSELB(3'b000),
|
||||
|
||||
.CLKA(PORT_W_CLK),
|
||||
.CEA(PORT_W_CLK_EN),
|
||||
.WREA(WRE),
|
||||
.CEA(WRE),
|
||||
.RESETA(1'b0),
|
||||
.ADA(ADW),
|
||||
.DI(DI),
|
||||
|
||||
.CLKB(PORT_R_CLK),
|
||||
.CEB(PORT_R_CLK_EN),
|
||||
.WREB(1'b0),
|
||||
.RESETB(RST),
|
||||
.OCE(1'b0),
|
||||
.OCE(1'b1),
|
||||
.ADB(PORT_R_ADDR),
|
||||
.DO(DO),
|
||||
);
|
||||
|
@ -377,28 +368,28 @@ end else begin
|
|||
|
||||
assign PORT_R_RD_DATA = DO;
|
||||
|
||||
SDPX9 #(
|
||||
SDPX9B #(
|
||||
`INIT(init_slice_x9)
|
||||
.READ_MODE(1'b0),
|
||||
.BIT_WIDTH_0(PORT_W_WIDTH),
|
||||
.BIT_WIDTH_1(PORT_R_WIDTH),
|
||||
.BLK_SEL(3'b000),
|
||||
.BLK_SEL_0(3'b000),
|
||||
.BLK_SEL_1(3'b000),
|
||||
.RESET_MODE(OPTION_RESET_MODE),
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.BLKSEL(3'b000),
|
||||
.BLKSELA(3'b000),
|
||||
.BLKSELB(3'b000),
|
||||
|
||||
.CLKA(PORT_W_CLK),
|
||||
.CEA(PORT_W_CLK_EN),
|
||||
.WREA(WRE),
|
||||
.CEA(WRE),
|
||||
.RESETA(1'b0),
|
||||
.ADA(ADW),
|
||||
.DI(DI),
|
||||
|
||||
.CLKB(PORT_R_CLK),
|
||||
.CEB(PORT_R_CLK_EN),
|
||||
.WREB(1'b0),
|
||||
.RESETB(RST),
|
||||
.OCE(1'b0),
|
||||
.OCE(1'b1),
|
||||
.ADB(PORT_R_ADDR),
|
||||
.DO(DO),
|
||||
);
|
||||
|
|
|
@ -432,7 +432,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
run("ice40_wrapcarry -unwrap");
|
||||
run("techmap -map +/ice40/ff_map.v");
|
||||
run("clean");
|
||||
run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3 -dlogic SB_CARRY:CO=3");
|
||||
run("opt_lut -tech ice40");
|
||||
}
|
||||
|
||||
if (check_label("map_cells"))
|
||||
|
|
|
@ -2,8 +2,13 @@
|
|||
|
||||
int main()
|
||||
{
|
||||
struct : public performer {
|
||||
int64_t vlog_time() const override { return 1; }
|
||||
void on_print(const lazy_fmt &output, const cxxrtl::metadata_map &) override { std::cerr << output(); }
|
||||
} performer;
|
||||
|
||||
cxxrtl_design::p_always__full uut;
|
||||
uut.p_clk.set(!uut.p_clk);
|
||||
uut.step();
|
||||
uut.step(&performer);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
|||
read_verilog <<EOF
|
||||
module top;
|
||||
wire a;
|
||||
wire b;
|
||||
assign a = b;
|
||||
endmodule
|
||||
EOF
|
||||
delete w:a
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue