aboutsummaryrefslogtreecommitdiffstats
path: root/frontends/verific/verificsva.cc
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/verific/verificsva.cc')
-rw-r--r--frontends/verific/verificsva.cc384
1 files changed, 384 insertions, 0 deletions
diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc
new file mode 100644
index 000000000..5a56b2fe0
--- /dev/null
+++ b/frontends/verific/verificsva.cc
@@ -0,0 +1,384 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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/yosys.h"
+#include "frontends/verific/verific.h"
+
+USING_YOSYS_NAMESPACE
+
+#ifdef VERIFIC_NAMESPACE
+using namespace Verific;
+#endif
+
+YOSYS_NAMESPACE_BEGIN
+
+struct VerificSvaImporter
+{
+ VerificImporter *importer = nullptr;
+ Module *module = nullptr;
+
+ Netlist *netlist = nullptr;
+ Instance *root = nullptr;
+
+ SigBit clock = State::Sx;
+ bool clock_posedge = false;
+
+ SigBit disable_iff = State::S0;
+
+ bool mode_assert = false;
+ bool mode_assume = false;
+ bool mode_cover = false;
+ bool eventually = false;
+ bool did_something = false;
+
+ Instance *net_to_ast_driver(Net *n)
+ {
+ if (n == nullptr)
+ return nullptr;
+
+ if (n->IsMultipleDriven())
+ return nullptr;
+
+ Instance *inst = n->Driver();
+
+ if (inst == nullptr)
+ return nullptr;
+
+ if (!verific_sva_prims.count(inst->Type()))
+ return nullptr;
+
+ if (inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL ||
+ inst->Type() == PRIM_SVA_STABLE || inst->Type() == OPER_SVA_STABLE || inst->Type() == PRIM_SVA_PAST)
+ return nullptr;
+
+ return inst;
+ }
+
+ Instance *get_ast_input(Instance *inst) { return net_to_ast_driver(inst->GetInput()); }
+ Instance *get_ast_input1(Instance *inst) { return net_to_ast_driver(inst->GetInput1()); }
+ Instance *get_ast_input2(Instance *inst) { return net_to_ast_driver(inst->GetInput2()); }
+ Instance *get_ast_input3(Instance *inst) { return net_to_ast_driver(inst->GetInput3()); }
+ Instance *get_ast_control(Instance *inst) { return net_to_ast_driver(inst->GetControl()); }
+
+ // ----------------------------------------------------------
+ // SVA Preprocessor
+
+ Net *rewrite_input(Instance *inst) { return rewrite(get_ast_input(inst), inst->GetInput()); }
+ Net *rewrite_input1(Instance *inst) { return rewrite(get_ast_input1(inst), inst->GetInput1()); }
+ Net *rewrite_input2(Instance *inst) { return rewrite(get_ast_input2(inst), inst->GetInput2()); }
+ Net *rewrite_input3(Instance *inst) { return rewrite(get_ast_input3(inst), inst->GetInput3()); }
+ Net *rewrite_control(Instance *inst) { return rewrite(get_ast_control(inst), inst->GetControl()); }
+
+ Net *rewrite(Instance *inst, Net *default_net = nullptr)
+ {
+ if (inst == nullptr)
+ return default_net;
+
+ if (inst->Type() == PRIM_SVA_ASSERT || inst->Type() == PRIM_SVA_COVER || inst->Type() == PRIM_SVA_ASSUME ||
+ inst->Type() == PRIM_SVA_IMMEDIATE_ASSERT || inst->Type() == PRIM_SVA_IMMEDIATE_COVER || inst->Type() == PRIM_SVA_IMMEDIATE_ASSUME) {
+ Net *new_net = rewrite(get_ast_input(inst));
+ if (new_net) {
+ inst->Disconnect(inst->View()->GetInput());
+ inst->Connect(inst->View()->GetInput(), new_net);
+ }
+ return default_net;
+ }
+
+ if (inst->Type() == PRIM_SVA_AT || inst->Type() == PRIM_SVA_DISABLE_IFF) {
+ Net *new_net = rewrite(get_ast_input2(inst));
+ if (new_net) {
+ inst->Disconnect(inst->View()->GetInput2());
+ inst->Connect(inst->View()->GetInput2(), new_net);
+ }
+ return default_net;
+ }
+
+ if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION)
+ {
+ if (mode_cover) {
+ did_something = true;
+ Net *new_in1 = rewrite_input1(inst);
+ Net *new_in2 = rewrite_input2(inst);
+ return netlist->SvaBinary(PRIM_SVA_SEQ_CONCAT, new_in1, new_in2, inst->Linefile());
+ }
+ return default_net;
+ }
+
+ if (inst->Type() == PRIM_SVA_NOT)
+ {
+ if (mode_assert || mode_assume) {
+ did_something = true;
+ Net *new_in = rewrite_input(inst);
+ Net *net_zero = netlist->Gnd(inst->Linefile());
+ return netlist->SvaBinary(PRIM_SVA_OVERLAPPED_IMPLICATION, new_in, net_zero, inst->Linefile());
+ }
+ return default_net;
+ }
+
+ return default_net;
+ }
+
+ void rewrite()
+ {
+ netlist = root->Owner();
+ do {
+ did_something = false;
+ rewrite(root);
+ } while (did_something);
+ }
+
+ // ----------------------------------------------------------
+ // SVA Importer
+
+ struct sequence_t {
+ int length = 0;
+ SigBit sig_a = State::S1;
+ SigBit sig_en = State::S1;
+ };
+
+ void sequence_cond(sequence_t &seq, SigBit cond)
+ {
+ seq.sig_a = module->And(NEW_ID, seq.sig_a, cond);
+ }
+
+ void sequence_ff(sequence_t &seq)
+ {
+ if (disable_iff != State::S0)
+ seq.sig_en = module->Mux(NEW_ID, seq.sig_en, State::S0, disable_iff);
+
+ Wire *sig_a_q = module->addWire(NEW_ID);
+ sig_a_q->attributes["\\init"] = Const(0, 1);
+
+ Wire *sig_en_q = module->addWire(NEW_ID);
+ sig_en_q->attributes["\\init"] = Const(0, 1);
+
+ module->addDff(NEW_ID, clock, seq.sig_a, sig_a_q, clock_posedge);
+ module->addDff(NEW_ID, clock, seq.sig_en, sig_en_q, clock_posedge);
+
+ seq.length++;
+ seq.sig_a = sig_a_q;
+ seq.sig_en = sig_en_q;
+ }
+
+ void parse_sequence(sequence_t &seq, Net *n)
+ {
+ Instance *inst = net_to_ast_driver(n);
+
+ // Regular expression
+
+ if (inst == nullptr) {
+ sequence_cond(seq, importer->net_map_at(n));
+ return;
+ }
+
+ // SVA Primitives
+
+ if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION)
+ {
+ parse_sequence(seq, inst->GetInput1());
+ seq.sig_en = module->And(NEW_ID, seq.sig_en, seq.sig_a);
+ parse_sequence(seq, inst->GetInput2());
+ return;
+ }
+
+ if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION)
+ {
+ parse_sequence(seq, inst->GetInput1());
+ seq.sig_en = module->And(NEW_ID, seq.sig_en, seq.sig_a);
+ sequence_ff(seq);
+ parse_sequence(seq, inst->GetInput2());
+ return;
+ }
+
+ if (inst->Type() == PRIM_SVA_SEQ_CONCAT)
+ {
+ int sva_low = atoi(inst->GetAttValue("sva:low"));
+ int sva_high = atoi(inst->GetAttValue("sva:low"));
+
+ if (sva_low != sva_high)
+ log_error("Ranges on SVA sequence concatenation operator are not supported at the moment.\n");
+
+ parse_sequence(seq, inst->GetInput1());
+
+ for (int i = 0; i < sva_low; i++)
+ sequence_ff(seq);
+
+ parse_sequence(seq, inst->GetInput2());
+ return;
+ }
+
+ if (inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT)
+ {
+ int sva_low = atoi(inst->GetAttValue("sva:low"));
+ int sva_high = atoi(inst->GetAttValue("sva:low"));
+
+ if (sva_low != sva_high)
+ log_error("Ranges on SVA consecutive repeat operator are not supported at the moment.\n");
+
+ parse_sequence(seq, inst->GetInput());
+
+ for (int i = 1; i < sva_low; i++) {
+ sequence_ff(seq);
+ parse_sequence(seq, inst->GetInput());
+ }
+ return;
+ }
+
+ // Handle unsupported primitives
+
+ if (!importer->mode_keep)
+ log_error("Unsupported Verific SVA primitive %s of type %s.\n", inst->Name(), inst->View()->Owner()->Name());
+ log_warning("Unsupported Verific SVA primitive %s of type %s.\n", inst->Name(), inst->View()->Owner()->Name());
+ }
+
+ void import()
+ {
+ module = importer->module;
+ netlist = root->Owner();
+
+ RTLIL::IdString root_name = module->uniquify(importer->mode_names || root->IsUserDeclared() ? RTLIL::escape_id(root->Name()) : NEW_ID);
+
+ // parse SVA property clock event
+
+ Instance *at_node = get_ast_input(root);
+
+ // asynchronous immediate assertion/assumption/cover
+ if (at_node == nullptr && (root->Type() == PRIM_SVA_IMMEDIATE_ASSERT ||
+ root->Type() == PRIM_SVA_IMMEDIATE_COVER || root->Type() == PRIM_SVA_IMMEDIATE_ASSUME))
+ {
+ SigSpec sig_a = importer->net_map_at(root->GetInput());
+ RTLIL::Cell *c = nullptr;
+
+ if (eventually) {
+ if (mode_assert) c = module->addLive(root_name, sig_a, State::S1);
+ if (mode_assume) c = module->addFair(root_name, sig_a, State::S1);
+ } else {
+ if (mode_assert) c = module->addAssert(root_name, sig_a, State::S1);
+ if (mode_assume) c = module->addAssume(root_name, sig_a, State::S1);
+ if (mode_cover) c = module->addCover(root_name, sig_a, State::S1);
+ }
+
+ importer->import_attributes(c->attributes, root);
+ return;
+ }
+
+ log_assert(at_node && at_node->Type() == PRIM_SVA_AT);
+
+ VerificClockEdge clock_edge(importer, get_ast_input1(at_node));
+ clock = clock_edge.clock_sig;
+ clock_posedge = clock_edge.posedge;
+
+ // parse disable_iff expression
+
+ Net *sequence_net = at_node->GetInput2();
+
+ while (1)
+ {
+ Instance *sequence_node = net_to_ast_driver(sequence_net);
+
+ if (sequence_node && sequence_node->Type() == PRIM_SVA_S_EVENTUALLY) {
+ eventually = true;
+ sequence_net = sequence_node->GetInput();
+ continue;
+ }
+
+ if (sequence_node && sequence_node->Type() == PRIM_SVA_DISABLE_IFF) {
+ disable_iff = importer->net_map_at(sequence_node->GetInput1());
+ sequence_net = sequence_node->GetInput2();
+ continue;
+ }
+
+ break;
+ }
+
+ // parse SVA sequence into trigger signal
+
+ sequence_t seq;
+ parse_sequence(seq, sequence_net);
+ sequence_ff(seq);
+
+ // generate assert/assume/cover cell
+
+ RTLIL::Cell *c = nullptr;
+
+ if (eventually) {
+ if (mode_assert) c = module->addLive(root_name, seq.sig_a, seq.sig_en);
+ if (mode_assume) c = module->addFair(root_name, seq.sig_a, seq.sig_en);
+ } else {
+ if (mode_assert) c = module->addAssert(root_name, seq.sig_a, seq.sig_en);
+ if (mode_assume) c = module->addAssume(root_name, seq.sig_a, seq.sig_en);
+ if (mode_cover) c = module->addCover(root_name, seq.sig_a, seq.sig_en);
+ }
+
+ importer->import_attributes(c->attributes, root);
+ }
+};
+
+void svapp_assert(Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.root = inst;
+ worker.mode_assert = true;
+ worker.rewrite();
+}
+
+void svapp_assume(Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.root = inst;
+ worker.mode_assume = true;
+ worker.rewrite();
+}
+
+void svapp_cover(Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.root = inst;
+ worker.mode_cover = true;
+ worker.rewrite();
+}
+
+void import_sva_assert(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_assert = true;
+ worker.import();
+}
+
+void import_sva_assume(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_assume = true;
+ worker.import();
+}
+
+void import_sva_cover(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_cover = true;
+ worker.import();
+}
+
+YOSYS_NAMESPACE_END