Compare commits

...

32 Commits

Author SHA1 Message Date
Emil J 133938bb43
Merge 26e293d71f into 5d0847f6fb 2025-11-07 13:31:06 +01:00
github-actions[bot] 5d0847f6fb Bump version 2025-11-07 00:24:35 +00:00
KrystalDelusion 24b69cabaa
Merge pull request #5422 from YosysHQ/krys/SVI_support
Catch partial support of SVI
2025-11-07 11:16:07 +13:00
Miodrag Milanović 691d6b8508
Merge pull request #5469 from YosysHQ/update_abc
Update ABC
2025-11-06 21:19:39 +01:00
Emil J a16fc9b4f3
Merge pull request #5467 from YosysHQ/emil/liberty-unquoted-expressions
libparse: support unquoted expressions
2025-11-06 19:45:17 +01:00
Emil J 3a23d4458e
Merge pull request #5470 from YosysHQ/emil/unit-test-makefile
Makefile: clean unit test on clean, ensure prepared to fix parallelism
2025-11-06 19:05:57 +01:00
Miodrag Milanovic dc9a787025 Fix out of tree clean 2025-11-06 14:28:28 +01:00
Emil J. Tywoniak 2bf7aac9d1 Makefile: clean unit test on clean, ensure prepared to fix parallelism 2025-11-06 13:59:14 +01:00
Emil J. Tywoniak fdcc4c1507 libparse: remove leftover comments 2025-11-06 13:30:09 +01:00
Miodrag Milanovic 75ce33c7b2 Update ABC 2025-11-06 09:54:47 +01:00
Emil J. Tywoniak 90553267b0 libparse: fix quoting and negedge in filterlib -verilogsim 2025-11-05 14:13:58 +01:00
Emil J. Tywoniak 504b668ea6 libparse: fix verilogsim negedge 2025-11-05 13:49:05 +01:00
Emil J. Tywoniak b0a3d6a3e7 libparse: fix up tests since liberty expression parsing now normalizes the form of these expressions 2025-11-05 13:06:12 +01:00
Emil J. Tywoniak bf29f6dc11 libparse: tolerate closing quotes in expression parsing 2025-11-05 13:06:09 +01:00
Emil J. Tywoniak 4fac7a1b20 libparse: fix space before closing paren in expressions 2025-11-05 13:05:56 +01:00
Emil J. Tywoniak 547e254a9b libparse: parse expressions in filterlib 2025-11-05 13:05:56 +01:00
Emil J. Tywoniak 66d8fc5c28 libparse: quirk-compatibility for unquoted boolean expression strings 2025-11-05 11:00:25 +01:00
Emil J. Tywoniak 26e293d71f proc_mux: default to case src when action src is missing 2025-11-02 12:42:04 +01:00
Emil J. Tywoniak 0c8e008ce7 proc_mux: add src test 2025-11-02 12:42:04 +01:00
Emil J. Tywoniak f9c528e981 docs: word_mux grammar 2025-11-02 12:42:04 +01:00
Emil J. Tywoniak 2db4208ca5 proc_mux: refactor 2025-11-02 12:42:04 +01:00
Emil J. Tywoniak d762c5f5e8 proc_mux: emit fused action location src attributes on procmuxes 2025-11-02 11:26:45 +01:00
Emil J. Tywoniak 304757c881 rtlil: add source tracking to CaseRule actions 2025-11-02 11:25:42 +01:00
Emil J. Tywoniak c45a035ebf gowin: lower LUT count sensitivity 2025-11-02 11:22:48 +01:00
Emil J. Tywoniak b5e5554553 verilog: fix case location 2025-11-02 11:22:33 +01:00
Emil J. Tywoniak 1eb696c786 rtlil: replace SigSig actions with new type SyncAction 2025-11-02 11:22:03 +01:00
Krystine Sherwin c599d6a67e
tests/svinterfaces: re-chmod test script 2025-10-15 09:49:53 +13:00
Krystine Sherwin 10a55119a9
hierarchy.cc: Tidying 2025-10-15 09:42:47 +13:00
Krystine Sherwin 5d2d544109
hierarchy.cc: Don't segfault 2025-10-15 09:38:43 +13:00
Krystine Sherwin 37ba29482e
docs: Amend modports support to all SVI
Also some formatting fixes.
2025-10-15 09:17:52 +13:00
Krystine Sherwin 7bb0a1913e
hierarchy.cc: Raise error on positional interface
Add test to check that it does error.
2025-10-15 09:10:33 +13:00
Krystine Sherwin bbceaa6b5e
docs: Note partial support of modports 2025-10-14 14:59:32 +13:00
43 changed files with 982 additions and 444 deletions

View File

@ -95,14 +95,14 @@ TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config
PRETTY = 1
SMALL = 0
# Unit test
UNITESTPATH := tests/unit
all: top-all
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
VPATH := $(YOSYS_SRC)
# Unit test
UNITESTPATH := $(YOSYS_SRC)/tests/unit
export CXXSTD ?= c++17
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
LIBS := $(LIBS) -lstdc++ -lm
@ -161,7 +161,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.58+138
YOSYS_VER := 0.58+162
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
@ -1135,7 +1135,7 @@ DOC_TARGET ?= html
docs: docs/prep
$(Q) $(MAKE) -C docs $(DOC_TARGET)
clean: clean-py
clean: clean-py clean-unit-test
rm -rf share
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
rm -f kernel/version_*.o kernel/version_*.cc
@ -1150,7 +1150,7 @@ clean: clean-py
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
rm -f tests/tools/cmp_tbdata
rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS))
-$(MAKE) -C docs clean
-$(MAKE) -C $(YOSYS_SRC)/docs clean
rm -rf docs/util/__pycache__
rm -f libyosys.so
@ -1162,7 +1162,7 @@ clean-py:
rm -rf kernel/*.pyh
clean-abc:
$(MAKE) -C abc DEP= clean
$(MAKE) -C $(YOSYS_SRC)/abc DEP= clean
rm -f $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a .git-abc-submodule-hash
mrproper: clean

2
abc

@ -1 +1 @@
Subproject commit fa186342baefea06e7c2aa13fe51f338ffc84912
Subproject commit 1c5ed1ce378cc04beac30bb31abc4c37c8467042

View File

@ -494,8 +494,8 @@ struct FlowGraph {
void add_case_rule_defs_uses(Node *node, const RTLIL::CaseRule *case_)
{
for (auto &action : case_->actions) {
add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false);
add_uses(node, action.second);
add_defs(node, action.lhs, /*is_ff=*/false, /*inlinable=*/false);
add_uses(node, action.rhs);
}
for (auto sub_switch : case_->switches) {
add_uses(node, sub_switch->signal);
@ -512,10 +512,10 @@ struct FlowGraph {
for (auto sync : process->syncs) {
for (auto &action : sync->actions) {
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe)
add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false);
add_defs(node, action.lhs, /*is_ff=*/true, /*inlinable=*/false);
else
add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false);
add_uses(node, action.second);
add_defs(node, action.lhs, /*is_ff=*/false, /*inlinable=*/false);
add_uses(node, action.rhs);
}
for (auto &memwr : sync->mem_write_actions) {
add_uses(node, memwr.address);
@ -1609,12 +1609,12 @@ struct CxxrtlWorker {
collect_sigspec_rhs(port.second, for_debug, cells);
}
void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false)
void dump_assign(const RTLIL::SyncAction &action, bool for_debug = false)
{
f << indent;
dump_sigspec_lhs(sigsig.first, for_debug);
dump_sigspec_lhs(action.lhs, for_debug);
f << " = ";
dump_sigspec_rhs(sigsig.second, for_debug);
dump_sigspec_rhs(action.rhs, for_debug);
f << ";\n";
}

View File

@ -189,7 +189,7 @@ void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::
void RTLIL_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs)
{
for (const auto& [lhs, rhs] : cs->actions) {
for (const auto& [lhs, rhs, _] : cs->actions) {
f << stringf("%s" "assign ", indent);
dump_sigspec(f, lhs);
f << stringf(" ");
@ -243,7 +243,7 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
case RTLIL::STi: f << stringf("init\n"); break;
}
for (const auto& [lhs, rhs] : sy->actions) {
for (const auto& [lhs, rhs, _] : sy->actions) {
f << stringf("%s update ", indent);
dump_sigspec(f, lhs);
f << stringf(" ");
@ -375,8 +375,11 @@ void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl
for (auto* module : design->modules()) {
if (design->selected_whole_module(module->name))
flag_m = true;
if (design->selected(module))
if (design->selected(module)) {
count_selected_mods++;
if (module->has_processes())
log_warning("Module %s contains processes. Case action sources attributes will be lost.\n", log_id(module));
}
}
if (count_selected_mods > 1)
flag_m = true;

View File

@ -2122,13 +2122,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
if (it->first.size() == 0)
if (it->lhs.size() == 0)
continue;
f << stringf("%s ", indent);
dump_sigspec(f, it->first);
dump_sigspec(f, it->lhs);
f << stringf(" = ");
dump_sigspec(f, it->second);
dump_sigspec(f, it->rhs);
f << stringf(";\n");
// TODO
}
}
@ -2259,7 +2260,7 @@ void case_body_find_regs(RTLIL::CaseRule *cs)
case_body_find_regs(*it2);
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
for (auto &c : it->first.chunks())
for (auto &c : it->lhs.chunks())
if (c.wire != NULL)
reg_wires.insert(c.wire->name);
}
@ -2271,7 +2272,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
case_body_find_regs(&proc->root_case);
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
for (auto &c : it2->first.chunks())
for (auto &c : it2->lhs.chunks())
if (c.wire != NULL)
reg_wires.insert(c.wire->name);
}
@ -2328,12 +2329,12 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
}
for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) {
if (it->first.size() == 0)
if (it->lhs.size() == 0)
continue;
f << stringf("%s ", indent);
dump_sigspec(f, it->first);
dump_sigspec(f, it->lhs);
f << stringf(" <= ");
dump_sigspec(f, it->second);
dump_sigspec(f, it->rhs);
f << stringf(";\n");
}

View File

@ -16,7 +16,7 @@ value from the ``B`` input is sent to the output. So the `$mux` cell implements
the function :verilog:`Y = S ? B : A`.
The `$pmux` cell is used to multiplex between many inputs using a one-hot select
signal. Cells of this type have a ``WIDTH`` and a ``S_WIDTH`` parameter and
signal. Cells of this type have a ``WIDTH`` and an ``S_WIDTH`` parameter and
inputs ``A``, ``B``, and ``S`` and an output ``Y``. The ``S`` input is
``S_WIDTH`` bits wide. The ``A`` input and the output are both ``WIDTH`` bits
wide and the ``B`` input is ``WIDTH*S_WIDTH`` bits wide. When all bits of ``S``

View File

@ -9,7 +9,7 @@ Yosys and there are currently no plans to add support
for them:
- Non-synthesizable language features as defined in
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
- The ``tri``, ``triand`` and ``trior`` net types
@ -356,21 +356,29 @@ from SystemVerilog:
files being read into the same design afterwards.
- typedefs are supported (including inside packages)
- type casts are currently not supported
- type casts are currently not supported
- enums are supported (including inside packages)
- but are currently not strongly typed
- but are currently not strongly typed
- packed structs and unions are supported
- arrays of packed structs/unions are currently not supported
- structure literals are currently not supported
- arrays of packed structs/unions are currently not supported
- structure literals are currently not supported
- multidimensional arrays are supported
- array assignment of unpacked arrays is currently not supported
- array literals are currently not supported
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
ports are inputs or outputs are supported.
- array assignment of unpacked arrays is currently not supported
- array literals are currently not supported
- SystemVerilog interfaces (SVIs), including modports for specifying whether
ports are inputs or outputs, are partially supported.
- interfaces must be provided as *named* arguments, not positional arguments.
i.e. ``foo bar(.intf(intf0), .x(x));`` is supported but ``foo bar(intf0,
x);`` is not.
- Assignments within expressions are supported.

View File

@ -382,7 +382,7 @@ struct AST_INTERNAL::ProcessGenerator
if (found_anyedge_syncs) {
if (found_global_syncs)
always->input_error("Found non-synthesizable event list!\n");
log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string());
log("Note: Assuming pure combinatorial block at %s in\n", always->location.to_string());
log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");
log("use of @* instead of @(...) for better match of synthesis and simulation.\n");
}
@ -402,14 +402,14 @@ struct AST_INTERNAL::ProcessGenerator
syncrule->signal = child->children[0]->genRTLIL();
if (GetSize(syncrule->signal) != 1)
always->input_error("Found posedge/negedge event on a signal that is not 1 bit wide!\n");
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, child.get(), true);
proc->syncs.push_back(syncrule);
}
if (proc->syncs.empty()) {
RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
syncrule->type = found_global_syncs ? RTLIL::STg : RTLIL::STa;
syncrule->signal = RTLIL::SigSpec();
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, always.get(), true);
proc->syncs.push_back(syncrule);
}
@ -417,7 +417,7 @@ struct AST_INTERNAL::ProcessGenerator
if ((flag_nolatches || always->get_bool_attribute(ID::nolatches) || current_module->get_bool_attribute(ID::nolatches)) && !found_clocked_sync) {
subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from)));
} else {
addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from, always.get());
}
// process the AST
@ -441,7 +441,8 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::SigSpec lhs = init_lvalue_c;
RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width);
remove_unwanted_lvalue_bits(lhs, rhs);
sync->actions.push_back(RTLIL::SigSig(lhs, rhs));
// TODO
sync->actions.push_back({lhs, rhs, Const("")});
offset += lhs.size();
}
}
@ -548,7 +549,7 @@ struct AST_INTERNAL::ProcessGenerator
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
it->first.remove2(pattern, &it->second);
it->lhs.remove2(pattern, &it->rhs);
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
@ -557,7 +558,7 @@ struct AST_INTERNAL::ProcessGenerator
// add an assignment (aka "action") but split it up in chunks. this way huge assignments
// are avoided and the generated $mux cells have a more "natural" size.
void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false)
void addChunkActions(std::vector<RTLIL::SyncAction> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, AstNode* ast, bool inSyncRule = false)
{
if (inSyncRule && initSyncSignals.size() > 0) {
init_lvalue.append(lvalue.extract(initSyncSignals));
@ -573,7 +574,7 @@ struct AST_INTERNAL::ProcessGenerator
if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute(ID::nosync))
rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size());
remove_unwanted_lvalue_bits(lhs, rhs);
actions.push_back(RTLIL::SigSig(lhs, rhs));
actions.push_back({lhs, rhs, ast ? ast->loc_string() : ""});
offset += lhs.size();
}
}
@ -613,7 +614,7 @@ struct AST_INTERNAL::ProcessGenerator
removeSignalFromCaseTree(lvalue, current_case);
remove_unwanted_lvalue_bits(lvalue, rvalue);
current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
current_case->actions.push_back({lvalue, rvalue, ast->loc_string()});
}
break;
@ -657,10 +658,11 @@ struct AST_INTERNAL::ProcessGenerator
subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
RTLIL::CaseRule *backup_case = current_case;
// here
current_case = new RTLIL::CaseRule;
set_src_attr(current_case, child.get());
last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue, child.get());
for (auto& node : child->children) {
if (node->type == AST_DEFAULT)
default_case = current_case;
@ -687,13 +689,13 @@ struct AST_INTERNAL::ProcessGenerator
last_generated_case->compare.clear();
#else
default_case = new RTLIL::CaseRule;
addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)));
addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)), ast);
sw->cases.push_back(default_case);
#endif
} else {
if (default_case == nullptr) {
default_case = new RTLIL::CaseRule;
addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue, ast);
}
sw->cases.push_back(default_case);
}
@ -703,7 +705,7 @@ struct AST_INTERNAL::ProcessGenerator
this_case_eq_lvalue.replace(subst_lvalue_map.stdmap());
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp, ast);
}
break;
@ -728,8 +730,8 @@ struct AST_INTERNAL::ProcessGenerator
Wire *en = current_module->addWire(sstr.str() + "_EN", 1);
set_src_attr(en, ast);
proc->root_case.actions.push_back(SigSig(en, false));
current_case->actions.push_back(SigSig(en, true));
proc->root_case.actions.push_back({en, SigSpec(false), ast->loc_string()});
current_case->actions.push_back({en, SigSpec(true), ast->loc_string()});
RTLIL::SigSpec triggers;
RTLIL::Const::Builder polarity_builder;
@ -826,8 +828,8 @@ struct AST_INTERNAL::ProcessGenerator
Wire *en = current_module->addWire(cellname.str() + "_EN", 1);
set_src_attr(en, ast);
proc->root_case.actions.push_back(SigSig(en, false));
current_case->actions.push_back(SigSig(en, true));
proc->root_case.actions.push_back({en, SigSpec(false), ast->loc_string()});
current_case->actions.push_back({en, SigSpec(true), ast->loc_string()});
RTLIL::SigSpec triggers;
RTLIL::Const::Builder polarity_builder;

View File

@ -23,6 +23,7 @@
#include "kernel/register.h"
#include "kernel/log.h"
#include "kernel/rtlil.h"
#include "kernel/utils.h"
#include <charconv>
#include <deque>
@ -623,7 +624,7 @@ struct RTLILFrontendWorker {
"The assign statement is reordered to come before all switch statements.");
RTLIL::SigSpec s1 = parse_sigspec();
RTLIL::SigSpec s2 = parse_sigspec();
current_case->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2)));
current_case->actions.push_back({std::move(s1), std::move(s2), Const("")});
expect_eol();
} else
return;
@ -714,7 +715,7 @@ struct RTLILFrontendWorker {
if (try_parse_keyword("update")) {
RTLIL::SigSpec s1 = parse_sigspec();
RTLIL::SigSpec s2 = parse_sigspec();
rule->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2)));
rule->actions.push_back({std::move(s1), std::move(s2), Const("")});
expect_eol();
continue;
}

View File

@ -3026,6 +3026,7 @@ case_item:
extra->case_type_stack.pop_back();
SET_AST_NODE_LOC(extra->ast_stack.back(), @4, @4);
extra->ast_stack.pop_back();
SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @2);
extra->ast_stack.pop_back();
};

View File

@ -116,6 +116,7 @@ namespace RTLIL
struct CaseRule;
struct SwitchRule;
struct MemWriteAction;
struct SyncAction;
struct SyncRule;
struct Process;
struct Binding;
@ -1114,6 +1115,13 @@ struct RTLIL::AttrObject
std::string get_src_attribute() const {
return get_string_attribute(ID::src);
}
void transfer_attribute(const AttrObject* from, const IdString& attr) {
if (from->has_attribute(attr))
attributes[attr] = from->attributes.at(attr);
}
void transfer_src_attribute(const AttrObject* from) {
transfer_attribute(from, ID::src);
}
void set_hdlname_attribute(const vector<string> &hierarchy);
vector<string> get_hdlname_attribute() const;
@ -2196,7 +2204,7 @@ public:
struct RTLIL::CaseRule : public RTLIL::AttrObject
{
std::vector<RTLIL::SigSpec> compare;
std::vector<RTLIL::SigSig> actions;
std::vector<RTLIL::SyncAction> actions;
std::vector<RTLIL::SwitchRule*> switches;
~CaseRule();
@ -2231,11 +2239,18 @@ struct RTLIL::MemWriteAction : RTLIL::AttrObject
RTLIL::Const priority_mask;
};
struct RTLIL::SyncAction
{
RTLIL::SigSpec lhs;
RTLIL::SigSpec rhs;
RTLIL::Const src;
};
struct RTLIL::SyncRule
{
RTLIL::SyncType type;
RTLIL::SigSpec signal;
std::vector<RTLIL::SigSig> actions;
std::vector<RTLIL::SyncAction> actions;
std::vector<RTLIL::MemWriteAction> mem_write_actions;
template<typename T> void rewrite_sigspecs(T &functor);
@ -2365,8 +2380,8 @@ void RTLIL::CaseRule::rewrite_sigspecs(T &functor) {
for (auto &it : compare)
functor(it);
for (auto &it : actions) {
functor(it.first);
functor(it.second);
functor(it.lhs);
functor(it.rhs);
}
for (auto it : switches)
it->rewrite_sigspecs(functor);
@ -2377,7 +2392,7 @@ void RTLIL::CaseRule::rewrite_sigspecs2(T &functor) {
for (auto &it : compare)
functor(it);
for (auto &it : actions) {
functor(it.first, it.second);
functor(it.lhs, it.rhs);
}
for (auto it : switches)
it->rewrite_sigspecs2(functor);
@ -2404,8 +2419,8 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor)
{
functor(signal);
for (auto &it : actions) {
functor(it.first);
functor(it.second);
functor(it.lhs);
functor(it.rhs);
}
for (auto &it : mem_write_actions) {
functor(it.address);
@ -2419,7 +2434,7 @@ void RTLIL::SyncRule::rewrite_sigspecs2(T &functor)
{
functor(signal);
for (auto &it : actions) {
functor(it.first, it.second);
functor(it.lhs, it.rhs);
}
for (auto &it : mem_write_actions) {
functor(it.address);

View File

@ -363,7 +363,7 @@ struct BugpointPass : public Pass {
{
if (index++ == seed)
{
log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first));
log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->lhs), log_signal(it->rhs), log_id(mod), log_id(pr.first));
cs->actions.erase(it);
return design_copy;
}
@ -389,7 +389,7 @@ struct BugpointPass : public Pass {
{
if (index++ == seed)
{
log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first));
log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->lhs), log_signal(it->rhs), log_id(mod), log_id(pr.first));
sy->actions.erase(it);
return design_copy;
}

View File

@ -130,12 +130,12 @@ struct CheckPass : public Pass {
std::vector<RTLIL::CaseRule*> all_cases = {&proc_it.second->root_case};
for (size_t i = 0; i < all_cases.size(); i++) {
for (auto action : all_cases[i]->actions) {
for (auto bit : sigmap(action.first))
for (auto bit : sigmap(action.lhs))
wire_drivers[bit].push_back(
stringf("action %s <= %s (case rule) in process %s",
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
log_signal(action.lhs), log_signal(action.rhs), log_id(proc_it.first)));
for (auto bit : sigmap(action.second))
for (auto bit : sigmap(action.rhs))
if (bit.wire) used_wires.insert(bit);
}
for (auto switch_ : all_cases[i]->switches) {
@ -151,11 +151,11 @@ struct CheckPass : public Pass {
for (auto bit : sigmap(sync->signal))
if (bit.wire) used_wires.insert(bit);
for (auto action : sync->actions) {
for (auto bit : sigmap(action.first))
for (auto bit : sigmap(action.lhs))
wire_drivers[bit].push_back(
stringf("action %s <= %s (sync rule) in process %s",
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
for (auto bit : sigmap(action.second))
log_signal(action.lhs), log_signal(action.rhs), log_id(proc_it.first)));
for (auto bit : sigmap(action.rhs))
if (bit.wire) used_wires.insert(bit);
}
for (auto memwr : sync->mem_write_actions) {

View File

@ -17,6 +17,7 @@
*
*/
#include "kernel/rtlil.h"
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/mem.h"
@ -40,9 +41,9 @@ struct CleanZeroWidthPass : public Pass {
void clean_case(RTLIL::CaseRule *cs)
{
std::vector<SigSig> new_actions;
std::vector<RTLIL::SyncAction> new_actions;
for (auto &action : cs->actions)
if (GetSize(action.first) != 0)
if (GetSize(action.lhs) != 0)
new_actions.push_back(action);
std::swap(new_actions, cs->actions);
for (auto sw : cs->switches)
@ -167,9 +168,9 @@ struct CleanZeroWidthPass : public Pass {
new_memwr_actions.push_back(memwr);
}
std::swap(new_memwr_actions, sync->mem_write_actions);
std::vector<SigSig> new_actions;
std::vector<RTLIL::SyncAction> new_actions;
for (auto &action : sync->actions)
if (GetSize(action.first) != 0)
if (GetSize(action.lhs) != 0)
new_actions.push_back(action);
std::swap(new_actions, sync->actions);
}

View File

@ -17,6 +17,7 @@
*
*/
#include "kernel/rtlil.h"
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/log_help.h"
@ -370,12 +371,12 @@ struct ShowWorker
signals.insert(it);
}
void collect_proc_signals(std::vector<RTLIL::SigSig> &obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
void collect_proc_signals(std::vector<RTLIL::SyncAction> &obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals)
{
for (auto &it : obj) {
output_signals.insert(it.first);
if (!it.second.is_fully_const())
input_signals.insert(it.second);
output_signals.insert(it.lhs);
if (!it.rhs.is_fully_const())
input_signals.insert(it.rhs);
}
}

View File

@ -156,6 +156,21 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
return basicType;
}
// Try to read an IdString as a numbered connection name ("$123" or similar),
// writing the result to dst. If the string isn't of the right format, ignore
// dst and return false.
bool read_id_num(RTLIL::IdString str, int *dst)
{
log_assert(dst);
const char *c_str = str.c_str();
if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9'))
return false;
*dst = atoi(c_str + 1);
return true;
}
// A helper struct for expanding a module's interface connections in expand_module
struct IFExpander
{
@ -283,15 +298,42 @@ struct IFExpander
RTLIL::IdString conn_name,
const RTLIL::SigSpec &conn_signals)
{
// Check if the connection is present as an interface in the sub-module's port list
const RTLIL::Wire *wire = submodule.wire(conn_name);
if (!wire || !wire->get_bool_attribute(ID::is_interface))
// Does the connection look like an interface
if (
conn_signals.size() != 1 ||
conn_signals[0].wire == nullptr ||
conn_signals[0].wire->get_bool_attribute(ID::is_interface) == false ||
conn_signals[0].wire->name.str().find("$dummywireforinterface") != 0
)
return;
// Check if the connection is present as an interface in the sub-module's port list
int id;
if (read_id_num(conn_name, &id)) {
/* Interface expansion is incompatible with positional arguments
* during expansion, the port list gets each interface signal
* inserted after the interface itself which means that the argument
* positions in the parent module no longer match.
*
* Supporting this would require expanding the interfaces in the
* parent module, renumbering the arguments to match, and then
* iterating over the ports list to find the matching interface
* (refactoring on_interface to accept different conn_names on the
* parent and child).
*/
log_error("Unable to connect `%s' to submodule `%s' with positional interface argument `%s'!\n",
module.name,
submodule.name,
conn_signals[0].wire->name.str().substr(23)
);
} else {
// Lookup connection by name
const RTLIL::Wire *wire = submodule.wire(conn_name);
if (!wire || !wire->get_bool_attribute(ID::is_interface))
return;
}
// If the connection looks like an interface, handle it.
const auto &bits = conn_signals;
if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface))
on_interface(submodule, conn_name, conn_signals);
on_interface(submodule, conn_name, conn_signals);
}
// Iterate over the connections in a cell, tracking any interface
@ -376,21 +418,6 @@ RTLIL::Module *get_module(RTLIL::Design &design,
return nullptr;
}
// Try to read an IdString as a numbered connection name ("$123" or similar),
// writing the result to dst. If the string isn't of the right format, ignore
// dst and return false.
bool read_id_num(RTLIL::IdString str, int *dst)
{
log_assert(dst);
const char *c_str = str.c_str();
if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9'))
return false;
*dst = atoi(c_str + 1);
return true;
}
// Check that the connections on the cell match those that are defined
// on the type: each named connection should match the name of a port
// and each positional connection should have an index smaller than

View File

@ -18,6 +18,7 @@
*/
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include <stdlib.h>
@ -89,9 +90,9 @@ void apply_const(RTLIL::Module *mod, const RTLIL::SigSpec rspec, RTLIL::SigSpec
{
for (auto &action : cs->actions) {
if (unknown)
rspec.replace(action.first, RTLIL::SigSpec(RTLIL::State::Sm, action.second.size()), &rval);
rspec.replace(action.lhs, RTLIL::SigSpec(RTLIL::State::Sm, action.rhs.size()), &rval);
else
rspec.replace(action.first, action.second, &rval);
rspec.replace(action.lhs, action.rhs, &rval);
}
for (auto sw : cs->switches) {
@ -209,7 +210,7 @@ void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map)
arst_syncs.push_back(sync);
edge_syncs.erase(it);
for (auto &action : sync->actions) {
action.second = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, action.second, action.first);
action.rhs = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, action.rhs, action.lhs);
}
for (auto &memwr : sync->mem_write_actions) {
RTLIL::SigSpec en = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.enable, memwr.enable);
@ -294,12 +295,12 @@ struct ProcArstPass : public Pass {
proc_arst(mod, proc, assign_map);
if (global_arst.empty() || mod->wire(global_arst) == nullptr)
continue;
std::vector<RTLIL::SigSig> arst_actions;
std::vector<RTLIL::SyncAction> arst_actions;
for (auto sync : proc->syncs)
if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn)
for (auto &act : sync->actions) {
RTLIL::SigSpec arst_sig, arst_val;
for (auto &chunk : act.first.chunks())
for (auto &chunk : act.lhs.chunks())
if (chunk.wire && chunk.wire->attributes.count(ID::init)) {
RTLIL::SigSpec value = chunk.wire->attributes.at(ID::init);
value.extend_u0(chunk.wire->width, false);
@ -310,7 +311,7 @@ struct ProcArstPass : public Pass {
if (arst_sig.size()) {
log("Added global reset to process %s: %s <- %s\n",
proc->name.c_str(), log_signal(arst_sig), log_signal(arst_val));
arst_actions.push_back(RTLIL::SigSig(arst_sig, arst_val));
arst_actions.push_back({arst_sig, arst_val, act.src});
}
}
if (!arst_actions.empty()) {

View File

@ -133,7 +133,7 @@ YOSYS_NAMESPACE_BEGIN
void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth)
{
for (size_t i = 0; i < cs->actions.size(); i++) {
if (cs->actions[i].first.size() == 0) {
if (cs->actions[i].lhs.size() == 0) {
did_something = true;
cs->actions.erase(cs->actions.begin() + (i--));
}
@ -159,7 +159,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool
bool did_something = true;
for (size_t i = 0; i < proc->syncs.size(); i++) {
for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++)
if (proc->syncs[i]->actions[j].first.size() == 0)
if (proc->syncs[i]->actions[j].lhs.size() == 0)
proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--));
if (proc->syncs[i]->actions.size() == 0 && proc->syncs[i]->mem_write_actions.size() == 0) {
delete proc->syncs[i];

View File

@ -34,8 +34,8 @@ RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc)
for (auto sync : proc->syncs)
for (auto &action : sync->actions)
if (action.first.size() > 0) {
lvalue = action.first;
if (action.lhs.size() > 0) {
lvalue = action.lhs;
lvalue.sort_and_unify();
break;
}
@ -43,7 +43,7 @@ RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc)
for (auto sync : proc->syncs) {
RTLIL::SigSpec this_lvalue;
for (auto &action : sync->actions)
this_lvalue.append(action.first);
this_lvalue.append(action.lhs);
this_lvalue.sort_and_unify();
RTLIL::SigSpec common_sig = this_lvalue.extract(lvalue);
if (common_sig.size() > 0)
@ -172,35 +172,35 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
for (auto sync : proc->syncs)
for (auto &action : sync->actions)
{
if (action.first.extract(sig).size() == 0)
if (action.lhs.extract(sig).size() == 0)
continue;
if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) {
RTLIL::SigSpec rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size());
sig.replace(action.first, action.second, &rstval);
sig.replace(action.lhs, action.rhs, &rstval);
async_rules.emplace_back(rstval, sync);
}
else if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) {
if (sync_edge != NULL && sync_edge != sync)
log_error("Multiple edge sensitive events found for this signal!\n");
sig.replace(action.first, action.second, &insig);
sig.replace(action.lhs, action.rhs, &insig);
sync_edge = sync;
}
else if (sync->type == RTLIL::SyncType::STa) {
if (sync_always != NULL && sync_always != sync)
log_error("Multiple always events found for this signal!\n");
sig.replace(action.first, action.second, &insig);
sig.replace(action.lhs, action.rhs, &insig);
sync_always = sync;
}
else if (sync->type == RTLIL::SyncType::STg) {
sig.replace(action.first, action.second, &insig);
sig.replace(action.lhs, action.rhs, &insig);
global_clock = true;
}
else {
log_error("Event with any-edge sensitivity found for this signal!\n");
}
action.first.remove2(sig, &action.second);
action.lhs.remove2(sig, &action.rhs);
}
// If all async rules assign the same value, priority ordering between
@ -223,7 +223,8 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
// as ones coming from the module
single_async_rule.type = RTLIL::SyncType::ST1;
single_async_rule.signal = mod->ReduceOr(NEW_ID, triggers);
single_async_rule.actions.push_back(RTLIL::SigSig(sig, rstval));
// TODO
single_async_rule.actions.push_back({sig, rstval, Const("")});
// Replace existing rules with this new rule
async_rules.clear();

View File

@ -364,17 +364,17 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
for (auto ss : sr->actions)
{
db.sigmap.apply(ss.first);
db.sigmap.apply(ss.second);
db.sigmap.apply(ss.lhs);
db.sigmap.apply(ss.rhs);
if (!db.quickcheck(ss.second, ss.first)) {
nolatches_bits.first.append(ss.first);
nolatches_bits.second.append(ss.second);
if (!db.quickcheck(ss.rhs, ss.lhs)) {
nolatches_bits.first.append(ss.lhs);
nolatches_bits.second.append(ss.rhs);
continue;
}
for (int i = 0; i < GetSize(ss.first); i++)
latches_out_in[ss.first[i]] = ss.second[i];
for (int i = 0; i < GetSize(ss.lhs); i++)
latches_out_in[ss.lhs[i]] = ss.rhs[i];
}
sr->actions.clear();
}

View File

@ -35,8 +35,8 @@ void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc)
for (auto &action : sync->actions)
{
RTLIL::SigSpec lhs = action.first;
RTLIL::SigSpec rhs = sigmap(action.second);
RTLIL::SigSpec lhs = action.lhs;
RTLIL::SigSpec rhs = sigmap(action.rhs);
if (!rhs.is_fully_const())
log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs));

View File

@ -20,6 +20,7 @@
#include "kernel/register.h"
#include "kernel/bitpattern.h"
#include "kernel/log.h"
#include "kernel/rtlil.h"
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
@ -27,11 +28,37 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
using SnippetSourceMap = dict<std::pair<int, const RTLIL::CaseRule*>, const Const*>;
struct SnippetSourceMapBuilder {
SnippetSourceMap map;
void insert(int snippet, const RTLIL::CaseRule* cs, const RTLIL::SyncAction& action) {
if (action.src.size())
map[std::make_pair(snippet, cs)] = &action.src;
}
};
struct SnippetSourceMapper {
const SnippetSourceMap map;
void try_map_into(pool<std::string>& sources, int snippet, const RTLIL::CaseRule* cs) const {
auto src_it = map.find(std::make_pair(snippet, cs));
if (src_it != map.end()) {
sources.insert(src_it->second->decode_string());
} else {
auto cs_src = cs->get_src_attribute();
if (cs_src.size()) {
sources.insert(cs_src);
}
}
}
};
struct SigSnippets
{
idict<SigSpec> sigidx;
dict<SigBit, int> bit2snippet;
pool<int> snippets;
SnippetSourceMapBuilder source_builder;
void insert(SigSpec sig)
{
@ -97,8 +124,11 @@ struct SigSnippets
void insert(const RTLIL::CaseRule *cs)
{
for (auto &action : cs->actions)
insert(action.first);
for (auto &action : cs->actions) {
insert(action.lhs);
int idx = sigidx(action.lhs);
source_builder.insert(idx, cs, action);
}
for (auto sw : cs->switches)
for (auto cs2 : sw->cases)
@ -121,7 +151,7 @@ struct SnippetSwCache
void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack)
{
for (auto &action : cs->actions)
for (auto bit : action.first) {
for (auto bit : action.lhs) {
int sn = snippets->bit2snippet.at(bit, -1);
if (sn < 0)
continue;
@ -144,136 +174,157 @@ struct SnippetSwCache
}
};
void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs)
void apply_attrs(RTLIL::Cell *cell, const RTLIL::CaseRule *cs)
{
cell->attributes = sw->attributes;
cell->add_strpool_attribute(ID::src, cs->get_strpool_attribute(ID::src));
Const old_src;
if (cell->attributes.count(ID::src)) {
std::swap(old_src, cell->attributes[ID::src]);
}
cell->attributes = cs->attributes;
if (old_src.size()) {
std::swap(old_src, cell->attributes[ID::src]);
}
}
RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
struct MuxGenCtx {
RTLIL::Module *mod;
const RTLIL::SigSpec &signal;
const std::vector<RTLIL::SigSpec> *compare;
RTLIL::Cell *last_mux_cell;
RTLIL::SwitchRule *sw;
RTLIL::CaseRule *cs;
bool ifxmode;
const SnippetSourceMapper& source_mapper;
int current_snippet;
pool<std::string>& snippet_sources;
RTLIL::Wire *cmp_wire = mod->addWire(sstr.str() + "_CMP", 0);
RTLIL::SigSpec gen_cmp() {
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
for (auto comp : compare)
{
RTLIL::SigSpec sig = signal;
RTLIL::Wire *cmp_wire = mod->addWire(sstr.str() + "_CMP", 0);
// get rid of don't-care bits
log_assert(sig.size() == comp.size());
for (int i = 0; i < comp.size(); i++)
if (comp[i] == RTLIL::State::Sa) {
sig.remove(i);
comp.remove(i--);
}
if (comp.size() == 0)
return RTLIL::SigSpec();
if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode)
for (auto comp : *compare)
{
mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig));
RTLIL::SigSpec sig = signal;
// get rid of don't-care bits
log_assert(sig.size() == comp.size());
for (int i = 0; i < comp.size(); i++)
if (comp[i] == RTLIL::State::Sa) {
sig.remove(i);
comp.remove(i--);
}
if (comp.size() == 0)
return RTLIL::SigSpec();
if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode)
{
mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig));
}
else
{
// create compare cell
RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str(), cmp_wire->width), ifxmode ? ID($eqx) : ID($eq));
apply_attrs(eq_cell, cs);
eq_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0);
eq_cell->parameters[ID::B_SIGNED] = RTLIL::Const(0);
eq_cell->parameters[ID::A_WIDTH] = RTLIL::Const(sig.size());
eq_cell->parameters[ID::B_WIDTH] = RTLIL::Const(comp.size());
eq_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1);
eq_cell->setPort(ID::A, sig);
eq_cell->setPort(ID::B, comp);
eq_cell->setPort(ID::Y, RTLIL::SigSpec(cmp_wire, cmp_wire->width++));
}
}
RTLIL::Wire *ctrl_wire;
if (cmp_wire->width == 1)
{
ctrl_wire = cmp_wire;
}
else
{
// create compare cell
RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str(), cmp_wire->width), ifxmode ? ID($eqx) : ID($eq));
apply_attrs(eq_cell, sw, cs);
ctrl_wire = mod->addWire(sstr.str() + "_CTRL");
eq_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0);
eq_cell->parameters[ID::B_SIGNED] = RTLIL::Const(0);
// reduce cmp vector to one logic signal
RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", ID($reduce_or));
apply_attrs(any_cell, cs);
eq_cell->parameters[ID::A_WIDTH] = RTLIL::Const(sig.size());
eq_cell->parameters[ID::B_WIDTH] = RTLIL::Const(comp.size());
eq_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1);
any_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0);
any_cell->parameters[ID::A_WIDTH] = RTLIL::Const(cmp_wire->width);
any_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1);
eq_cell->setPort(ID::A, sig);
eq_cell->setPort(ID::B, comp);
eq_cell->setPort(ID::Y, RTLIL::SigSpec(cmp_wire, cmp_wire->width++));
any_cell->setPort(ID::A, cmp_wire);
any_cell->setPort(ID::Y, RTLIL::SigSpec(ctrl_wire));
}
return RTLIL::SigSpec(ctrl_wire);
}
RTLIL::Wire *ctrl_wire;
if (cmp_wire->width == 1)
{
ctrl_wire = cmp_wire;
}
else
{
ctrl_wire = mod->addWire(sstr.str() + "_CTRL");
RTLIL::SigSpec gen_mux(RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal) {
log_assert(when_signal.size() == else_signal.size());
// reduce cmp vector to one logic signal
RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", ID($reduce_or));
apply_attrs(any_cell, sw, cs);
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
any_cell->parameters[ID::A_SIGNED] = RTLIL::Const(0);
any_cell->parameters[ID::A_WIDTH] = RTLIL::Const(cmp_wire->width);
any_cell->parameters[ID::Y_WIDTH] = RTLIL::Const(1);
// the trivial cases
if (compare->size() == 0 || when_signal == else_signal)
return when_signal;
any_cell->setPort(ID::A, cmp_wire);
any_cell->setPort(ID::Y, RTLIL::SigSpec(ctrl_wire));
// compare results
RTLIL::SigSpec ctrl_sig = gen_cmp();
if (ctrl_sig.size() == 0)
return when_signal;
log_assert(ctrl_sig.size() == 1);
// prepare multiplexer output signal
RTLIL::Wire *result_wire = mod->addWire(sstr.str() + "_Y", when_signal.size());
// create the multiplexer itself
RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), ID($mux));
mux_cell->parameters[ID::WIDTH] = RTLIL::Const(when_signal.size());
mux_cell->setPort(ID::A, else_signal);
mux_cell->setPort(ID::B, when_signal);
mux_cell->setPort(ID::S, ctrl_sig);
mux_cell->setPort(ID::Y, RTLIL::SigSpec(result_wire));
source_mapper.try_map_into(snippet_sources, current_snippet, cs);
last_mux_cell = mux_cell;
return RTLIL::SigSpec(result_wire);
}
return RTLIL::SigSpec(ctrl_wire);
}
void append_pmux(RTLIL::SigSpec when_signal) {
log_assert(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort(ID::A).size());
RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
log_assert(when_signal.size() == else_signal.size());
if (when_signal == last_mux_cell->getPort(ID::A)) {
// when_signal already covered by the default value at port A
return;
}
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
RTLIL::SigSpec ctrl_sig = gen_cmp();
log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = ID($pmux);
// the trivial cases
if (compare.size() == 0 || when_signal == else_signal)
return when_signal;
RTLIL::SigSpec new_s = last_mux_cell->getPort(ID::S);
new_s.append(ctrl_sig);
last_mux_cell->setPort(ID::S, new_s);
// compare results
RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
if (ctrl_sig.size() == 0)
return when_signal;
log_assert(ctrl_sig.size() == 1);
RTLIL::SigSpec new_b = last_mux_cell->getPort(ID::B);
new_b.append(when_signal);
last_mux_cell->setPort(ID::B, new_b);
// prepare multiplexer output signal
RTLIL::Wire *result_wire = mod->addWire(sstr.str() + "_Y", when_signal.size());
last_mux_cell->parameters[ID::S_WIDTH] = last_mux_cell->getPort(ID::S).size();
// create the multiplexer itself
RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), ID($mux));
apply_attrs(mux_cell, sw, cs);
mux_cell->parameters[ID::WIDTH] = RTLIL::Const(when_signal.size());
mux_cell->setPort(ID::A, else_signal);
mux_cell->setPort(ID::B, when_signal);
mux_cell->setPort(ID::S, ctrl_sig);
mux_cell->setPort(ID::Y, RTLIL::SigSpec(result_wire));
last_mux_cell = mux_cell;
return RTLIL::SigSpec(result_wire);
}
void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
log_assert(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort(ID::A).size());
if (when_signal == last_mux_cell->getPort(ID::A))
return;
RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = ID($pmux);
RTLIL::SigSpec new_s = last_mux_cell->getPort(ID::S);
new_s.append(ctrl_sig);
last_mux_cell->setPort(ID::S, new_s);
RTLIL::SigSpec new_b = last_mux_cell->getPort(ID::B);
new_b.append(when_signal);
last_mux_cell->setPort(ID::B, new_b);
last_mux_cell->parameters[ID::S_WIDTH] = last_mux_cell->getPort(ID::S).size();
}
source_mapper.try_map_into(snippet_sources, current_snippet, cs);
}
};
const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRule *sw)
{
@ -290,7 +341,7 @@ const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul
pool<SigBit> case_bits;
for (auto it : cs->actions) {
for (auto bit : it.first)
for (auto bit : it.lhs)
case_bits.insert(bit);
}
@ -317,50 +368,61 @@ const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul
return swcache.full_case_bits_cache.at(sw);
}
struct MuxTreeContext {
RTLIL::Module* mod;
SnippetSwCache& swcache;
const SnippetSourceMapper& source_mapper;
dict<RTLIL::SwitchRule*, bool> &swpara;
RTLIL::CaseRule *cs;
const RTLIL::SigSpec &sig;
RTLIL::SigSpec defval;
const bool ifxmode;
};
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool> &swpara,
RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
bool is_simple_parallel_case(RTLIL::SwitchRule* sw, dict<RTLIL::SwitchRule*, bool> &swpara)
{
RTLIL::SigSpec result = defval;
bool ret = true;
if (!sw->get_bool_attribute(ID::parallel_case)) {
if (!swpara.count(sw)) {
pool<Const> case_values;
for (size_t i = 0; i < sw->cases.size(); i++) {
RTLIL::CaseRule *cs2 = sw->cases[i];
for (auto pat : cs2->compare) {
if (!pat.is_fully_def())
return false;
Const cpat = pat.as_const();
if (case_values.count(cpat))
return false;
case_values.insert(cpat);
}
}
swpara[sw] = ret;
} else {
return swpara.at(sw);
}
}
return ret;
}
for (auto &action : cs->actions) {
sig.replace(action.first, action.second, &result);
action.first.remove2(sig, &action.second);
RTLIL::SigSpec signal_to_mux_tree(MuxTreeContext ctx)
{
RTLIL::SigSpec result = ctx.defval;
for (auto &action : ctx.cs->actions) {
ctx.sig.replace(action.lhs, action.rhs, &result);
action.lhs.remove2(ctx.sig, &action.rhs);
}
for (auto sw : cs->switches)
for (auto sw : ctx.cs->switches)
{
if (!swcache.check(sw))
if (!ctx.swcache.check(sw))
continue;
// detect groups of parallel cases
std::vector<int> pgroups(sw->cases.size());
bool is_simple_parallel_case = true;
pool<std::string> case_sources;
if (!sw->get_bool_attribute(ID::parallel_case)) {
if (!swpara.count(sw)) {
pool<Const> case_values;
for (size_t i = 0; i < sw->cases.size(); i++) {
RTLIL::CaseRule *cs2 = sw->cases[i];
for (auto pat : cs2->compare) {
if (!pat.is_fully_def())
goto not_simple_parallel_case;
Const cpat = pat.as_const();
if (case_values.count(cpat))
goto not_simple_parallel_case;
case_values.insert(cpat);
}
}
if (0)
not_simple_parallel_case:
is_simple_parallel_case = false;
swpara[sw] = is_simple_parallel_case;
} else {
is_simple_parallel_case = swpara.at(sw);
}
}
if (!is_simple_parallel_case) {
if (!is_simple_parallel_case(sw, ctx.swpara)) {
BitPatternPool pool(sw->signal.size());
bool extra_group_for_next_case = false;
for (size_t i = 0; i < sw->cases.size(); i++) {
@ -382,28 +444,52 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
for (auto pat : cs2->compare)
if (!pat.is_fully_const())
extra_group_for_next_case = true;
else if (!ifxmode)
else if (!ctx.ifxmode)
pool.take(pat);
}
}
// Create sources for default cases
for (auto cs2 : sw -> cases) {
if (cs2->compare.empty()) {
int sn = ctx.swcache.current_snippet;
ctx.source_mapper.try_map_into(case_sources, sn, cs2);
}
}
// mask default bits that are irrelevant because the output is driven by a full case
const pool<SigBit> &full_case_bits = get_full_case_bits(swcache, sw);
for (int i = 0; i < GetSize(sig); i++)
if (full_case_bits.count(sig[i]))
const pool<SigBit> &full_case_bits = get_full_case_bits(ctx.swcache, sw);
for (int i = 0; i < GetSize(ctx.sig); i++)
if (full_case_bits.count(ctx.sig[i]))
result[i] = State::Sx;
// evaluate in reverse order to give the first entry the top priority
RTLIL::SigSpec initial_val = result;
RTLIL::Cell *last_mux_cell = NULL;
MuxGenCtx mux_gen_ctx {ctx.mod,
sw->signal,
nullptr,
nullptr,
sw,
nullptr,
ctx.ifxmode,
ctx.source_mapper,
ctx.swcache.current_snippet,
case_sources
};
// evaluate in reverse order to give the first entry the top priority
for (size_t i = 0; i < sw->cases.size(); i++) {
int case_idx = sw->cases.size() - i - 1;
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode);
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, cs2, ifxmode);
else
result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode);
MuxTreeContext new_ctx = ctx;
new_ctx.cs = sw->cases[case_idx];
new_ctx.defval = initial_val;
RTLIL::SigSpec value = signal_to_mux_tree(new_ctx);
mux_gen_ctx.cs = new_ctx.cs;
mux_gen_ctx.compare = &new_ctx.cs->compare;
if (mux_gen_ctx.last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) {
mux_gen_ctx.append_pmux(value);
} else {
result = mux_gen_ctx.gen_mux(value, result);
}
}
if (mux_gen_ctx.last_mux_cell) {
mux_gen_ctx.last_mux_cell->set_strpool_attribute(ID::src, case_sources);
}
}
@ -429,9 +515,19 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
swcache.current_snippet = idx;
RTLIL::SigSpec sig = sigsnip.sigidx[idx];
log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig));
log_debug("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig));
RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode);
const SnippetSourceMapper mapper{sigsnip.source_builder.map};
RTLIL::SigSpec value = signal_to_mux_tree({
mod,
swcache,
mapper,
swpara,
&proc->root_case,
sig,
RTLIL::SigSpec(RTLIL::State::Sx, sig.size()),
ifxmode
});
mod->connect(RTLIL::SigSig(sig, value));
}
}

View File

@ -66,8 +66,8 @@ struct PruneWorker
assigned.insert(sw_assigned.begin(), sw_assigned.end());
}
for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ) {
RTLIL::SigSpec lhs = sigmap(it->first);
RTLIL::SigSpec rhs = sigmap(it->second);
RTLIL::SigSpec lhs = sigmap(it->lhs);
RTLIL::SigSpec rhs = sigmap(it->rhs);
SigSpec new_lhs, new_rhs;
SigSpec conn_lhs, conn_rhs;
for (int i = 0; i < GetSize(lhs); i++) {
@ -93,8 +93,8 @@ struct PruneWorker
removed_count++;
it = decltype(cs->actions)::reverse_iterator(cs->actions.erase(it.base() - 1));
} else {
it->first = new_lhs;
it->second = new_rhs;
it->lhs = new_lhs;
it->rhs = new_rhs;
it++;
}
}

View File

@ -58,7 +58,7 @@ struct RomWorker
SigSpec lhs;
dict<SigBit, int> lhs_lookup;
for (auto &it: sw->cases[0]->actions) {
for (auto bit: it.first) {
for (auto bit: it.lhs) {
if (!lhs_lookup.count(bit)) {
lhs_lookup[bit] = GetSize(lhs);
lhs.append(bit);
@ -87,17 +87,17 @@ struct RomWorker
}
Const val = Const(State::Sm, GetSize(lhs));
for (auto &it: cs->actions) {
if (!it.second.is_fully_const()) {
if (!it.rhs.is_fully_const()) {
log_debug("rejecting switch: rhs not const\n");
return;
}
for (int i = 0; i < GetSize(it.first); i++) {
auto it2 = lhs_lookup.find(it.first[i]);
for (int i = 0; i < GetSize(it.lhs); i++) {
auto it2 = lhs_lookup.find(it.lhs[i]);
if (it2 == lhs_lookup.end()) {
log_debug("rejecting switch: lhs not uniform\n");
return;
}
val.set(it2->second, it.second[i].data);
val.set(it2->second, it.rhs[i].data);
}
}
for (auto bit: val) {
@ -193,19 +193,20 @@ struct RomWorker
delete cs;
sw->cases.clear();
sw->signal = sw->signal.extract(0, swsigbits);
Const action_src = mem.has_attribute(ID::src) ? mem.attributes[ID::src] : Const("");
if (abits == GetSize(sw->signal)) {
sw->signal = SigSpec();
RTLIL::CaseRule *cs = new RTLIL::CaseRule;
cs->actions.push_back(SigSig(lhs, rdata));
cs->actions.push_back({lhs, rdata, std::move(action_src)});
sw->cases.push_back(cs);
} else {
sw->signal = sw->signal.extract_end(abits);
RTLIL::CaseRule *cs = new RTLIL::CaseRule;
cs->compare.push_back(Const(State::S0, GetSize(sw->signal)));
cs->actions.push_back(SigSig(lhs, rdata));
cs->actions.push_back({lhs, rdata, action_src});
sw->cases.push_back(cs);
RTLIL::CaseRule *cs2 = new RTLIL::CaseRule;
cs2->actions.push_back(SigSig(lhs, default_val));
cs2->actions.push_back({lhs, default_val, std::move(action_src)});
sw->cases.push_back(cs2);
}

View File

@ -127,7 +127,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
return false;
}
auto pin_names = pool<std::string>{};
auto pin_names = std::unordered_set<std::string>{};
tree.get_pin_names(pin_names);
// from the `ff` block, we know the flop output signal name for loopback.
@ -156,7 +156,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
auto pins = std::vector<std::string>(pin_names.begin(), pin_names.end());
int lut = 0;
for (int n = 0; n < 8; n++) {
auto values = dict<std::string, bool>{};
auto values = std::unordered_map<std::string, bool>{};
values.insert(std::make_pair(pins[0], (n & 1) == 1));
values.insert(std::make_pair(pins[1], (n & 2) == 2));
values.insert(std::make_pair(ff_output, (n & 4) == 4));

View File

@ -25,9 +25,22 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#ifndef FILTERLIB
#ifdef FILTERLIB
#undef log_assert
void log_assert(bool cond) {
if (!cond)
fprintf(stderr, "Unspecified assertion failed\n");
}
void warn(std::string str) {
std::cerr << str;
}
#else
#include "kernel/log.h"
void warn(std::string str) {
Yosys::log_formatted_warning("", str);
}
#endif
using namespace Yosys;
@ -162,13 +175,15 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i
fprintf(f, " ;\n");
}
#ifndef FILTERLIB
// binary operators excluding ' '
bool LibertyExpression::is_nice_binop(char c) {
bool LibertyExpression::char_is_nice_binop(char c) {
return c == '*' || c == '&' || c == '^' || c == '+' || c == '|';
}
bool LibertyExpression::is_binop() {
return kind == AND || kind == OR || kind == XOR;
}
// https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
if (s.empty())
@ -177,7 +192,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
char c = s.peek();
auto lhs = LibertyExpression{};
while (isspace(c)) {
while (isspace(c) || c == '"') {
if (s.empty())
return lhs;
s.next();
@ -191,7 +206,9 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
s.next();
lhs = parse(s);
if (s.peek() != ')') {
log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr());
std::stringstream ss;
ss << "expected ')' instead of " << s.peek() << " while parsing Liberty expression '" << s.full_expr() << "'\n";
warn(ss.str());
return lhs;
}
s.next();
@ -200,10 +217,11 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
lhs.kind = Kind::NOT;
lhs.children.push_back(parse(s, 7));
} else {
log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr());
std::stringstream ss;
ss << "unrecognised character " << c << " while parsing Liberty expression " << s.full_expr() << "\n";
warn(ss.str());
return lhs;
}
while (true) {
if (s.empty())
break;
@ -246,9 +264,10 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
s.next();
c = s.peek();
}
if (is_nice_binop(c)) {
if (char_is_nice_binop(c) || c == ')' || c == '\'' || c == '\"') {
// We found a real binop, so this space wasn't an AND
// and we just discard it as meaningless whitespace
// Tail operators also imply this isn't an AND
continue;
}
} else {
@ -286,7 +305,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
return lhs;
}
void LibertyExpression::get_pin_names(pool<std::string>& names) {
void LibertyExpression::get_pin_names(std::unordered_set<std::string>& names) {
if (kind == Kind::PIN) {
names.insert(name);
} else {
@ -295,7 +314,7 @@ void LibertyExpression::get_pin_names(pool<std::string>& names) {
}
}
bool LibertyExpression::eval(dict<std::string, bool>& values) {
bool LibertyExpression::eval(std::unordered_map<std::string, bool>& values) {
bool result = false;
switch (kind) {
case Kind::AND:
@ -324,7 +343,7 @@ bool LibertyExpression::eval(dict<std::string, bool>& values) {
return false;
}
std::string LibertyExpression::str(int indent)
std::string LibertyExpression::sexpr_str(int indent)
{
std::string prefix;
switch (kind) {
@ -355,16 +374,55 @@ std::string LibertyExpression::str(int indent)
if (!first) {
prefix += "\n" + std::string(indent + add_indent, ' ');
}
prefix += child.str(indent + add_indent);
prefix += child.sexpr_str(indent + add_indent);
first = false;
}
prefix += ")";
return prefix;
}
#endif
std::string LibertyExpression::vlog_str()
{
std::string prefix;
if (kind != PIN)
prefix += "(";
if (is_binop()) {
log_assert(children.size() == 2);
prefix += children[0].vlog_str();
switch (kind) {
case AND:
prefix += "&";
break;
case OR:
prefix += "|";
break;
case XOR:
prefix += "^";
break;
default:
log_assert(false);
}
prefix += children[1].vlog_str();
} else {
switch (kind) {
case NOT:
log_assert(children.size() == 1);
prefix += "~";
prefix += children[0].vlog_str();
break;
case PIN:
prefix += name;
break;
default:
log_assert(false);
}
}
if (kind != PIN)
prefix += ")";
return prefix;
}
int LibertyParser::lexer(std::string &str)
int LibertyParser::lexer_inner(std::string &str)
{
int c;
@ -390,11 +448,9 @@ int LibertyParser::lexer(std::string &str)
if (str == "+" || str == "-") {
/* Single operator is not an identifier */
// fprintf(stderr, "LEX: char >>%s<<\n", str.c_str());
return str[0];
}
else {
// fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
return 'v';
}
}
@ -402,24 +458,25 @@ int LibertyParser::lexer(std::string &str)
// if it wasn't an identifer, number of array range,
// maybe it's a string?
if (c == '"') {
f.consume(1);
size_t i = 0;
while (true) {
c = f.peek(i);
line += (c == '\n');
if (c != '"')
if (c != '"' && c != EOF)
i += 1;
else
break;
}
str.clear();
#ifdef FILTERLIB
f.unget();
str.append(f.buffered_data(), f.buffered_data() + i + 2);
f.consume(i + 2);
#else
str.append(f.buffered_data(), f.buffered_data() + i);
f.consume(i + 1);
str.append(f.buffered_data(), f.buffered_data() + i + 1);
// Usage in filterlib is expected to retain quotes
// but yosys expects to get unquoted
#ifdef FILTERLIB
str = "\"" + str + "\"";
#endif
f.consume(i + 2);
return 'v';
}
@ -442,13 +499,12 @@ int LibertyParser::lexer(std::string &str)
return lexer(str);
}
f.unget();
// fprintf(stderr, "LEX: char >>/<<\n");
return '/'; // a single '/' charater.
}
// check for a backslash
if (c == '\\') {
c = f.get();
c = f.get();
if (c == '\r')
c = f.get();
if (c == '\n') {
@ -467,14 +523,22 @@ int LibertyParser::lexer(std::string &str)
// anything else, such as ';' will get passed
// through as literal items.
// if (c >= 32 && c < 255)
// fprintf(stderr, "LEX: char >>%c<<\n", c);
// else
// fprintf(stderr, "LEX: char %d\n", c);
return c;
}
int LibertyParser::lexer(std::string &str)
{
int ret = lexer_inner(str);
// if (ret >= 32 && ret < 255) {
// fprintf(stdout, "LEX: ret >>%c<<\n", ret);
// } else if (ret == 'v') {
// fprintf(stdout, "LEX: ret v str %s\n", str.c_str());
// } else {
// fprintf(stdout, "LEX: ret %d\n", ret);
// }
return ret;
}
void LibertyParser::report_unexpected_token(int tok)
{
std::string eReport;
@ -545,6 +609,25 @@ void LibertyParser::parse_vector_range(int tok)
}
}
// Consume into out_str any string-ish tokens, seperated with spaces
// to cope with abuse of the underdefined spec by real world PDKs
// enabled by proprietary implementations.
// Sorry.
int LibertyParser::consume_wrecked_str(int tok, std::string& out_str) {
std::string str = "";
while (tok != ';' && tok != EOF && tok != 'n') {
out_str += " ";
if (tok == 'v')
out_str += str;
else
out_str += tok;
tok = lexer(str);
}
if (tok == EOF)
error("wrecked string EOF");
return tok;
}
LibertyAst *LibertyParser::parse(bool top_level)
{
std::string str;
@ -591,7 +674,14 @@ LibertyAst *LibertyParser::parse(bool top_level)
if (tok == '[') {
parse_vector_range(tok);
tok = lexer(str);
} else {
// Hack for when an expression string is unquoted
tok = consume_wrecked_str(tok, ast->value);
}
} else if (tok == '(') {
// Hack for when an expression string is unquoted and starts with
// parentheses
tok = consume_wrecked_str(tok, ast->value);
}
while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') {
ast->value += tok;
@ -601,7 +691,7 @@ LibertyAst *LibertyParser::parse(bool top_level)
ast->value += str;
tok = lexer(str);
}
// In a liberty file, all key : value pairs should end in ';'
// However, there are some liberty files in the wild that
// just have a newline. We'll be kind and accept a newline
@ -621,11 +711,11 @@ LibertyAst *LibertyParser::parse(bool top_level)
continue;
if (tok == ')')
break;
if (tok == '[')
{
parse_vector_range(tok);
continue;
continue;
}
if (tok == 'n')
continue;
@ -727,42 +817,13 @@ const LibertyAst *find_non_null(const LibertyAst *node, const char *name)
std::string func2vl(std::string str)
{
for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
char c_left = pos > 0 ? str[pos-1] : ' ';
char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
if (std::string("\" \t*+").find(c_left) != std::string::npos)
str.erase(pos, 1);
else if (std::string("\" \t*+").find(c_right) != std::string::npos)
str.erase(pos, 1);
else
str[pos] = '*';
}
std::vector<size_t> group_start;
for (size_t pos = 0; pos < str.size(); pos++) {
if (str[pos] == '(')
group_start.push_back(pos);
if (str[pos] == ')' && group_start.size() > 0) {
if (pos+1 < str.size() && str[pos+1] == '\'') {
std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
str[group_start.back()] = '~';
str.replace(group_start.back()+1, group.size(), group);
pos++;
}
group_start.pop_back();
}
if (str[pos] == '\'' && pos > 0) {
size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
std::string group = str.substr(start, pos-start);
str[start] = '~';
str.replace(start+1, group.size(), group);
}
if (str[pos] == '*')
str[pos] = '&';
if (str[pos] == '+')
str[pos] = '|';
}
auto helper = LibertyExpression::Lexer(str);
return LibertyExpression::parse(helper).vlog_str();
}
std::string vlog_identifier(std::string str)
{
str.erase(std::remove(str.begin(), str.end(), '\"'), str.end());
return str;
}
@ -772,11 +833,13 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
expr.clear();
if (ast != NULL) {
expr = func2vl(ast->value);
if (expr.size() > 0 && expr[0] == '~')
edge = "negedge " + expr.substr(1);
auto helper = LibertyExpression::Lexer(ast->value);
auto parsed = LibertyExpression::parse(helper);
expr = parsed.vlog_str();
if (parsed.kind == LibertyExpression::Kind::NOT)
edge = "negedge " + parsed.children[0].vlog_str();
else
edge = "posedge " + expr;
edge = "posedge " + parsed.vlog_str();
}
}
@ -806,13 +869,13 @@ void gen_verilogsim_cell(const LibertyAst *ast)
return;
CHECK_NV(ast->args.size(), == 1);
printf("module %s (", ast->args[0].c_str());
printf("module %s (", vlog_identifier(ast->args[0]).c_str());
bool first = true;
for (auto child : ast->children) {
if (child->id != "pin")
continue;
CHECK_NV(child->args.size(), == 1);
printf("%s%s", first ? "" : ", ", child->args[0].c_str());
printf("%s%s", first ? "" : ", ", vlog_identifier(child->args[0]).c_str());
first = false;
}
printf(");\n");
@ -823,7 +886,7 @@ void gen_verilogsim_cell(const LibertyAst *ast)
printf(" reg ");
first = true;
for (auto arg : child->args) {
printf("%s%s", first ? "" : ", ", arg.c_str());
printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str());
first = false;
}
printf(";\n");
@ -835,9 +898,10 @@ void gen_verilogsim_cell(const LibertyAst *ast)
CHECK_NV(child->args.size(), == 1);
const LibertyAst *dir = find_non_null(child, "direction");
const LibertyAst *func = child->find("function");
printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
std::string var = vlog_identifier(child->args[0]);
printf(" %s %s;\n", dir->value.c_str(), var.c_str());
if (func != NULL)
printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
printf(" assign %s = %s; // %s\n", var.c_str(), func2vl(func->value).c_str(), func->value.c_str());
}
for (auto child : ast->children)
@ -845,8 +909,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
if (child->id != "ff" || child->args.size() != 2)
continue;
std::string iq_var = child->args[0];
std::string iqn_var = child->args[1];
std::string iq_var = vlog_identifier(child->args[0]);
std::string iqn_var = vlog_identifier(child->args[1]);
std::string clock_edge, clock_expr;
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
@ -909,8 +973,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
if (child->id != "latch" || child->args.size() != 2)
continue;
std::string iq_var = child->args[0];
std::string iqn_var = child->args[1];
std::string iq_var = vlog_identifier(child->args[0]);
std::string iqn_var = vlog_identifier(child->args[1]);
std::string enable_edge, enable_expr;
event2vl(child->find("enable"), enable_edge, enable_expr);

View File

@ -63,7 +63,7 @@ namespace Yosys
}
std::string pin() {
auto length = s.find_first_of("\t()'!^*& +|");
auto length = s.find_first_of("\t()'!^*& +|\"");
if (length == std::string::npos) {
// nothing found so use size of s
length = s.size();
@ -91,11 +91,13 @@ namespace Yosys
LibertyExpression() : kind(Kind::EMPTY) {}
static LibertyExpression parse(Lexer &s, int min_prio = 0);
void get_pin_names(pool<std::string>& names);
bool eval(dict<std::string, bool>& values);
std::string str(int indent = 0);
void get_pin_names(std::unordered_set<std::string>& names);
bool eval(std::unordered_map<std::string, bool>& values);
std::string sexpr_str(int indent = 0);
std::string vlog_str();
private:
static bool is_nice_binop(char c);
static bool char_is_nice_binop(char c);
bool is_binop();
};
class LibertyInputStream {
@ -170,10 +172,12 @@ namespace Yosys
'n': newline
anything else is a single character.
*/
int lexer_inner(std::string &str);
int lexer(std::string &str);
void report_unexpected_token(int tok);
void parse_vector_range(int tok);
int consume_wrecked_str(int tok, std::string& out_str);
LibertyAst *parse(bool top_level);
void error() const;
void error(const std::string &str) const;

View File

@ -27,14 +27,6 @@
USING_YOSYS_NAMESPACE
YOSYS_NAMESPACE_BEGIN
static void transfer_attr (Cell* to, const Cell* from, const IdString& attr) {
if (from->has_attribute(attr))
to->attributes[attr] = from->attributes.at(attr);
}
static void transfer_src (Cell* to, const Cell* from) {
transfer_attr(to, from, ID::src);
}
void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
@ -44,7 +36,7 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::Y, sig_y[i]);
}
@ -104,7 +96,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::Y, sig_y[i]);
@ -155,7 +147,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_a[i+1]);
gate->setPort(ID::Y, sig_t[i/2]);
@ -168,7 +160,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
if (cell->type == ID($reduce_xnor)) {
RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a);
gate->setPort(ID::Y, sig_t);
last_output_cell = gate;
@ -196,7 +188,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell
}
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_OR_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig[i]);
gate->setPort(ID::B, sig[i+1]);
gate->setPort(ID::Y, sig_t[i/2]);
@ -225,7 +217,7 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a);
gate->setPort(ID::Y, sig_y);
}
@ -254,7 +246,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
log_assert(!gate_type.empty());
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a);
gate->setPort(ID::B, sig_b);
gate->setPort(ID::Y, sig_y);
@ -270,19 +262,19 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b)));
RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed);
transfer_src(xor_cell, cell);
xor_cell->transfer_src_attribute(cell);
simplemap_bitop(module, xor_cell);
module->remove(xor_cell);
RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out);
transfer_src(reduce_cell, cell);
reduce_cell->transfer_src_attribute(cell);
simplemap_reduce(module, reduce_cell);
module->remove(reduce_cell);
if (!is_ne) {
RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y);
transfer_src(not_cell, cell);
not_cell->transfer_src_attribute(cell);
simplemap_lognot(module, not_cell);
module->remove(not_cell);
}
@ -296,7 +288,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::S, cell->getPort(ID::S));
@ -313,7 +305,7 @@ void simplemap_bwmux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::S, sig_s[i]);
@ -329,7 +321,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_TBUF_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::E, sig_e);
gate->setPort(ID::Y, sig_y[i]);
@ -347,7 +339,7 @@ void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(new_data); i += width) {
for (int k = 0; k < width; k++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, data[i*2+k]);
gate->setPort(ID::B, data[i*2+width+k]);
gate->setPort(ID::S, sel[idx]);
@ -370,7 +362,7 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
for (int i = 0; i < GetSize(lut_data); i += 2) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
transfer_src(gate, cell);
gate->transfer_src_attribute(cell);
gate->setPort(ID::A, lut_data[i]);
gate->setPort(ID::B, lut_data[i+1]);
gate->setPort(ID::S, lut_ctrl[idx]);

View File

@ -18,7 +18,6 @@ proc
equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux4 # Constrain all select calls below inside the top module
select -assert-count 4 t:LUT*
select -assert-count 2 t:MUX2_LUT5
select -assert-count 1 t:MUX2_LUT6
select -assert-count 6 t:IBUF
@ -32,9 +31,6 @@ proc
equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux8 # Constrain all select calls below inside the top module
select -assert-count 3 t:LUT1
select -assert-count 2 t:LUT3
select -assert-count 1 t:LUT4
select -assert-count 5 t:MUX2_LUT5
select -assert-count 2 t:MUX2_LUT6
select -assert-count 1 t:MUX2_LUT7

View File

@ -2,5 +2,5 @@ module XNOR2X1 (B, A, Y);
input B;
input A;
output Y;
assign Y = !(B&!A|!B&A); // "!(B&!A|!B&A)"
assign Y = (~((B&(~A))|((~B)&A))); // "!(B&!A|!B&A)"
endmodule

View File

@ -5,7 +5,7 @@ library(dff) {
area : 1;
ff("IQ", "IQN") {
next_state : "(D)";
clocked_on : "CLK";
clocked_on : (CLK);
}
pin(D) {
direction : input;
@ -15,7 +15,7 @@ library(dff) {
}
pin(Q) {
direction: output;
function : "IQ";
function : IQ;
}
}

View File

@ -3,7 +3,7 @@ library(dff) {
area : 1 ;
ff("IQ", "IQN") {
next_state : "(D)" ;
clocked_on : "CLK" ;
clocked_on : ( CLK ) ;
}
pin(D) {
direction : input ;
@ -13,7 +13,7 @@ library(dff) {
}
pin(Q) {
direction : output ;
function : "IQ" ;
function : IQ ;
}
}
}

View File

@ -1,12 +1,12 @@
module dff (D, CLK, Q);
reg "IQ", "IQN";
reg IQ, IQN;
input D;
input CLK;
output Q;
assign Q = IQ; // "IQ"
assign Q = IQ; // IQ
always @(posedge CLK) begin
// "(D)"
"IQ" <= (D);
"IQN" <= ~((D));
IQ <= D;
IQN <= ~(D);
end
endmodule

View File

@ -1,13 +1,13 @@
module inv (A, Y);
input A;
output Y;
assign Y = ~A; // "A'"
assign Y = (~A); // "A'"
endmodule
module tri_inv (A, S, Z);
input A;
input S;
output Z;
assign Z = ~A; // "A'"
assign Z = (~A); // "A'"
endmodule
module buffer (A, Y);
input A;
@ -18,29 +18,29 @@ module nand2 (A, B, Y);
input A;
input B;
output Y;
assign Y = ~(A&B); // "(A * B)'"
assign Y = (~(A&B)); // "(A * B)'"
endmodule
module nor2 (A, B, Y);
input A;
input B;
output Y;
assign Y = ~(A|B); // "(A + B)'"
assign Y = (~(A|B)); // "(A + B)'"
endmodule
module xor2 (A, B, Y);
input A;
input B;
output Y;
assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)"
assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)"
endmodule
module imux2 (A, B, S, Y);
input A;
input B;
input S;
output Y;
assign Y = ~(&(A&S)|(B&~S)&); // "( (A * S) + (B * S') )'"
assign Y = (~((A&S)|(B&(~S)))); // "( (A * S) + (B * S') )'"
endmodule
module dff (D, CLK, RESET, PRESET, Q, QN);
reg "IQ", "IQN";
reg IQ, IQN;
input D;
input CLK;
input RESET;
@ -51,26 +51,26 @@ module dff (D, CLK, RESET, PRESET, Q, QN);
assign QN = IQN; // "IQN"
always @(posedge CLK, posedge RESET, posedge PRESET) begin
if ((RESET) && (PRESET)) begin
"IQ" <= 0;
"IQN" <= 0;
IQ <= 0;
IQN <= 0;
end
else if (RESET) begin
"IQ" <= 0;
"IQN" <= 1;
IQ <= 0;
IQN <= 1;
end
else if (PRESET) begin
"IQ" <= 1;
"IQN" <= 0;
IQ <= 1;
IQN <= 0;
end
else begin
// "D"
"IQ" <= D;
"IQN" <= ~(D);
IQ <= D;
IQN <= ~(D);
end
end
endmodule
module latch (D, G, Q, QN);
reg "IQ", "IQN";
reg IQ, IQN;
input D;
input G;
output Q;
@ -79,8 +79,8 @@ module latch (D, G, Q, QN);
assign QN = IQN; // "IQN"
always @* begin
if (G) begin
"IQ" <= D;
"IQN" <= ~(D);
IQ <= D;
IQN <= ~(D);
end
end
endmodule
@ -89,14 +89,14 @@ module aoi211 (A, B, C, Y);
input B;
input C;
output Y;
assign Y = ~((A&B)|C); // "((A * B) + C)'"
assign Y = (~((A&B)|C)); // "((A * B) + C)'"
endmodule
module oai211 (A, B, C, Y);
input A;
input B;
input C;
output Y;
assign Y = ~((A|B)&C); // "((A + B) * C)'"
assign Y = (~((A|B)&C)); // "((A + B) * C)'"
endmodule
module halfadder (A, B, C, Y);
input A;
@ -104,7 +104,7 @@ module halfadder (A, B, C, Y);
output C;
assign C = (A&B); // "(A * B)"
output Y;
assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)"
assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)"
endmodule
module fulladder (A, B, CI, CO, Y);
input A;

View File

@ -0,0 +1,60 @@
library(dff_unquoted) {
cell (dff1) {
area : 1;
ff("IQ", "IQN") {
next_state : !D;
clocked_on : (CLK);
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : IQ;
}
}
cell (dff2) {
area : 1;
ff(IQ, IQN) {
next_state : D';
clocked_on : CLK;
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
}
cell (dffe) {
area : 6;
ff("IQ", "IQN") {
next_state : (D&EN) | (IQ&!EN);
clocked_on : !CLK;
}
pin(D) {
direction : input;
}
pin(EN) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
pin(QN) {
direction: output;
function : "IQN";
}
}
}

View File

@ -0,0 +1,60 @@
library(dff_unquoted) {
cell(dff1) {
area : 1 ;
ff("IQ", "IQN") {
next_state : !D ;
clocked_on : ( CLK ) ;
}
pin(D) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : IQ ;
}
}
cell(dff2) {
area : 1 ;
ff(IQ, IQN) {
next_state : D ' ;
clocked_on : CLK ;
}
pin(D) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : "IQ" ;
}
}
cell(dffe) {
area : 6 ;
ff("IQ", "IQN") {
next_state : ( D & EN ) | ( IQ & ! EN ) ;
clocked_on : !CLK ;
}
pin(D) {
direction : input ;
}
pin(EN) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : "IQ" ;
}
pin(QN) {
direction : output ;
function : "IQN" ;
}
}
}

View File

@ -0,0 +1,39 @@
module dff1 (D, CLK, Q);
reg IQ, IQN;
input D;
input CLK;
output Q;
assign Q = IQ; // IQ
always @(posedge CLK) begin
// !D
IQ <= (~D);
IQN <= ~((~D));
end
endmodule
module dff2 (D, CLK, Q);
reg IQ, IQN;
input D;
input CLK;
output Q;
assign Q = IQ; // "IQ"
always @(posedge CLK) begin
// D '
IQ <= (~D);
IQN <= ~((~D));
end
endmodule
module dffe (D, EN, CLK, Q, QN);
reg IQ, IQN;
input D;
input EN;
input CLK;
output Q;
assign Q = IQ; // "IQ"
output QN;
assign QN = IQN; // "IQN"
always @(negedge CLK) begin
// ( D & EN ) | ( IQ & ! EN )
IQ <= ((D&EN)|(IQ&(~EN)));
IQN <= ~(((D&EN)|(IQ&(~EN))));
end
endmodule

92
tests/proc/proc_mux_src.v Normal file
View File

@ -0,0 +1,92 @@
module nested(
input clk,
input [7:0] A,
input [7:0] B,
input [3:0] mode1,
input [3:0] mode2,
output reg [7:0] result1,
output reg [7:0] result2,
output reg [1:0] arith
);
localparam OP_A = 4'b0000;
localparam OP_BA = 4'b0001;
localparam OP_BB = 4'b0010;
localparam OP_C = 4'b0011;
always @(posedge clk)
begin
case (mode1)
OP_A: begin
result1 = A + B;
result2 = A - B;
arith = 2'b01;
end
OP_BA , OP_BB : begin
result1 = A * B;
result2 = A / B;
arith = 2'b00;
end
OP_C : begin
arith = 2'b10;
case (mode2)
OP_A: begin
result1 = ~B;
result2 = B;
end
OP_C: begin
result1 = A ^ B;
result2 = A == B;
end
default: begin
result1 = 1'b0;
// result2 omitted
end
endcase
end
default: begin
result1 = 8'b0;
result2 = 8'b0;
arith = 2'b11;
end
endcase
end
endmodule
module tiny(
input clk,
input ya,
input [7:0] in,
output reg [7:0] out,
);
always @(posedge clk)
begin
case (ya)
1'b1: begin
out = in;
end
endcase
end
endmodule
module tiny2(
input clk,
input [1:0] ya,
input [7:0] in,
output reg [7:0] out,
);
always @(posedge clk)
begin
case (ya)
2'b01: begin
out = in;
end
2'b10: begin
out = 1'b1;
end
default begin
out = 1'b0;
end
endcase
end
endmodule

View File

@ -0,0 +1,33 @@
read_verilog proc_mux_src.v
proc -noopt
check -assert
# eq refer to the values compared against
select -assert-count 2 tiny2/t:$eq
select -assert-count 1 tiny2/t:$eq a:src=proc_mux_src.v:81.4-81.10 %i
select -assert-count 1 tiny2/t:$eq a:src=proc_mux_src.v:84.4-84.10 %i
# Flops cover the whole process
select -assert-count 1 tiny2/t:$dff
select -assert-count 1 tiny2/t:$dff a:src=proc_mux_src.v:78.2-91.5 %i
# Muxes are marked to the exact assignment statements they represent including the explicit default case
select -assert-count 1 tiny2/t:$pmux
select -assert-count 1 tiny2/t:$pmux a:src=proc_mux_src.v:80.5-80.13|proc_mux_src.v:83.5-83.15|proc_mux_src.v:86.5-86.15
select -assert-count 0 tiny/t:$reduce_or
# Implicit default cases add src attributes to muxes that cover the whole switch
select -assert-count 1 tiny/t:$mux
select -assert-count 1 tiny/t:$mux a:proc_mux_src.v:65.5-65.13|proc_mux_src.v:63.3-67.10
select -assert-count 0 tiny/t:$reduce_or
dump nested
#dump nested/t:$pmux
# $reduce_or src covers the entire list of comparison RHSs
# Each snippet is treated separately so it gets its own $eq and $reduce_or etc
select -assert-count 3 nested/t:$reduce_or
select -assert-count 3 nested/t:$reduce_or a:src=proc_mux_src.v:25.4-25.19 %i
# When switches are nested, the top mux considers the inner switch the entire source
# for one of its inputs. Here, that's proc_mux_src.v:32.5-45.12
select -assert-count 5 nested/t:$pmux
select -assert-count 1 nested/t:$pmux a:src=proc_mux_src.v:21.5-21.20|proc_mux_src.v:26.5-26.20|proc_mux_src.v:32.5-45.12|proc_mux_src.v:48.5-48.19 %i
# No nesting for output reg arith
select -assert-count 1 nested/t:$pmux a:src=proc_mux_src.v:23.5-23.18|proc_mux_src.v:28.5-28.18|proc_mux_src.v:31.5-31.18|proc_mux_src.v:50.5-50.18 %i
dump nested/t:$pmux

View File

@ -0,0 +1,33 @@
read_verilog -sv << EOF
interface simple_if;
logic receiver;
logic driver;
endinterface
module driver_mod(simple_if intf, input in);
assign intf.driver = in;
endmodule
module receiver_mod(simple_if intf);
assign intf.receiver = intf.driver;
endmodule
module top(
input logic [1:0] inputs,
output logic [1:0] outputs
);
simple_if intf0();
simple_if intf1();
driver_mod d0(intf0, inputs[0]);
driver_mod d1(intf1, inputs[1]);
receiver_mod r0(intf0);
receiver_mod r1(intf1);
assign outputs = {intf0.receiver, intf1.receiver};
endmodule
EOF
logger -expect error "Unable to connect.* with positional interface" 1
hierarchy -top top

View File

@ -5,3 +5,4 @@
./run_simple.sh load_and_derive
./run_simple.sh resolve_types
./run_simple.sh positional_args

View File

@ -30,11 +30,11 @@ TESTS := $(addprefix $(BINTEST)/, $(basename $(ALLTESTFILE:%Test.cc=%Test.o)))
all: prepare $(TESTS) run-tests
$(BINTEST)/%: $(OBJTEST)/%.o
$(BINTEST)/%: $(OBJTEST)/%.o | prepare
$(CXX) -L$(ROOTPATH) $(RPATH) $(LINKFLAGS) -o $@ $^ $(LIBS) \
$(GTEST_LDFLAGS) $(EXTRAFLAGS)
$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc
$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc | prepare
$(CXX) -o $@ -c -I$(ROOTPATH) $(CPPFLAGS) $(CXXFLAGS) $(GTEST_CXXFLAGS) $^
.PHONY: prepare run-tests clean

View File

@ -13,7 +13,7 @@ namespace RTLIL {
void checkAll(std::initializer_list<std::string> expressions, std::string expected) {
for (const auto& e : expressions) {
auto helper = LibertyExpression::Lexer(e);
auto tree_s = LibertyExpression::parse(helper).str();
auto tree_s = LibertyExpression::parse(helper).sexpr_str();
EXPECT_EQ(tree_s, expected);
}
}
@ -82,6 +82,11 @@ namespace RTLIL {
}, "(and (pin \"x\")\n"
" (not (pin \"y\")))"
);
checkAll({
"( D & EN )",
}, "(and (pin \"D\")\n"
" (pin \"EN\"))"
);
}
}