/* * Copyright (c) 2003-2021 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "compile.h" # include "vpi_priv.h" # include "array.h" # include "vvp_net_sig.h" # include "logic.h" # include "schedule.h" #ifdef CHECK_WITH_VALGRIND # include "vvp_cleanup.h" #endif # include # include # include # include # include using namespace std; static void __compile_var_real(char*label, char*name, vvp_array_t array, unsigned long array_addr) { vvp_net_t*net = new vvp_net_t; if (vpip_peek_current_scope()->is_automatic()) { vvp_fun_signal_real_aa*tmp = new vvp_fun_signal_real_aa; net->fil = tmp; net->fun = tmp; } else { net->fil = new vvp_wire_real; net->fun = new vvp_fun_signal_real_sa; } define_functor_symbol(label, net); vpiHandle obj = vpip_make_real_var(name, net); compile_vpi_symbol(label, obj); if (name) { assert(!array); vpip_attach_to_current_scope(obj); if (!vpip_peek_current_scope()->is_automatic()) schedule_init_vector(vvp_net_ptr_t(net,0), 0.0); } if (array) { assert(!name); array->attach_word(array_addr, obj); } free(label); delete[] name; } void compile_var_real(char*label, char*name) { __compile_var_real(label, name, 0, 0); } void compile_var_string(char*label, char*name) { vvp_net_t*net = new vvp_net_t; if (vpip_peek_current_scope()->is_automatic()) { vvp_fun_signal_string_aa*tmp = new vvp_fun_signal_string_aa; net->fil = tmp; net->fun = tmp; } else { net->fil = 0; net->fun = new vvp_fun_signal_string_sa; } define_functor_symbol(label, net); vpiHandle obj = vpip_make_string_var(name, net); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); free(label); delete[] name; } void compile_var_darray(char*label, char*name, unsigned size) { vvp_net_t*net = new vvp_net_t; if (vpip_peek_current_scope()->is_automatic()) { vvp_fun_signal_object_aa*tmp = new vvp_fun_signal_object_aa(size); net->fil = tmp; net->fun = tmp; } else { net->fil = 0; net->fun = new vvp_fun_signal_object_sa(size); } define_functor_symbol(label, net); vpiHandle obj = vpip_make_darray_var(name, net); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); free(label); delete[] name; } void compile_var_queue(char*label, char*name, unsigned size) { vvp_net_t*net = new vvp_net_t; if (vpip_peek_current_scope()->is_automatic()) { vvp_fun_signal_object_aa*tmp = new vvp_fun_signal_object_aa(size); net->fil = tmp; net->fun = tmp; } else { net->fil = 0; net->fun = new vvp_fun_signal_object_sa(size); } define_functor_symbol(label, net); vpiHandle obj = vpip_make_queue_var(name, net); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); free(label); delete[] name; } void compile_var_cobject(char*label, char*name) { vvp_net_t*net = new vvp_net_t; if (vpip_peek_current_scope()->is_automatic()) { vvp_fun_signal_object_aa*tmp = new vvp_fun_signal_object_aa(1); net->fil = tmp; net->fun = tmp; } else { net->fil = 0; net->fun = new vvp_fun_signal_object_sa(1); } define_functor_symbol(label, net); vpiHandle obj = vpip_make_cobject_var(name, net); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); free(label); delete[] name; } /* * A variable is a special functor, so we allocate that functor and * write the label into the symbol table. */ void compile_variable(char*label, char*name, int msb, int lsb, int vpi_type_code, bool signed_flag, bool local_flag) { unsigned wid = ((msb > lsb)? msb-lsb : lsb-msb) + 1; vvp_net_t*net = new vvp_net_t; if (vpip_peek_current_scope()->is_automatic()) { vvp_fun_signal4_aa*tmp = new vvp_fun_signal4_aa(wid); net->fil = tmp; net->fun = tmp; } else if (vpi_type_code == vpiIntVar) { net->fil = new vvp_wire_vec4(wid, BIT4_0); net->fun = new vvp_fun_signal4_sa(wid); } else { net->fil = new vvp_wire_vec4(wid, BIT4_X); net->fun = new vvp_fun_signal4_sa(wid); } vvp_signal_value*vfil = dynamic_cast(net->fil); define_functor_symbol(label, net); vpiHandle obj = 0; if (! local_flag) { /* Make the vpiHandle for the reg. */ switch (vpi_type_code) { case vpiLogicVar: obj = vpip_make_var4(name, msb, lsb, signed_flag, net); break; case vpiIntegerVar: obj = vpip_make_int4(name, msb, lsb, net); break; case vpiIntVar: // This handles all the atom2 int types obj = vpip_make_int2(name, msb, lsb, signed_flag, net); break; default: fprintf(stderr, "internal error: %s: vpi_type_code=%d\n", name, vpi_type_code); break; } assert(obj); compile_vpi_symbol(label, obj); } // If the signal has a name, then it goes into the current // scope as a signal. if (name) { if (obj) vpip_attach_to_current_scope(obj); if (!vpip_peek_current_scope()->is_automatic()) { vvp_vector4_t tmp; vfil->vec4_value(tmp); schedule_init_vector(vvp_net_ptr_t(net,0), tmp); } } free(label); delete[] name; } vvp_net_t* create_constant_node(const char*val_str) { if (c4string_test(val_str)) { vvp_net_t*net = new vvp_net_t; net->fun = new vvp_fun_bufz; schedule_init_vector(vvp_net_ptr_t(net,0), c4string_to_vector4(val_str)); return net; } if (c8string_test(val_str)) { vvp_net_t*net = new vvp_net_t; net->fun = new vvp_fun_bufz; schedule_init_vector(vvp_net_ptr_t(net,0), c8string_to_vector8(val_str)); return net; } if (crstring_test(val_str)) { vvp_net_t*net = new vvp_net_t; net->fun = new vvp_fun_bufz; schedule_init_vector(vvp_net_ptr_t(net,0), crstring_to_double(val_str)); return net; } return 0; } class base_net_resolv : public resolv_list_s { public: explicit base_net_resolv(char*ref_label, vvp_array_t array, __vpiScope*scope, char*my_label, char*name, unsigned array_addr, bool local_flag) : resolv_list_s(ref_label) { my_label_ = my_label; array_ = array; name_ = name; scope_ = scope; array_addr_ = array_addr; local_flag_ = local_flag; } protected: char*my_label_; vvp_array_t array_; char*name_; __vpiScope*scope_; unsigned array_addr_; bool local_flag_; }; class __compile_net_resolv : public base_net_resolv { public: explicit __compile_net_resolv(char*ref_label, vvp_array_t array, __vpiScope*scope, char*my_label, char*name, int msb, int lsb, unsigned array_addr, int vpi_type_code, bool signed_flag, bool local_flag) : base_net_resolv(ref_label, array, scope, my_label, name, array_addr, local_flag) { msb_ = msb; lsb_ = lsb; vpi_type_code_ = vpi_type_code; signed_flag_ = signed_flag; } ~__compile_net_resolv() { } bool resolve(bool message_flag); private: int msb_, lsb_; int vpi_type_code_; bool signed_flag_; }; /* * Here we handle .net records from the vvp source: * * .net , , , ; * .net/s , , , ; * .net8 , , , ; * .net8/s , , , ; * * Create a VPI handle to represent it, and fill that handle in with * references into the net. */ static void do_compile_net(vvp_net_t*node, vvp_array_t array, __vpiScope*scope, char*my_label, char*name, int msb, int lsb, unsigned array_addr, int vpi_type_code, bool signed_flag, bool local_flag) { unsigned wid = ((msb > lsb)? msb-lsb : lsb-msb) + 1; assert(node); vvp_wire_base*vsig = dynamic_cast(node->fil); if (vsig == 0) { switch (vpi_type_code) { case vpiIntVar: vsig = new vvp_wire_vec4(wid,BIT4_0); break; case vpiLogicVar: vsig = new vvp_wire_vec4(wid,BIT4_Z); break; case -vpiLogicVar: vsig = new vvp_wire_vec8(wid); break; } assert(vsig); node->fil = vsig; } vpiHandle obj = 0; if (! local_flag) { /* Make the vpiHandle for the reg. */ obj = vpip_make_net4(scope, name, msb, lsb, signed_flag, node); /* This attaches the label to the vpiHandle */ compile_vpi_symbol(my_label, obj); } #ifdef CHECK_WITH_VALGRIND else pool_local_net(node); #endif // REMOVE ME! Giving the net a label is a legacy of the times // when the .net was a functor of its own. In the long run, we // must fix the code generator to not rely on the label of the // .net, then we will remove that label. define_functor_symbol(my_label, node); if (array) array->attach_word(array_addr, obj); else if (obj) vpip_attach_to_scope(scope,obj); free(my_label); delete[] name; } static void __compile_net(char*label, char*name, char*array_label, unsigned long array_addr, int msb, int lsb, int vpi_type_code, bool signed_flag, bool local_flag, unsigned argc, struct symb_s*argv) { vvp_array_t array = array_label? array_find(array_label) : 0; assert(array_label ? array!=0 : true); free(array_label); assert(argc == 1); vvp_net_t*node = vvp_net_lookup(argv[0].text); #if 1 if (node == 0) { /* No existing net, but the string value may be a constant. In that case, we will wind up generating a bufz node that can carry the constant value. NOTE: This is a hack! The code generator should be fixed so that this is no longer needed. */ node = create_constant_node(argv[0].text); } #endif if (node == 0) { __vpiScope*scope = vpip_peek_current_scope(); __compile_net_resolv*res = new __compile_net_resolv(argv[0].text, array, scope, label, name, msb, lsb, array_addr, vpi_type_code, signed_flag, local_flag); resolv_submit(res); free(argv); return; } assert(node); __vpiScope*scope = vpip_peek_current_scope(); do_compile_net(node, array, scope, label, name, msb, lsb, array_addr, vpi_type_code, signed_flag, local_flag); free(argv[0].text); free(argv); } bool __compile_net_resolv::resolve(bool msg_flag) { vvp_net_t*node = vvp_net_lookup(label()); if (node == 0) { if (msg_flag) cerr << "Unable to resolve label " << label() << endl; return false; } do_compile_net(node, array_, scope_, my_label_, name_, msb_, lsb_, array_addr_, vpi_type_code_, signed_flag_, local_flag_); return true; } void compile_net(char*label, char*name, int msb, int lsb, int vpi_type_code, bool signed_flag, bool local_flag, unsigned argc, struct symb_s*argv) { __compile_net(label, name, 0, 0, msb, lsb, vpi_type_code, signed_flag, local_flag, argc, argv); } void compile_netw(char*label, char*array_label, unsigned long array_addr, int msb, int lsb, int vpi_type_code, bool signed_flag, unsigned argc, struct symb_s*argv) { __compile_net(label, 0, array_label, array_addr, msb, lsb, vpi_type_code, signed_flag, false, argc, argv); } class __compile_real_net_resolv : public base_net_resolv { public: explicit __compile_real_net_resolv(char*ref_label, vvp_array_t array, __vpiScope*scope, char*my_label, char*name, unsigned array_addr, bool local_flag) : base_net_resolv(ref_label, array, scope, my_label, name, array_addr, local_flag) { } ~__compile_real_net_resolv() { } bool resolve(bool message_flag); private: }; static void __compile_real_net2(vvp_net_t*node, vvp_array_t array, __vpiScope*scope, char*my_label, char*name, unsigned array_addr, bool local_flag) { vvp_wire_base*fil = dynamic_cast (node->fil); if (fil == 0) { fil = new vvp_wire_real; node->fil = fil; } vpiHandle obj = 0; if (!local_flag) { obj = vpip_make_real_net(scope, name, node); compile_vpi_symbol(my_label, obj); } #ifdef CHECK_WITH_VALGRIND else pool_local_net(node); #endif // REMOVE ME! Giving the net a label is a legacy of the times // when the .net was a functor of its own. In the long run, we // must fix the code generator to not rely on the label of the // .net, then we will remove that label. define_functor_symbol(my_label, node); if (array) array->attach_word(array_addr, obj); else if (obj) vpip_attach_to_scope(scope, obj); free(my_label); delete[] name; } static void __compile_real(char*label, char*name, char*array_label, unsigned long array_addr, int msb, int lsb, bool local_flag, unsigned argc, struct symb_s*argv) { assert(msb == 0 && lsb == 0); vvp_array_t array = array_label ? array_find(array_label) : 0; assert(array_label ? array!=0 : true); free(array_label); assert(argc == 1); vvp_net_t*node = vvp_net_lookup(argv[0].text); if (node == 0) { /* No existing net, but the string value may be a constant. In that case, we will wind up generating a bufz node that can carry the constant value. */ node = create_constant_node(argv[0].text); } __vpiScope*scope = vpip_peek_current_scope(); if (node == 0) { __compile_real_net_resolv*res = new __compile_real_net_resolv(argv[0].text, array, scope, label, name, array_addr, local_flag); resolv_submit(res); free(argv); return; } assert(node); __compile_real_net2(node, array, scope, label, name, array_addr, local_flag); free(argv[0].text); free(argv); } bool __compile_real_net_resolv::resolve(bool msg_flag) { vvp_net_t*node = vvp_net_lookup(label()); if (node == 0) { if (msg_flag) cerr << "Unable to resolve label " << label() << endl; return false; } __compile_real_net2(node, array_, scope_, my_label_, name_, array_addr_, local_flag_); return true; } void compile_net_real(char*label, char*name, int msb, int lsb, bool local_flag, unsigned argc, struct symb_s*argv) { __compile_real(label, name, 0, 0, msb, lsb, local_flag, argc, argv); } void compile_netw_real(char*label, char*array_label, unsigned long array_addr, int msb, int lsb, unsigned argc, struct symb_s*argv) { __compile_real(label, 0, array_label, array_addr, msb, lsb, false, argc, argv); }