diff --git a/Makefile.in b/Makefile.in index d648f66a45..65223fb600 100644 --- a/Makefile.in +++ b/Makefile.in @@ -119,7 +119,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o vpi_modules.o target.o \ Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PFunction.o \ - PGate.o PGenerate.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o \ + PGate.o PGenerate.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o PTimingCheck.o \ PTask.o PUdp.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) all: dep config.h _pli_types.h version_tag.h ivl@EXEEXT@ version.exe iverilog-vpi.man diff --git a/Module.h b/Module.h index 3630d4b1cb..3b77fb8c84 100644 --- a/Module.h +++ b/Module.h @@ -37,6 +37,7 @@ class PGate; class PGenerate; class PModport; class PSpecPath; +class PTimingCheck; class PTask; class PFunction; class PWire; @@ -136,7 +137,9 @@ class Module : public PScopeExtra, public PNamedItem { program blocks. */ std::map modports; + /* List for specify paths and timing checks */ std::list specify_paths; + std::list timing_checks; // The mod_name() is the name of the module type. perm_string mod_name() const { return pscope_name(); } @@ -170,6 +173,7 @@ class Module : public PScopeExtra, public PNamedItem { private: void dump_specparams_(std::ostream&out, unsigned indent) const; + void dump_timingchecks_(std::ostream&out, unsigned indent) const; std::list gates_; private: // Not implemented diff --git a/PTimingCheck.cc b/PTimingCheck.cc new file mode 100644 index 0000000000..49ae2ec8ac --- /dev/null +++ b/PTimingCheck.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2023 Stephen Williams + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "PTimingCheck.h" + +PRecRem::PRecRem(event_t* reference_event, + event_t* data_event, + PExpr* setup_limit, + PExpr* hold_limit, + pform_name_t* notifier, + PExpr* timestamp_cond, + PExpr* timecheck_cond, + pform_name_t* delayed_reference, + pform_name_t* delayed_data) + : + reference_event_ (reference_event), + data_event_ (data_event), + setup_limit_ (setup_limit), + hold_limit_ (hold_limit), + notifier_ (notifier), + timestamp_cond_ (timestamp_cond), + timecheck_cond_ (timecheck_cond), + delayed_reference_ (delayed_reference), + delayed_data_ (delayed_data) +{ +} + +PRecRem::~PRecRem() +{ +} + +PSetupHold::PSetupHold(event_t* reference_event, + event_t* data_event, + PExpr* setup_limit, + PExpr* hold_limit, + pform_name_t* notifier, + PExpr* timestamp_cond, + PExpr* timecheck_cond, + pform_name_t* delayed_reference, + pform_name_t* delayed_data) + : + reference_event_ (reference_event), + data_event_ (data_event), + setup_limit_ (setup_limit), + hold_limit_ (hold_limit), + notifier_ (notifier), + timestamp_cond_ (timestamp_cond), + timecheck_cond_ (timecheck_cond), + delayed_reference_ (delayed_reference), + delayed_data_ (delayed_data) +{ +} + +PSetupHold::~PSetupHold() +{ +} diff --git a/PTimingCheck.h b/PTimingCheck.h new file mode 100644 index 0000000000..cc5a5c61ca --- /dev/null +++ b/PTimingCheck.h @@ -0,0 +1,139 @@ +#ifndef IVL_PTimingCheck_H +#define IVL_PTimingCheck_H +/* + * Copyright (c) 2006-2023 Stephen Williams + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "LineInfo.h" +# include "PExpr.h" +# include "pform_types.h" +# include + +/* +* The PTimingCheck is the base class for all timing checks +*/ +class PTimingCheck : public LineInfo { + + public: + enum EdgeType {EDGE_01, EDGE_0X, EDGE_10, EDGE_1X, EDGE_X0, EDGE_X1}; + + struct event_t { + pform_name_t name; + bool posedge; + bool negedge; + std::vector edges; + std::unique_ptr condition; + }; + + // This struct is used to parse the optional arguments + struct optional_args_t { + pform_name_t* notifier = nullptr; + PExpr* timestamp_cond = nullptr; + PExpr* timecheck_cond = nullptr; + pform_name_t* delayed_reference = nullptr; + pform_name_t* delayed_data = nullptr; + PExpr* event_based_flag = nullptr; + PExpr* remain_active_flag = nullptr; + }; + + PTimingCheck() { } + virtual ~PTimingCheck() { } + + virtual void elaborate(class Design*des, class NetScope*scope) const = 0; + + virtual void dump(std::ostream&out, unsigned ind) const = 0; +}; + +/* +* The PRecRem is the parse of a $recrem timing check +*/ +class PRecRem : public PTimingCheck { + + public: + + PRecRem(event_t* reference_event, + event_t* data_event, + PExpr* setup_limit, + PExpr* hold_limit, + pform_name_t* notifier, + PExpr* timestamp_cond, + PExpr* timecheck_cond, + pform_name_t* delayed_reference, + pform_name_t* delayed_data); + + ~PRecRem(); + + void elaborate(class Design*des, class NetScope*scope) const override; + + void dump(std::ostream&out, unsigned ind) const override; + + private: + std::unique_ptr reference_event_; + std::unique_ptr data_event_; + + std::unique_ptr setup_limit_; + std::unique_ptr hold_limit_; + + std::unique_ptr notifier_; + + std::unique_ptr timestamp_cond_; + std::unique_ptr timecheck_cond_; + + std::unique_ptr delayed_reference_; + std::unique_ptr delayed_data_; +}; + +/* +* The PSetupHold is the parse of a $setuphold timing check +*/ +class PSetupHold : public PTimingCheck { + + public: + PSetupHold(event_t* reference_event, + event_t* data_event, + PExpr* setup_limit, + PExpr* hold_limit, + pform_name_t* notifier, + PExpr* timestamp_cond, + PExpr* timecheck_cond, + pform_name_t* delayed_reference, + pform_name_t* delayed_data); + + ~PSetupHold(); + + void elaborate(class Design*des, class NetScope*scope) const override; + + void dump(std::ostream&out, unsigned ind) const override; + + private: + std::unique_ptr reference_event_; + std::unique_ptr data_event_; + + std::unique_ptr setup_limit_; + std::unique_ptr hold_limit_; + + std::unique_ptr notifier_; + + std::unique_ptr timestamp_cond_; + std::unique_ptr timecheck_cond_; + + std::unique_ptr delayed_reference_; + std::unique_ptr delayed_data_; +}; + +#endif /* IVL_PTimingCheck_H */ diff --git a/elaborate.cc b/elaborate.cc index 0ee0656c4c..c5a530666e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -41,6 +41,7 @@ # include "PPackage.h" # include "PScope.h" # include "PSpec.h" +# include "PTimingCheck.h" # include "netlist.h" # include "netenum.h" # include "netvector.h" @@ -6308,6 +6309,140 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const } } +void PRecRem::elaborate(Design*des, NetScope*scope) const +{ + // At present, no timing checks are supported. + // Still, in order to get some models working + // assign the original reference and data signals to + // the delayed reference and data signals as per + // 15.5.4 Option behavior + + if (delayed_reference_ != nullptr) + { + if (debug_elaborate) { + cerr << get_fileline() << ": PRecRem::elaborate: Assigning " + << reference_event_->name + << " to " << *delayed_reference_ << endl; + } + + NetNet*sig = des->find_signal(scope, reference_event_->name); + + if (sig == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << reference_event_->name << endl; + des->errors += 1; + return; + } + + NetNet*sig_delayed = des->find_signal(scope, *delayed_reference_); + + if (sig_delayed == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << *delayed_reference_ << endl; + des->errors += 1; + return; + } + + connect(sig->pin(0), sig_delayed->pin(0)); + } + + if (delayed_data_ != nullptr) + { + if (debug_elaborate) { + cerr << get_fileline() << ": PRecRem::elaborate: Assigning " + << data_event_->name + << " to " << *delayed_data_ << endl; + } + + NetNet*sig = des->find_signal(scope, data_event_->name); + + if (sig == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << data_event_->name << endl; + des->errors += 1; + return; + } + + NetNet*sig_delayed = des->find_signal(scope, *delayed_data_); + + if (sig_delayed == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << *delayed_data_ << endl; + des->errors += 1; + return; + } + + connect(sig->pin(0), sig_delayed->pin(0)); + } +} + +void PSetupHold::elaborate(Design*des, NetScope*scope) const +{ + // At present, no timing checks are supported. + // Still, in order to get some models working + // assign the original reference and data signals to + // the delayed reference and data signals as per + // 15.5.4 Option behavior + + if (delayed_reference_ != nullptr) + { + if (debug_elaborate) { + cerr << get_fileline() << ": PSetupHold::elaborate: Assigning" + << reference_event_->name + << " to " << *delayed_reference_ << endl; + } + + NetNet*sig = des->find_signal(scope, reference_event_->name); + + if (sig == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << reference_event_->name << endl; + des->errors += 1; + return; + } + + NetNet*sig_delayed = des->find_signal(scope, *delayed_reference_); + + if (sig_delayed == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << *delayed_reference_ << endl; + des->errors += 1; + return; + } + + connect(sig->pin(0), sig_delayed->pin(0)); + } + + if (delayed_data_ != nullptr) + { + if (debug_elaborate) { + cerr << get_fileline() << ": PSetupHold::elaborate: Assigning" + << data_event_->name + << " to " << *delayed_data_ << endl; + } + + NetNet*sig = des->find_signal(scope, data_event_->name); + + if (sig == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << data_event_->name << endl; + des->errors += 1; + return; + } + + NetNet*sig_delayed = des->find_signal(scope, *delayed_data_); + + if (sig_delayed == nullptr) { + cerr << get_fileline() << ": error: Cannot find: " + << *delayed_data_ << endl; + des->errors += 1; + return; + } + + connect(sig->pin(0), sig_delayed->pin(0)); + } +} + static void elaborate_functions(Design*des, NetScope*scope, const map&funcs) { @@ -6383,10 +6518,8 @@ bool Module::elaborate(Design*des, NetScope*scope) const bool result_flag = true; // Elaborate within the generate blocks. - typedef list::const_iterator generate_it_t; - for (generate_it_t cur = generate_schemes.begin() - ; cur != generate_schemes.end() ; ++ cur ) { - (*cur)->elaborate(des, scope); + for (const auto cur : generate_schemes) { + cur->elaborate(des, scope); } // Elaborate functions. @@ -6405,10 +6538,8 @@ bool Module::elaborate(Design*des, NetScope*scope) const // complex. const list&gl = get_gates(); - for (list::const_iterator gt = gl.begin() - ; gt != gl.end() ; ++ gt ) { - - (*gt)->elaborate(des, scope); + for (const auto gt : gl) { + gt->elaborate(des, scope); } // Elaborate the variable initialization statements, making a @@ -6421,17 +6552,18 @@ bool Module::elaborate(Design*des, NetScope*scope) const result_flag &= elaborate_behaviors_(des, scope); // Elaborate the specify paths of the module. + for (const auto sp : specify_paths) { + sp->elaborate(des, scope); + } - for (list::const_iterator sp = specify_paths.begin() - ; sp != specify_paths.end() ; ++ sp ) { - - (*sp)->elaborate(des, scope); + // Elaborate the timing checks of the module. + for (const auto tc : timing_checks) { + tc->elaborate(des, scope); } // Elaborate the elaboration tasks. - for (list::const_iterator et = elab_tasks.begin() - ; et != elab_tasks.end() ; ++ et ) { - result_flag &= (*et)->elaborate_elab(des, scope); + for (const auto et : elab_tasks) { + result_flag &= et->elaborate_elab(des, scope); } return result_flag; diff --git a/ivtest/ivltests/timing_check_delayed_signals.v b/ivtest/ivltests/timing_check_delayed_signals.v new file mode 100644 index 0000000000..9c0542ce6f --- /dev/null +++ b/ivtest/ivltests/timing_check_delayed_signals.v @@ -0,0 +1,39 @@ +// Check that when timing checks are disabled (or in the case of Icarus Verilog not supported) +// that the delayed reference and data signals become copies of the original reference and data signals + +module test; + + wire sig1, sig2, del_sig1, del_sig2, del_sig3, del_sig4, notifier, cond1, cond2; + + assign sig1 = 1'b0; + assign sig2 = 1'b1; + + specify + + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier , cond1 , cond2 , del_sig1 , del_sig2 ) ; + + /* + Internally the simulator does the following: + assign del_sig1 = sig1; + assign del_sig2 = sig2; + */ + + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier, cond1 , cond2 , del_sig3 , del_sig4 ); + + /* + Internally the simulator does the following: + assign del_sig3 = sig1; + assign del_sig4 = sig2; + */ + + endspecify + + initial begin + + if (del_sig1 == 1'b0 && del_sig2 == 1'b1 && del_sig3 == 1'b0 && del_sig4 == 1'b1) + $display("PASSED"); + else + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/timing_check_syntax.v b/ivtest/ivltests/timing_check_syntax.v new file mode 100644 index 0000000000..c43347f019 --- /dev/null +++ b/ivtest/ivltests/timing_check_syntax.v @@ -0,0 +1,118 @@ +// Check that various timing checks can be parsed + +module test; + + initial begin + $display("PASSED"); + end + + wire sig1, sig2, del_sig1, del_sig2, notifier, cond1, cond2; + + specify + $setup(posedge sig1 , negedge sig2 , 0:0:0); + $setup(negedge sig1 , posedge sig2 , 0:0:0); + $setup(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $setup(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $setup(posedge sig1, negedge sig2 , 0:0:0 , notifier); + $setup(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , notifier); + $setup(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , notifier); + + $hold(posedge sig1 , negedge sig2 , 0:0:0); + $hold(negedge sig1 , posedge sig2 , 0:0:0); + $hold(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $hold(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $hold(posedge sig1, negedge sig2 , 0:0:0 , notifier); + $hold(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , notifier); + $hold(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , notifier); + + $setuphold(posedge sig1 , negedge sig2 , 0:0:0 , 0:0:0 , notifier); + $setuphold(negedge sig1 , posedge sig2 , 0:0:0 , 0:0:0 , notifier); + $setuphold(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , 0:0:0 , notifier); + $setuphold(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , 0:0:0 , notifier); + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier); + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier, cond1 , cond2) ; + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier, cond1 , cond2 , del_sig1 , del_sig2 ) ; + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,, del_sig1 , del_sig2 ) ; + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,, del_sig1 , del_sig2 ) ; + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,,, del_sig2 ) ; + $setuphold(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,, del_sig1 ,) ; + $setuphold(edge [10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , 0:0:0 , notifier); + $setuphold(posedge sig1 , edge [10, x0, 1x] sig2 , 0:0:0 , 0:0:0 , notifier); + + $removal(posedge sig1 , negedge sig2 , 0:0:0); + $removal(negedge sig1 , posedge sig2 , 0:0:0); + $removal(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $removal(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $removal(posedge sig1, negedge sig2 , 0:0:0 , notifier); + $removal(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , notifier); + $removal(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , notifier); + + $recovery(posedge sig1 , negedge sig2 , 0:0:0); + $recovery(negedge sig1 , posedge sig2 , 0:0:0); + $recovery(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $recovery(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $recovery(posedge sig1, negedge sig2 , 0:0:0 , notifier); + $recovery(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , notifier); + $recovery(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , notifier); + + $recrem(posedge sig1 , negedge sig2 , 0:0:0 , 0:0:0); + $recrem(negedge sig1 , posedge sig2 , 0:0:0 , 0:0:0); + $recrem(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , 0:0:0 , notifier); + $recrem(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , 0:0:0 , notifier); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier, cond1 , cond2); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier, cond1 , cond2 , del_sig1 , del_sig2 ); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,, del_sig1 , del_sig2 ); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,, del_sig1 , del_sig2 ); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,,, del_sig2 ); + $recrem(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier,,, del_sig1 ,); + $recrem(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , 0:0:0 , notifier); + $recrem(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , 0:0:0 , notifier); + + $skew(posedge sig1 , negedge sig2 , 0:0:0); + $skew(negedge sig1 , posedge sig2 , 0:0:0); + $skew(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $skew(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $skew(posedge sig1, negedge sig2 , 0:0:0 , notifier); + $skew(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , notifier); + $skew(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , notifier); + + $timeskew(posedge sig1 , negedge sig2 , 0:0:0); + $timeskew(negedge sig1 , posedge sig2 , 0:0:0); + $timeskew(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $timeskew(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , notifier); + $timeskew(posedge sig1, negedge sig2 , 0:0:0 , notifier); + $timeskew(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , notifier); + $timeskew(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , notifier); + $timeskew(posedge sig1 , negedge sig2 , 0:0:0 , notifier , 1'b0); + $timeskew(negedge sig1 , posedge sig2 , 0:0:0 , notifier , 1'b1 , 1'b0); + $timeskew(negedge sig1 , posedge sig2 , 0:0:0 , , , 1'b1); + + $fullskew(posedge sig1 , negedge sig2 , 0:0:0 , 0:0:0); + $fullskew(negedge sig1 , posedge sig2 , 0:0:0 , 0:0:0); + $fullskew(posedge sig1 &&& cond1 == cond2 , posedge sig2 &&& cond1 == cond2 , 0:0:0 , 0:0:0 , notifier); + $fullskew(negedge sig1 &&& cond1 == cond2 , negedge sig2 &&& cond1 == cond2 , 0:0:0 , 0:0:0 , notifier); + $fullskew(posedge sig1, negedge sig2 , 0:0:0 , 0:0:0 , notifier); + $fullskew(edge[10, x0, 1x] sig1 , posedge sig2 , 0:0:0 , 0:0:0 , notifier); + $fullskew(posedge sig1 , edge[10, x0, 1x] sig2 , 0:0:0 , 0:0:0 , notifier); + $fullskew(posedge sig1 , negedge sig2 , 0:0:0 , 0:0:0 , notifier , 1'b0); + $fullskew(negedge sig1 , posedge sig2 , 0:0:0 , 0:0:0 , notifier , 1'b1 , 1'b0); + $fullskew(negedge sig1 , posedge sig2 , 0:0:0 , 0:0:0 , , , 1'b1); + + $width(posedge sig1 , 0:0:0 ); + $width(posedge sig1 &&& cond1 , 0:0:0 , 0 ); + $width(posedge sig1 &&& cond1 , 0:0:0 , 0 , notifier ); + $width(edge[10, x0, 1x] sig1 &&& cond1 , 0:0:0 ); + + $period(posedge sig1 , 0:0:0 ); + $period(negedge sig1 &&& cond1 , 0:0:0 , notifier ); + $period(edge[10, x0, 1x] sig1 &&& cond1 , 0:0:0 ); + + $nochange(posedge sig1 , posedge sig2 , 10 , 20 ); + $nochange(negedge sig1 &&& cond1 , posedge sig2 , 10 , 20 ); + $nochange(negedge sig1 , posedge sig2 &&& cond1 , 10 , 20 , notifier ); + $nochange(edge[10, x0, 1x] sig1 &&& cond1 , posedge sig2 , 10 , 20 ); + $nochange(posedge sig1 &&& cond1 , edge[10, x0, 1x] sig2 , 10 , 20 ); + + endspecify +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index fdacfbc4db..05fd8b6d4c 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -72,3 +72,5 @@ task_return1 vvp_tests/task_return1.json task_return2 vvp_tests/task_return2.json task_return_fail1 vvp_tests/task_return_fail1.json task_return_fail2 vvp_tests/task_return_fail2.json +timing_check_syntax vvp_tests/timing_check_syntax.json +timing_check_delayed_signals vvp_tests/timing_check_delayed_signals.json diff --git a/ivtest/vvp_tests/timing_check_delayed_signals.json b/ivtest/vvp_tests/timing_check_delayed_signals.json new file mode 100644 index 0000000000..b93e39f096 --- /dev/null +++ b/ivtest/vvp_tests/timing_check_delayed_signals.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "timing_check_delayed_signals.v" +} diff --git a/ivtest/vvp_tests/timing_check_syntax.json b/ivtest/vvp_tests/timing_check_syntax.json new file mode 100644 index 0000000000..8b6df22fae --- /dev/null +++ b/ivtest/vvp_tests/timing_check_syntax.json @@ -0,0 +1,4 @@ +{ + "type" : "normal", + "source" : "timing_check_syntax.v" +} diff --git a/parse.y b/parse.y index b7f880b8c8..c0d4fc32f8 100644 --- a/parse.y +++ b/parse.y @@ -28,10 +28,12 @@ # include "pform.h" # include "Statement.h" # include "PSpec.h" +# include "PTimingCheck.h" # include "PPackage.h" # include # include # include +# include using namespace std; @@ -87,13 +89,6 @@ static pform_name_t* pform_create_super(void) return res; } -/* This is used to keep track of the extra arguments after the notifier - * in the $setuphold and $recrem timing checks. This allows us to print - * a warning message that the delayed signals will not be created. We - * need to do this since not driving these signals creates real - * simulation issues. */ -static unsigned args_after_notifier; - /* The rules sometimes push attributes into a global context where sub-rules may grab them. This makes parser rules a little easier to write in some cases. */ @@ -530,6 +525,9 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, PSpecPath* specpath; std::list *dimensions; + PTimingCheck::event_t* timing_check_event; + PTimingCheck::optional_args_t* spec_optional_args; + LexicalScope::lifetime_t lifetime; enum typedef_t::basic_type typedef_basic_type; @@ -696,6 +694,15 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, %type let_port_item %type hierarchy_identifier implicit_class_handle class_hierarchy_identifier +%type spec_notifier_opt spec_notifier +%type spec_reference_event +%type setuphold_opt_args recrem_opt_args setuphold_recrem_opt_notifier +%type setuphold_recrem_opt_timestamp_cond setuphold_recrem_opt_timecheck_cond +%type setuphold_recrem_opt_delayed_reference setuphold_recrem_opt_delayed_data +%type timeskew_opt_args fullskew_opt_args +%type timeskew_fullskew_opt_notifier timeskew_fullskew_opt_event_based_flag +%type timeskew_fullskew_opt_remain_active_flag + %type assignment_pattern expression expr_mintypmax %type expr_primary_or_typename expr_primary %type class_new dynamic_array_new @@ -5900,72 +5907,169 @@ specify_item yyerrok; } | K_Sfullskew '(' spec_reference_event ',' spec_reference_event - ',' delay_value ',' delay_value spec_notifier_opt ')' ';' - { delete $7; - delete $9; + ',' delay_value ',' delay_value fullskew_opt_args ')' ';' + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $9; // delay_value + + delete $10->notifier; + delete $10->event_based_flag; + delete $10->remain_active_flag; + + delete $10; // fullskew_opt_args } | K_Shold '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' - { delete $7; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $8; // spec_notifier_opt } | K_Snochange '(' spec_reference_event ',' spec_reference_event ',' delay_value ',' delay_value spec_notifier_opt ')' ';' - { delete $7; - delete $9; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $9; // delay_value + delete $10; // spec_notifier_opt } | K_Speriod '(' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' - { delete $5; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // delay_value + delete $6; // spec_notifier_opt } | K_Srecovery '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' - { delete $7; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $8; // spec_notifier_opt } | K_Srecrem '(' spec_reference_event ',' spec_reference_event - ',' delay_value ',' delay_value spec_notifier_opt ')' ';' - { delete $7; - delete $9; + ',' expr_mintypmax ',' expr_mintypmax recrem_opt_args ')' ';' + { + cerr << @3 << ": warning: Timing checks are not supported. "; + if ($10->delayed_reference != nullptr || $10->delayed_data != nullptr) + { + cerr << "Delayed reference and data signals become copies of the" + << " original reference and data signals." << endl; + } + else + { + cerr << endl; + } + + PRecRem*recrem = pform_make_recrem(@1, $3, $5, $7, $9, $10); + pform_module_timing_check(recrem); + + delete $10; // setuphold_recrem_opt_notifier } | K_Sremoval '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' - { delete $7; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $8; // spec_notifier_opt } | K_Ssetup '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' - { delete $7; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $8; // spec_notifier_opt } | K_Ssetuphold '(' spec_reference_event ',' spec_reference_event - ',' delay_value ',' delay_value spec_notifier_opt ')' ';' - { delete $7; - delete $9; + ',' expr_mintypmax ',' expr_mintypmax setuphold_opt_args ')' ';' + { + cerr << @3 << ": warning: Timing checks are not supported. "; + if ($10->delayed_reference != nullptr || $10->delayed_data != nullptr) + { + cerr << "Delayed reference and data signals become copies of the" + << " original reference and data signals." << endl; + } + else + { + cerr << endl; + } + + PSetupHold*setuphold = pform_make_setuphold(@1, $3, $5, $7, $9, $10); + pform_module_timing_check(setuphold); + + delete $10; // setuphold_recrem_opt_notifier } | K_Sskew '(' spec_reference_event ',' spec_reference_event ',' delay_value spec_notifier_opt ')' ';' - { delete $7; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + delete $8; // spec_notifier_opt } | K_Stimeskew '(' spec_reference_event ',' spec_reference_event - ',' delay_value spec_notifier_opt ')' ';' - { delete $7; + ',' delay_value timeskew_opt_args ')' ';' + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // spec_reference_event + delete $7; // delay_value + + delete $8->notifier; + delete $8->event_based_flag; + delete $8->remain_active_flag; + + delete $8; // timeskew_opt_args } | K_Swidth '(' spec_reference_event ',' delay_value ',' expression spec_notifier_opt ')' ';' - { delete $5; - delete $7; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // delay_value + delete $7; // expression + delete $8; } | K_Swidth '(' spec_reference_event ',' delay_value ')' ';' - { delete $5; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $3; // spec_reference_event + delete $5; // delay_value } | K_pulsestyle_onevent specify_path_identifiers ';' - { delete $2; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $2; // specify_path_identifiers } | K_pulsestyle_ondetect specify_path_identifiers ';' - { delete $2; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $2; // specify_path_identifiers } | K_showcancelled specify_path_identifiers ';' - { delete $2; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $2; // specify_path_identifiers } | K_noshowcancelled specify_path_identifiers ';' - { delete $2; + { + cerr << @3 << ": warning: Timing checks are not supported." << endl; + delete $2; // specify_path_identifiers } ; @@ -6110,47 +6214,8 @@ specify_path_identifiers ; specparam - : IDENTIFIER '=' expression - { PExpr*tmp = $3; - pform_set_specparam(@1, lex_strings.make($1), specparam_active_range, tmp); - delete[]$1; - } - | IDENTIFIER '=' expression ':' expression ':' expression - { PExpr*tmp = 0; - switch (min_typ_max_flag) { - case MIN: - tmp = $3; - delete $5; - delete $7; - break; - case TYP: - delete $3; - tmp = $5; - delete $7; - break; - case MAX: - delete $3; - delete $5; - tmp = $7; - break; - } - if (min_typ_max_warn > 0) { - cerr << tmp->get_fileline() << ": warning: Choosing "; - switch (min_typ_max_flag) { - case MIN: - cerr << "min"; - break; - case TYP: - cerr << "typ"; - break; - case MAX: - cerr << "max"; - break; - } - cerr << " expression." << endl; - min_typ_max_warn -= 1; - } - pform_set_specparam(@1, lex_strings.make($1), specparam_active_range, tmp); + : IDENTIFIER '=' expr_mintypmax + { pform_set_specparam(@1, lex_strings.make($1), specparam_active_range, $3); delete[]$1; } | PATHPULSE_IDENTIFIER '=' expression @@ -6183,31 +6248,82 @@ spec_polarity | { $$ = 0; } ; +// TODO spec_controlled_reference_event spec_reference_event - : K_posedge expression - { delete $2; } - | K_negedge expression - { delete $2; } - | K_posedge expr_primary K_TAND expression - { delete $2; - delete $4; - } - | K_negedge expr_primary K_TAND expression - { delete $2; - delete $4; - } - | K_edge '[' edge_descriptor_list ']' expr_primary - { delete $5; } - | K_edge '[' edge_descriptor_list ']' expr_primary K_TAND expression - { delete $5; - delete $7; - } - | expr_primary K_TAND expression - { delete $1; - delete $3; + : hierarchy_identifier + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$1; + event->posedge = false; + event->negedge = false; + event->condition = nullptr; + delete $1; + $$ = event; + } + | hierarchy_identifier K_TAND expression + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$1; + event->posedge = false; + event->negedge = false; + event->condition = std::unique_ptr($3); + delete $1; + $$ = event; + } + | K_posedge hierarchy_identifier + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$2; + event->posedge = true; + event->negedge = false; + event->condition = nullptr; + delete $2; + $$ = event; + } + | K_negedge hierarchy_identifier + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$2; + event->posedge = false; + event->negedge = true; + event->condition = nullptr; + delete $2; + $$ = event; + } + | K_posedge hierarchy_identifier K_TAND expression + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$2; + event->posedge = true; + event->negedge = false; + event->condition = std::unique_ptr($4); + delete $2; + $$ = event; + } + | K_negedge hierarchy_identifier K_TAND expression + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$2; + event->posedge = false; + event->negedge = true; + event->condition = std::unique_ptr($4); + delete $2; + $$ = event; + } + | K_edge '[' edge_descriptor_list ']' hierarchy_identifier + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$5; + event->posedge = false; + event->negedge = false; + // TODO add edge descriptors + event->condition = nullptr; + delete $5; + $$ = event; + } + | K_edge '[' edge_descriptor_list ']' hierarchy_identifier K_TAND expression + { PTimingCheck::event_t* event = new PTimingCheck::event_t; + event->name = *$5; + event->posedge = false; + event->negedge = false; + // TODO add edge descriptors + event->condition = std::unique_ptr($7); + delete $5; + $$ = event; } - | expr_primary - { delete $1; } ; /* The edge_descriptor is detected by the lexor as the various @@ -6219,31 +6335,204 @@ edge_descriptor_list | K_edge_descriptor ; +setuphold_opt_args + : setuphold_recrem_opt_notifier + { $$ = $1; } + | + { $$ = new PTimingCheck::optional_args_t; } + ; + +recrem_opt_args + : setuphold_recrem_opt_notifier + { $$ = $1; } + | + { $$ = new PTimingCheck::optional_args_t; } + ; + + /* The following rules are used for the optional arguments + in $recrem and $setuphold */ +setuphold_recrem_opt_notifier + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' hierarchy_identifier // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->notifier = $2; + $$ = args; + } + | ',' setuphold_recrem_opt_timestamp_cond // Empty + { $$ = $2; } + | ',' hierarchy_identifier setuphold_recrem_opt_timestamp_cond + { + $$ = $3; + $$->notifier = $2; + } + ; + +setuphold_recrem_opt_timestamp_cond + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' expression // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->timestamp_cond = $2; + $$ = args; + } + | ',' setuphold_recrem_opt_timecheck_cond // Empty + { $$ = $2; } + | ',' expression setuphold_recrem_opt_timecheck_cond + { + $$ = $3; + $$->timestamp_cond = $2; + } + ; + +setuphold_recrem_opt_timecheck_cond + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' expression // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->timecheck_cond = $2; + $$ = args; + } + | ',' setuphold_recrem_opt_delayed_reference // Empty + { $$ = $2; } + | ',' expression setuphold_recrem_opt_delayed_reference + { + $$ = $3; + $$->timecheck_cond = $2; + } + ; + +setuphold_recrem_opt_delayed_reference + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' hierarchy_identifier // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->delayed_reference = $2; + $$ = args; + } + | ',' setuphold_recrem_opt_delayed_data // Empty + { $$ = $2; } + | ',' hierarchy_identifier setuphold_recrem_opt_delayed_data + { + $$ = $3; + $$->delayed_reference = $2; + } + ; + +setuphold_recrem_opt_delayed_data + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' hierarchy_identifier // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->delayed_data = $2; + $$ = args; + } + ; + +timeskew_opt_args + : timeskew_fullskew_opt_notifier + { $$ = $1; } + | + { $$ = new PTimingCheck::optional_args_t; } + ; + +fullskew_opt_args + : timeskew_fullskew_opt_notifier + { $$ = $1; } + | + { $$ = new PTimingCheck::optional_args_t; } + ; + + /* The following rules are used for the optional arguments + in $timeskew and $fullskew */ +timeskew_fullskew_opt_notifier + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' hierarchy_identifier // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->notifier = $2; + $$ = args; + } + | ',' timeskew_fullskew_opt_event_based_flag // Empty + { $$ = $2; } + | ',' hierarchy_identifier timeskew_fullskew_opt_event_based_flag + { + $$ = $3; + $$->notifier = $2; + } + ; + +timeskew_fullskew_opt_event_based_flag + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' expression // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->event_based_flag = $2; + $$ = args; + } + | ',' timeskew_fullskew_opt_remain_active_flag // Empty + { $$ = $2; } + | ',' expression timeskew_fullskew_opt_remain_active_flag + { + $$ = $3; + $$->event_based_flag = $2; + } + ; + +timeskew_fullskew_opt_remain_active_flag + : ',' // Empty and end of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + $$ = args; + } + | ',' expression // End of list + { + PTimingCheck::optional_args_t* args = new PTimingCheck::optional_args_t; + args->remain_active_flag = $2; + $$ = args; + } + ; + spec_notifier_opt : /* empty */ - { } + { $$ = nullptr; } | spec_notifier - { } + { $$ = $1; } ; + spec_notifier : ',' - { args_after_notifier = 0; } + { $$ = nullptr; } | ',' hierarchy_identifier - { args_after_notifier = 0; delete $2; } - | spec_notifier ',' - { args_after_notifier += 1; } - | spec_notifier ',' hierarchy_identifier - { args_after_notifier += 1; - if (args_after_notifier >= 3) { - cerr << @3 << ": warning: Timing checks are not supported " - "and delayed signal \"" << *$3 - << "\" will not be driven." << endl; - } - delete $3; - } - /* How do we match this path? */ - | IDENTIFIER - { args_after_notifier = 0; delete[]$1; } + { $$ = $2; } ; subroutine_call diff --git a/pform.cc b/pform.cc index 234134d887..3957019ed1 100644 --- a/pform.cc +++ b/pform.cc @@ -32,6 +32,7 @@ # include "PGenerate.h" # include "PModport.h" # include "PSpec.h" +# include "PTimingCheck.h" # include "discipline.h" # include # include @@ -3109,6 +3110,68 @@ extern void pform_module_specify_path(PSpecPath*obj) pform_cur_module.front()->specify_paths.push_back(obj); } +/* + * Timing checks. + */ + extern PRecRem* pform_make_recrem(const struct vlltype&li, + PTimingCheck::event_t*reference_event, + PTimingCheck::event_t*data_event, + PExpr*setup_limit, + PExpr*hold_limit, + PTimingCheck::optional_args_t* args) +{ + ivl_assert(li, args); + + PRecRem*recrem = new PRecRem( + reference_event, + data_event, + setup_limit, + hold_limit, + args->notifier, + args->timestamp_cond, + args->timecheck_cond, + args->delayed_reference, + args->delayed_data + ); + + FILE_NAME(recrem, li); + + return recrem; +} +extern PSetupHold* pform_make_setuphold(const struct vlltype&li, + PTimingCheck::event_t*reference_event, + PTimingCheck::event_t*data_event, + PExpr*setup_limit, + PExpr*hold_limit, + PTimingCheck::optional_args_t* args) +{ + ivl_assert(li, args); + + PSetupHold*setuphold = new PSetupHold( + reference_event, + data_event, + setup_limit, + hold_limit, + args->notifier, + args->timestamp_cond, + args->timecheck_cond, + args->delayed_reference, + args->delayed_data + ); + + FILE_NAME(setuphold, li); + + return setuphold; +} + +extern void pform_module_timing_check(PTimingCheck*obj) +{ + if (!obj) + return; + + pform_cur_module.front()->timing_checks.push_back(obj); +} + void pform_set_port_type(const struct vlltype&li, list*ports, diff --git a/pform.h b/pform.h index f88513f035..7eeb936217 100644 --- a/pform.h +++ b/pform.h @@ -30,6 +30,7 @@ # include "PTask.h" # include "PUdp.h" # include "PWire.h" +# include "PTimingCheck.h" # include "verinum.h" # include "discipline.h" # include @@ -429,6 +430,25 @@ extern PSpecPath*pform_assign_path_delay(PSpecPath*obj, std::list*delays extern void pform_module_specify_path(PSpecPath*obj); +/* + * Functions related to timing checks. + */ +extern PRecRem* pform_make_recrem(const struct vlltype&li, + PTimingCheck::event_t*reference_event, + PTimingCheck::event_t*data_event, + PExpr*setup_limit, + PExpr*hold_limit, + PTimingCheck::optional_args_t* args + ); +extern PSetupHold* pform_make_setuphold(const struct vlltype&li, + PTimingCheck::event_t*reference_event, + PTimingCheck::event_t*data_event, + PExpr*setup_limit, + PExpr*hold_limit, + PTimingCheck::optional_args_t* args + ); +extern void pform_module_timing_check(PTimingCheck*obj); + /* * pform_make_behavior creates processes that are declared with always * or initial items. diff --git a/pform_dump.cc b/pform_dump.cc index 4f1e5391c9..4f5ba286ec 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1442,6 +1442,16 @@ void PSpecPath::dump(std::ostream&out, unsigned ind) const out << ");" << endl; } +void PRecRem::dump(std::ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "recrem "; +} + +void PSetupHold::dump(std::ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "setuphold "; +} + void PGenerate::dump(ostream&out, unsigned indent) const { out << setw(indent) << "" << "generate(" << id_number << ")"; @@ -1704,6 +1714,15 @@ void Module::dump_specparams_(ostream&out, unsigned indent) const } } +void Module::dump_timingchecks_(ostream&out, unsigned indent) const +{ + cout << "dump_timingchecks_" << endl; + + for (const auto cur : timing_checks) { + cur->dump(out, indent); + } +} + void Module::dump(ostream&out) const { if (attributes.begin() != attributes.end()) { @@ -1752,6 +1771,8 @@ void Module::dump(ostream&out) const dump_specparams_(out, 4); + dump_timingchecks_(out, 4); + dump_enumerations_(out, 4); dump_classes_(out, 4);