From 46620b6c444e444c910ba6a2723c52af5003b3b3 Mon Sep 17 00:00:00 2001 From: mole99 Date: Fri, 28 Jul 2023 10:54:47 +0200 Subject: [PATCH 1/4] Implement $width timing check in vvp --- vpi/sdf_lexor.lex | 32 +++------ vpi/sdf_parse.y | 87 +++++++++++++----------- vpi/sdf_parse_priv.h | 3 - vpi/sdf_priv.h | 13 ++++ vpi/sys_sdf.c | 82 +++++++++++++++++++++++ vpi_user.h | 18 +++++ vvp/Makefile.in | 4 +- vvp/compile.cc | 46 +++++++++++++ vvp/compile.h | 6 ++ vvp/event.cc | 36 ---------- vvp/event.h | 5 -- vvp/lexor.lex | 8 +++ vvp/parse.y | 10 +++ vvp/tchk.cc | 139 ++++++++++++++++++++++++++++++++++++++ vvp/tchk.h | 65 ++++++++++++++++++ vvp/vpi_priv.h | 106 ++++++++++++++++++++++++++++- vvp/vpi_tchk.cc | 155 +++++++++++++++++++++++++++++++++++++++++++ vvp/vvp_net.cc | 22 ++++-- vvp/vvp_net.h | 13 ++++ 19 files changed, 733 insertions(+), 117 deletions(-) create mode 100644 vvp/tchk.cc create mode 100644 vvp/tchk.h create mode 100644 vvp/vpi_tchk.cc diff --git a/vpi/sdf_lexor.lex b/vpi/sdf_lexor.lex index 832e416018..23105ae5cf 100644 --- a/vpi/sdf_lexor.lex +++ b/vpi/sdf_lexor.lex @@ -45,8 +45,6 @@ static int yywrap(void) %} %x CCOMMENT -%x COND_EDGE_ID -%x EDGE_ID %% @@ -64,16 +62,15 @@ static int yywrap(void) /* Count lines so that the parser can assign line numbers. */ \n { sdflloc.first_line += 1; } - /* The other edge identifiers. */ -"01" {return K_01; } -"10" {return K_10; } -"0"[zZ] {return K_0Z; } -[zZ]"1" {return K_Z1; } -"1"[zZ] {return K_1Z; } -[zZ]"0" {return K_Z0; } -[pP][oO][sS][eE][dD][gG][eE] {return K_POSEDGE; } -[nN][eE][gG][eE][dD][gG][eE] {return K_NEGEDGE; } -[cC][oO][nN][dD] {return K_COND; } + /* The edge identifiers. */ +"posedge" { return K_POSEDGE; } +"negedge" { return K_NEGEDGE; } +"01" {return K_01; } +"10" {return K_10; } +"0z" {return K_0Z; } +"z1" {return K_Z1; } +"1z" {return K_1Z; } +"z0" {return K_Z0; } /* Integer values */ [0-9]+ { @@ -165,17 +162,6 @@ static struct { { 0, IDENTIFIER } }; -void start_edge_id(unsigned cond) -{ - if (cond) BEGIN(COND_EDGE_ID); - else BEGIN(EDGE_ID); -} - -void stop_edge_id(void) -{ - BEGIN(0); -} - static int lookup_keyword(const char*text) { unsigned idx, len, skip; diff --git a/vpi/sdf_parse.y b/vpi/sdf_parse.y index 029a11762d..69de16b30c 100644 --- a/vpi/sdf_parse.y +++ b/vpi/sdf_parse.y @@ -43,6 +43,7 @@ char sdf_use_hchar = '.'; struct port_with_edge_s port_with_edge; struct sdf_delval_list_s delval_list; struct interconnect_port_s interconnect_port; + struct port_tchk_s port_tchk; }; %token K_ABSOLUTE K_CELL K_CELLTYPE K_COND K_CONDELSE K_DATE K_DELAYFILE @@ -68,6 +69,8 @@ char sdf_use_hchar = '.'; %type port_interconnect +%type port_tchk + %type signed_real_number %type delval rvalue_opt rvalue rtriple signed_real_number_opt @@ -291,8 +294,6 @@ timing_spec { vpi_printf("SDF ERROR: %s:%d: Syntax error in CELL DELAY SPEC\n", sdf_parse_path, @2.first_line); } | '(' K_TIMINGCHECK tchk_def_list ')' - { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK not supported.\n", - sdf_parse_path, @2.first_line); } | '(' K_TIMINGCHECK error ')' { vpi_printf("SDF ERROR: %s:%d: Syntax error in TIMINGCHECK SPEC\n", sdf_parse_path, @2.first_line); } @@ -382,55 +383,61 @@ tchk_def_list /* Timing checks are ignored. */ tchk_def : '(' K_SETUP port_tchk port_tchk rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $setup not supported.\n", + sdf_parse_path, @2.first_line); } | '(' K_HOLD port_tchk port_tchk rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $hold not supported.\n", + sdf_parse_path, @2.first_line); } | '(' K_SETUPHOLD port_tchk port_tchk rvalue rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $setuphold not supported.\n", + sdf_parse_path, @2.first_line); } | '(' K_RECOVERY port_tchk port_tchk rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $recovery not supported.\n", + sdf_parse_path, @2.first_line); } | '(' K_RECREM port_tchk port_tchk rvalue rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $recrem not supported.\n", + sdf_parse_path, @2.first_line); } | '(' K_REMOVAL port_tchk port_tchk rvalue ')' - | '(' K_WIDTH port_tchk rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $removal not supported.\n", + sdf_parse_path, @2.first_line); } + | '(' K_WIDTH port_tchk rvalue ')' // TODO + { if (sdf_flag_inform) { + vpi_printf("SDF INFO: %s:%d: TIMINGCHECK $width with " + "ref_event = %d %s %s, value = %f\n", + sdf_parse_path, @2.first_line, $3.vpi_edge, $3.signal, $3.condition, $4.value); + } + sdf_tchk_width_limits($3, $4, @2.first_line); + free($3.signal); + free($3.condition); + } | '(' K_PERIOD port_tchk rvalue ')' + { vpi_printf("SDF WARNING: %s:%d: TIMINGCHECK $period not supported.\n", + sdf_parse_path, @2.first_line); } ; port_tchk - : port_instance - { free($1); } - /* This must only be an edge. For now we just accept everything. */ - | cond_edge_start port_instance ')' - { free($2); } - /* These must only be a cond. For now we just accept everything. */ - | cond_edge_start timing_check_condition port_spec ')' - { free($3.string_val); } - | cond_edge_start QSTRING timing_check_condition port_spec ')' - { free($2); - free($4.string_val); + : port_spec + { $$.signal = $1.string_val; + $$.vpi_edge = $1.vpi_edge; + $$.condition = NULL; + } + | '(' K_COND timing_check_condition port_spec ')' + { vpi_printf("SDF WARNING: %s:%d: Conditions for timing checks not supported.\n", + sdf_parse_path, @2.first_line);$$.signal = $4.string_val; + $$.vpi_edge = $4.vpi_edge; + $$.condition = NULL; // TODO pass condition } - ; - -cond_edge_start - : '(' { start_edge_id(1); } cond_edge_identifier { stop_edge_id(); } - ; - -cond_edge_identifier - : K_POSEDGE - | K_NEGEDGE - | K_01 - | K_10 - | K_0Z - | K_Z1 - | K_1Z - | K_Z0 - | K_COND ; timing_check_condition - : port_interconnect - { free($1.name); } - | '~' port_interconnect - { free($2.name); } - | '!' port_interconnect - { free($2.name); } - | port_interconnect equality_operator scalar_constant - { free($1.name); } + : hierarchical_identifier + { free($1); } + | '~' hierarchical_identifier + { free($2); } + | '!' hierarchical_identifier + { free($2); } + | hierarchical_identifier equality_operator scalar_constant + { free($1); } ; /* This is not complete! */ @@ -495,8 +502,8 @@ port_interconnect ; port_edge - : '(' {start_edge_id(0);} edge_identifier {stop_edge_id();} port_instance ')' - { $$.vpi_edge = $3; $$.string_val = $5; } + : '(' edge_identifier port_instance ')' + { $$.vpi_edge = $2; $$.string_val = $3; } ; edge_identifier diff --git a/vpi/sdf_parse_priv.h b/vpi/sdf_parse_priv.h index da35f525be..b51cee934f 100644 --- a/vpi/sdf_parse_priv.h +++ b/vpi/sdf_parse_priv.h @@ -30,7 +30,4 @@ extern const char*sdf_parse_path; /* Hierarchy separator character to use. */ extern char sdf_use_hchar; -extern void start_edge_id(unsigned cond); -extern void stop_edge_id(void); - #endif /* IVL_sdf_parse_priv_h */ diff --git a/vpi/sdf_priv.h b/vpi/sdf_priv.h index 60889eb9ac..371c0618e9 100644 --- a/vpi/sdf_priv.h +++ b/vpi/sdf_priv.h @@ -54,6 +54,15 @@ struct port_with_edge_s { char*string_val; }; +// The reference and data signals of timing checks +// can have logical condition expressions and edges +// associated with them. +struct port_tchk_s { + int vpi_edge; + char* condition; + char* signal; +}; + struct interconnect_port_s { char* name; bool has_index; @@ -72,5 +81,9 @@ extern void sdf_interconnect_delays(struct interconnect_port_s port1, const struct sdf_delval_list_s*delval_list, const int sdf_lineno); +extern void sdf_tchk_width_limits(struct port_tchk_s ref_event, + struct sdf_delay_s limit, + const int sdf_lineno); + #endif /* IVL_sdf_priv_h */ diff --git a/vpi/sys_sdf.c b/vpi/sys_sdf.c index 830837a485..8b034f743b 100644 --- a/vpi/sys_sdf.c +++ b/vpi/sys_sdf.c @@ -358,6 +358,88 @@ void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst, } } +void sdf_tchk_width_limits(struct port_tchk_s ref_event, struct sdf_delay_s limit, const int sdf_lineno) +{ + int num_annotated = 0; + + if (!limit.defined) { + vpi_printf("SDF ERROR: %s:%d: limit not defined\n", + sdf_fname, sdf_lineno); + return; + } + + vpiHandle iter, vpi_tchk, vpi_refterm, vpi_ref; + iter = vpi_iterate(vpiTchk, sdf_scope); + + if (!iter) { + vpi_printf("SDF WARNING: %s:%d: no timing checks found\n", + sdf_fname, sdf_lineno); + return; + } + + while ((vpi_tchk = vpi_scan(iter))) { + + // Wrong type, continue with next tchk + if (vpi_get(vpiTchkType, vpi_tchk) != vpiWidth) continue; + + // Get the reference term + vpi_refterm = vpi_handle(vpiTchkRefTerm, vpi_tchk); + + if (!vpi_refterm) { + vpi_printf("SDF WARNING: %s:%d: could not access reference term\n", + sdf_fname, sdf_lineno); + continue; + } + + // There is an edge to test for + if (ref_event.vpi_edge != vpiNoEdge) { + // Wrong edge, continue with next tchk + if (vpi_get(vpiEdge, vpi_refterm) != ref_event.vpi_edge) continue; + } + + // TODO Once conditions are implemented, test here + + // Get the reference + vpi_ref = vpi_handle(vpiExpr, vpi_refterm); + + if (!vpi_ref) { + vpi_printf("SDF WARNING: %s:%d: could not access reference\n", + sdf_fname, sdf_lineno); + continue; + } + + char* ref_name = vpi_get_str(vpiName, vpi_ref); + + // Compare names of reference signals + if (strcmp(ref_event.signal, ref_name) == 0) { + + // Setup the delay structure + s_vpi_delay delays; + struct t_vpi_time delay_val = { .real = limit.value }; + delays.da = &delay_val; + delays.no_of_delays = 1; + delays.time_type = vpiScaledRealTime; + delays.mtm_flag = 0; + delays.append_flag = 0; + delays.pulsere_flag = 0; + + if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Putting limit %f\n", + sdf_fname, sdf_lineno, delay_val.real); + + vpi_put_delays(vpi_tchk, &delays); + vpi_get_delays(vpi_tchk, &delays); + vpi_printf("new limit: %f\n", delay_val.real); + + num_annotated++; + } + } + + if (num_annotated == 0) { + vpi_printf("SDF WARNING: %s:%d: no timing checks match\n", + sdf_fname, sdf_lineno); + } +} + static void check_command_line_args(void) { struct t_vpi_vlog_info vlog_info; diff --git a/vpi_user.h b/vpi_user.h index 40bf4879d6..7ef058d141 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -299,6 +299,8 @@ typedef struct t_vpi_delay { #define vpiSysFuncCall 56 #define vpiSysTaskCall 57 #define vpiTask 59 +#define vpiTchk 61 +#define vpiTchkTerm 62 #define vpiTimeVar 63 #define vpiUdpDefn 66 #define vpiUserSystf 67 @@ -309,6 +311,9 @@ typedef struct t_vpi_delay { #define vpiRightRange 83 #define vpiScope 84 #define vpiSysTfCall 85 +#define vpiTchkDataTerm 86 +#define vpiTchkNotifier 87 +#define vpiTchkRefTerm 88 #define vpiArgument 89 #define vpiBit 90 #define vpiInternalScope 92 @@ -376,6 +381,19 @@ typedef struct t_vpi_delay { # define vpiPosedge (vpiEdgex1|vpiEdge01|vpiEdge0x) # define vpiNegedge (vpiEdgex0|vpiEdge10|vpiEdge1x) # define vpiAnyEdge (vpiPosedge|vpiNegedge) +#define vpiTchkType 38 +# define vpiSetup 1 +# define vpiHold 2 +# define vpiPeriod 3 +# define vpiWidth 4 +# define vpiSkew 5 +# define vpiRecovery 6 +# define vpiNoChange 7 +# define vpiSetupHold 8 +# define vpiFullskew 9 +# define vpiRecrem 10 +# define vpiRemoval 11 +# define vpiTimeskew 12 #define vpiConstType 40 # define vpiDecConst 1 # define vpiRealConst 2 diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 3dbd57b2fe..2f24385d7e 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -67,7 +67,7 @@ MDIR1 = -DMODULE_DIR1='"$(libdir)/ivl$(suffix)"' VPI = vpi_modules.o vpi_bit.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \ vpi_event.o vpi_iter.o vpi_mcd.o \ vpi_priv.o vpi_scope.o vpi_real.o vpi_signal.o vpi_string.o vpi_tasks.o vpi_time.o \ - vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \ + vpi_vthr_vector.o vpi_tchk.o vpip_bin.o vpip_hex.o vpip_oct.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \ @@ -78,7 +78,7 @@ O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o c symbols.o ufunc.o codes.o vthread.o schedule.o \ statistics.o tables.o udp.o vvp_island.o vvp_net.o vvp_net_sig.o \ vvp_object.o vvp_cobject.o vvp_darray.o event.o logic.o delay.o \ - words.o island_tran.o $(VPI) + words.o island_tran.o tchk.o $(VPI) all: dep vvp@EXEEXT@ vvp.man diff --git a/vvp/compile.cc b/vvp/compile.cc index 46ec619d0d..31589f146a 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -32,11 +32,14 @@ # include "parse_misc.h" # include "statistics.h" # include "schedule.h" +# include "event.h" # include # include # include # include # include +# include "vvp_net_sig.h" +# include "tchk.h" #ifdef __MINGW32__ #include @@ -2049,3 +2052,46 @@ void compile_island(char*label, char*type) free(type); } + +void compile_tchk_width(char* edge, struct symb_s ref, char* condition, double limit, double threshold, char* notifier, long file_idx, long lineno ) +{ + if (strcmp(condition, "v0x000000000000_0") != 0) { + fprintf(stderr, "sorry: Conditions not implemented for timing checks\n"); + } + free(condition); + + edge_t tchk_edge; + + if (strcmp(edge, "posedge") == 0) { + tchk_edge = vvp_edge_posedge; + } else if (strcmp(edge, "negedge") == 0) { + tchk_edge = vvp_edge_negedge; + } else { + yyerror("invalid edge"); + return; + } + + __vpiScope* scope = vpip_peek_current_scope(); + + // Create new net and functor + vvp_net_t* my_tchk_width = new vvp_net_t; + vvp_fun_tchk_width* obj_tchk_width = new vvp_fun_tchk_width(tchk_edge, + vpip_scaled_real_to_time64(limit, scope), + vpip_scaled_real_to_time64(threshold, scope)); + my_tchk_width->fun = obj_tchk_width; + + // Copy string because it is used twice for lookup + // TODO is there a better way? + char* ref_tmp = (char*)malloc(50); + strcpy(ref_tmp, ref.text); + + // Connect reference signal to port 0 and create VPI object + input_connect(my_tchk_width, 0, ref_tmp); + __vpiTchkWidth* obj = (__vpiTchkWidth*)vpip_make_tchk_width(file_idx, lineno, obj_tchk_width); + + // Add vpiHandles to object for later lookup + if (notifier) compile_vpi_lookup(&(obj->vpi_notifier_), notifier); + if (ref.text) compile_vpi_lookup(&(obj->vpi_reference_), ref.text); + + vpip_attach_to_current_scope(obj); +} diff --git a/vvp/compile.h b/vvp/compile.h index 57cdf3e4d1..dbed5c5012 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -483,6 +483,12 @@ extern void compile_scope_decl(char*typ, char*lab, char*nam, char*tnam, long is_cell); extern void compile_scope_recall(char*sym); +/* + * The parser uses this function to compile .tchk_width statements. + */ +extern void compile_tchk_width(char* edge, struct symb_s ref, char* condition, + double limit, double threshold, char* notifier, long file_idx, long lineno ); + /* * The parser uses this function to declare a thread. The start_sym is * the start instruction, and must already be defined. diff --git a/vvp/event.cc b/vvp/event.cc index 22379bb6f7..e197cbc972 100644 --- a/vvp/event.cc +++ b/vvp/event.cc @@ -178,42 +178,6 @@ void schedule_evctl(vvp_array_t memory, unsigned index, ep->last = &((*(ep->last))->next); } -inline vvp_fun_edge::edge_t VVP_EDGE(vvp_bit4_t from, vvp_bit4_t to) -{ - return 1 << ((from << 2) | to); -} - -const vvp_fun_edge::edge_t vvp_edge_posedge - = VVP_EDGE(BIT4_0,BIT4_1) - | VVP_EDGE(BIT4_0,BIT4_X) - | VVP_EDGE(BIT4_0,BIT4_Z) - | VVP_EDGE(BIT4_X,BIT4_1) - | VVP_EDGE(BIT4_Z,BIT4_1) - ; - -const vvp_fun_edge::edge_t vvp_edge_negedge - = VVP_EDGE(BIT4_1,BIT4_0) - | VVP_EDGE(BIT4_1,BIT4_X) - | VVP_EDGE(BIT4_1,BIT4_Z) - | VVP_EDGE(BIT4_X,BIT4_0) - | VVP_EDGE(BIT4_Z,BIT4_0) - ; - -const vvp_fun_edge::edge_t vvp_edge_edge - = VVP_EDGE(BIT4_0,BIT4_1) - | VVP_EDGE(BIT4_1,BIT4_0) - | VVP_EDGE(BIT4_0,BIT4_X) - | VVP_EDGE(BIT4_X,BIT4_0) - | VVP_EDGE(BIT4_0,BIT4_Z) - | VVP_EDGE(BIT4_Z,BIT4_0) - | VVP_EDGE(BIT4_X,BIT4_1) - | VVP_EDGE(BIT4_1,BIT4_X) - | VVP_EDGE(BIT4_Z,BIT4_1) - | VVP_EDGE(BIT4_1,BIT4_Z) - ; - -const vvp_fun_edge::edge_t vvp_edge_none = 0; - struct vvp_fun_edge_state_s : public waitable_state_s { vvp_fun_edge_state_s() { diff --git a/vvp/event.h b/vvp/event.h index b4b8815f5f..856858d884 100644 --- a/vvp/event.h +++ b/vvp/event.h @@ -166,11 +166,6 @@ class vvp_fun_edge : public vvp_net_fun_t, public waitable_hooks_s { edge_t edge_; }; -extern const vvp_fun_edge::edge_t vvp_edge_edge; -extern const vvp_fun_edge::edge_t vvp_edge_posedge; -extern const vvp_fun_edge::edge_t vvp_edge_negedge; -extern const vvp_fun_edge::edge_t vvp_edge_none; - /* * Statically allocated vvp_fun_edge. */ diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 94318db35e..3d133a5fd6 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -46,6 +46,11 @@ static char* strdupnew(char const *str) %} +digit [0-9] +integer {digit}+ +real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+) +exp ({integer}|{real})[eE]-?{integer} + %% /* These are some special header/footer keywords. */ @@ -228,6 +233,7 @@ static char* strdupnew(char const *str) ".udp" { return K_UDP; } ".udp/c"(omb)? { return K_UDP_C; } ".udp/s"(equ)? { return K_UDP_S; } +".tchk_width" { return K_TCHK_WIDTH; } "-debug" { return K_DEBUG; } /* instructions start with a % character. The compiler decides what @@ -261,6 +267,8 @@ static char* strdupnew(char const *str) yylval.numb = strtouint64(yytext, 0, 0); return T_NUMBER; } +({real}|{exp}) { yylval.real = atof(yytext); return T_REAL; } + /* Handle some specialized constant/literals as symbols. */ "C4<"[01xz]*">" { diff --git a/vvp/parse.y b/vvp/parse.y index 68466e2a55..88be9196fa 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -53,6 +53,7 @@ static struct __vpiModPath*modpath_dst = 0; char*text; char **table; uint64_t numb; + double real; bool flag; comp_operands_t opa; @@ -108,10 +109,14 @@ static struct __vpiModPath*modpath_dst = 0; %token K_ivl_version K_ivl_delay_selection %token K_vpi_module K_vpi_time_precision K_file_names K_file_line %token K_PORT_INPUT K_PORT_OUTPUT K_PORT_INOUT K_PORT_MIXED K_PORT_NODIR +%token K_TCHK_WIDTH +%token K_POSEDGE "posedge" +%token K_NEGEDGE "negedge" %token T_INSTR %token T_LABEL %token T_NUMBER +%token T_REAL %token T_STRING %token T_SYMBOL %token T_VECTOR @@ -715,6 +720,11 @@ statement { compile_scope_recall($2); } + /* Timing checks */ + + | K_TCHK_WIDTH T_NUMBER T_NUMBER ',' T_STRING ',' symbol ',' T_SYMBOL ',' T_REAL ',' T_NUMBER ',' T_SYMBOL ';' + { compile_tchk_width($5, $7, $9, $11, $13, $15, $2, $3); } + /* Port information for scopes... currently this is just meta-data for VPI queries */ | K_PORT_INFO T_NUMBER port_type T_NUMBER T_STRING T_SYMBOL ';' diff --git a/vvp/tchk.cc b/vvp/tchk.cc new file mode 100644 index 0000000000..6afad4328a --- /dev/null +++ b/vvp/tchk.cc @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023 Stephen Williams (steve@icarus.com) + * Copyright (c) 2023 Leo Moser (leo.moser@pm.me) + * + * 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 "tchk.h" + +# include "compile.h" +# include "schedule.h" +# include "vvp_net_sig.h" +# include "event.h" + +#include + +vvp_fun_tchk_width::vvp_fun_tchk_width(edge_t start_edge, vvp_time64_t limit, vvp_time64_t threshold) +: bit_(BIT4_X), start_edge_(start_edge), limit_(limit), threshold_(threshold), t1_(0), t2_(0), width_(0) +{ + if (start_edge != vvp_edge_posedge && start_edge != vvp_edge_negedge) { + std::cout << "tchk error: invalid reference edge" << std::endl; + assert(0); + } +} + +vvp_fun_tchk_width::~vvp_fun_tchk_width() +{ +} + +void vvp_fun_tchk_width::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, + vvp_context_t) +{ + // Port 0 is reference input + if (port.port() == 0) { + + // See what kind of edge this represents. + edge_t mask = VVP_EDGE(bit_, bit.value(0)); + + // Save the current input for the next time around. + bit_ = bit.value(0); + + if (start_edge_ & mask) { + t1_ = schedule_simtime(); + } + + if (!(start_edge_ & mask)) { + t2_ = schedule_simtime(); + + width_ = t2_ - t1_; + if (width_ < limit_ && width_ > threshold_) { + violation(); + } + } + } +} + +void vvp_fun_tchk_width::violation() +{ + vpiHandle scope = vpi_tchk->vpi_handle(vpiScope); + char* fullname = vpi_get_str(vpiFullName, scope); + std::string violation_message; + + assert(vpi_tchk->file_idx_ < file_names.size()); + + std::string time_unit("ps"); // For now let's just print everything in ps + + // Build the violation message + violation_message += "Timing violation!\n"; + violation_message += "\t$width( "; + if (start_edge_ == vvp_edge_posedge) violation_message += "posedge "; + else violation_message += "negedge "; + violation_message += vpi_get_str(vpiName, vpi_tchk->vpi_reference_); + violation_message += ":" + std::to_string(t1_) + " " + time_unit + ", "; + violation_message += std::to_string(limit_) + " " + time_unit + " : "; + violation_message += std::to_string(width_); + violation_message +=" "+time_unit+", "; + violation_message += std::to_string(threshold_) + " " + time_unit + ", "; + violation_message += vpi_get_str(vpiName, vpi_tchk->vpi_notifier_); + violation_message += " );\n"; + violation_message += "\tfile_idx = "; + violation_message += file_names[vpi_tchk->file_idx_]; + violation_message += " line = " + std::to_string(vpi_tchk->lineno_) + "\n"; + violation_message += "\tScope: "; + violation_message += fullname; + violation_message += "\n"; + violation_message += "\tTime: "; + violation_message += std::to_string(t2_); + violation_message += " "+time_unit; + + // Print the violation message + std::cout << violation_message << std::endl; + + if (vpi_tchk->vpi_notifier_) { + + __vpiSignal* vpi_notifier_sig = dynamic_cast<__vpiSignal*> (vpi_tchk->vpi_notifier_); + + if (vpi_notifier_sig == 0) { + std::cout << "tchk error: notifier not a signal?" << std::endl; + assert(vpi_notifier_sig); + } + + vvp_vector4_t sig_value; + + // Is it a signal functor? + vvp_signal_value* sig = dynamic_cast (vpi_notifier_sig->node->fil); + if (sig == 0) { + std::cout << "tchk error: notifier not a signal?" << std::endl; + assert(sig); + } + + // Extract the value from the signal + sig->vec4_value(sig_value); + + // If notifier is z, nothing is to do + if (sig_value.value(0) == BIT4_Z) return; + + // Update notifier value + if (sig_value.value(0) != BIT4_1) { + vvp_net_ptr_t ptr (vpi_notifier_sig->node, 0); + vvp_send_vec4(ptr, vvp_vector4_t(1, BIT4_1), nullptr); + } else { + vvp_net_ptr_t ptr (vpi_notifier_sig->node, 0); + vvp_send_vec4(ptr, vvp_vector4_t(1, BIT4_0), nullptr); + } + } +} diff --git a/vvp/tchk.h b/vvp/tchk.h new file mode 100644 index 0000000000..a642bac422 --- /dev/null +++ b/vvp/tchk.h @@ -0,0 +1,65 @@ +#ifndef IVL_tchk_H +#define IVL_tchk_H +/* + * Copyright (c) 2023 Stephen Williams (steve@icarus.com) + * Copyright (c) 2023 Leo Moser (leo.moser@pm.me) + * + * 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 +# include "vvp_net.h" +# include "schedule.h" +# include "event.h" + +class __vpiTchkWidth; + +/* + * The vvp_fun_tchk_width functor implements the $width timing check + * The reference event is connected to port 0, the data event is the + * inverse of the referenc event + */ +class vvp_fun_tchk_width : public vvp_net_fun_t { + + public: + typedef unsigned short edge_t; + explicit vvp_fun_tchk_width(edge_t start_edge, vvp_time64_t limit, vvp_time64_t threshold); + virtual ~vvp_fun_tchk_width(); + + __vpiTchkWidth* vpi_tchk; + + protected: + void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, + vvp_context_t); + + vvp_bit4_t bit_; + + private: + edge_t start_edge_; + // Time values already scaled for + // higher performance + vvp_time64_t limit_; + vvp_time64_t threshold_; + vvp_time64_t t1_; + vvp_time64_t t2_; + vvp_time64_t width_; + + void violation(); + + friend __vpiTchkWidth; +}; + +#endif /* IVL_tchk_H */ diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 1c5f016527..2ba04d72a5 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -34,7 +34,10 @@ * and "vvp_fun_modpath" classes definitions */ #include "delay.h" - +/* + * Added to use some "vvp_fun_tchk_width" definition + */ +#include "tchk.h" class class_type; class vvp_darray; @@ -610,6 +613,107 @@ struct __vpiInterModPath : public __vpiHandle { extern struct __vpiInterModPath* vpip_make_intermodpath(vvp_net_t *net, vpiPortInfo* port1, vpiPortInfo* port2); +/* + * The __vpiTchkTerm is the base class for + * __vpiTchkRefTerm and __vpiTchkDataTerm + */ + +class __vpiTchkTerm : public __vpiHandle { + public: + __vpiTchkTerm(int edge, vpiHandle expr); + virtual ~__vpiTchkTerm(); + virtual int get_type_code(void) const = 0; + int vpi_get(int code); + vpiHandle vpi_handle(int code); + private: + vpiHandle expr_; + /* The value returned by vpi_get(vpiEdge, ...); */ + int edge_; +}; + +/* + * The __vpiTchkRefTerm represents the reference event of a timing check + */ + +class __vpiTchkRefTerm : public __vpiTchkTerm { + public: + __vpiTchkRefTerm(int edge, vpiHandle expr); + ~__vpiTchkRefTerm(); + int get_type_code(void) const override { return vpiTchkRefTerm; }; +}; + +/* + * The __vpiTchkDataTerm represents the data event of a timing check + */ + +class __vpiTchkDataTerm : public __vpiTchkTerm { + public: + __vpiTchkDataTerm(int edge, vpiHandle expr); + ~__vpiTchkDataTerm(); + int get_type_code(void) const override { return vpiTchkDataTerm; }; +}; + +/* + * The __vpiTchk is the base class for all timing check vpiHandles + */ + +class __vpiTchk : public __vpiHandle { + public: + __vpiTchk( __vpiScope *scope, unsigned file_idx, unsigned lineno); + ~__vpiTchk(); + + int get_type_code(void) const { return vpiTchk; } + + virtual int vpi_get(int code) = 0; + virtual vpiHandle vpi_handle(int code) = 0; + virtual void vpi_get_delays(p_vpi_delay del) = 0; + virtual void vpi_put_delays(p_vpi_delay del) = 0; + + protected: + __vpiScope *scope_; + unsigned file_idx_; + unsigned lineno_; + +}; + +/* + * + * The vpiInterModPath vpiHandle will define + * a __vpiTchkWidth of record .tchk_width as defined + * in the IEEE 1364 + * + */ + +class __vpiTchkWidth : public __vpiTchk { + public: + __vpiTchkWidth( __vpiScope *scope, unsigned file_idx, unsigned lineno, vvp_fun_tchk_width* fun ); + ~__vpiTchkWidth(); + + int get_type_code(void) const { return vpiTchk; } + + int vpi_get(int code) override; + vpiHandle vpi_handle(int code) override; + void vpi_get_delays(p_vpi_delay del) override; + void vpi_put_delays(p_vpi_delay del) override; + + vpiHandle vpi_notifier_; + vpiHandle vpi_reference_; + + vpiHandle vpi_tchk_ref_term_; + + private: + vvp_fun_tchk_width* fun_; + + friend vvp_fun_tchk_width; +}; + +/* + * The Function is used to create the vpiHandle + * for __vpiTchkWidth + */ + +extern vpiHandle vpip_make_tchk_width(long file_idx, long lineno, vvp_fun_tchk_width* fun); + /* * These methods support the vpi creation of events. The name string * passed in will be saved, so the caller must allocate it (or not diff --git a/vvp/vpi_tchk.cc b/vvp/vpi_tchk.cc new file mode 100644 index 0000000000..6195ad00ca --- /dev/null +++ b/vvp/vpi_tchk.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2023 Stephen Williams (steve@icarus.com) + * Copyright (c) 2023 Leo Moser (leo.moser@pm.me) + * + * 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 "tchk.h" + +# include "compile.h" +# include "event.h" + +#include + +__vpiTchk::__vpiTchk( __vpiScope *scope, unsigned file_idx, unsigned lineno) +: scope_(scope), file_idx_(file_idx), lineno_(lineno) +{ +} + +__vpiTchk::~__vpiTchk() +{ +} + +__vpiTchkWidth::__vpiTchkWidth( __vpiScope *scope, unsigned file_idx, unsigned lineno, vvp_fun_tchk_width* fun ) +: __vpiTchk(scope, file_idx, lineno), fun_(fun) +{ +} + +__vpiTchkWidth::~__vpiTchkWidth() +{ +} + +int __vpiTchkWidth::vpi_get(int code) { + + switch (code) { + + case vpiTchkType: + return vpiWidth; + + default: + fprintf(stderr, "VPI error: unknown signal_get property %d.\n", + code); + return vpiUndefined; + } + +}; + +vpiHandle __vpiTchkWidth::vpi_handle(int code) { + + switch (code) { + + case vpiScope: + return scope_; + + case vpiTchkNotifier: + return vpi_notifier_; + + case vpiTchkRefTerm: + if (!vpi_tchk_ref_term_) { + if (fun_->start_edge_ == vvp_edge_posedge) vpi_tchk_ref_term_ = new __vpiTchkRefTerm(vpiPosedge, vpi_reference_); + else vpi_tchk_ref_term_ = new __vpiTchkRefTerm(vpiNegedge, vpi_reference_); + } + return vpi_tchk_ref_term_; + + // There is no data term for $width + case vpiTchkDataTerm: + return 0; + } + + return 0; +}; + +void __vpiTchkWidth::vpi_get_delays(p_vpi_delay delays) { + + // $width has one limit TODO or also threshold? + if (delays->no_of_delays != 1) return; + + delays->da[0].real = vpip_time_to_scaled_real(fun_->limit_, scope_); +}; + +void __vpiTchkWidth::vpi_put_delays(p_vpi_delay delays) { + + // $width has one limit TODO or also threshold? + if (delays->no_of_delays != 1) return; + + fun_->limit_ = vpip_scaled_real_to_time64(delays->da[0].real, scope_); +}; + +__vpiTchkTerm::__vpiTchkTerm(int edge, vpiHandle expr) +: expr_(expr), edge_(edge) +{ +} + +__vpiTchkTerm::~__vpiTchkTerm() +{ +} + +int __vpiTchkTerm::vpi_get(int code) +{ + switch (code) { + case vpiEdge: + return edge_; + default: + return 0; + } +} + +vpiHandle __vpiTchkTerm::vpi_handle(int code) +{ + switch (code) { + case vpiExpr: + return expr_; + default: + return nullptr; + } +} + +__vpiTchkRefTerm::__vpiTchkRefTerm(int edge, vpiHandle expr) +: __vpiTchkTerm(edge, expr) +{ +} + +__vpiTchkRefTerm::~__vpiTchkRefTerm() +{ +} + +__vpiTchkDataTerm::__vpiTchkDataTerm(int edge, vpiHandle expr) +: __vpiTchkTerm(edge, expr) +{ +} + +__vpiTchkDataTerm::~__vpiTchkDataTerm() +{ +} + +vpiHandle vpip_make_tchk_width(long file_idx, long lineno, vvp_fun_tchk_width* fun) +{ + __vpiTchkWidth* obj = new __vpiTchkWidth(vpip_peek_current_scope(), file_idx, lineno, fun); + fun->vpi_tchk = obj; + + return obj; +} diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index ef995d57ed..f14502d55a 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -510,13 +510,6 @@ ostream& operator<<(ostream&out, vvp_bit4_t bit) return out; } -typedef unsigned short edge_t; - -inline edge_t VVP_EDGE(vvp_bit4_t from, vvp_bit4_t to) -{ - return 1 << ((from << 2) | to); -} - const edge_t vvp_edge_posedge = VVP_EDGE(BIT4_0,BIT4_1) | VVP_EDGE(BIT4_0,BIT4_X) @@ -533,6 +526,21 @@ const edge_t vvp_edge_negedge | VVP_EDGE(BIT4_Z,BIT4_0) ; +const edge_t vvp_edge_edge + = VVP_EDGE(BIT4_0,BIT4_1) + | VVP_EDGE(BIT4_1,BIT4_0) + | VVP_EDGE(BIT4_0,BIT4_X) + | VVP_EDGE(BIT4_X,BIT4_0) + | VVP_EDGE(BIT4_0,BIT4_Z) + | VVP_EDGE(BIT4_Z,BIT4_0) + | VVP_EDGE(BIT4_X,BIT4_1) + | VVP_EDGE(BIT4_1,BIT4_X) + | VVP_EDGE(BIT4_Z,BIT4_1) + | VVP_EDGE(BIT4_1,BIT4_Z) + ; + +const edge_t vvp_edge_none = 0; + int edge(vvp_bit4_t from, vvp_bit4_t to) { edge_t mask = VVP_EDGE(from, to); diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index b7e14dd7a4..5106194985 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -185,6 +185,19 @@ inline vvp_bit4_t operator & (vvp_bit4_t a, vvp_bit4_t b) extern vvp_bit4_t operator ^ (vvp_bit4_t a, vvp_bit4_t b); extern std::ostream& operator<< (std::ostream&o, vvp_bit4_t a); + /* Specifies edges in vvp */ +typedef unsigned short edge_t; + +static inline edge_t VVP_EDGE(vvp_bit4_t from, vvp_bit4_t to) +{ + return 1 << ((from << 2) | to); +} + +extern const edge_t vvp_edge_edge; +extern const edge_t vvp_edge_posedge; +extern const edge_t vvp_edge_negedge; +extern const edge_t vvp_edge_none; + /* Return >0, ==0 or <0 if the from-to transition represents a posedge, no edge, or negedge. */ extern int edge(vvp_bit4_t from, vvp_bit4_t to); From 9fa64ecef905d1cac048c0f8828c06ebdc63ac2c Mon Sep 17 00:00:00 2001 From: mole99 Date: Tue, 12 Sep 2023 11:26:13 +0200 Subject: [PATCH 2/4] Implement cbTchkViolation for $width --- vvp/tchk.cc | 6 ++++++ vvp/vpi_callback.cc | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/vvp/tchk.cc b/vvp/tchk.cc index 6afad4328a..5b44182979 100644 --- a/vvp/tchk.cc +++ b/vvp/tchk.cc @@ -27,6 +27,9 @@ #include +// Trigger cbTchkViolation +extern void vpiTchkViolation(vpiHandle tchk); + vvp_fun_tchk_width::vvp_fun_tchk_width(edge_t start_edge, vvp_time64_t limit, vvp_time64_t threshold) : bit_(BIT4_X), start_edge_(start_edge), limit_(limit), threshold_(threshold), t1_(0), t2_(0), width_(0) { @@ -136,4 +139,7 @@ void vvp_fun_tchk_width::violation() vvp_send_vec4(ptr, vvp_vector4_t(1, BIT4_0), nullptr); } } + + // Trigger cbTchkViolation + vpiTchkViolation(vpi_tchk); } diff --git a/vvp/vpi_callback.cc b/vvp/vpi_callback.cc index 3427f32f6d..0ea4a0b263 100644 --- a/vvp/vpi_callback.cc +++ b/vvp/vpi_callback.cc @@ -471,6 +471,7 @@ static simulator_callback*NextSimTime = 0; static simulator_callback*EndOfCompile = 0; static simulator_callback*StartOfSimulation = 0; static simulator_callback*EndOfSimulation = 0; +static simulator_callback*TchkViolation = 0; #ifdef CHECK_WITH_VALGRIND /* This is really only needed if the simulator aborts before starting the @@ -619,6 +620,49 @@ static simulator_callback* make_prepost(p_cb_data data) return obj; } +/* + * Run all TchkViolation callbacks with cb_data.obj set to the tchk + */ +void vpiTchkViolation(vpiHandle tchk) +{ + simulator_callback *cur, *next, *prev; + + assert(vpi_mode_flag == VPI_MODE_NONE); + vpi_mode_flag = VPI_MODE_RWSYNC; + + cur = TchkViolation; + prev = nullptr; + while (cur) { + next = dynamic_cast(cur->next); + cur->cb_data.obj = tchk; + if (cur->cb_data.cb_rtn != 0) { + (cur->cb_data.cb_rtn)(&cur->cb_data); + } else { + if (!prev) { + TchkViolation = next; + } else { + prev->next = next; + } + delete cur; + } + prev = cur; + cur = next; + } + + vpi_mode_flag = VPI_MODE_NONE; +} + +static simulator_callback* make_tchk_violation(p_cb_data data) +{ + simulator_callback*obj = new simulator_callback(data); + + // Insert at head of list + obj->next = TchkViolation; + TchkViolation = obj; + + return obj; +} + vpiHandle vpi_register_cb(p_cb_data data) { struct __vpiCallback*obj = 0; @@ -657,6 +701,10 @@ vpiHandle vpi_register_cb(p_cb_data data) obj = make_prepost(data); break; + case cbTchkViolation: + obj = make_tchk_violation(data); + break; + default: fprintf(stderr, "vpi error: vpi_register_cb invalid or " "unsupported callback reason: %d\n", From 58567116c54d05fb61a2d6813d11a015d3fa4e95 Mon Sep 17 00:00:00 2001 From: mole99 Date: Tue, 12 Sep 2023 16:51:48 +0200 Subject: [PATCH 3/4] Cleanup of violation message --- vvp/tchk.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vvp/tchk.cc b/vvp/tchk.cc index 5b44182979..ea26863915 100644 --- a/vvp/tchk.cc +++ b/vvp/tchk.cc @@ -73,7 +73,7 @@ void vvp_fun_tchk_width::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, void vvp_fun_tchk_width::violation() { vpiHandle scope = vpi_tchk->vpi_handle(vpiScope); - char* fullname = vpi_get_str(vpiFullName, scope); + std::string scope_fullname(vpi_get_str(vpiFullName, scope)); std::string violation_message; assert(vpi_tchk->file_idx_ < file_names.size()); @@ -93,11 +93,11 @@ void vvp_fun_tchk_width::violation() violation_message += std::to_string(threshold_) + " " + time_unit + ", "; violation_message += vpi_get_str(vpiName, vpi_tchk->vpi_notifier_); violation_message += " );\n"; - violation_message += "\tfile_idx = "; + violation_message += "\tfile = "; violation_message += file_names[vpi_tchk->file_idx_]; violation_message += " line = " + std::to_string(vpi_tchk->lineno_) + "\n"; violation_message += "\tScope: "; - violation_message += fullname; + violation_message += scope_fullname; violation_message += "\n"; violation_message += "\tTime: "; violation_message += std::to_string(t2_); From 94fa601e66f0445a4fceb9a456f97d249ee1ab27 Mon Sep 17 00:00:00 2001 From: mole99 Date: Thu, 12 Oct 2023 16:02:38 +0200 Subject: [PATCH 4/4] Work on emitting timing check --- emit.cc | 6 ++++++ ivl_target.h | 3 +++ t-dll-api.cc | 13 +++++++++++++ t-dll.cc | 13 +++++++++++++ t-dll.h | 10 ++++++++++ target.h | 3 +++ tgt-vvp/draw_tchk.c | 38 ++++++++++++++++++++++++++++++++++++++ tgt-vvp/vvp_priv.h | 7 +++++++ tgt-vvp/vvp_scope.c | 9 +++++++++ vvp/compile.cc | 7 +------ 10 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 tgt-vvp/draw_tchk.c diff --git a/emit.cc b/emit.cc index 39339c281a..642bff75fd 100644 --- a/emit.cc +++ b/emit.cc @@ -489,6 +489,12 @@ void NetScope::emit_scope(struct target_t*tgt) const tgt->signal(cur->second); } + // TODO use for timing checks! + for (signals_map_iter_t cur = signals_map_.begin() + ; cur != signals_map_.end() ; ++ cur ) { + tgt->tchk(cur->second); + } + // Run the signals again, but this time to connect the // delay paths. This is done as a second pass because // the paths reference other signals that may be later diff --git a/ivl_target.h b/ivl_target.h index b33ea9aa41..42ba47b949 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -190,6 +190,7 @@ typedef struct ivl_parameter_s*ivl_parameter_t; typedef struct ivl_process_s *ivl_process_t; typedef struct ivl_scope_s *ivl_scope_t; typedef struct ivl_signal_s *ivl_signal_t; +typedef struct ivl_tchk_s *ivl_tchk_t; typedef struct ivl_port_info_s*ivl_port_info_t; typedef struct ivl_switch_s *ivl_switch_t; typedef struct ivl_memory_s *ivl_memory_t; //XXXX __attribute__((deprecated)); @@ -1905,6 +1906,8 @@ extern ivl_signal_t ivl_scope_port(ivl_scope_t net, unsigned idx); extern ivl_nexus_t ivl_scope_mod_port(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_sigs(ivl_scope_t net); extern ivl_signal_t ivl_scope_sig(ivl_scope_t net, unsigned idx); +extern unsigned ivl_scope_tchks(ivl_scope_t net); +extern ivl_tchk_t ivl_scope_tchk(ivl_scope_t net, unsigned idx); extern unsigned ivl_scope_switches(ivl_scope_t net); extern ivl_switch_t ivl_scope_switch(ivl_scope_t net, unsigned idx); extern ivl_scope_type_t ivl_scope_type(ivl_scope_t net); diff --git a/t-dll-api.cc b/t-dll-api.cc index acb0e69355..580ee0b0f0 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -2366,6 +2366,19 @@ extern "C" ivl_signal_t ivl_scope_sig(ivl_scope_t net, unsigned idx) return net->sigs_[idx]; } +extern "C" unsigned ivl_scope_tchks(ivl_scope_t net) +{ + assert(net); + return net->tchks_.size(); +} + +extern "C" ivl_tchk_t ivl_scope_tchk(ivl_scope_t net, unsigned idx) +{ + assert(net); + assert(idx < net->tchks_.size()); + return net->tchks_[idx]; +} + extern "C" unsigned ivl_scope_switches(ivl_scope_t net) { assert(net); diff --git a/t-dll.cc b/t-dll.cc index db9bf3905d..830b76c3ed 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2790,6 +2790,19 @@ void dll_target::signal(const NetNet*net) if (debug_optimizer && obj->array_words > 1000) cerr << "debug: t-dll done with big nexus array" << endl; } +void dll_target::tchk(const NetNet*net) +{ + ivl_tchk_t obj = new struct ivl_tchk_s; + + obj->name_ = net->name(); + + obj->scope_ = find_scope(des_, net->scope()); + assert(obj->scope_); + //TODO FILE_NAME(obj, net); + + obj->scope_->tchks_.push_back(obj); +} + bool dll_target::signal_paths(const NetNet*net) { /* Nothing to do if there are no paths for this signal. */ diff --git a/t-dll.h b/t-dll.h index 9ae6fd58a9..6641aad1be 100644 --- a/t-dll.h +++ b/t-dll.h @@ -98,6 +98,7 @@ struct dll_target : public target_t, public expr_scan_t { void scope(const NetScope*); void convert_module_ports(const NetScope*); void signal(const NetNet*); + void tchk(const NetNet*); bool signal_paths(const NetNet*); ivl_dll_t dll_; @@ -693,6 +694,8 @@ struct ivl_scope_s { std::vector sigs_; + std::vector tchks_; + unsigned nlog_; ivl_net_logic_t*log_; @@ -778,6 +781,13 @@ struct ivl_signal_s { unsigned nattr; }; +/* + * A timing check TODO + */ +struct ivl_tchk_s { + perm_string name_; + ivl_scope_t scope_; +}; /* * The ivl_statement_t represents any statement. The type of statement diff --git a/target.h b/target.h index 6a5879a0ad..56bb2621f7 100644 --- a/target.h +++ b/target.h @@ -76,6 +76,9 @@ struct target_t { virtual void signal(const NetNet*) =0; virtual bool signal_paths(const NetNet*); + /* Output a timing check (called for each timing check) */ + virtual void tchk(const NetNet*) =0; + /* Analog branches */ virtual bool branch(const NetBranch*); diff --git a/tgt-vvp/draw_tchk.c b/tgt-vvp/draw_tchk.c new file mode 100644 index 0000000000..1fc9f63762 --- /dev/null +++ b/tgt-vvp/draw_tchk.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Stephen Williams (steve@icarus.com) + * Copyright (c) 2023 Leo Moser (leo.moser@pm.me) + * + * 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 "vvp_priv.h" +# include +# include +# include +# include +# include +# include +# include "ivl_alloc.h" + +/* + * This function draws a timing check + */ +void draw_tchk_in_scope(ivl_tchk_t tchk) +{ + // TODO use nexus??? + + fprintf(vvp_out, " .tchk_width ;\n"); +} diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index f6857e24f1..935e32bfff 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -118,6 +118,13 @@ extern char* process_octal_codes(const char*txt, unsigned wid); extern void draw_modpath(ivl_signal_t path_sig, char*drive_label, unsigned drive_index); extern void cleanup_modpath(void); +/* + * timing check symbols + * TODO + */ + +extern void draw_tchk_in_scope(ivl_tchk_t tchk); + /* * This function draws the execution of a vpi_call statement, along * with the tricky handling of arguments. If this is called with a diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index c4a6599581..e0b9d1979f 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -2515,6 +2515,15 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) } } + /* Draw the timing checks. */ + + for (idx = 0 ; idx < ivl_scope_tchks(net) ; idx += 1) { + ivl_tchk_t tchk = ivl_scope_tchk(net, idx); + + // TODO check for tchk type + draw_tchk_in_scope(tchk); + } + for (idx = 0 ; idx < ivl_scope_events(net) ; idx += 1) { ivl_event_t event = ivl_scope_event(net, idx); draw_event_in_scope(event); diff --git a/vvp/compile.cc b/vvp/compile.cc index 31589f146a..f056abbfae 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -2080,13 +2080,8 @@ void compile_tchk_width(char* edge, struct symb_s ref, char* condition, double l vpip_scaled_real_to_time64(threshold, scope)); my_tchk_width->fun = obj_tchk_width; - // Copy string because it is used twice for lookup - // TODO is there a better way? - char* ref_tmp = (char*)malloc(50); - strcpy(ref_tmp, ref.text); - // Connect reference signal to port 0 and create VPI object - input_connect(my_tchk_width, 0, ref_tmp); + input_connect(my_tchk_width, 0, strdup(ref.text)); __vpiTchkWidth* obj = (__vpiTchkWidth*)vpip_make_tchk_width(file_idx, lineno, obj_tchk_width); // Add vpiHandles to object for later lookup