Initial support for $countdrivers.

This patch implements the $countdrivers system function. It does not
yet support wires connected to islands (and outputs a suitable "sorry"
message when this is detected).
This commit is contained in:
Martin Whitaker 2012-07-10 22:33:17 +01:00 committed by Stephen Williams
parent a290c58a2e
commit 6836068a16
14 changed files with 399 additions and 16 deletions

View File

@ -52,11 +52,11 @@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@
LDFLAGS = @LDFLAGS@
# Object files for system.vpi
O = sys_table.o sys_convert.o sys_deposit.o sys_display.o sys_fileio.o \
sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o sys_random.o \
sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \
sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o sys_priv.o \
sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o \
O = sys_table.o sys_convert.o sys_countdrivers.o sys_deposit.o sys_display.o \
sys_fileio.o sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o \
sys_random.o sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o \
sys_sdf.o sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o \
sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o \
table_mod.o table_mod_lexor.o table_mod_parse.o
OPP = vcd_priv2.o

212
vpi/sys_countdrivers.c Normal file
View File

@ -0,0 +1,212 @@
/*
* Copyright (C) 2012 Martin Whitaker. (icarus@martin-whitaker.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it 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 "sys_priv.h"
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include "ivl_alloc.h"
/*
* Check to see if an argument is a single bit net.
*/
static void check_net_arg(vpiHandle arg, vpiHandle callh, const char *name)
{
assert(arg);
switch (vpi_get(vpiType, arg)) {
case vpiPartSelect:
if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) != vpiNet)
break;
case vpiNet:
if (vpi_get(vpiSize, arg) != 1)
break;
return;
default:
break;
}
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
vpi_printf("%s's first argument must be a scalar net or "
"a bit-select of a vector net.\n", name);
vpi_control(vpiFinish, 1);
}
/*
* Check to see if an argument is a variable.
*/
static void check_var_arg(vpiHandle arg, vpiHandle callh, const char *name,
const char *arg_name)
{
assert(arg);
switch (vpi_get(vpiType, arg)) {
case vpiPartSelect:
if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) == vpiNet)
break;
case vpiMemoryWord:
case vpiBitVar:
case vpiReg:
case vpiIntegerVar:
case vpiIntVar:
case vpiLongIntVar:
case vpiTimeVar:
return;
default:
break;
}
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
vpi_printf("%s's %s argument must be a variable.\n",
name, arg_name);
vpi_control(vpiFinish, 1);
}
/*
* Check that the given $countdrivers() call has valid arguments.
*/
static PLI_INT32 sys_countdrivers_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
unsigned arg_num;
/* Check that there are arguments. */
if (argv == 0) {
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
vpi_printf("%s requires at least one argument.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
/* The first argument must be a scalar net or a net bit select. */
arg = vpi_scan(argv);
check_net_arg(arg, callh, name);
/* The optional arguments must be variables. */
for (arg_num = 2; arg_num < 7; arg_num += 1) {
char *arg_name = NULL;
switch (arg_num) {
case 2: arg_name = "second"; break;
case 3: arg_name = "third"; break;
case 4: arg_name = "fourth"; break;
case 5: arg_name = "fifth"; break;
case 6: arg_name = "sixth"; break;
default: assert(0);
}
arg = vpi_scan(argv);
if (arg == 0)
return 0;
check_var_arg(arg, callh, name, arg_name);
}
/* Make sure there are no extra arguments. */
check_for_extra_args(argv, callh, name, "six arguments", 0);
return 0;
}
/*
* The runtime code for $countdrivers().
*/
static PLI_INT32 sys_countdrivers_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
unsigned idx;
unsigned counts[4];
unsigned num_drivers;
s_vpi_value val;
/* All returned values are integers. */
val.format = vpiIntVal;
/* Get the base net reference and bit select */
idx = 0;
arg = vpi_scan(argv);
assert(arg);
if (vpi_get(vpiType, arg) == vpiPartSelect) {
idx = vpi_get(vpiLeftRange, arg);
arg = vpi_handle(vpiParent, arg);
assert(arg);
}
/* Get the net driver counts from the runtime. */
vpip_count_drivers(arg, idx, counts);
num_drivers = counts[0] + counts[1] + counts[2];
/* Handle optional net_is_forced argument. */
arg = vpi_scan(argv);
if (arg == 0) goto args_done;
val.value.integer = counts[3];
vpi_put_value(arg, &val, 0, vpiNoDelay);
/* Handle optional number_of_01x_drivers argument. */
arg = vpi_scan(argv);
if (arg == 0) goto args_done;
val.value.integer = num_drivers;
vpi_put_value(arg, &val, 0, vpiNoDelay);
/* Handle optional number_of_0_drivers argument. */
arg = vpi_scan(argv);
if (arg == 0) goto args_done;
val.value.integer = counts[0];
vpi_put_value(arg, &val, 0, vpiNoDelay);
/* Handle optional number_of_1_drivers argument. */
arg = vpi_scan(argv);
if (arg == 0) goto args_done;
val.value.integer = counts[1];
vpi_put_value(arg, &val, 0, vpiNoDelay);
/* Handle optional number_of_x_drivers argument. */
arg = vpi_scan(argv);
if (arg == 0) goto args_done;
val.value.integer = counts[2];
vpi_put_value(arg, &val, 0, vpiNoDelay);
/* Free the argument iterator. */
vpi_free_object(argv);
args_done:
val.value.integer = (num_drivers > 1) ? 1 : 0;
vpi_put_value(callh, &val, 0, vpiNoDelay);
return 0;
}
/*
* Routine to register the system tasks/functions provided in this file.
*/
void sys_countdrivers_register()
{
s_vpi_systf_data tf_data;
vpiHandle res;
tf_data.type = vpiSysFunc;
tf_data.tfname = "$countdrivers";
tf_data.calltf = sys_countdrivers_calltf;
tf_data.compiletf = sys_countdrivers_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = "$countdrivers";
res = vpi_register_systf(&tf_data);
vpip_make_systf_system_defined(res);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2011 Cary R. (cygcary@yahoo.com)
* Copyright (C) 2008-2012 Cary R. (cygcary@yahoo.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -301,11 +301,6 @@ void sys_special_register(void)
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiIntFunc;
tf_data.tfname = "$countdrivers";
tf_data.user_data = "$countdrivers";
res = vpi_register_systf(&tf_data);
vpip_make_systf_system_defined(res);
tf_data.tfname = "$getpattern";
tf_data.user_data = "$getpattern";
res = vpi_register_systf(&tf_data);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2010,2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -24,6 +24,7 @@
# include <string.h>
extern void sys_convert_register();
extern void sys_countdrivers_register();
extern void sys_fileio_register();
extern void sys_finish_register();
extern void sys_deposit_register();
@ -195,6 +196,7 @@ static void sys_lxt_or_vcd_register()
void (*vlog_startup_routines[])() = {
sys_convert_register,
sys_countdrivers_register,
sys_fileio_register,
sys_finish_register,
sys_deposit_register,

View File

@ -616,6 +616,17 @@ extern void vpip_set_return_value(int value);
extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg);
extern void vpip_make_systf_system_defined(vpiHandle ref);
/* Return driver information for a net bit. The information is returned
in the 'counts' array as follows:
counts[0] - number of drivers driving '0' onto the net
counts[1] - number of drivers driving '1' onto the net
counts[2] - number of drivers driving 'X' onto the net
counts[3] - set to 1 if the net is forced, 0 otherwise
The 'ref' argument should reference a net. The 'idx' argument selects
which bit of the net is examined. */
extern void vpip_count_drivers(vpiHandle ref, unsigned idx,
unsigned counts[4]);
EXTERN_C_END
#endif

View File

@ -23,12 +23,16 @@
# include "schedule.h"
# include <list>
# include <iostream>
using namespace std;
class vvp_island_tran : public vvp_island {
public:
void run_island();
void count_drivers(vvp_island_port*port, unsigned bit_idx,
unsigned counts[3]);
};
enum tran_state_t {
@ -102,6 +106,18 @@ void vvp_island_tran::run_island()
}
}
static bool warn_count_drivers = true;
void vvp_island_tran::count_drivers(vvp_island_port*port, unsigned bit_idx,
unsigned counts[3])
{
if (warn_count_drivers) {
cerr << "sorry: $countdrivers is not yet fully implemented." << endl;
cerr << " Some driver counts will not be correct." << endl;
warn_count_drivers = false;
}
}
bool vvp_island_branch_tran::run_test_enabled()
{
vvp_island_port*ep = en? dynamic_cast<vvp_island_port*> (en->fun) : 0;

View File

@ -178,6 +178,16 @@ void resolv_tri::recv_vec8_(unsigned port, const vvp_vector8_t&bit)
net_->send_vec8(val_[base]);
}
void resolv_tri::count_drivers(unsigned bit_idx, unsigned counts[3])
{
for (unsigned idx = 0 ; idx < nports_ ; idx += 1) {
if (val_[idx].size() == 0)
continue;
update_driver_counts(val_[idx].value(bit_idx).value(), counts);
}
}
resolv_wired_logic::resolv_wired_logic(unsigned nports, vvp_net_t*net)
: resolv_core(nports, net)
@ -253,6 +263,16 @@ void resolv_wired_logic::recv_vec8_(unsigned port, const vvp_vector8_t&bit)
recv_vec4_(port, reduce4(bit));
}
void resolv_wired_logic::count_drivers(unsigned bit_idx, unsigned counts[3])
{
for (unsigned idx = 0 ; idx < nports_ ; idx += 1) {
if (val_[idx].size() == 0)
continue;
update_driver_counts(val_[idx].value(bit_idx), counts);
}
}
resolv_triand::resolv_triand(unsigned nports, vvp_net_t*net)
: resolv_wired_logic(nports, net)

View File

@ -52,6 +52,8 @@ class resolv_core : public vvp_net_fun_t {
unsigned base, unsigned wid, unsigned vwid)
{ recv_vec8_pv_(port.port(), bit, base, wid, vwid); }
virtual void count_drivers(unsigned bit_idx, unsigned counts[3]) =0;
private:
friend class resolv_extend;
virtual void recv_vec4_(unsigned port, const vvp_vector4_t&bit) =0;
@ -115,6 +117,8 @@ class resolv_tri : public resolv_core {
vvp_scalar_t hiz_value);
~resolv_tri();
void count_drivers(unsigned bit_idx, unsigned counts[3]);
private:
void recv_vec4_(unsigned port, const vvp_vector4_t&bit);
void recv_vec8_(unsigned port, const vvp_vector8_t&bit);
@ -141,6 +145,8 @@ class resolv_wired_logic : public resolv_core {
explicit resolv_wired_logic(unsigned nports, vvp_net_t*net);
virtual ~resolv_wired_logic();
void count_drivers(unsigned bit_idx, unsigned counts[3]);
protected:
virtual vvp_vector4_t wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b) =0;

View File

@ -1458,3 +1458,16 @@ extern "C" s_vpi_vecval vpip_calc_clog2(vpiHandle arg)
rtn.bval = 0;
return rtn;
}
/*
* This routine provides the information needed to implement $countdrivers.
* It is done here for performance reasons - interrogating the drivers
* individually via the VPI interface would be much slower.
*/
extern "C" void vpip_count_drivers(vpiHandle ref, unsigned idx,
unsigned counts[4])
{
struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref);
assert(rfp);
rfp->node->count_drivers(idx, counts);
}

View File

@ -55,7 +55,7 @@
*/
struct vvp_island_branch;
class vvp_island_node;
class vvp_island_port;
class vvp_island : private vvp_gen_event_s {
@ -74,6 +74,10 @@ class vvp_island : private vvp_gen_event_s {
// method to give the island its character.
virtual void run_island() =0;
// Support for $countdrivers.
virtual void count_drivers(vvp_island_port*port, unsigned bit_idx,
unsigned counts[3]) =0;
protected:
// The base class collects a list of all the branches in the
// island. The derived island class can access this list for
@ -144,6 +148,12 @@ class vvp_island_port : public vvp_net_fun_t {
private:
vvp_island*island_;
public: // Support for $countdrivers.
inline void count_drivers(unsigned bit_idx, unsigned counts[3])
{
island_->count_drivers(this, bit_idx, counts);
}
private: // not implemented
vvp_island_port(const vvp_island_port&);
vvp_island_port& operator = (const vvp_island_port&);

View File

@ -19,7 +19,10 @@
# include "config.h"
# include "vvp_net.h"
# include "vvp_net_sig.h"
# include "vvp_island.h"
# include "vpi_priv.h"
# include "resolv.h"
# include "schedule.h"
# include "statistics.h"
# include <cstdio>
@ -209,6 +212,36 @@ void vvp_net_t::unlink(vvp_net_ptr_t dst_ptr)
net->port[net_port] = vvp_net_ptr_t(0,0);
}
void vvp_net_t::count_drivers(unsigned idx, unsigned counts[4])
{
counts[0] = 0;
counts[1] = 0;
counts[2] = 0;
counts[3] = 0;
/* $countdrivers can only be used on wires. */
vvp_wire_base*wire=dynamic_cast<vvp_wire_base*>(fil);
assert(wire);
if (wire->is_forced(idx))
counts[3] = 1;
/* If the net has multiple drivers, we need to interrogate the
resolver network to get the driven values. */
if (resolv_core*resolver = dynamic_cast<resolv_core*>(fun)) {
resolver->count_drivers(idx, counts);
return;
}
if (vvp_island_port*resolver = dynamic_cast<vvp_island_port*>(fun)) {
resolver->count_drivers(idx, counts);
return;
}
/* If the functor is not a resolver, there is only one driver, so
we can just interrogate the filter to get the driven value. */
update_driver_counts(wire->driven_value(idx), counts);
}
void vvp_net_fun_t::operator delete(void*)
{
assert(0);

View File

@ -1,7 +1,7 @@
#ifndef __vvp_net_H
#define __vvp_net_H
/*
* Copyright (c) 2004-2011 Stephen Williams (steve@icarus.com)
* Copyright (c) 2004-2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -187,6 +187,24 @@ extern ostream& operator<< (ostream&o, vvp_bit4_t a);
posedge, no edge, or negedge. */
extern int edge(vvp_bit4_t from, vvp_bit4_t to);
/* Support for $countdrivers. */
inline void update_driver_counts(vvp_bit4_t bit, unsigned counts[3])
{
switch (bit) {
case BIT4_0:
counts[0] += 1;
break;
case BIT4_1:
counts[1] += 1;
break;
case BIT4_X:
counts[2] += 1;
break;
default:
break;
}
}
/*
* This class represents scalar values collected into vectors. The
* vector values can be accessed individually, or treated as a
@ -1100,6 +1118,9 @@ class vvp_net_t {
void force_vec8(const vvp_vector8_t&val, vvp_vector2_t mask);
void force_real(double val, vvp_vector2_t mask);
public: // Method to support $countdrivers
void count_drivers(unsigned idx, unsigned counts[4]);
private:
vvp_net_ptr_t out_;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2004-2010,2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -621,6 +621,18 @@ vvp_wire_base::~vvp_wire_base()
{
}
vvp_bit4_t vvp_wire_base::driven_value(unsigned) const
{
assert(0);
return BIT4_X;
}
bool vvp_wire_base::is_forced(unsigned) const
{
assert(0);
return false;
}
vvp_wire_vec4::vvp_wire_vec4(unsigned wid, vvp_bit4_t init)
: bits4_(wid, init)
{
@ -795,6 +807,16 @@ void vvp_wire_vec4::vec4_value(vvp_vector4_t&val) const
val.set_bit(idx, filtered_value_(idx));
}
vvp_bit4_t vvp_wire_vec4::driven_value(unsigned idx) const
{
return bits4_.value(idx);
}
bool vvp_wire_vec4::is_forced(unsigned idx) const
{
return test_force_mask(idx);
}
vvp_wire_vec8::vvp_wire_vec8(unsigned wid)
: bits8_(wid)
{
@ -954,6 +976,16 @@ void vvp_wire_vec8::vec4_value(vvp_vector4_t&val) const
val = reduce4(vec8_value());
}
vvp_bit4_t vvp_wire_vec8::driven_value(unsigned idx) const
{
return bits8_.value(idx).value();
}
bool vvp_wire_vec8::is_forced(unsigned idx) const
{
return test_force_mask(idx);
}
vvp_wire_real::vvp_wire_real()
: bit_(0.0), force_(0.0)
{

View File

@ -1,7 +1,7 @@
#ifndef __vvp_net_sig_H
#define __vvp_net_sig_H
/*
* Copyright (c) 2004-2011 Stephen Williams (steve@icarus.com)
* Copyright (c) 2004-2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -272,6 +272,10 @@ class vvp_wire_base : public vvp_net_fil_t, public vvp_signal_value {
public:
vvp_wire_base();
~vvp_wire_base();
// Support for $countdrivers
virtual vvp_bit4_t driven_value(unsigned idx) const;
virtual bool is_forced(unsigned idx) const;
};
class vvp_wire_vec4 : public vvp_wire_base {
@ -304,6 +308,10 @@ class vvp_wire_vec4 : public vvp_wire_base {
vvp_scalar_t scalar_value(unsigned idx) const;
void vec4_value(vvp_vector4_t&) const;
// Support for $countdrivers
vvp_bit4_t driven_value(unsigned idx) const;
bool is_forced(unsigned idx) const;
private:
vvp_bit4_t filtered_value_(unsigned idx) const;
@ -347,6 +355,10 @@ class vvp_wire_vec8 : public vvp_wire_base {
// This is new to vvp_wire_vec8
vvp_vector8_t vec8_value() const;
// Support for $countdrivers
vvp_bit4_t driven_value(unsigned idx) const;
bool is_forced(unsigned idx) const;
private:
vvp_scalar_t filtered_value_(unsigned idx) const;