/* * Copyright (c) 2007-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 * 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 */ # include "array.h" #include "symbols.h" #include "schedule.h" #include "vpi_priv.h" #ifdef HAVE_MALLOC_H # include #endif # include # include # include # include "compile.h" # include static symbol_table_t array_table =0; class vvp_fun_arrayport; static void array_attach_port(vvp_array_t, vvp_fun_arrayport*); vvp_array_t array_find(char*label) { if (array_table == 0) return 0; symbol_value_t v = sym_get_value(array_table, label); return (vvp_array_t)v.ptr; } /* * The vpiArray object holds an array of vpi objects that themselves * represent the words of the array. The vpi_array_t is a pointer to this. */ struct __vpiArray { struct __vpiHandle base; struct __vpiScope*scope; const char*name; /* Permanently allocated string */ unsigned array_count; struct __vpiDecConst first_addr; struct __vpiDecConst last_addr; vpiHandle*words; class vvp_fun_arrayport*ports_; }; struct __vpiArrayIterator { struct __vpiHandle base; struct __vpiArray*array; unsigned next; }; struct __vpiArrayIndex { struct __vpiHandle base; struct __vpiDecConst *index; unsigned done; }; static int vpi_array_get(int code, vpiHandle ref); static char*vpi_array_get_str(int code, vpiHandle ref); static vpiHandle vpi_array_get_handle(int code, vpiHandle ref); static vpiHandle vpi_array_iterate(int code, vpiHandle ref); static vpiHandle vpi_array_index(vpiHandle ref, int index); static vpiHandle array_iterator_scan(vpiHandle ref, int); static int array_iterator_free_object(vpiHandle ref); static vpiHandle array_index_scan(vpiHandle ref, int); static int array_index_free_object(vpiHandle ref); static const struct __vpirt vpip_arraymem_rt = { vpiMemory, vpi_array_get, vpi_array_get_str, 0, 0, vpi_array_get_handle, vpi_array_iterate, vpi_array_index, }; static const struct __vpirt vpip_array_iterator_rt = { vpiIterator, 0, 0, 0, 0, 0, 0, array_iterator_scan, &array_iterator_free_object }; /* This should look a bit odd since it provides a fake iteration on * this object. This trickery is used to implement the two forms of * index access, simple handle access and iteration access. */ static const struct __vpirt vpip_array_index_rt = { vpiIterator, 0, 0, 0, 0, 0, array_index_iterate, array_index_scan, array_index_free_object }; # define ARRAY_HANDLE(ref) (assert(ref->vpi_type->type_code==vpiMemory), \ (struct __vpiArray*)ref) static int vpi_array_get(int code, vpiHandle ref) { struct __vpiArray*obj = ARRAY_HANDLE(ref); switch (code) { case vpiSize: return (int) obj->array_count; default: return 0; } } static char*vpi_array_get_str(int code, vpiHandle ref) { struct __vpiArray*obj = ARRAY_HANDLE(ref); return generic_get_str(code, &obj->scope->base, obj->name, NULL); } static vpiHandle vpi_array_get_handle(int code, vpiHandle ref) { struct __vpiArray*obj = ARRAY_HANDLE(ref); switch (code) { case vpiLeftRange: return &(obj->first_addr.base); case vpiRightRange: return &(obj->last_addr.base); case vpiScope: return &obj->scope->base; } return 0; } static vpiHandle vpi_array_iterate(int code, vpiHandle ref) { struct __vpiArray*obj = ARRAY_HANDLE(ref); switch (code) { case vpiMemoryWord: { struct __vpiArrayIterator*res; res = (struct __vpiArrayIterator*) calloc(1, sizeof (*res)); res->base.vpi_type = &vpip_array_iterator_rt; res->array = obj; res->next = 0; return &res->base; } } return 0; } /* * VPI code passes indices that are not yet converted to canonical * form, so this index function does it here. */ static vpiHandle vpi_array_index(vpiHandle ref, int index) { struct __vpiArray*obj = ARRAY_HANDLE(ref); index -= obj->first_addr.value; if (index >= (long)obj->array_count) return 0; if (index < 0) return 0; return obj->words[index]; } # define ARRAY_ITERATOR(ref) (assert(ref->vpi_type->type_code==vpiIterator), \ (struct __vpiArrayIterator*)ref) static vpiHandle array_iterator_scan(vpiHandle ref, int) { struct __vpiArrayIterator*obj = ARRAY_ITERATOR(ref); if (obj->next >= obj->array->array_count) { vpi_free_object(ref); return 0; } vpiHandle res = obj->array->words[obj->next]; obj->next += 1; return res; } static int array_iterator_free_object(vpiHandle ref) { struct __vpiArrayIterator*obj = ARRAY_ITERATOR(ref); free(obj); return 1; } # define ARRAY_INDEX(ref) (assert(ref->vpi_type->type_code==vpiIterator), \ (struct __vpiArrayIndex*)ref) vpiHandle array_index_iterate(int code, vpiHandle ref) { assert(ref->vpi_type->type_code == vpiConstant); struct __vpiDecConst*obj = (struct __vpiDecConst*)ref; if (code == vpiIndex) { struct __vpiArrayIndex*res; res = (struct __vpiArrayIndex*) calloc(1, sizeof (*res)); res->base.vpi_type = &vpip_array_index_rt; res->index = obj; res->done = 0; return &res->base; } return 0; } static vpiHandle array_index_scan(vpiHandle ref, int) { struct __vpiArrayIndex*obj = ARRAY_INDEX(ref); if (obj->done == 0) { obj->done = 1; return &obj->index->base; } vpi_free_object(ref); return 0; } static int array_index_free_object(vpiHandle ref) { struct __vpiArrayIndex*obj = ARRAY_INDEX(ref); free(obj); return 1; } void array_set_word(vvp_array_t arr, unsigned address, unsigned part_off, vvp_vector4_t val) { if (address >= arr->array_count) return; // Select the word of the array that we affect. vpiHandle word = arr->words[address]; struct __vpiSignal*vsig = vpip_signal_from_handle(word); assert(vsig); vvp_net_ptr_t ptr (vsig->node, 0); vvp_send_vec4_pv(ptr, val, part_off, val.size(), vpip_size(vsig)); } vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address) { if (address >= arr->array_count) { // Reading outside the array. Return X's but get the // width by looking at a word that we know is present. assert(arr->array_count > 0); vpiHandle word = arr->words[0]; struct __vpiSignal*vsig = vpip_signal_from_handle(word); assert(vsig); vvp_fun_signal_vec*sig = dynamic_cast (vsig->node->fun); assert(sig); return vvp_vector4_t(sig->size(), BIT4_X); } vpiHandle word = arr->words[address]; struct __vpiSignal*vsig = vpip_signal_from_handle(word); assert(vsig); vvp_fun_signal_vec*sig = dynamic_cast (vsig->node->fun); assert(sig); vvp_vector4_t val = sig->vec4_value(); return val; } static vpiHandle vpip_make_array(char*label, const char*name, int first_addr, int last_addr) { struct __vpiArray*obj = (struct __vpiArray*) malloc(sizeof(struct __vpiArray)); // Assume increasing addresses. assert(last_addr >= first_addr); unsigned array_count = last_addr+1-first_addr; // For now, treat all arrays as memories. This is not quite // correct, as arrays are arrays with memories a special case. obj->base.vpi_type = &vpip_arraymem_rt; obj->scope = vpip_peek_current_scope(); obj->name = vpip_name_string(name); obj->array_count = array_count; vpip_make_dec_const(&obj->first_addr, first_addr); vpip_make_dec_const(&obj->last_addr, last_addr); obj->words = (vpiHandle*)calloc(array_count, sizeof(vpiHandle)); // Initialize (clear) the read-ports list. obj->ports_ = 0; /* Add this symbol to the array_symbols table for later lookup. */ if (!array_table) array_table = new_symbol_table(); assert(!array_find(label)); symbol_value_t v; v.ptr = obj; sym_set_value(array_table, label, v); return &(obj->base); } void array_alias_word(vvp_array_t array, unsigned long addr, vpiHandle word) { assert(addr < array->array_count); array->words[addr] = word; } void array_attach_word(vvp_array_t array, unsigned long addr, vpiHandle word) { assert(addr < array->array_count); array->words[addr] = word; if (struct __vpiSignal*sig = vpip_signal_from_handle(word)) { vvp_net_t*net = sig->node; assert(net); vvp_fun_signal_base*fun = dynamic_cast(net->fun); assert(fun); fun->attach_as_word(array, addr); sig->parent = &array->base; sig->id.index = vpip_make_dec_const(addr + array->first_addr.value); } } static vpiHandle common_array_build(char*label, char*name, int last, int first) { vpiHandle obj = vpip_make_array(label, name, first, last); /* Add this into the table of VPI objects. This is used for contexts that try to look up VPI objects in general. (i.e. arguments to vpi_task calls.) */ compile_vpi_symbol(label, obj); /* Blindly attach to the scope as an object. */ vpip_attach_to_current_scope(obj); return obj; } void compile_var_array(char*label, char*name, int last, int first, int msb, int lsb, char signed_flag) { vpiHandle obj = common_array_build(label, name, last, first); struct __vpiArray*arr = ARRAY_HANDLE(obj); vvp_array_t array = array_find(label); /* Make the words. */ for (unsigned idx = 0 ; idx < arr->array_count ; idx += 1) { char buf[64]; snprintf(buf, sizeof buf, "%s_%u", label, idx); compile_variablew(strdup(buf), array, idx, msb, lsb, signed_flag); } free(label); free(name); } void compile_real_array(char*label, char*name, int last, int first, int msb, int lsb) { vpiHandle obj = common_array_build(label, name, last, first); struct __vpiArray*arr = ARRAY_HANDLE(obj); vvp_array_t array = array_find(label); /* Make the words. */ for (unsigned idx = 0 ; idx < arr->array_count ; idx += 1) { char buf[64]; snprintf(buf, sizeof buf, "%s_%u", label, idx); compile_varw_real(strdup(buf), array, idx, msb, lsb); } free(label); free(name); } void compile_net_array(char*label, char*name, int last, int first) { /* vpiHandle obj = */ common_array_build(label, name, last, first); free(label); free(name); } class vvp_fun_arrayport : public vvp_net_fun_t { public: explicit vvp_fun_arrayport(vvp_array_t mem, vvp_net_t*net); ~vvp_fun_arrayport(); void check_word_change(unsigned long addr); void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit); private: vvp_array_t arr_; vvp_net_t *net_; unsigned long addr_; friend void array_attach_port(vvp_array_t, vvp_fun_arrayport*); friend void array_word_change(vvp_array_t, unsigned long); vvp_fun_arrayport*next_; }; vvp_fun_arrayport::vvp_fun_arrayport(vvp_array_t mem, vvp_net_t*net) : arr_(mem), net_(net), addr_(0) { next_ = 0; } vvp_fun_arrayport::~vvp_fun_arrayport() { } void vvp_fun_arrayport::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit) { bool addr_valid_flag; switch (port.port()) { case 0: // Address input addr_valid_flag = vector4_to_value(bit, addr_); if (! addr_valid_flag) addr_ = arr_->array_count; vvp_send_vec4(port.ptr()->out, array_get_word(arr_,addr_)); break; default: fprintf(stdout, "XXXX write ports not implemented.\n"); assert(0); } } void vvp_fun_arrayport::check_word_change(unsigned long addr) { if (addr != addr_) return; vvp_vector4_t bit = array_get_word(arr_, addr_); vvp_send_vec4(net_->out, bit); } static void array_attach_port(vvp_array_t array, vvp_fun_arrayport*fun) { assert(fun->next_ == 0); fun->next_ = array->ports_; array->ports_ = fun; } void array_word_change(vvp_array_t array, unsigned long addr) { for (vvp_fun_arrayport*cur = array->ports_; cur; cur = cur->next_) cur->check_word_change(addr); } void compile_array_port(char*label, char*array, char*addr) { vvp_array_t mem = array_find(array); assert(mem); vvp_net_t*ptr = new vvp_net_t; vvp_fun_arrayport*fun = new vvp_fun_arrayport(mem, ptr); ptr->fun = fun; define_functor_symbol(label, ptr); // Connect the port-0 input as the address. input_connect(ptr, 0, addr); array_attach_port(mem, fun); free(label); free(array); // The input_connect arranges for the array string to be free'ed. } void compile_array_alias(char*label, char*name, char*src) { vvp_array_t mem = array_find(src); assert(mem); struct __vpiArray*obj = (struct __vpiArray*) malloc(sizeof (struct __vpiArray)); obj->base.vpi_type = &vpip_arraymem_rt; obj->scope = vpip_peek_current_scope(); obj->name = vpip_name_string(name); obj->array_count = mem->array_count; // XXXX Need to set an accurate range of addresses. vpip_make_dec_const(&obj->first_addr, mem->first_addr.value); vpip_make_dec_const(&obj->last_addr, mem->last_addr.value); // Share the words with the source array. obj->words = mem->words; obj->ports_ = 0; assert(array_table); assert(!array_find(label)); symbol_value_t v; v.ptr = obj; sym_set_value(array_table, label, v); compile_vpi_symbol(label, &obj->base); vpip_attach_to_current_scope(&obj->base); free(label); free(name); free(src); }