This commit is contained in:
Leo Moser 2025-10-06 12:40:46 +03:00 committed by GitHub
commit 407eba65ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 884 additions and 117 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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
View File

@ -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

View File

@ -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*);

38
tgt-vvp/draw_tchk.c Normal file
View File

@ -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");
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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.

View File

@ -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()
{

View File

@ -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.
*/

View File

@ -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]*">" {

View File

@ -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 ';'

145
vvp/tchk.cc Normal file
View File

@ -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);
}

65
vvp/tchk.h Normal file
View File

@ -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 */

View File

@ -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",

View File

@ -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

155
vvp/vpi_tchk.cc Normal file
View File

@ -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;
}

View File

@ -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);

View File

@ -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);