diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 01b4bde53..cfdf3efc5 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -242,11 +242,28 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT case RTLIL::STi: f << stringf("init\n"); break; } - for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) { + for (auto &it: sy->actions) { f << stringf("%s update ", indent.c_str()); - dump_sigspec(f, it->first); + dump_sigspec(f, it.first); f << stringf(" "); - dump_sigspec(f, it->second); + dump_sigspec(f, it.second); + f << stringf("\n"); + } + + for (auto &it: sy->mem_write_actions) { + for (auto it2 = it.attributes.begin(); it2 != it.attributes.end(); ++it2) { + f << stringf("%s attribute %s ", indent.c_str(), it2->first.c_str()); + dump_const(f, it2->second); + f << stringf("\n"); + } + f << stringf("%s memwr %s ", indent.c_str(), it.memid.c_str()); + dump_sigspec(f, it.address); + f << stringf(" "); + dump_sigspec(f, it.data); + f << stringf(" "); + dump_sigspec(f, it.enable); + f << stringf(" "); + dump_sigspec(f, it.priority_mask); f << stringf("\n"); } } diff --git a/frontends/rtlil/rtlil_lexer.l b/frontends/rtlil/rtlil_lexer.l index beef220f6..897ebf667 100644 --- a/frontends/rtlil/rtlil_lexer.l +++ b/frontends/rtlil/rtlil_lexer.l @@ -79,6 +79,7 @@ USING_YOSYS_NAMESPACE "global" { return TOK_GLOBAL; } "init" { return TOK_INIT; } "update" { return TOK_UPDATE; } +"memwr" { return TOK_MEMWR; } "process" { return TOK_PROCESS; } "end" { return TOK_END; } diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y index 646489196..7a8f508bf 100644 --- a/frontends/rtlil/rtlil_parser.y +++ b/frontends/rtlil/rtlil_parser.y @@ -69,7 +69,7 @@ USING_YOSYS_NAMESPACE %token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC %token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT -%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET +%token TOK_UPDATE TOK_MEMWR TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET %token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO %type sigspec_list_reversed @@ -155,6 +155,7 @@ param_defval_stmt: TOK_PARAMETER TOK_ID constant EOL { current_module->avail_parameters($2); current_module->parameter_default_values[$2] = *$3; + delete $3; free($2); }; @@ -389,6 +390,22 @@ update_list: delete $3; delete $4; } | + update_list attr_list TOK_MEMWR TOK_ID sigspec sigspec sigspec constant EOL { + RTLIL::MemWriteAction act; + act.attributes = attrbuf; + act.memid = $4; + act.address = *$5; + act.data = *$6; + act.enable = *$7; + act.priority_mask = *$8; + current_process->syncs.back()->mem_write_actions.push_back(std::move(act)); + attrbuf.clear(); + free($4); + delete $5; + delete $6; + delete $7; + delete $8; + } | /* empty */; constant: diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 40079ffc5..c3ae5d243 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -4538,6 +4538,7 @@ RTLIL::SyncRule *RTLIL::SyncRule::clone() const new_syncrule->type = type; new_syncrule->signal = signal; new_syncrule->actions = actions; + new_syncrule->mem_write_actions = mem_write_actions; return new_syncrule; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f03f27617..10cb039d5 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -69,6 +69,7 @@ namespace RTLIL struct SigSpec; struct CaseRule; struct SwitchRule; + struct MemWriteAction; struct SyncRule; struct Process; @@ -1541,11 +1542,21 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject RTLIL::SwitchRule *clone() const; }; +struct RTLIL::MemWriteAction : RTLIL::AttrObject +{ + RTLIL::IdString memid; + RTLIL::SigSpec address; + RTLIL::SigSpec data; + RTLIL::SigSpec enable; + RTLIL::Const priority_mask; +}; + struct RTLIL::SyncRule { RTLIL::SyncType type; RTLIL::SigSpec signal; std::vector actions; + std::vector mem_write_actions; template void rewrite_sigspecs(T &functor); template void rewrite_sigspecs2(T &functor); @@ -1693,6 +1704,11 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor) functor(it.first); functor(it.second); } + for (auto &it : mem_write_actions) { + functor(it.address); + functor(it.data); + functor(it.enable); + } } template @@ -1702,6 +1718,11 @@ void RTLIL::SyncRule::rewrite_sigspecs2(T &functor) for (auto &it : actions) { functor(it.first, it.second); } + for (auto &it : mem_write_actions) { + functor(it.address); + functor(it.data); + functor(it.enable); + } } template diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex index 83db5aac7..50a56137c 100644 --- a/manual/CHAPTER_Overview.tex +++ b/manual/CHAPTER_Overview.tex @@ -350,8 +350,9 @@ to update {\tt \textbackslash{}q}. An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and exactly one RTLIL::CaseRule object, which is called the {\it root case}. -An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type) and zero or -more assignments (RTLIL::SigSig). The {\tt always} synchronization condition is used to break combinatorial +An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type), zero or +more assignments (RTLIL::SigSig), and zero or more memory writes (RTLIL::MemWriteAction). +The {\tt always} synchronization condition is used to break combinatorial loops when a latch should be inferred instead. An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig) diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index da81e7f09..40207b1fc 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -339,6 +339,23 @@ struct BugpointPass : public Pass { return design_copy; } } + int i = 0; + for (auto it = sy->mem_write_actions.begin(); it != sy->mem_write_actions.end(); ++it, ++i) + { + if (index++ == seed) + { + log_header(design, "Trying to remove sync %s memwr %s %s %s %s in %s.%s.\n", log_signal(sy->signal), log_id(it->memid), log_signal(it->address), log_signal(it->data), log_signal(it->enable), log_id(mod), log_id(pr.first)); + sy->mem_write_actions.erase(it); + // Remove the bit for removed action from other actions' priority masks. + for (auto it2 = sy->mem_write_actions.begin(); it2 != sy->mem_write_actions.end(); ++it2) { + auto &mask = it2->priority_mask; + if (GetSize(mask) > i) { + mask.bits.erase(mask.bits.begin() + i); + } + } + return design_copy; + } + } } } } diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 36febb98a..b502b0788 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -141,6 +141,14 @@ struct CheckPass : public Pass { for (auto bit : sigmap(action.second)) if (bit.wire) used_wires.insert(bit); } + for (auto memwr : sync->mem_write_actions) { + for (auto bit : sigmap(memwr.address)) + if (bit.wire) used_wires.insert(bit); + for (auto bit : sigmap(memwr.data)) + if (bit.wire) used_wires.insert(bit); + for (auto bit : sigmap(memwr.enable)) + if (bit.wire) used_wires.insert(bit); + } } } diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 0c96f8c5d..a389c3179 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -339,6 +339,11 @@ struct ShowWorker { input_signals.insert(obj->signal); collect_proc_signals(obj->actions, input_signals, output_signals); + for (auto it : obj->mem_write_actions) { + input_signals.insert(it.address); + input_signals.insert(it.data); + input_signals.insert(it.enable); + } } void collect_proc_signals(RTLIL::Process *obj, std::set &input_signals, std::set &output_signals) diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index 4b56979f8..50244bf33 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -8,3 +8,4 @@ OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o +OBJS += passes/proc/proc_memwr.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 09cf0af82..2151b0ce7 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -43,6 +43,7 @@ struct ProcPass : public Pass { log(" proc_mux\n"); log(" proc_dlatch\n"); log(" proc_dff\n"); + log(" proc_memwr\n"); log(" proc_clean\n"); log("\n"); log("This replaces the processes in the design with multiplexers,\n"); @@ -102,6 +103,7 @@ struct ProcPass : public Pass { Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux"); Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); + Pass::call(design, "proc_memwr"); Pass::call(design, "proc_clean"); log_pop(); diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 16db461b2..4351321e0 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -153,6 +153,30 @@ void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec con } } +RTLIL::SigSpec apply_reset(RTLIL::Module *mod, RTLIL::Process *proc, RTLIL::SyncRule *sync, SigMap &assign_map, RTLIL::SigSpec root_sig, bool polarity, RTLIL::SigSpec sig, RTLIL::SigSpec log_sig) { + RTLIL::SigSpec rspec = assign_map(sig); + RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); + for (int i = 0; i < GetSize(rspec); i++) + if (rspec[i].wire == NULL) + rval[i] = rspec[i]; + RTLIL::SigSpec last_rval; + for (int count = 0; rval != last_rval; count++) { + last_rval = rval; + apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false); + assign_map.apply(rval); + if (rval.is_fully_const()) + break; + if (count > 100) + log_error("Async reset %s yields endless loop at value %s for signal %s.\n", + log_signal(sync->signal), log_signal(rval), log_signal(log_sig)); + rspec = rval; + } + if (rval.has_marked_bits()) + log_error("Async reset %s yields non-constant value %s for signal %s.\n", + log_signal(sync->signal), log_signal(rval), log_signal(log_sig)); + return rval; +} + void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) { restart_proc_arst: @@ -172,28 +196,18 @@ restart_proc_arst: sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; } for (auto &action : sync->actions) { - RTLIL::SigSpec rspec = assign_map(action.second); - RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); - for (int i = 0; i < GetSize(rspec); i++) - if (rspec[i].wire == NULL) - rval[i] = rspec[i]; - RTLIL::SigSpec last_rval; - for (int count = 0; rval != last_rval; count++) { - last_rval = rval; - apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false); - assign_map.apply(rval); - if (rval.is_fully_const()) - break; - if (count > 100) - log_error("Async reset %s yields endless loop at value %s for signal %s.\n", - log_signal(sync->signal), log_signal(rval), log_signal(action.first)); - rspec = rval; - } - if (rval.has_marked_bits()) - log_error("Async reset %s yields non-constant value %s for signal %s.\n", - log_signal(sync->signal), log_signal(rval), log_signal(action.first)); - action.second = rval; + action.second = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, action.second, action.first); } + for (auto &memwr : sync->mem_write_actions) { + RTLIL::SigSpec en = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.enable, memwr.enable); + if (!en.is_fully_zero()) { + log_error("Async reset %s causes memory write to %s.\n", + log_signal(sync->signal), log_id(memwr.memid)); + } + apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.address, memwr.address); + apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.data, memwr.data); + } + sync->mem_write_actions.clear(); eliminate_const(mod, &proc->root_case, root_sig, polarity); goto restart_proc_arst; } diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 54d5a81ff..9e0b671f4 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -161,7 +161,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++) if (proc->syncs[i]->actions[j].first.size() == 0) proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--)); - if (proc->syncs[i]->actions.size() == 0) { + if (proc->syncs[i]->actions.size() == 0 && proc->syncs[i]->mem_write_actions.size() == 0) { delete proc->syncs[i]; proc->syncs.erase(proc->syncs.begin() + (i--)); } diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 7b8c05b21..03d072cf4 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -342,7 +342,6 @@ struct proc_dlatch_db_t void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) { - std::vector new_syncs; RTLIL::SigSig latches_bits, nolatches_bits; dict latches_out_in; dict latches_hold; @@ -351,7 +350,6 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) for (auto sr : proc->syncs) { if (sr->type != RTLIL::SyncType::STa) { - new_syncs.push_back(sr); continue; } @@ -373,8 +371,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) for (int i = 0; i < GetSize(ss.first); i++) latches_out_in[ss.first[i]] = ss.second[i]; } - - delete sr; + sr->actions.clear(); } latches_out_in.sort(); @@ -441,8 +438,6 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) offset += width; } - - new_syncs.swap(proc->syncs); } struct ProcDlatchPass : public Pass { diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index eb323038d..b705251dd 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -71,17 +71,8 @@ void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc) offset += lhs_c.width; } } + sync->actions.clear(); } - - if (found_init) { - std::vector new_syncs; - for (auto &sync : proc->syncs) - if (sync->type == RTLIL::SyncType::STi) - delete sync; - else - new_syncs.push_back(sync); - proc->syncs.swap(new_syncs); - } } struct ProcInitPass : public Pass { diff --git a/passes/proc/proc_memwr.cc b/passes/proc/proc_memwr.cc new file mode 100644 index 000000000..f898979d8 --- /dev/null +++ b/passes/proc/proc_memwr.cc @@ -0,0 +1,111 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Koƛcielnicka + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/ffinit.h" +#include "kernel/consteval.h" +#include "kernel/log.h" +#include +#include +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void proc_memwr(RTLIL::Module *mod, RTLIL::Process *proc, dict &next_priority) +{ + for (auto sr : proc->syncs) + { + for (auto memwr : sr->mem_write_actions) { + RTLIL::Cell *cell = mod->addCell(NEW_ID, ID($memwr)); + cell->attributes = memwr.attributes; + cell->setParam(ID::MEMID, Const(memwr.memid.str())); + cell->setParam(ID::ABITS, GetSize(memwr.address)); + cell->setParam(ID::WIDTH, GetSize(memwr.data)); + cell->setParam(ID::PRIORITY, next_priority[memwr.memid]++); + cell->setPort(ID::ADDR, memwr.address); + cell->setPort(ID::DATA, memwr.data); + SigSpec enable = memwr.enable; + for (auto sr2 : proc->syncs) { + if (sr2->type == RTLIL::SyncType::ST0) { + log_assert(sr2->mem_write_actions.empty()); + enable = mod->Mux(NEW_ID, Const(State::S0, GetSize(enable)), enable, sr2->signal); + } else if (sr2->type == RTLIL::SyncType::ST1) { + log_assert(sr2->mem_write_actions.empty()); + enable = mod->Mux(NEW_ID, enable, Const(State::S0, GetSize(enable)), sr2->signal); + } + } + cell->setPort(ID::EN, enable); + if (sr->type == RTLIL::SyncType::STa) { + cell->setPort(ID::CLK, State::Sx); + cell->setParam(ID::CLK_ENABLE, State::S0); + cell->setParam(ID::CLK_POLARITY, State::Sx); + } else if (sr->type == RTLIL::SyncType::STp) { + cell->setPort(ID::CLK, sr->signal); + cell->setParam(ID::CLK_ENABLE, State::S1); + cell->setParam(ID::CLK_POLARITY, State::S1); + } else if (sr->type == RTLIL::SyncType::STn) { + cell->setPort(ID::CLK, sr->signal); + cell->setParam(ID::CLK_ENABLE, State::S1); + cell->setParam(ID::CLK_POLARITY, State::S0); + } else { + log_error("process memory write with unsupported sync type in %s.%s", log_id(mod), log_id(proc)); + } + } + sr->mem_write_actions.clear(); + } +} + +struct ProcMemWrPass : public Pass { + ProcMemWrPass() : Pass("proc_memwr", "extract memory writes from processes") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_memwr [selection]\n"); + log("\n"); + log("This pass converts memory writes in processes into $memwr cells.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing PROC_MEMWR pass (convert process memory writes to cells).\n"); + + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) { + dict next_priority; + for (auto cell : module->cells()) { + if (cell->type == ID($memwr)) { + IdString memid = cell->parameters.at(ID::MEMID).decode_string(); + int priority = cell->parameters.at(ID::PRIORITY).as_int(); + if (priority >= next_priority[memid]) + next_priority[memid] = priority + 1; + } + } + for (auto &proc_it : module->processes) + if (design->selected(module, proc_it.second)) + proc_memwr(module, proc_it.second, next_priority); + } + } +} ProcMemWrPass; + +PRIVATE_NAMESPACE_END +