Merge 94fa601e66 into 1fdeb7b982
This commit is contained in:
commit
407eba65ed
6
emit.cc
6
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
13
t-dll-api.cc
13
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);
|
||||
|
|
|
|||
13
t-dll.cc
13
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. */
|
||||
|
|
|
|||
10
t-dll.h
10
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<ivl_signal_t> sigs_;
|
||||
|
||||
std::vector<ivl_tchk_t> 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
|
||||
|
|
|
|||
3
target.h
3
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*);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <stdlib.h>
|
||||
# include <math.h>
|
||||
# include <string.h>
|
||||
# include <inttypes.h>
|
||||
# include <limits.h>
|
||||
# include <assert.h>
|
||||
# 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");
|
||||
}
|
||||
|
|
@ -107,6 +107,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
|
||||
|
|
|
|||
|
|
@ -2504,6 +2504,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);
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
<COND_EDGE_ID,EDGE_ID>"01" {return K_01; }
|
||||
<COND_EDGE_ID,EDGE_ID>"10" {return K_10; }
|
||||
<COND_EDGE_ID,EDGE_ID>"0"[zZ] {return K_0Z; }
|
||||
<COND_EDGE_ID,EDGE_ID>[zZ]"1" {return K_Z1; }
|
||||
<COND_EDGE_ID,EDGE_ID>"1"[zZ] {return K_1Z; }
|
||||
<COND_EDGE_ID,EDGE_ID>[zZ]"0" {return K_Z0; }
|
||||
<COND_EDGE_ID,EDGE_ID>[pP][oO][sS][eE][dD][gG][eE] {return K_POSEDGE; }
|
||||
<COND_EDGE_ID,EDGE_ID>[nN][eE][gG][eE][dD][gG][eE] {return K_NEGEDGE; }
|
||||
<COND_EDGE_ID>[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;
|
||||
|
|
|
|||
|
|
@ -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 <interconnect_port> port_interconnect
|
||||
|
||||
%type <port_tchk> port_tchk
|
||||
|
||||
%type <real_val> signed_real_number
|
||||
%type <delay> 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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
18
vpi_user.h
18
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
|
||||
|
|
|
|||
|
|
@ -70,7 +70,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 = lib_main.o \
|
||||
|
|
@ -82,7 +82,7 @@ O = lib_main.o \
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -32,11 +32,14 @@
|
|||
# include "parse_misc.h"
|
||||
# include "statistics.h"
|
||||
# include "schedule.h"
|
||||
# include "event.h"
|
||||
# include <iostream>
|
||||
# include <list>
|
||||
# include <cstdlib>
|
||||
# include <cstring>
|
||||
# include <cassert>
|
||||
# include "vvp_net_sig.h"
|
||||
# include "tchk.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <windows.h>
|
||||
|
|
@ -2075,3 +2078,41 @@ 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;
|
||||
|
||||
// Connect reference signal to port 0 and create VPI object
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
36
vvp/event.cc
36
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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
||||
[Cc]"4<"[01xz]*">" {
|
||||
|
|
|
|||
10
vvp/parse.y
10
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 <text> T_INSTR
|
||||
%token <text> T_LABEL
|
||||
%token <numb> T_NUMBER
|
||||
%token <real> T_REAL
|
||||
%token <text> T_STRING
|
||||
%token <text> T_SYMBOL
|
||||
%token <vect> 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 ';'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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 <iostream>
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
std::string scope_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 = ";
|
||||
violation_message += file_names[vpi_tchk->file_idx_];
|
||||
violation_message += " line = " + std::to_string(vpi_tchk->lineno_) + "\n";
|
||||
violation_message += "\tScope: ";
|
||||
violation_message += scope_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<vvp_signal_value*> (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);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger cbTchkViolation
|
||||
vpiTchkViolation(vpi_tchk);
|
||||
}
|
||||
|
|
@ -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 <stddef.h>
|
||||
# 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 */
|
||||
|
|
@ -584,6 +584,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
|
||||
|
|
@ -737,6 +738,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<simulator_callback*>(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;
|
||||
|
|
@ -775,6 +819,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",
|
||||
|
|
|
|||
106
vvp/vpi_priv.h
106
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
|
||||
|
|
|
|||
|
|
@ -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 <iostream>
|
||||
|
||||
__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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -184,6 +184,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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue