iverilog/vvp/vvp_net.h

1537 lines
47 KiB
C
Raw Normal View History

#ifndef __vvp_net_H
#define __vvp_net_H
/*
* Copyright (c) 2004-2009 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
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
2005-04-03 07:45:51 +02:00
# include "config.h"
# include "vpi_user.h"
# include "vvp_vpi_callback.h"
2005-06-14 02:42:06 +02:00
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
# include <new>
# include <assert.h>
#ifdef HAVE_IOSFWD
# include <iosfwd>
#else
class ostream;
#endif
2005-07-15 01:34:18 +02:00
using namespace std;
/* Data types */
class vvp_scalar_t;
/* Basic netlist types. */
class vvp_net_t;
class vvp_net_fun_t;
class vvp_net_fil_t;
/* Core net function types. */
class vvp_fun_concat;
class vvp_fun_drive;
class vvp_fun_part;
2005-04-03 07:45:51 +02:00
class vvp_delay_t;
/*
* Storage for items declared in automatically allocated scopes (i.e. automatic
* tasks and functions). The first two slots in each context are reserved for
* linking to other contexts. The function that adds items to a context knows
* this, and allocates context indices accordingly.
*/
typedef void**vvp_context_t;
typedef void*vvp_context_item_t;
inline vvp_context_t vvp_allocate_context(unsigned nitem)
{
return (vvp_context_t)malloc((2 + nitem) * sizeof(void*));
}
inline vvp_context_t vvp_get_next_context(vvp_context_t context)
{
return (vvp_context_t)context[0];
}
inline void vvp_set_next_context(vvp_context_t context, vvp_context_t next)
{
context[0] = next;
}
inline vvp_context_t vvp_get_stacked_context(vvp_context_t context)
{
return (vvp_context_t)context[1];
}
inline void vvp_set_stacked_context(vvp_context_t context, vvp_context_t stack)
{
context[1] = stack;
}
inline vvp_context_item_t vvp_get_context_item(vvp_context_t context,
unsigned item_idx)
{
return (vvp_context_item_t)context[item_idx];
}
inline void vvp_set_context_item(vvp_context_t context, unsigned item_idx,
vvp_context_item_t item)
{
context[item_idx] = item;
}
/*
* An "automatic" functor is one which may be associated with an automatically
* allocated scope item. This provides the infrastructure needed to allocate
* the state information for individual instances of the item.
*/
struct automatic_hooks_s {
automatic_hooks_s() {}
virtual ~automatic_hooks_s() {}
virtual void alloc_instance(vvp_context_t context) = 0;
virtual void reset_instance(vvp_context_t context) = 0;
#ifdef CHECK_WITH_VALGRIND
virtual void free_instance(vvp_context_t context) = 0;
#endif
};
/*
* This is the set of Verilog 4-value bit values. Scalars have this
* value along with strength, vectors are a collection of these
* values. The enumeration has fixed numeric values that can be
* expressed in 2 real bits, so that some of the internal classes can
* pack them tightly.
*/
enum vvp_bit4_t {
BIT4_0 = 0,
BIT4_1 = 1,
BIT4_X = 3,
BIT4_Z = 2
};
/* Return an ASCII character that represents the vvp_bit4_t value. */
inline char vvp_bit4_to_ascii(vvp_bit4_t a) { return "01zx"[a]; }
extern vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c);
extern vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar);
/* Return TRUE if the bit is BIT4_X or BIT4_Z. The fast
implementation here relies on the encoding of vvp_bit4_t values. */
inline bool bit4_is_xz(vvp_bit4_t a) { return a >= 2; }
/* This function converts BIT4_Z to BIT4_X, but passes other values
unchanged. This fast implementation relies of the encoding of the
vvp_bit4_t values. In particular, the BIT4_X==3 and BIT4_Z==2 */
inline vvp_bit4_t bit4_z2x(vvp_bit4_t a)
{ return (vvp_bit4_t) ( (int)a | ((int)a >> 1) ); }
2005-01-30 06:06:49 +01:00
/* Some common boolean operators. These implement the Verilog rules
for 4-value bit operations. The fast implementations here rely
on the encoding of vvp_bit4_t values. */
// ~BIT4_0 --> BIT4_1
// ~BIT4_1 --> BIT4_0
// ~BIT4_X --> BIT4_X
// ~BIT4_Z --> BIT4_X
inline vvp_bit4_t operator ~ (vvp_bit4_t a)
{ return bit4_z2x((vvp_bit4_t) (((int)a) ^ 1)); }
inline vvp_bit4_t operator | (vvp_bit4_t a, vvp_bit4_t b)
{
if (a==BIT4_1 || b==BIT4_1)
return BIT4_1;
return bit4_z2x( (vvp_bit4_t) ((int)a | (int)b) );
}
inline vvp_bit4_t operator & (vvp_bit4_t a, vvp_bit4_t b)
{
if (a==BIT4_0 || b==BIT4_0)
return BIT4_0;
return bit4_z2x( (vvp_bit4_t) ((int)a | (int)b) );
}
2005-02-03 05:55:13 +01:00
extern vvp_bit4_t operator ^ (vvp_bit4_t a, vvp_bit4_t b);
extern ostream& operator<< (ostream&o, vvp_bit4_t a);
/* 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);
/*
2005-07-15 01:34:18 +02:00
* This class represents scalar values collected into vectors. The
* vector values can be accessed individually, or treated as a
* unit. in any case, the elements of the vector are addressed from
* zero(LSB) to size-1(MSB).
*
* No strength values are stored here, if strengths are needed, use a
* collection of vvp_scalar_t objects instead.
*/
class vvp_vector4_t {
friend vvp_vector4_t operator ~(const vvp_vector4_t&that);
friend class vvp_vector4array_t;
friend class vvp_vector4array_sa;
friend class vvp_vector4array_aa;
public:
static const vvp_vector4_t nil;
public:
explicit vvp_vector4_t(unsigned size =0, vvp_bit4_t bits =BIT4_X);
explicit vvp_vector4_t(unsigned size, double val);
// Construct a vector4 from the subvalue of another vector4.
explicit vvp_vector4_t(const vvp_vector4_t&that,
unsigned adr, unsigned wid);
vvp_vector4_t(const vvp_vector4_t&that);
vvp_vector4_t& operator= (const vvp_vector4_t&that);
~vvp_vector4_t();
unsigned size() const { return size_; }
void resize(unsigned new_size);
// Get the bit at the specified address
vvp_bit4_t value(unsigned idx) const;
// Get the vector4 subvector starting at the address
vvp_vector4_t subvalue(unsigned idx, unsigned size) const;
// Get the 2-value bits for the subvector. This returns a new
// array of longs, or a nil pointer if an XZ bit was detected
// in the array.
unsigned long*subarray(unsigned idx, unsigned size) const;
void setarray(unsigned idx, unsigned size, const unsigned long*val);
void set_bit(unsigned idx, vvp_bit4_t val);
void set_vec(unsigned idx, const vvp_vector4_t&that);
// Get the bits from another vector, but keep my size.
void copy_bits(const vvp_vector4_t&that);
// Test that the vectors are exactly equal
bool eeq(const vvp_vector4_t&that) const;
// Return true if there is an X or Z anywhere in the vector.
bool has_xz() const;
// Change all Z bits to X bits.
void change_z2x();
// Change all bits to X bits.
void set_to_x();
2005-01-29 18:52:06 +01:00
// Display the value into the buf as a string.
char*as_string(char*buf, size_t buf_len);
void invert();
vvp_vector4_t& operator &= (const vvp_vector4_t&that);
vvp_vector4_t& operator |= (const vvp_vector4_t&that);
vvp_vector4_t& operator += (int64_t);
private:
// Number of vvp_bit4_t bits that can be shoved into a word.
enum { BITS_PER_WORD = 8*sizeof(unsigned long) };
// The double value constructor requires that WORD_0_BBITS
// and WORD_1_BBITS have the same value!
#if SIZEOF_UNSIGNED_LONG == 8
enum { WORD_0_ABITS = 0x0000000000000000UL,
WORD_0_BBITS = 0x0000000000000000UL };
enum { WORD_1_ABITS = 0xFFFFFFFFFFFFFFFFUL,
WORD_1_BBITS = 0x0000000000000000UL };
enum { WORD_X_ABITS = 0xFFFFFFFFFFFFFFFFUL,
WORD_X_BBITS = 0xFFFFFFFFFFFFFFFFUL };
enum { WORD_Z_ABITS = 0x0000000000000000UL,
WORD_Z_BBITS = 0xFFFFFFFFFFFFFFFFUL };
#elif SIZEOF_UNSIGNED_LONG == 4
enum { WORD_0_ABITS = 0x00000000UL, WORD_0_BBITS = 0x00000000UL };
enum { WORD_1_ABITS = 0xFFFFFFFFUL, WORD_1_BBITS = 0x00000000UL };
enum { WORD_X_ABITS = 0xFFFFFFFFUL, WORD_X_BBITS = 0xFFFFFFFFUL };
enum { WORD_Z_ABITS = 0x00000000UL, WORD_Z_BBITS = 0xFFFFFFFFUL };
#else
#error "WORD_X_xBITS not defined for this architecture?"
#endif
// Initialize and operator= use this private method to copy
// the data from that object into this object.
void copy_from_(const vvp_vector4_t&that);
2005-06-19 20:42:00 +02:00
void allocate_words_(unsigned size, unsigned long inita, unsigned long initb);
// Values in the vvp_vector4_t are stored split across two
// arrays. For each bit in the vector, there is an abit and a
// bbit. the encoding of a vvp_vector4_t is:
//
// abit bbit
// ---- ----
// BIT4_0 0 0 (Note that for BIT4_0 and BIT4_1, the bbit
// BIT4_1 1 0 value is 0. This makes detecting XZ fast.)
// BIT4_X 1 1
// BIT4_Z 0 1
unsigned size_;
union {
unsigned long abits_val_;
unsigned long*abits_ptr_;
};
union {
unsigned long bbits_val_;
unsigned long*bbits_ptr_;
};
};
inline vvp_vector4_t::vvp_vector4_t(const vvp_vector4_t&that)
{
copy_from_(that);
}
inline vvp_vector4_t::vvp_vector4_t(unsigned size__, vvp_bit4_t val)
: size_(size__)
{
/* note: this relies on the bit encoding for the vvp_bit4_t. */
const static unsigned long init_atable[4] = {
WORD_0_ABITS,
WORD_1_ABITS,
WORD_Z_ABITS,
WORD_X_ABITS };
const static unsigned long init_btable[4] = {
WORD_0_BBITS,
WORD_1_BBITS,
WORD_Z_BBITS,
WORD_X_BBITS };
allocate_words_(size_, init_atable[val], init_btable[val]);
}
2005-06-19 20:42:00 +02:00
inline vvp_vector4_t::~vvp_vector4_t()
{
if (size_ > BITS_PER_WORD) {
delete[] abits_ptr_;
// bbits_ptr_ actually points half-way into a
// double-length array started at abits_ptr_
2005-06-19 20:42:00 +02:00
}
}
inline vvp_vector4_t& vvp_vector4_t::operator= (const vvp_vector4_t&that)
{
if (this == &that)
return *this;
if (size_ > BITS_PER_WORD)
delete[] abits_ptr_;
copy_from_(that);
return *this;
}
2005-06-19 20:42:00 +02:00
inline vvp_bit4_t vvp_vector4_t::value(unsigned idx) const
{
if (idx >= size_)
return BIT4_X;
unsigned wdx = idx / BITS_PER_WORD;
unsigned long off = idx % BITS_PER_WORD;
2005-06-19 20:42:00 +02:00
unsigned long abits, bbits;
2005-06-19 20:42:00 +02:00
if (size_ > BITS_PER_WORD) {
abits = abits_ptr_[wdx];
bbits = bbits_ptr_[wdx];
2005-06-19 20:42:00 +02:00
} else {
abits = abits_val_;
bbits = bbits_val_;
2005-06-19 20:42:00 +02:00
}
abits >>= off;
bbits >>= off;
int tmp = ((bbits&1) << 1) + (abits&1);
static const vvp_bit4_t bits_bit4_map[4] = {
BIT4_0, // bbit==0, abit==0
BIT4_1, // bbit==0, abit==1
BIT4_Z, // bbit==1, abit==0
BIT4_X // bbit==1, abit==1
};
2005-06-19 20:42:00 +02:00
/* Casting is evil, but this cast matches the un-cast done
when the vvp_bit4_t value is put into the vector. */
return bits_bit4_map[tmp];
2005-06-19 20:42:00 +02:00
}
inline vvp_vector4_t vvp_vector4_t::subvalue(unsigned adr, unsigned wid) const
{
return vvp_vector4_t(*this, adr, wid);
}
inline void vvp_vector4_t::set_bit(unsigned idx, vvp_bit4_t val)
{
assert(idx < size_);
unsigned long off = idx % BITS_PER_WORD;
unsigned long mask = 1UL << off;
if (size_ > BITS_PER_WORD) {
unsigned wdx = idx / BITS_PER_WORD;
switch (val) {
case BIT4_0:
abits_ptr_[wdx] &= ~mask;
bbits_ptr_[wdx] &= ~mask;
break;
case BIT4_1:
abits_ptr_[wdx] |= mask;
bbits_ptr_[wdx] &= ~mask;
break;
case BIT4_X:
abits_ptr_[wdx] |= mask;
bbits_ptr_[wdx] |= mask;
break;
case BIT4_Z:
abits_ptr_[wdx] &= ~mask;
bbits_ptr_[wdx] |= mask;
break;
}
} else {
switch (val) {
case BIT4_0:
abits_val_ &= ~mask;
bbits_val_ &= ~mask;
break;
case BIT4_1:
abits_val_ |= mask;
bbits_val_ &= ~mask;
break;
case BIT4_X:
abits_val_ |= mask;
bbits_val_ |= mask;
break;
case BIT4_Z:
abits_val_ &= ~mask;
bbits_val_ |= mask;
break;
}
}
}
inline vvp_vector4_t operator ~ (const vvp_vector4_t&that)
{
vvp_vector4_t res = that;
res.invert();
return res;
}
extern ostream& operator << (ostream&, const vvp_vector4_t&);
extern vvp_bit4_t compare_gtge(const vvp_vector4_t&a,
const vvp_vector4_t&b,
vvp_bit4_t val_if_equal);
extern vvp_bit4_t compare_gtge_signed(const vvp_vector4_t&a,
const vvp_vector4_t&b,
vvp_bit4_t val_if_equal);
template <class T> extern T coerce_to_width(const T&that, unsigned width);
/*
* These functions extract the value of the vector as a native type,
* if possible, and return true to indicate success. If the vector has
2007-06-12 04:36:58 +02:00
* any X or Z bits, the resulting value will have 0 bits in their
* place (this follows the rules of Verilog conversions from vector4
* to real and integers) and the return value becomes false to
* indicate an error.
*
* The "is_arithmetic" flag true will cause a result to be entirely 0
* if any bits are X/Z. That is normally what you want if this value
* is in the midst of an arithmetic expression. If is_arithmetic=false
* then the X/Z bits will be replaced with 0 bits, and the return
* value will be "false", but the other bits will be transferred. This
* is what you want if you are doing "vpi_get_value", for example.
*/
extern bool vector4_to_value(const vvp_vector4_t&a, long&val, bool is_signed, bool is_arithmetic =true);
extern bool vector4_to_value(const vvp_vector4_t&a, unsigned long&val);
#ifndef UL_AND_TIME64_SAME
extern bool vector4_to_value(const vvp_vector4_t&a, vvp_time64_t&val);
#endif
extern bool vector4_to_value(const vvp_vector4_t&a, double&val, bool is_signed);
/*
* The __vpiArray handle uses instances of this to keep an array of
* real valued variables.
*/
class vvp_realarray_t {
public:
vvp_realarray_t(unsigned words);
~vvp_realarray_t();
unsigned words() const { return words_; }
double get_word(unsigned idx) const;
void set_word(unsigned idx, double val);
private:
unsigned words_;
double*array_;
};
/*
* vvp_vector4array_t
*/
class vvp_vector4array_t {
public:
vvp_vector4array_t(unsigned width, unsigned words);
virtual ~vvp_vector4array_t();
unsigned width() const { return width_; }
unsigned words() const { return words_; }
virtual vvp_vector4_t get_word(unsigned idx) const = 0;
virtual void set_word(unsigned idx, const vvp_vector4_t&that) = 0;
protected:
struct v4cell {
union {
unsigned long abits_val_;
unsigned long*abits_ptr_;
};
union {
unsigned long bbits_val_;
unsigned long*bbits_ptr_;
};
};
vvp_vector4_t get_word_(v4cell*cell) const;
void set_word_(v4cell*cell, const vvp_vector4_t&that);
unsigned width_;
unsigned words_;
private: // Not implemented
vvp_vector4array_t(const vvp_vector4array_t&);
vvp_vector4array_t& operator = (const vvp_vector4array_t&);
};
/*
* Statically allocated vvp_vector4array_t
*/
class vvp_vector4array_sa : public vvp_vector4array_t {
public:
vvp_vector4array_sa(unsigned width, unsigned words);
~vvp_vector4array_sa();
vvp_vector4_t get_word(unsigned idx) const;
void set_word(unsigned idx, const vvp_vector4_t&that);
private:
v4cell* array_;
};
/*
* Automatically allocated vvp_vector4array_t
*/
class vvp_vector4array_aa : public vvp_vector4array_t, public automatic_hooks_s {
public:
vvp_vector4array_aa(unsigned width, unsigned words);
~vvp_vector4array_aa();
void alloc_instance(vvp_context_t context);
void reset_instance(vvp_context_t context);
#ifdef CHECK_WITH_VALGRIND
void free_instance(vvp_context_t context);
#endif
vvp_vector4_t get_word(unsigned idx) const;
void set_word(unsigned idx, const vvp_vector4_t&that);
private:
unsigned context_idx_;
};
/* vvp_vector2_t
*/
class vvp_vector2_t {
friend vvp_vector2_t operator - (const vvp_vector2_t&);
friend vvp_vector2_t operator + (const vvp_vector2_t&,
const vvp_vector2_t&);
friend vvp_vector2_t operator * (const vvp_vector2_t&,
const vvp_vector2_t&);
friend bool operator > (const vvp_vector2_t&, const vvp_vector2_t&);
friend bool operator >= (const vvp_vector2_t&, const vvp_vector2_t&);
friend bool operator < (const vvp_vector2_t&, const vvp_vector2_t&);
friend bool operator <= (const vvp_vector2_t&, const vvp_vector2_t&);
friend bool operator == (const vvp_vector2_t&, const vvp_vector2_t&);
public:
vvp_vector2_t();
vvp_vector2_t(const vvp_vector2_t&);
vvp_vector2_t(const vvp_vector2_t&, unsigned newsize);
// Make a vvp_vector2_t from a vvp_vector4_t. If there are X
// or Z bits, then the result becomes a NaN value.
explicit vvp_vector2_t(const vvp_vector4_t&that);
// Make from a native long and a specified width.
vvp_vector2_t(unsigned long val, unsigned wid);
2005-11-26 18:16:05 +01:00
// Make with the width, and filled with 1 or 0 bits.
enum fill_t {FILL0, FILL1};
vvp_vector2_t(fill_t fill, unsigned wid);
~vvp_vector2_t();
vvp_vector2_t&operator += (const vvp_vector2_t&that);
vvp_vector2_t&operator -= (const vvp_vector2_t&that);
vvp_vector2_t&operator <<= (unsigned shift);
vvp_vector2_t&operator >>= (unsigned shift);
vvp_vector2_t&operator = (const vvp_vector2_t&);
bool is_NaN() const;
bool is_zero() const;
unsigned size() const;
int value(unsigned idx) const;
2005-11-26 18:16:05 +01:00
void set_bit(unsigned idx, int bit);
// Make the size just big enough to hold the first 1 bit.
void trim();
// Trim off extra 1 bit since this is representing a negative value.
// Always keep at least 32 bits.
void trim_neg();
private:
enum { BITS_PER_WORD = 8 * sizeof(unsigned long) };
unsigned long*vec_;
unsigned wid_;
private:
void copy_from_that_(const vvp_vector2_t&that);
};
extern bool operator > (const vvp_vector2_t&, const vvp_vector2_t&);
extern bool operator >= (const vvp_vector2_t&, const vvp_vector2_t&);
extern bool operator < (const vvp_vector2_t&, const vvp_vector2_t&);
extern bool operator <= (const vvp_vector2_t&, const vvp_vector2_t&);
extern bool operator == (const vvp_vector2_t&, const vvp_vector2_t&);
extern vvp_vector2_t operator - (const vvp_vector2_t&);
extern vvp_vector2_t operator + (const vvp_vector2_t&, const vvp_vector2_t&);
extern vvp_vector2_t operator * (const vvp_vector2_t&, const vvp_vector2_t&);
extern vvp_vector2_t operator / (const vvp_vector2_t&, const vvp_vector2_t&);
extern vvp_vector2_t operator % (const vvp_vector2_t&, const vvp_vector2_t&);
vvp_vector2_t pow(const vvp_vector2_t&, vvp_vector2_t&);
extern vvp_vector4_t vector2_to_vector4(const vvp_vector2_t&, unsigned wid);
2006-03-08 06:29:42 +01:00
/* A c4string is of the form C4<...> where ... are bits. */
extern bool c4string_test(const char*str);
2006-03-08 06:29:42 +01:00
extern vvp_vector4_t c4string_to_vector4(const char*str);
/* A crstring is of the form Cr<...> where ... defines are real. */
extern bool crstring_test(const char*str);
extern double crstring_to_double(const char*str);
extern ostream& operator<< (ostream&, const vvp_vector2_t&);
/* Inline some of the vector2_t methods. */
inline unsigned vvp_vector2_t::size() const
{
return wid_;
}
/*
* This class represents a scalar value with strength. These are
* heavier then the simple vvp_bit4_t, but more information is
* carried by that weight.
*
* The strength values are as defined here:
* HiZ - 0
* Small - 1
* Medium - 2
* Weak - 3
* Large - 4
* Pull - 5
* Strong - 6
* Supply - 7
*
* There are two strengths for a value: strength0 and strength1. If
* the value is Z, then strength0 is the strength of the 0-value, and
* strength of the 1-value. If the value is 0 or 1, then the strengths
* are the range for that value.
*/
class vvp_scalar_t {
friend vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b);
public:
// Make a HiZ value.
explicit vvp_scalar_t();
// Make an unambiguous value.
explicit vvp_scalar_t(vvp_bit4_t val, unsigned str0, unsigned str1);
// Get the vvp_bit4_t version of the value
vvp_bit4_t value() const;
unsigned strength0() const;
unsigned strength1() const;
bool eeq(vvp_scalar_t that) const { return value_ == that.value_; }
bool is_hiz() const { return value_ == 0; }
private:
unsigned char value_;
};
inline vvp_scalar_t::vvp_scalar_t()
{
value_ = 0;
}
inline vvp_scalar_t::vvp_scalar_t(vvp_bit4_t val, unsigned str0, unsigned str1)
{
assert(str0 <= 7);
assert(str1 <= 7);
if (str0 == 0 && str1 == 0) {
value_ = 0x00;
} else switch (val) {
case BIT4_0:
value_ = str0 | (str0<<4);
break;
case BIT4_1:
value_ = str1 | (str1<<4) | 0x88;
break;
case BIT4_X:
value_ = str0 | (str1<<4) | 0x80;
break;
case BIT4_Z:
value_ = 0x00;
break;
}
}
inline vvp_bit4_t vvp_scalar_t::value() const
{
if (value_ == 0) {
return BIT4_Z;
} else switch (value_ & 0x88) {
case 0x00:
return BIT4_0;
case 0x88:
return BIT4_1;
default:
return BIT4_X;
}
}
inline vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b)
{
extern vvp_scalar_t fully_featured_resolv_(vvp_scalar_t, vvp_scalar_t);
// If the value is HiZ, resolution is simply a matter of
// returning the *other* value.
if (a.is_hiz())
return b;
if (b.is_hiz())
return a;
// If the values are the identical, then resolution is simply
// returning *either* value.
if (a .eeq( b ))
return a;
return fully_featured_resolv_(a,b);
}
extern ostream& operator<< (ostream&, vvp_scalar_t);
/*
* This class is a way to carry vectors of strength modeled
* values. The 8 in the name is the number of possible distinct values
* a well defined bit may have. When you add in ambiguous values, the
* number of distinct values span the vvp_scalar_t.
*
* A vvp_vector8_t object can be created from a vvp_vector4_t and a
* strength value. The vvp_vector8_t bits have the values of the input
2005-06-12 02:44:49 +02:00
* vector, all with the strength specified. If no strength is
* specified, then the conversion from bit4 to a scalar will use the
* Verilog convention default of strong (6).
*/
class vvp_vector8_t {
friend vvp_vector8_t part_expand(const vvp_vector8_t&, unsigned, unsigned);
public:
explicit vvp_vector8_t(unsigned size =0);
// Make a vvp_vector8_t from a vector4 and a specified
// strength.
explicit vvp_vector8_t(const vvp_vector4_t&that,
unsigned str0,
unsigned str1);
~vvp_vector8_t();
static const vvp_vector8_t nil;
public:
unsigned size() const { return size_; }
vvp_scalar_t value(unsigned idx) const;
vvp_vector8_t subvalue(unsigned adr, unsigned width) const;
void set_bit(unsigned idx, vvp_scalar_t val);
void set_vec(unsigned idx, const vvp_vector8_t&that);
// Test that the vectors are exactly equal
bool eeq(const vvp_vector8_t&that) const;
vvp_vector8_t(const vvp_vector8_t&that);
vvp_vector8_t& operator= (const vvp_vector8_t&that);
private:
// This is the number of vvp_scalar_t objects we can keep in
// the val_ buffer. If the vector8 is bigger then this, then
// resort to allocations to get a larger buffer.
enum { PTR_THRESH = 8 };
unsigned size_;
union {
vvp_scalar_t*ptr_;
char val_[PTR_THRESH * sizeof(vvp_scalar_t)];
};
};
2005-06-12 17:13:37 +02:00
/* Resolve uses the default Verilog resolver algorithm to resolve
two drive vectors to a single output. */
inline vvp_vector8_t resolve(const vvp_vector8_t&a, const vvp_vector8_t&b)
{
assert(a.size() == b.size());
vvp_vector8_t out (a.size());
for (unsigned idx = 0 ; idx < out.size() ; idx += 1) {
out.set_bit(idx, resolve(a.value(idx), b.value(idx)));
}
return out;
}
2005-06-12 17:13:37 +02:00
/* This function implements the strength reduction implied by
Verilog standard resistive devices. */
extern vvp_vector8_t resistive_reduction(const vvp_vector8_t&a);
/* The reduce4 function converts a vector8 to a vector4, losing
strength information in the process. */
extern vvp_vector4_t reduce4(const vvp_vector8_t&that);
extern vvp_vector8_t part_expand(const vvp_vector8_t&a, unsigned wid, unsigned off);
/* A c8string is of the form C8<...> where ... are bits. */
extern bool c8string_test(const char*str);
extern vvp_vector8_t c8string_to_vector8(const char*str);
2005-06-12 17:13:37 +02:00
/* Print a vector8 value to a stream. */
extern ostream& operator<< (ostream&, const vvp_vector8_t&);
inline vvp_vector8_t::vvp_vector8_t(unsigned size__)
: size_(size__)
{
if (size_ <= PTR_THRESH) {
new (val_) vvp_scalar_t[PTR_THRESH];
} else {
ptr_ = new vvp_scalar_t[size_];
}
}
inline vvp_vector8_t::~vvp_vector8_t()
{
if (size_ > PTR_THRESH)
delete[]ptr_;
}
inline vvp_scalar_t vvp_vector8_t::value(unsigned idx) const
{
assert(idx < size_);
if (size_ <= PTR_THRESH)
return reinterpret_cast<const vvp_scalar_t*>(val_) [idx];
else
return ptr_[idx];
}
inline void vvp_vector8_t::set_bit(unsigned idx, vvp_scalar_t val)
{
assert(idx < size_);
if (size_ <= PTR_THRESH)
reinterpret_cast<vvp_scalar_t*>(val_) [idx] = val;
else
ptr_[idx] = val;
}
// Exactly-equal for vvp_vector8_t is common and should be as tight
// as possible.
inline bool vvp_vector8_t::eeq(const vvp_vector8_t&that) const
{
if (size_ != that.size_)
return false;
if (size_ == 0)
return true;
if (size_ <= PTR_THRESH)
return 0 == memcmp(val_, that.val_, sizeof(val_));
for (unsigned idx = 0 ; idx < size_ ; idx += 1) {
if (! ptr_[idx] .eeq( that.ptr_[idx] ))
return false;
}
return true;
}
/*
* This class implements a pointer that points to an item within a
* target. The ptr() method returns a pointer to the vvp_net_t, and
* the port() method returns a 0-3 value that selects the input within
* the vvp_net_t. Use this pointer to point only to the inputs of
* vvp_net_t objects. To point to vvp_net_t objects as a whole, use
* vvp_net_t* pointers.
*
* Alert! Ugly details. Protective clothing recommended!
* The vvp_net_ptr_t encodes the bits of a C pointer, and two bits of
* port identifier into an unsigned long. This works only if vvp_net_t*
* values are always aligned on 4-byte boundaries.
*/
template <class T> class vvp_sub_pointer_t {
public:
vvp_sub_pointer_t() : bits_(0) { }
vvp_sub_pointer_t(T*ptr__, unsigned port__)
{
bits_ = reinterpret_cast<unsigned long> (ptr__);
assert( (bits_ & 3) == 0 );
assert( (port__ & ~3) == 0 );
bits_ |= port__;
}
~vvp_sub_pointer_t() { }
T* ptr()
{ return reinterpret_cast<T*> (bits_ & ~3UL); }
const T* ptr() const
{ return reinterpret_cast<const T*> (bits_ & ~3UL); }
unsigned port() const { return bits_ & 3; }
bool nil() const { return bits_ == 0; }
bool operator == (vvp_sub_pointer_t that) const { return bits_ == that.bits_; }
bool operator != (vvp_sub_pointer_t that) const { return bits_ != that.bits_; }
2006-08-04 06:37:37 +02:00
private:
unsigned long bits_;
};
2006-08-04 06:37:37 +02:00
typedef vvp_sub_pointer_t<vvp_net_t> vvp_net_ptr_t;
template <class T> ostream& operator << (ostream&out, vvp_sub_pointer_t<T> val)
{ out << val.ptr() << "[" << val.port() << "]"; return out; }
/*
* This is the basic unit of netlist connectivity. It is a fan-in of
* up to 4 inputs, and output pointer, and a pointer to the node's
* functionality.
*
* A net output that is non-nil points to the input of one of its
* destination nets. If there are multiple destinations, then the
* referenced input port points to the next input. For example:
*
* +--+--+--+--+---+
* | | | | | . | Output from this vvp_net_t points to...
* +--+--+--+--+-|-+
* |
* /
* /
* /
* |
* v
* +--+--+--+--+---+
* | | | | | . | ... the fourth input of this vvp_net_t, and...
* +--+--+--+--+-|-+
* | |
* / .
* / .
* | .
* v
* +--+--+--+--+---+
* | | | | | . | ... the third input of this vvp_net_t.
* +--+--+--+--+-|-+
*
* Thus, the fan-in of a vvp_net_t node is limited to 4 inputs, but
* the fan-out is unlimited.
*
* The vvp_send_*() functions take as input a vvp_net_ptr_t and follow
* all the fan-out chain, delivering the specified value. The send_*()
* methods of the vvp_net_t class are similar, but they follow the
* output, possibly filtered, from the vvp_net_t.
*/
class vvp_net_t {
public:
vvp_net_t();
#ifdef CHECK_WITH_VALGRIND
vvp_net_t *pool;
#endif
vvp_net_ptr_t port[4];
vvp_net_fun_t*fun;
vvp_net_fil_t*fil;
public:
// Connect the port to the output from this net.
void link(vvp_net_ptr_t port);
// Disconnect the port from the output of this net.
void unlink(vvp_net_ptr_t port);
public: // Methods to propagate output from this node.
void send_vec4(const vvp_vector4_t&val, vvp_context_t context);
void send_vec8(const vvp_vector8_t&val);
void send_real(double val, vvp_context_t context);
void send_long(long val);
void send_vec4_pv(const vvp_vector4_t&val,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t context);
void send_vec8_pv(const vvp_vector8_t&val,
unsigned base, unsigned wid, unsigned vwid);
public: // Methods to arrange for the output of this net to be forced.
// The intent is that all efforts at force are directed to
// operate only on the vvp_net_t whose output is to be
// forced. These methods then communicate the force to the
// attached filter to set up the actual force.
void force_vec4(const vvp_vector4_t&val, vvp_vector2_t mask);
void force_vec8(const vvp_vector8_t&val, vvp_vector2_t mask);
void force_real(double val, vvp_vector2_t mask);
private:
vvp_net_ptr_t out_;
public: // Need a better new for these objects.
static void* operator new(std::size_t size);
static void operator delete(void*); // not implemented
private: // not implemented
static void* operator new[](std::size_t size);
static void operator delete[](void*);
};
/*
* Instances of this class represent the functionality of a
* node. vvp_net_t objects hold pointers to the vvp_net_fun_t
* associated with it. Objects of this type take inputs that arrive at
* a port and perform some sort of action in response.
*
* Whenever a bit is delivered to a vvp_net_t object, the associated
* vvp_net_fun_t::recv_*() method is invoked with the port pointer and
* the bit value. The port pointer is used to figure out which exact
* input receives the bit.
*
* In this context, a "bit" is the thing that arrives at a single
* input. The bit may be a single data bit, a bit vector, various
* sorts of numbers or aggregate objects.
*
* recv_vec4 is the most common way for a datum to arrive at a
* port. The value is a vvp_vector4_t.
*
* Most nodes do not care about the specific strengths of bits, so the
* default behavior for recv_vec8 and recv_vec8_pv is to reduce the
* operand to a vvp_vector4_t and pass it on to the recv_vec4 or
* recv_vec4_pv method.
*
* The recv_vec4, recv_vec4_pv, and recv_real methods are also
* passed a context pointer. When the received bit has propagated
* from a statically allocated node, this will be a null pointer.
* When the received bit has propagated from an automatically
* allocated node, this will be a pointer to the context that
* contains the instance of that bit that has just been modified.
* When the received bit was from a procedural assignment or from
* a VPI set_value() operation, this will be a pointer to the
* writable context associated with the currently running thread.
*/
class vvp_net_fun_t {
public:
vvp_net_fun_t();
virtual ~vvp_net_fun_t();
virtual void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
virtual void recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit);
virtual void recv_real(vvp_net_ptr_t port, double bit,
vvp_context_t context);
virtual void recv_long(vvp_net_ptr_t port, long bit);
// Part select variants of above
virtual void recv_vec4_pv(vvp_net_ptr_t p, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t context);
virtual void recv_vec8_pv(vvp_net_ptr_t p, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid);
virtual void recv_long_pv(vvp_net_ptr_t port, long bit,
unsigned base, unsigned wid);
public: // These objects are only permallocated.
static void* operator new(std::size_t size);
static void operator delete(void*); // not implemented
private: // not implemented
vvp_net_fun_t(const vvp_net_fun_t&);
vvp_net_fun_t& operator= (const vvp_net_fun_t&);
static void* operator new[](std::size_t size);
static void operator delete[](void*);
};
/*
* A vvp_net_fil_t is a filter object that filters an output from a
* vvp_net_t. The send_*() methods of the vvp_net_t object call the
* filter of the output being transmitted. The filter function will
* decide if this value is to be propagated, and return true or
* false. If false, then send_*() continues as usual. If false, output
* propagation is stopped.
*
* The filter object also provides an implementation hooks for
* force/release.
*/
class vvp_net_fil_t : public vvp_vpi_callback {
public:
vvp_net_fil_t();
virtual ~vvp_net_fil_t();
public:
enum prop_t { STOP=0, PROP, REPL };
// Return a non-empty vector if the filter allows an
// output. The output result may be different from the
// input. If the output is nil, then suppress propagation.
// Return true if the value is to be propagated, or false if
// propagation is suppressed. The value may be edited by the
// filter, or overridden by the rep argument if present.
virtual prop_t filter_vec4(const vvp_vector4_t&bit, vvp_vector4_t&rep,
unsigned base, unsigned vwid);
virtual prop_t filter_vec8(const vvp_vector8_t&val, vvp_vector8_t&rep,
unsigned base, unsigned vwid);
virtual prop_t filter_real(double&val);
virtual prop_t filter_long(long&val);
virtual void release(vvp_net_ptr_t ptr, bool net_flag) =0;
virtual void release_pv(vvp_net_ptr_t ptr, unsigned base, unsigned wid) =0;
// The %force/link instruction needs a place to write the
// source node of the force, so that subsequent %force and
// %release instructions can undo the link as needed. */
void force_link(vvp_net_t*dst, vvp_net_t*src);
void force_unlink(void);
virtual unsigned filter_size() const =0;
public:
// Suport for force methods
virtual void force_fil_vec4(const vvp_vector4_t&val, vvp_vector2_t mask) =0;
virtual void force_fil_vec8(const vvp_vector8_t&val, vvp_vector2_t mask) =0;
virtual void force_fil_real(double val, vvp_vector2_t mask) =0;
protected:
// Set bits of the filter force mask
void force_mask(vvp_vector2_t mask);
// Release the force on the bits set in the mask.
void release_mask(vvp_vector2_t mask);
// Test bits of the filter force mask;
bool test_force_mask(unsigned bit) const;
bool test_force_mask_is_zero() const;
// This template method is used by derived classes to process
// the val through the force mask. The force value is the
// currently forced value, and the buf is a value that this
// method will use to hold a filtered value, if needed. This
// method returns a pointer to val or buf.
template <class T> prop_t filter_mask_(const T&val, const T&force, T&rep, unsigned addr);
// This template method is similar to the above, but works for
// native types that are not so expensive to edit in place.
template <class T> prop_t filter_mask_(T&val, T force);
private:
// Mask of forced bits
vvp_vector2_t force_mask_;
// True if the next filter must propagate. Need this to allow
// the forced value to get through.
bool force_propagate_;
// force link back.
struct vvp_net_t*force_link_;
};
/* **** Some core net functions **** */
/* vvp_fun_concat
* This node function creates vectors (vvp_vector4_t) from the
* concatenation of the inputs. The inputs (4) may be vector or
* vector8 objects, but they are reduced to vector4 values and
* strength information lost.
*
* The expected widths of the input vectors must be given up front so
* that the positions in the output vector (and also the size of the
* output vector) can be worked out. The input vectors must match the
* expected width.
*/
class vvp_fun_concat : public vvp_net_fun_t {
public:
vvp_fun_concat(unsigned w0, unsigned w1,
unsigned w2, unsigned w3);
~vvp_fun_concat();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
private:
unsigned wid_[4];
vvp_vector4_t val_;
};
/*
* The vvp_fun_force class objects are net functors that use their input
* to force the associated filter. They do not actually have an
* output, they instead drive the force_* methods of the net filter.
*
* This functor is also special in that we know a priori that only
* port-0 is used, so we can use ports 1-3 for local storage. See the
* implementation of vvp_filter_wire_base::force_link in
* vvp_net_sig.cc for details.
*/
class vvp_fun_force : public vvp_net_fun_t {
public:
vvp_fun_force();
~vvp_fun_force();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
void recv_real(vvp_net_ptr_t port, double bit, vvp_context_t);
};
/* vvp_fun_repeat
* This node function create vectors by repeating the input. The width
* is the width of the output vector, and the repeat is the number of
* times to repeat the input. The width of the input vector is
* implicit from these values.
*/
class vvp_fun_repeat : public vvp_net_fun_t {
public:
vvp_fun_repeat(unsigned width, unsigned repeat);
~vvp_fun_repeat();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
private:
unsigned wid_;
unsigned rep_;
};
/* vvp_fun_drive
* This node function takes an input vvp_vector4_t as input, and
* repeats that value as a vvp_vector8_t with all the bits given the
* strength of the drive. This is the way vvp_scalar8_t objects are
* created. Input 0 is the value to be driven (vvp_vector4_t) and
* inputs 1 and two are the strength0 and strength1 values to use. The
* strengths may be taken as constant values, or adjusted as longs
* from the network.
*
* This functor only propagates vvp_vector8_t values.
*/
class vvp_fun_drive : public vvp_net_fun_t {
public:
vvp_fun_drive(vvp_bit4_t init, unsigned str0 =6, unsigned str1 =6);
~vvp_fun_drive();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
//void recv_long(vvp_net_ptr_t port, long bit);
private:
unsigned char drive0_;
unsigned char drive1_;
};
2005-05-24 03:43:27 +02:00
/*
* EXTEND functors expand an input vector to the desired output
* width. The extend_signed functor sign extends the input. If the
* input is already wider then the desired output, then it is passed
* unmodified.
*/
class vvp_fun_extend_signed : public vvp_net_fun_t {
public:
explicit vvp_fun_extend_signed(unsigned wid);
~vvp_fun_extend_signed();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
2005-05-24 03:43:27 +02:00
private:
unsigned width_;
};
2005-04-01 08:02:45 +02:00
/*
* Wide Functors:
2007-03-22 17:08:14 +01:00
* Wide functors represent special devices that may have more than 4
2005-04-01 08:02:45 +02:00
* input ports. These devices need a set of N/4 actual functors to
* catch the inputs, and use another to deliver the output.
*
* vvp_wide_fun_t --+--> vvp_wide_fun_core --> ...
* |
* vvp_wide_fun_t --+
* |
* vvp_wide_fun_t --+
*
* There are enough input functors to take all the functor inputs, 4
* per functor. These inputs deliver the changed input value to the
* wide_fun_core, which carries the infrastructure for the thread. The
2005-04-01 08:02:45 +02:00
* wide_fun_core is also a functor whose output is connected to the rest
* of the netlist. This is where the result is delivered back to the
* netlist.
*
* The vvp_wide_fun_core keeps a store of the inputs from all the
* input functors, and makes them available to the derived class that
* does the processing.
*/
class vvp_wide_fun_core : public vvp_net_fun_t {
public:
vvp_wide_fun_core(vvp_net_t*net, unsigned nports);
virtual ~vvp_wide_fun_core();
protected:
2005-04-03 07:45:51 +02:00
void propagate_vec4(const vvp_vector4_t&bit, vvp_time64_t delay =0);
void propagate_real(double bit, vvp_time64_t delay =0);
2005-04-01 08:02:45 +02:00
unsigned port_count() const;
2005-04-01 08:02:45 +02:00
vvp_vector4_t& value(unsigned);
double value_r(unsigned);
2005-04-01 08:02:45 +02:00
private:
// the derived class implements this to receive an indication
// that one of the port input values changed.
virtual void recv_vec4_from_inputs(unsigned port) =0;
virtual void recv_real_from_inputs(unsigned port);
2005-04-01 08:02:45 +02:00
friend class vvp_wide_fun_t;
void dispatch_vec4_from_input_(unsigned port, vvp_vector4_t bit);
void dispatch_real_from_input_(unsigned port, double bit);
2005-04-01 08:02:45 +02:00
private:
// Back-point to the vvp_net_t that points to me.
vvp_net_t*ptr_;
// Structure to track the input values from the input functors.
unsigned nports_;
vvp_vector4_t*port_values_;
double*port_rvalues_;
2005-04-01 08:02:45 +02:00
};
/*
* The job of the input functor is only to monitor inputs to the
* function and pass them to the ufunc_core object. Each functor takes
* up to 4 inputs, with the base the port number for the first
* function input that this functor represents.
*/
class vvp_wide_fun_t : public vvp_net_fun_t {
public:
vvp_wide_fun_t(vvp_wide_fun_core*c, unsigned base);
~vvp_wide_fun_t();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context);
void recv_real(vvp_net_ptr_t port, double bit,
vvp_context_t context);
2005-04-01 08:02:45 +02:00
private:
vvp_wide_fun_core*core_;
unsigned port_base_;
};
inline void vvp_send_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&val, vvp_context_t context)
{
while (struct vvp_net_t*cur = ptr.ptr()) {
vvp_net_ptr_t next = cur->port[ptr.port()];
if (cur->fun)
cur->fun->recv_vec4(ptr, val, context);
ptr = next;
}
}
2005-06-24 04:16:42 +02:00
extern void vvp_send_vec8(vvp_net_ptr_t ptr, const vvp_vector8_t&val);
extern void vvp_send_real(vvp_net_ptr_t ptr, double val,
vvp_context_t context);
extern void vvp_send_long(vvp_net_ptr_t ptr, long val);
extern void vvp_send_long_pv(vvp_net_ptr_t ptr, long val,
unsigned base, unsigned width);
/*
* Part-vector versions of above functions. This function uses the
* corresponding recv_vec4_pv method in the vvp_net_fun_t functor to
* deliver parts of a vector.
*
* The ptr is the destination input port to write to.
*
* <val> is the vector to be written. The width of this vector must
* exactly match the <wid> vector.
*
* The <base> is where in the receiver the bit vector is to be
2005-09-19 23:45:35 +02:00
* written. This address is given in canonical units; 0 is the LSB, 1
* is the next bit, and so on.
*
* The <vwid> is the width of the destination vector that this part is
* part of. This is used by intermediate nodes, i.e. resolvers, to
* know how wide to pad with Z, if it needs to transform the part to a
* mirror of the destination vector.
*/
2005-06-24 04:16:42 +02:00
inline void vvp_send_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&val,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t context)
2005-06-24 04:16:42 +02:00
{
while (struct vvp_net_t*cur = ptr.ptr()) {
vvp_net_ptr_t next = cur->port[ptr.port()];
if (cur->fun)
cur->fun->recv_vec4_pv(ptr, val, base, wid, vwid, context);
2005-06-24 04:16:42 +02:00
ptr = next;
}
}
inline void vvp_net_t::send_vec8_pv(const vvp_vector8_t&val,
unsigned base, unsigned wid, unsigned vwid)
{
vvp_net_ptr_t ptr = out_;
while (struct vvp_net_t*cur = ptr.ptr()) {
vvp_net_ptr_t next = cur->port[ptr.port()];
if (cur->fun)
cur->fun->recv_vec8_pv(ptr, val, base, wid, vwid);
ptr = next;
}
}
inline void vvp_net_t::send_vec4(const vvp_vector4_t&val, vvp_context_t context)
{
if (fil == 0) {
vvp_send_vec4(out_, val, context);
return;
}
vvp_vector4_t rep;
switch (fil->filter_vec4(val, rep, 0, val.size())) {
case vvp_net_fil_t::STOP:
break;
case vvp_net_fil_t::PROP:
vvp_send_vec4(out_, val, context);
break;
case vvp_net_fil_t::REPL:
vvp_send_vec4(out_, rep, context);
break;
}
}
inline void vvp_net_t::send_vec4_pv(const vvp_vector4_t&val,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t context)
{
if (fil == 0) {
vvp_send_vec4_pv(out_, val, base, wid, vwid, context);
return;
}
assert(val.size() == wid);
vvp_vector4_t rep;
switch (fil->filter_vec4(val, rep, base, vwid)) {
case vvp_net_fil_t::STOP:
break;
case vvp_net_fil_t::PROP:
vvp_send_vec4_pv(out_, val, base, wid, vwid, context);
break;
case vvp_net_fil_t::REPL:
vvp_send_vec4_pv(out_, rep, base, wid, vwid, context);
break;
}
}
inline void vvp_net_t::send_vec8(const vvp_vector8_t&val)
{
if (fil == 0) {
vvp_send_vec8(out_, val);
return;
}
vvp_vector8_t rep;
switch (fil->filter_vec8(val, rep, 0, val.size())) {
case vvp_net_fil_t::STOP:
break;
case vvp_net_fil_t::PROP:
vvp_send_vec8(out_, val);
break;
case vvp_net_fil_t::REPL:
vvp_send_vec8(out_, rep);
break;
}
}
inline void vvp_net_t::send_real(double val, vvp_context_t context)
{
if (fil && ! fil->filter_real(val))
return;
vvp_send_real(out_, val, context);
}
inline bool vvp_net_fil_t::test_force_mask(unsigned bit) const
{
if (bit >= force_mask_.size())
return false;
if (force_mask_.value(bit))
return true;
else
return false;
}
inline bool vvp_net_fil_t::test_force_mask_is_zero(void) const
{
if (force_mask_.size() == 0)
return true;
if (force_mask_.is_zero())
return true;
return false;
}
#endif