From 49363c660cdcedfa7563c688c50ad5239f3752c4 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 16 Jun 2008 13:40:20 -0700 Subject: [PATCH 1/6] Remove the duplicate schedule_assign_vector. The schedule_assign_plucked_vector is a better way to implement the schedule_assign_vector, or at least no worse, so remove the now redundent schedule_assign_vector. --- vvp/schedule.cc | 25 +++++-------------------- vvp/schedule.h | 5 +---- vvp/vthread.cc | 4 ++-- vvp/vvp_net.cc | 2 +- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 1d4f497bd..c2546120d 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -118,7 +118,7 @@ void del_thr_event_s::run_run(void) struct assign_vector4_event_s : public event_s { /* The default constructor. */ - assign_vector4_event_s() { } + assign_vector4_event_s(const vvp_vector4_t&that) : val(that) { } /* A constructor that makes the val directly. */ assign_vector4_event_s(const vvp_vector4_t&that, unsigned adr, unsigned wid) : val(that,adr,wid) { } @@ -572,23 +572,10 @@ void schedule_assign_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, vvp_time64_t delay) { - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; + struct assign_vector4_event_s*cur = new struct assign_vector4_event_s(bit); cur->ptr = ptr; cur->base = base; cur->vwid = vwid; - cur->val = bit; - schedule_event_(cur, delay, SEQ_NBASSIGN); -} - -void schedule_assign_vector(vvp_net_ptr_t ptr, - const vvp_vector4_t&bit, - vvp_time64_t delay) -{ - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; - cur->ptr = ptr; - cur->val = bit; - cur->vwid = 0; - cur->base = 0; schedule_event_(cur, delay, SEQ_NBASSIGN); } @@ -619,11 +606,10 @@ void schedule_assign_array_word(vvp_array_t mem, schedule_event_(cur, delay, SEQ_NBASSIGN); } -void schedule_set_vector(vvp_net_ptr_t ptr, vvp_vector4_t bit) +void schedule_set_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) { - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; + struct assign_vector4_event_s*cur = new struct assign_vector4_event_s(bit); cur->ptr = ptr; - cur->val = bit; cur->base = 0; cur->vwid = 0; schedule_event_(cur, 0, SEQ_ACTIVE); @@ -647,9 +633,8 @@ void schedule_set_vector(vvp_net_ptr_t ptr, double bit) void schedule_init_vector(vvp_net_ptr_t ptr, vvp_vector4_t bit) { - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; + struct assign_vector4_event_s*cur = new struct assign_vector4_event_s(bit); cur->ptr = ptr; - cur->val = bit; cur->base = 0; cur->vwid = 0; cur->next = schedule_init_list; diff --git a/vvp/schedule.h b/vvp/schedule.h index d40813709..b0f7bea23 100644 --- a/vvp/schedule.h +++ b/vvp/schedule.h @@ -47,9 +47,6 @@ extern void schedule_assign_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&val, vvp_time64_t delay); -extern void schedule_assign_vector(vvp_net_ptr_t ptr, - const vvp_vector4_t&val, - vvp_time64_t delay); extern void schedule_assign_plucked_vector(vvp_net_ptr_t ptr, vvp_time64_t delay, const vvp_vector4_t&val, @@ -66,7 +63,7 @@ extern void schedule_assign_array_word(vvp_array_t mem, * constant value (i.e. C4<...>) to the input of a functor. This * creates an event in the active queue. */ -extern void schedule_set_vector(vvp_net_ptr_t ptr, vvp_vector4_t val); +extern void schedule_set_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&val); extern void schedule_set_vector(vvp_net_ptr_t ptr, vvp_vector8_t val); extern void schedule_set_vector(vvp_net_ptr_t ptr, double val); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 7f38809f0..f8a6d5b49 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -707,7 +707,7 @@ bool of_ASSIGN_V0(vthread_t thr, vvp_code_t cp) schedule_assign_plucked_vector(ptr, delay, thr->bits4, bit, wid); } else { vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); - schedule_assign_vector(ptr, value, delay); + schedule_assign_plucked_vector(ptr, delay, value, 0, wid); } return true; @@ -732,7 +732,7 @@ bool of_ASSIGN_V0D(vthread_t thr, vvp_code_t cp) schedule_assign_plucked_vector(ptr, delay, thr->bits4, bit, wid); } else { vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); - schedule_assign_vector(ptr, value, delay); + schedule_assign_plucked_vector(ptr, delay, value, 0, wid); } return true; diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 01393d105..141ff1f33 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -2621,7 +2621,7 @@ void vvp_wide_fun_core::propagate_vec4(const vvp_vector4_t&bit, vvp_time64_t delay) { if (delay) - schedule_assign_vector(ptr_->out, bit, delay); + schedule_assign_plucked_vector(ptr_->out, delay, bit, 0, bit.size()); else vvp_send_vec4(ptr_->out, bit); } From 86e5762b1c39b4ffe8630b855454eb6828e0976c Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 16 Jun 2008 15:02:07 -0700 Subject: [PATCH 2/6] Compact of vvp_vector4_t in arrays. Arrays of vvp_vector4_t values redundantly store some fields in every word. Create a special type that stores vvp_vector4_t values in a form that does not duplicate the width of all the items. This can save a lot of space when big memories are simulated. --- vvp/array.cc | 43 ++++++++++-------------- vvp/vvp_net.cc | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ vvp/vvp_net.h | 37 +++++++++++++++++++++ 3 files changed, 142 insertions(+), 26 deletions(-) diff --git a/vvp/array.cc b/vvp/array.cc index 75bb22f44..29de4f15d 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -56,6 +56,8 @@ vvp_array_t array_find(const char*label) * represent the words of the array. The vpi_array_t is a pointer to this. */ struct __vpiArray { + __vpiArray() { } + struct __vpiHandle base; struct __vpiScope*scope; const char*name; /* Permanently allocated string */ @@ -64,11 +66,11 @@ struct __vpiArray { struct __vpiDecConst last_addr; struct __vpiDecConst msb; struct __vpiDecConst lsb; + unsigned vals_width; // If this is a net array, nets lists the handles. vpiHandle*nets; // If this is a var array, then these are used instead of nets. - vvp_vector4_t *vals; - unsigned vals_width; + vvp_vector4array_t *vals; struct __vpiArrayWord*vals_words; class vvp_fun_arrayport*ports_; @@ -411,7 +413,7 @@ static int vpi_array_var_word_get(int code, vpiHandle ref) switch (code) { case vpiSize: - return (int) parent->vals_width; + return (int) parent->vals->width(); case vpiLeftRange: return parent->msb.value; @@ -448,14 +450,9 @@ static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value) assert(obj); unsigned index = decode_array_word_pointer(obj, parent); - unsigned width = parent->vals_width; + unsigned width = parent->vals->width(); - /* If we don't have a value yet just return X. */ - if (parent->vals[index].size() == 0) { - vpip_vec4_get_value(vvp_vector4_t(width), width, false, value); - } else { - vpip_vec4_get_value(parent->vals[index], width, false, value); - } + vpip_vec4_get_value(parent->vals->get_word(index), width, false, value); } static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int flags) @@ -696,18 +693,18 @@ void array_set_word(vvp_array_t arr, if (arr->vals) { assert(arr->nets == 0); if (part_off != 0 || val.size() != arr->vals_width) { - if (arr->vals[address].size() == 0) - arr->vals[address] = vvp_vector4_t(arr->vals_width, BIT4_X); - if ((part_off + val.size()) > arr->vals[address].size()) { + vvp_vector4_t tmp = arr->vals->get_word(address); + if ((part_off + val.size()) > tmp.size()) { cerr << "part_off=" << part_off << " val.size()=" << val.size() - << " arr->vals[address].size()=" << arr->vals[address].size() + << " arr->vals[address].size()=" << tmp.size() << " arr->vals_width=" << arr->vals_width << endl; assert(0); } - arr->vals[address].set_vec(part_off, val); + tmp.set_vec(part_off, val); + arr->vals->set_word(address, tmp); } else { - arr->vals[address] = val; + arr->vals->set_word(address, val); } array_word_change(arr, address); return; @@ -730,14 +727,7 @@ vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address) if (arr->vals) { assert(arr->nets == 0); - vvp_vector4_t tmp; - if (address < arr->array_count) - tmp = arr->vals[address]; - - if (tmp.size() == 0) - tmp = vvp_vector4_t(arr->vals_width, BIT4_X); - - return tmp; + return arr->vals->get_word(address); } assert(arr->vals == 0); @@ -848,8 +838,8 @@ void compile_var_array(char*label, char*name, int last, int first, struct __vpiArray*arr = ARRAY_HANDLE(obj); /* Make the words. */ - arr->vals = new vvp_vector4_t[arr->array_count]; arr->vals_width = labs(msb-lsb) + 1; + arr->vals = new vvp_vector4array_t(arr->vals_width, arr->array_count); vpip_make_dec_const(&arr->msb, msb); vpip_make_dec_const(&arr->lsb, lsb); @@ -989,7 +979,8 @@ void array_word_change(vvp_array_t array, unsigned long addr) if (cur->cb_data.cb_rtn != 0) { if (cur->cb_data.value) - vpip_vec4_get_value(array->vals[addr], array->vals_width, + vpip_vec4_get_value(array->vals->get_word(addr), + array->vals_width, false, cur->cb_data.value); callback_execute(cur); diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 141ff1f33..9f9a76065 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -1177,6 +1177,94 @@ bool vector4_to_value(const vvp_vector4_t&vec, double&val, bool signed_flag) return flag; } +vvp_vector4array_t::vvp_vector4array_t(unsigned width, unsigned words) +: width_(width), words_(words) +{ + array_ = new v4cell[words_]; + + if (width_ <= vvp_vector4_t::BITS_PER_WORD) { + for (unsigned idx = 0 ; idx < words_ ; idx += 1) { + array_[idx].abits_val_ = vvp_vector4_t::WORD_X_ABITS; + array_[idx].bbits_val_ = vvp_vector4_t::WORD_X_BBITS; + } + } else { + for (unsigned idx = 0 ; idx < words_ ; idx += 1) { + array_[idx].abits_ptr_ = 0; + array_[idx].bbits_ptr_ = 0; + } + } +} + +vvp_vector4array_t::~vvp_vector4array_t() +{ + if (array_) { + if (width_ > vvp_vector4_t::BITS_PER_WORD) { + for (unsigned idx = 0 ; idx < words_ ; idx += 1) + if (array_[idx].abits_ptr_) + delete[]array_[idx].abits_ptr_; + } + delete[]array_; + } +} + +void vvp_vector4array_t::set_word(unsigned index, const vvp_vector4_t&that) +{ + assert(index < words_); + assert(that.size_ == width_); + + v4cell&cell = array_[index]; + + if (width_ <= vvp_vector4_t::BITS_PER_WORD) { + cell.abits_val_ = that.abits_val_; + cell.bbits_val_ = that.bbits_val_; + return; + } + + unsigned cnt = (width_ + vvp_vector4_t::BITS_PER_WORD-1)/vvp_vector4_t::BITS_PER_WORD; + + if (cell.abits_ptr_ == 0) { + cell.abits_ptr_ = new unsigned long[2*cnt]; + cell.bbits_ptr_ = cell.abits_ptr_ + cnt; + } + + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + cell.abits_ptr_[idx] = that.abits_ptr_[idx]; + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + cell.bbits_ptr_[idx] = that.bbits_ptr_[idx]; +} + +vvp_vector4_t vvp_vector4array_t::get_word(unsigned index) const +{ + if (index >= words_) + return vvp_vector4_t(width_, BIT4_X); + + assert(index < words_); + + v4cell&cell = array_[index]; + + if (width_ <= vvp_vector4_t::BITS_PER_WORD) { + vvp_vector4_t res; + res.size_ = width_; + res.abits_val_ = cell.abits_val_; + res.bbits_val_ = cell.bbits_val_; + return res; + } + + vvp_vector4_t res (width_, BIT4_X); + if (cell.abits_ptr_ == 0) + return res; + + unsigned cnt = (width_ + vvp_vector4_t::BITS_PER_WORD-1)/vvp_vector4_t::BITS_PER_WORD; + + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + res.abits_ptr_[idx] = cell.abits_ptr_[idx]; + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + res.bbits_ptr_[idx] = cell.bbits_ptr_[idx]; + + return res; + +} + template T coerce_to_width(const T&that, unsigned width) { if (that.size() == width) diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 928bfb3a9..604f4b65d 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -122,6 +122,7 @@ extern int edge(vvp_bit4_t from, vvp_bit4_t to); class vvp_vector4_t { friend vvp_vector4_t operator ~(const vvp_vector4_t&that); + friend class vvp_vector4array_t; public: explicit vvp_vector4_t(unsigned size =0, vvp_bit4_t bits =BIT4_X); @@ -383,6 +384,42 @@ extern bool vector4_to_value(const vvp_vector4_t&a, long&val, bool is_signed); extern bool vector4_to_value(const vvp_vector4_t&a, unsigned long&val); extern bool vector4_to_value(const vvp_vector4_t&a, double&val, bool is_signed); +/* + * vvp_vector4array_t + */ +class vvp_vector4array_t { + + public: + vvp_vector4array_t(unsigned width, unsigned words); + ~vvp_vector4array_t(); + + unsigned width() const { return width_; } + unsigned words() const { return words_; } + + vvp_vector4_t get_word(unsigned idx) const; + void set_word(unsigned idx, const vvp_vector4_t&that); + + private: + struct v4cell { + union { + unsigned long abits_val_; + unsigned long*abits_ptr_; + }; + union { + unsigned long bbits_val_; + unsigned long*bbits_ptr_; + }; + }; + + unsigned width_; + unsigned words_; + v4cell* array_; + + private: // Not implemented + vvp_vector4array_t(const vvp_vector4array_t&); + vvp_vector4array_t& operator = (const vvp_vector4array_t&); +}; + /* vvp_vector2_t */ class vvp_vector2_t { From 69ba0094397b8f422c06c52a2306f22dba9ba700 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 16 Jun 2008 17:45:08 -0700 Subject: [PATCH 3/6] Cleanup the resolver function. First, handle the trivial (but possibly common) resolution cases in inlined code, and only call the complete function for the complicated cases. Then clean up the complex function for readability, and account for the constraints that the front-end function established. --- vvp/vvp_net.cc | 175 +++++++++++++++++++++++++------------------------ vvp/vvp_net.h | 21 +++++- 2 files changed, 107 insertions(+), 89 deletions(-) diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 9f9a76065..bad2894e7 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -2862,16 +2862,13 @@ ostream& operator <<(ostream&out, vvp_scalar_t a) return out; } -vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b) +/* + * This function is only called if the actual interface function rules + * out some of the eazy cases. If we get here, we can assume that + * neither of the values is HiZ, and the values are not exactly equal. + */ +vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b) { - // If the value is 0, that is the same as HiZ. In that case, - // resolution is simply a matter of returning the *other* value. - if (a.value_ == 0) - return b; - if (b.value_ == 0) - return a; - - vvp_scalar_t res = a; if (UNAMBIG(a.value_) && UNAMBIG(b.value_)) { @@ -2880,108 +2877,112 @@ vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b) but different values, then this becomes ambiguous. */ - if (a.value_ == b.value_) { + if ((b.value_&0x07) > (a.value_&0x07)) { - /* values are equal. do nothing. */ + /* b value is stronger. Take it. */ + return b; - } else if ((b.value_&0x07) > (res.value_&0x07)) { + } else if ((b.value_&0x77) == (a.value_&0x77)) { - /* New value is stronger. Take it. */ - res.value_ = b.value_; - - } else if ((b.value_&0x77) == (res.value_&0x77)) { - - /* Strengths are the same. Make value ambiguous. */ - res.value_ = (res.value_&0x70) | (b.value_&0x07) | 0x80; + // Strengths are the same. Since we know already + // that the values are not the same, Make value + // into "x". + vvp_scalar_t tmp (a); + tmp.value_ = (tmp.value_&0x77) | 0x80; + return tmp; } else { - /* Must be res is the stronger one. */ + /* Must be "a" is the stronger one. */ + return a; } - } else if (UNAMBIG(res.value_)) { - unsigned tmp = 0; + } - if ((res.value_&0x70) > (b.value_&0x70)) - tmp |= res.value_&0xf0; + /* If one of the signals is unambiguous, then it + will sweep up the weaker parts of the ambiguous + signal. The result may be ambiguous, or maybe not. */ + + if (UNAMBIG(a.value_)) { + vvp_scalar_t res; + + if ((a.value_&0x70) > (b.value_&0x70)) + res.value_ |= a.value_&0xf0; else - tmp |= b.value_&0xf0; + res.value_ |= b.value_&0xf0; - if ((res.value_&0x07) > (b.value_&0x07)) - tmp |= res.value_&0x0f; + if ((a.value_&0x07) > (b.value_&0x07)) + res.value_ |= a.value_&0x0f; else - tmp |= b.value_&0x0f; + res.value_ |= b.value_&0x0f; - res.value_ = tmp; + return res; } else if (UNAMBIG(b.value_)) { - /* If one of the signals is unambiguous, then it - will sweep up the weaker parts of the ambiguous - signal. The result may be ambiguous, or maybe not. */ + vvp_scalar_t res; - unsigned tmp = 0; - - if ((b.value_&0x70) > (res.value_&0x70)) - tmp |= b.value_&0xf0; + if ((b.value_&0x70) > (a.value_&0x70)) + res.value_ |= b.value_&0xf0; else - tmp |= res.value_&0xf0; + res.value_ |= a.value_&0xf0; - if ((b.value_&0x07) > (res.value_&0x07)) - tmp |= b.value_&0x0f; + if ((b.value_&0x07) > (a.value_&0x07)) + res.value_ |= b.value_&0x0f; else - tmp |= res.value_&0x0f; + res.value_ |= a.value_&0x0f; - res.value_ = tmp; + return res; - } else { - - /* If both signals are ambiguous, then the result - has an even wider ambiguity. */ - - unsigned tmp = 0; - int sv1a = a.value_&0x80 ? STREN1(a.value_) : - STREN1(a.value_); - int sv0a = a.value_&0x08 ? STREN0(a.value_) : - STREN0(a.value_); - int sv1b = b.value_&0x80 ? STREN1(b.value_) : - STREN1(b.value_); - int sv0b = b.value_&0x08 ? STREN0(b.value_) : - STREN0(b.value_); - - int sv1 = sv1a; - int sv0 = sv0a; - - if (sv0a > sv1) - sv1 = sv0a; - if (sv1b > sv1) - sv1 = sv1b; - if (sv0b > sv1) - sv1 = sv0b; - - if (sv1a < sv0) - sv0 = sv1a; - if (sv1b < sv0) - sv0 = sv1b; - if (sv0b < sv0) - sv0 = sv0b; - - if (sv1 > 0) { - tmp |= 0x80; - tmp |= sv1 << 4; - } else { - /* Set the MSB when both arguments MSBs are set. This - can only happen if both one strengths are zero. */ - tmp |= (a.value_&b.value_)&0x80; - tmp |= (-sv1) << 4; - } - - if (sv0 > 0) { - tmp |= 0x08; - tmp |= sv0; - } else { - tmp |= (-sv0); - } - - res.value_ = tmp; } + + /* If both signals are ambiguous, then the result + has an even wider ambiguity. */ + + unsigned tmp = 0; + int sv1a = a.value_&0x80 ? STREN1(a.value_) : - STREN1(a.value_); + int sv0a = a.value_&0x08 ? STREN0(a.value_) : - STREN0(a.value_); + int sv1b = b.value_&0x80 ? STREN1(b.value_) : - STREN1(b.value_); + int sv0b = b.value_&0x08 ? STREN0(b.value_) : - STREN0(b.value_); + + int sv1 = sv1a; + int sv0 = sv0a; + + if (sv0a > sv1) + sv1 = sv0a; + if (sv1b > sv1) + sv1 = sv1b; + if (sv0b > sv1) + sv1 = sv0b; + + if (sv1a < sv0) + sv0 = sv1a; + if (sv1b < sv0) + sv0 = sv1b; + if (sv0b < sv0) + sv0 = sv0b; + + if (sv1 > 0) { + tmp |= 0x80; + tmp |= sv1 << 4; + } else { + /* Set the MSB when both arguments MSBs are set. This + can only happen if both one strengths are zero. */ + tmp |= (a.value_&b.value_)&0x80; + tmp |= (-sv1) << 4; + } + + if (sv0 > 0) { + tmp |= 0x08; + tmp |= sv0; + } else { + tmp |= (-sv0); + } + + vvp_scalar_t res; + res.value_ = tmp; + /* Canonicalize the HiZ value. */ if ((res.value_&0x77) == 0) res.value_ = 0; diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 604f4b65d..c9a0fe70f 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -516,7 +516,7 @@ inline unsigned vvp_vector2_t::size() const */ class vvp_scalar_t { - friend vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b); + friend vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b); public: // Make a HiZ value. @@ -580,7 +580,24 @@ inline vvp_bit4_t vvp_scalar_t::value() const } -extern vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b); +inline vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b) +{ + extern vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b); + + // 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); /* From 6321fb6a92f2434aa7c7a9268c4271b9baff8dd6 Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 16 Jun 2008 10:43:17 -0700 Subject: [PATCH 4/6] Pad Octal string value correctly. This patch changes the base oct to string converter to correctly pad the top digit. x or xx should display as a single lower case x when they are located in the top bits. Before these were being interpreted as 00x or 0xx and displayed X. Also modified the hex conversion to use this same scheme instead of a loop. --- vvp/vpip_hex.cc | 34 +++++++++++++++++----------------- vvp/vpip_oct.cc | 18 +++++++++++++----- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/vvp/vpip_hex.cc b/vvp/vpip_hex.cc index 3ef3056cd..9c6a6a2e4 100644 --- a/vvp/vpip_hex.cc +++ b/vvp/vpip_hex.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 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 @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: vpip_hex.cc,v 1.4 2006/02/21 02:39:27 steve Exp $" -#endif # include "config.h" # include "vpi_priv.h" @@ -140,21 +137,24 @@ void vpip_vec4_to_hex_str(const vvp_vector4_t&bits, char*buf, } } - if (slen > 0) { - unsigned padd = 0; + /* Fill in X or Z if they are the only thing in the value. */ + switch (bits.size() % 4) { + case 1: + if (val == 2) val = 170; + else if (val == 3) val = 255; + break; + case 2: + if (val == 10) val = 170; + else if (val == 15) val = 255; + break; + case 3: + if (val == 42) val = 170; + else if (val == 63) val = 255; + break; + } + if (slen > 0) { slen -= 1; buf[slen] = hex_digits[val]; - switch(buf[slen]) { - case 'X': padd = 2; break; - case 'Z': padd = 3; break; - } - if (padd) { - for (unsigned idx = bits.size() % 4; idx < 4; idx += 1) { - val = val | padd << 2*idx; - } - buf[slen] = hex_digits[val]; - } } } - diff --git a/vvp/vpip_oct.cc b/vvp/vpip_oct.cc index e8a1ac057..f60e87b24 100644 --- a/vvp/vpip_oct.cc +++ b/vvp/vpip_oct.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 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 @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: vpip_oct.cc,v 1.4 2006/02/21 02:39:27 steve Exp $" -#endif # include "config.h" # include "vpi_priv.h" @@ -120,9 +117,20 @@ void vpip_vec4_to_oct_str(const vvp_vector4_t&bits, char*buf, unsigned nbuf, } } + /* Fill in X or Z if they are the only thing in the value. */ + switch (bits.size() % 3) { + case 1: + if (val == 2) val = 42; + else if (val == 3) val = 63; + break; + case 2: + if (val == 10) val = 42; + else if (val == 15) val = 63; + break; + } + if (slen > 0) { slen -= 1; buf[slen] = oct_digits[val]; } } - From f6edd098a9adafa35c3b53491ec55b15b8bf3aed Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 16 Jun 2008 16:25:37 -0700 Subject: [PATCH 5/6] More file name and mode checks for $fopen{a,r,w}?. This patch adds checks that $fopen is only called with a valid mode argument. It also checks that the file name for $fopen{a,r,w}? is a valid looking file name (all characters satisfy isprint()). The later should prevent creating weird file names because of Verilog bugs. --- vpi/sys_fileio.c | 99 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 8 deletions(-) diff --git a/vpi/sys_fileio.c b/vpi/sys_fileio.c index 9d705a82b..3d666a9d2 100644 --- a/vpi/sys_fileio.c +++ b/vpi/sys_fileio.c @@ -20,6 +20,7 @@ # include "vpi_user.h" # include "sys_priv.h" # include +# include # include # include # include @@ -91,6 +92,7 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) s_vpi_value val; int fail = 0; char *mode_string = 0; + unsigned idx; vpiHandle item = vpi_scan(argv); vpiHandle mode = vpi_scan(argv); @@ -103,10 +105,54 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's mode argument was not a valid string.\n", + vpi_printf("%s's mode argument is not a valid string.\n", name); fail = 1; } + + /* Make sure the mode string is correct. */ + if (strlen(val.value.str) > 3) { + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument (%s) is too long.\n", + name, val.value.str); + fail = 1; + } else { + unsigned bin = 0, plus = 0; + switch (val.value.str[0]) { + case 'r': + case 'w': + case 'a': + for (idx = 1; idx < 3 ; idx++) { + if (val.value.str[idx] == '\0') break; + switch (val.value.str[idx]) { + case 'b': + if (bin) fail = 1; + bin = 1; + break; + case '+': + if (plus) fail = 1; + plus = 1; + break; + default: + fail = 1; + break; + } + } + if (! fail) break; + + default: + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument (%s) is invalid.\n", + name, val.value.str); + fail = 1; + break; + } + } + mode_string = strdup(val.value.str); vpi_free_object(argv); @@ -121,12 +167,31 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's file name argument was not a valid string.\n", + vpi_printf("%s's file name argument is not a valid string.\n", name); fail = 1; if (mode) free(mode_string); } + /* + * Verify that the file name is composed of only printable + * characters. + */ + unsigned len = strlen(val.value.str); + for (idx = 0; idx < len; idx++) { + if (! isprint(val.value.str[idx])) { + char msg [64]; + snprintf(msg, 64, "WARNING: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s %s's file name argument contains non-" + "printable characters.\n", msg, name); + vpi_printf("%*s \"%s\"\n", strlen(msg), " ", val.value.str); + fail = 1; + if (mode) free(mode_string); + } + } + /* If either the mode or file name are not valid just return. */ if (fail) return 0; @@ -168,11 +233,29 @@ static PLI_INT32 sys_fopenrwa_calltf(PLI_BYTE8*name) if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's file name argument was not a valid string.\n", + vpi_printf("%s's file name argument is not a valid string.\n", name); return 0; } + /* + * Verify that the file name is composed of only printable + * characters. + */ + unsigned idx, len = strlen(val.value.str); + for (idx = 0; idx < len; idx++) { + if (! isprint(val.value.str[idx])) { + char msg [64]; + snprintf(msg, 64, "WARNING: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s %s's file name argument contains non-" + "printable characters.\n", msg, name); + vpi_printf("%*s \"%s\"\n", strlen(msg), " ", val.value.str); + return 0; + } + } + /* Open the file and return the result. */ val.format = vpiIntVal; val.value.integer = vpi_fopen(val.value.str, mode); @@ -616,19 +699,19 @@ static PLI_INT32 sys_common_fd_calltf(PLI_BYTE8*name) val.format = vpiIntVal; switch (name[4]) { - case 'l': /* $ftell() */ + case 'l': /* $ftell() */ val.value.integer = ftell(fp); break; - case 'f': /* $feof() is from 1264-2005*/ + case 'f': /* $feof() is from 1264-2005*/ val.value.integer = feof(fp); break; - case 'i': /* $rewind() */ + case 'i': /* $rewind() */ val.value.integer = fseek(fp, 0L, SEEK_SET); break; - case 't': /* $fgetc() */ + case 't': /* $fgetc() */ val.value.integer = fgetc(fp); break; - default: + default: vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot be processed with this routine.\n", name); From 37723698dcaa156fdc16debd7b240869f96cbe97 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Tue, 17 Jun 2008 17:07:19 -0700 Subject: [PATCH 6/6] Handle non-real operands to real division. This handles the general case of a non-real operand to a real-valued division. This can turn up if only 1 operand of a divide is real. In this case the division as a whole is real and the other operand must be cast to real. This method creates an extra node, but it should be a very compact node and this node does no evaluation tricks so in the run time should be no more expensive then folding the cast into the .arith/div.r itself. --- design_dump.cc | 8 ++++++++ elab_net.cc | 10 ++++++++++ emit.cc | 5 +++++ ivl_target.h | 1 + netlist.cc | 9 +++++++++ netlist.h | 20 ++++++++++++++++++++ netmisc.cc | 19 +++++++++++++++++++ netmisc.h | 6 ++++++ t-dll-api.cc | 16 ++++++++-------- t-dll.cc | 33 +++++++++++++++++++++++++++++++++ t-dll.h | 1 + target.cc | 7 +++++++ target.h | 1 + tgt-stub/stub.c | 29 +++++++++++++++++++++++++++++ tgt-vvp/draw_net_input.c | 1 + tgt-vvp/vvp_scope.c | 15 +++++++++++++++ vvp/arith.cc | 16 ++++++++++++++++ vvp/arith.h | 11 +++++++++++ vvp/compile.cc | 15 +++++++++++++++ vvp/compile.h | 2 ++ vvp/lexor.lex | 1 + vvp/parse.y | 6 ++++++ 22 files changed, 224 insertions(+), 8 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index 3c77aa1e9..53b625f3d 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -302,6 +302,14 @@ void NetArrayDq::dump_node(ostream&o, unsigned ind) const dump_obj_attr(o, ind+4); } +void NetCastReal::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Cast to real (NetCastReal): " << + name() << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetCLShift::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " << diff --git a/elab_net.cc b/elab_net.cc index 8cbab1fdf..4b5370e34 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -716,6 +716,16 @@ NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope, unsigned rwidth = lwidth; + // If either operand is IVL_VT_REAL, then cast the other to + // IVL_VT_REAL so that the division can become IVL_VT_REAL. + + if (lsig->data_type()==IVL_VT_REAL || rsig->data_type()==IVL_VT_REAL) { + if (lsig->data_type() != IVL_VT_REAL) + lsig = cast_to_real(des, scope, lsig); + if (rsig->data_type() != IVL_VT_REAL) + rsig = cast_to_real(des, scope, rsig); + } + if (rwidth == 0) { rwidth = lsig->vector_width(); if (rsig->vector_width() > rwidth) diff --git a/emit.cc b/emit.cc index 606d13beb..84c4d6a25 100644 --- a/emit.cc +++ b/emit.cc @@ -72,6 +72,11 @@ bool NetCaseCmp::emit_node(struct target_t*tgt) const return true; } +bool NetCastReal::emit_node(struct target_t*tgt) const +{ + return tgt->lpm_cast_real(this); +} + bool NetCLShift::emit_node(struct target_t*tgt) const { tgt->lpm_clshift(this); diff --git a/ivl_target.h b/ivl_target.h index 6ab07a9ea..61069a6eb 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -254,6 +254,7 @@ typedef enum ivl_lpm_type_e { IVL_LPM_ABS = 32, IVL_LPM_ADD = 0, IVL_LPM_ARRAY = 30, + IVL_LPM_CAST_REAL = 33, IVL_LPM_CONCAT = 16, IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ IVL_LPM_CMP_EQ = 10, diff --git a/netlist.cc b/netlist.cc index fefd23581..db630eb6b 100644 --- a/netlist.cc +++ b/netlist.cc @@ -849,6 +849,15 @@ const NetScope* NetProcTop::scope() const return scope_; } +NetCastReal::NetCastReal(NetScope*scope, perm_string n, bool signed_flag) +: NetNode(scope, n, 2), signed_flag_(signed_flag) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("O"), 0); + pin(1).set_dir(Link::INPUT); + pin(1).set_name(perm_string::literal("I"), 0); +} + NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt) : NetNode(scope, n, cnt+1), width_(wid) { diff --git a/netlist.h b/netlist.h index e23fcb3fd..7c9186060 100644 --- a/netlist.h +++ b/netlist.h @@ -916,6 +916,26 @@ class NetArrayDq : public NetNode { }; +/* + * Convert an input to IVL_VT_REAL. The input is pin(1), which can be + * any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0), + * which is IVL_VT_REAL. The conversion interprets the input as an + * unsigned value unless the signed_flag is true. + */ +class NetCastReal : public NetNode { + + public: + NetCastReal(NetScope*s, perm_string n, bool signed_flag); + + bool signed_flag() const { return signed_flag_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + + private: + bool signed_flag_; +}; + /* * This type represents the LPM_CLSHIFT device. */ diff --git a/netmisc.cc b/netmisc.cc index 39787d621..eea23d1e2 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -76,6 +76,25 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val) #endif } +NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) +{ + if (src->data_type() == IVL_VT_REAL) + return src; + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE); + tmp->data_type(IVL_VT_REAL); + tmp->set_line(*src); + + NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed()); + cast->set_line(*src); + des->add_node(cast); + + connect(cast->pin(0), tmp->pin(0)); + connect(cast->pin(1), src->pin(0)); + + return tmp; +} + /* * Add a signed constant to an existing expression. Generate a new * NetEBAdd node that has the input expression and an expression made diff --git a/netmisc.h b/netmisc.h index e82c3ef20..3c8ce1ca9 100644 --- a/netmisc.h +++ b/netmisc.h @@ -64,6 +64,12 @@ extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w); extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w); +/* + * Generate the nodes necessary to cast an expression (a net) to a + * real value. + */ +extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src); + /* * Take the input expression and return a variation that assures that * the expression is 1-bit wide and logical. This reflects the needs diff --git a/t-dll-api.cc b/t-dll-api.cc index a9c4c561e..e5e275aad 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -886,6 +886,7 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) assert(net); switch (net->type) { case IVL_LPM_ABS: + case IVL_LPM_CAST_REAL: assert(idx == 0); return net->u_.arith.a; @@ -1028,20 +1029,18 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) switch (net->type) { case IVL_LPM_ABS: case IVL_LPM_ADD: - case IVL_LPM_DIVIDE: - case IVL_LPM_MOD: - case IVL_LPM_MULT: - case IVL_LPM_POW: - case IVL_LPM_SUB: - assert(idx == 0); - return net->u_.arith.q; - + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_NEE: + case IVL_LPM_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_MULT: + case IVL_LPM_POW: + case IVL_LPM_SUB: assert(idx == 0); return net->u_.arith.q; @@ -1144,6 +1143,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) return 0; case IVL_LPM_ABS: case IVL_LPM_ADD: + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_GE: diff --git a/t-dll.cc b/t-dll.cc index 0261a8346..edeb58a8c 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1550,6 +1550,39 @@ void dll_target::lpm_clshift(const NetCLShift*net) scope_add_lpm(obj->scope, obj); } +bool dll_target::lpm_cast_real(const NetCastReal*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_CAST_REAL; + obj->name = net->name(); // NetCastReal names are permallocated + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->width = 0; + obj->u_.arith.signed_flag = net->signed_flag()? 1 : 0; + + const Nexus*nex; + + nex = net->pin(0).nexus(); + assert(nex->t_cookie()); + + obj->u_.arith.q = nex->t_cookie(); + + nex = net->pin(1).nexus(); + assert(nex->t_cookie()); + obj->u_.arith.a = nex->t_cookie(); + + nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + make_lpm_delays_(obj, net); + + scope_add_lpm(obj->scope, obj); + + return true; +} + /* * Make out of the NetCompare object an ivl_lpm_s object. The * comparators in ivl_target do not support < or <=, but they can be diff --git a/t-dll.h b/t-dll.h index 63f0a1bb2..3b017bf83 100644 --- a/t-dll.h +++ b/t-dll.h @@ -76,6 +76,7 @@ struct dll_target : public target_t, public expr_scan_t { void lpm_abs(const NetAbs*); void lpm_add_sub(const NetAddSub*); bool lpm_array_dq(const NetArrayDq*); + bool lpm_cast_real(const NetCastReal*); void lpm_clshift(const NetCLShift*); void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); diff --git a/target.cc b/target.cc index e474c7cf7..0d71e0e26 100644 --- a/target.cc +++ b/target.cc @@ -107,6 +107,13 @@ bool target_t::lpm_array_dq(const NetArrayDq*) return false; } +bool target_t::lpm_cast_real(const NetCastReal*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetCastReal." << endl; + return false; +} + void target_t::lpm_clshift(const NetCLShift*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index d002a9797..89c78c037 100644 --- a/target.h +++ b/target.h @@ -72,6 +72,7 @@ struct target_t { virtual void lpm_add_sub(const NetAddSub*); virtual bool lpm_array_dq(const NetArrayDq*); virtual void lpm_clshift(const NetCLShift*); + virtual bool lpm_cast_real(const NetCastReal*); virtual void lpm_compare(const NetCompare*); virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index ecec77782..4238a4820 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -240,6 +240,31 @@ static void show_lpm_array(ivl_lpm_t net) } } +static void show_lpm_cast_real(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_CAST_REAL %s: \n", + ivl_lpm_basename(net), width); + + ivl_nexus_t q = ivl_lpm_q(net,0); + ivl_nexus_t a = ivl_lpm_data(net,0); + fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0))); + fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0))); + + if (type_of_nexus(q) != IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of Q is %s, expecting real\n", + data_type_string(type_of_nexus(q))); + stub_errors += 1; + } + + if (type_of_nexus(a) == IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of A is %s, expecting !real\n", + data_type_string(type_of_nexus(a))); + stub_errors += 1; + } +} + static void show_lpm_divide(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -802,6 +827,10 @@ static void show_lpm(ivl_lpm_t net) show_lpm_array(net); break; + case IVL_LPM_CAST_REAL: + show_lpm_cast_real(net); + break; + case IVL_LPM_DIVIDE: show_lpm_divide(net); break; diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 9a9d51d6d..f7616ade0 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -387,6 +387,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: + case IVL_LPM_CAST_REAL: case IVL_LPM_CONCAT: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index ed1aa48d8..1f2cdeaec 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1079,6 +1079,17 @@ static void draw_lpm_abs(ivl_lpm_t net) net, dly, src_table[0]); } +static void draw_lpm_cast_real(ivl_lpm_t net) +{ + const char*src_table[1]; + draw_lpm_data_inputs(net, 0, 1, src_table); + + const char*dly = draw_lpm_output_delay(net); + + fprintf(vvp_out, "L_%p%s .cast/real %s;\n", + net, dly, src_table[0]); +} + static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; @@ -1644,6 +1655,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_abs(net); return; + case IVL_LPM_CAST_REAL: + draw_lpm_cast_real(net); + return; + case IVL_LPM_ADD: case IVL_LPM_SUB: case IVL_LPM_MULT: diff --git a/vvp/arith.cc b/vvp/arith.cc index c7b827bbc..d263981aa 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -90,6 +90,22 @@ void vvp_arith_abs::recv_real(vvp_net_ptr_t ptr, double bit) vvp_send_real(ptr.ptr()->out, out); } +vvp_arith_cast_real::vvp_arith_cast_real(bool signed_flag) +: signed_(signed_flag) +{ +} + +vvp_arith_cast_real::~vvp_arith_cast_real() +{ +} + +void vvp_arith_cast_real::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) +{ + double val; + vector4_to_value(bit, val, signed_); + vvp_send_real(ptr.ptr()->out, val); +} + // Division vvp_arith_div::vvp_arith_div(unsigned wid, bool signed_flag) diff --git a/vvp/arith.h b/vvp/arith.h index 41ccf5371..31a479dd3 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -60,6 +60,17 @@ class vvp_arith_abs : public vvp_net_fun_t { private: }; +class vvp_arith_cast_real : public vvp_net_fun_t { + public: + explicit vvp_arith_cast_real(bool signed_flag); + ~vvp_arith_cast_real(); + + void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit); + + private: + bool signed_; +}; + class vvp_arith_div : public vvp_arith_ { public: diff --git a/vvp/compile.cc b/vvp/compile.cc index 18174c937..9cdc5d514 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -889,6 +889,21 @@ template void make_arith(T_ *arith, char*label, free(argv); } +void compile_arith_cast_real(char*label, unsigned argc, struct symb_s*argv) +{ + vvp_arith_cast_real*arith = new vvp_arith_cast_real(false); + + vvp_net_t* ptr = new vvp_net_t; + ptr->fun = arith; + + define_functor_symbol(label, ptr); + free(label); + + assert(argc == 1); + inputs_connect(ptr, argc, argv); + free(argv); +} + void compile_arith_abs(char*label, unsigned argc, struct symb_s*argv) { vvp_arith_abs*arith = new vvp_arith_abs; diff --git a/vvp/compile.h b/vvp/compile.h index 93daf0f30..0d57b3bf5 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -151,6 +151,8 @@ extern void compile_arith_pow(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_abs(char*label, unsigned argc, struct symb_s*argv); +extern void compile_arith_cast_real(char*label, + unsigned argc, struct symb_s*argv); extern void compile_arith_div(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_mod(char*label, long width, diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 0e21eb087..2f8b381c3 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -107,6 +107,7 @@ ".array/real" { return K_ARRAY_R; } ".array/s" { return K_ARRAY_S; } ".array/port" { return K_ARRAY_PORT; } +".cast/real" { return K_CAST_REAL; } ".cmp/eeq" { return K_CMP_EEQ; } ".cmp/eq" { return K_CMP_EQ; } ".cmp/eq.r" { return K_CMP_EQ_R; } diff --git a/vvp/parse.y b/vvp/parse.y index 6947f8996..c7678f0ae 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -71,6 +71,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_ARITH_MULT K_ARITH_MULT_R K_ARITH_SUB K_ARITH_SUB_R %token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R K_ARITH_POW_S %token K_ARRAY K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_PORT +%token K_CAST_REAL %token K_CMP_EEQ K_CMP_EQ K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R %token K_CMP_GE K_CMP_GE_R K_CMP_GE_S K_CMP_GT K_CMP_GT_R K_CMP_GT_S %token K_CONCAT K_DEBUG K_DELAY K_DFF @@ -252,6 +253,11 @@ statement compile_arith_abs($1, obj.cnt, obj.vect); } + | T_LABEL K_CAST_REAL symbols ';' + { struct symbv_s obj = $3; + compile_arith_cast_real($1, obj.cnt, obj.vect); + } + /* Arithmetic statements generate functor arrays of a given width that take like size input vectors. */