From aea6ec4a369c649478430ca76a12b4443f249a2e Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Mon, 1 Apr 2024 14:57:19 +0100 Subject: [PATCH] Add basic support for VHPI foreign subprograms --- src/jit/jit-ffi.c | 37 ++ src/symbols.txt | 2 + src/vhpi/vhpi-model.c | 715 ++++++++++++++++++++++++++++++-------- src/vhpi/vhpi-util.c | 3 + src/vhpi/vhpi-util.h | 5 + test/regress/testlist.txt | 1 + test/regress/vhpi14.vhd | 48 +++ test/vhpi/Makemodule.am | 3 +- test/vhpi/vhpi14.c | 225 ++++++++++++ test/vhpi/vhpi_test.c | 1 + test/vhpi/vhpi_test.h | 1 + 11 files changed, 895 insertions(+), 146 deletions(-) create mode 100644 test/regress/vhpi14.vhd create mode 100644 test/vhpi/vhpi14.c diff --git a/src/jit/jit-ffi.c b/src/jit/jit-ffi.c index e9dd373f..3233fcb0 100644 --- a/src/jit/jit-ffi.c +++ b/src/jit/jit-ffi.c @@ -24,6 +24,7 @@ #include "option.h" #include "thread.h" #include "type.h" +#include "vhpi/vhpi-util.h" #include #include @@ -280,6 +281,23 @@ static void jit_internal_entry(jit_func_t *f, jit_anchor_t *caller, thread->anchor = NULL; } +static void jit_vhpi_entry(jit_func_t *f, jit_anchor_t *caller, + jit_scalar_t *args, tlab_t *tlab) +{ + jit_thread_local_t *thread = jit_attach_thread(caller); + + vhpiHandleT handle = *jit_get_privdata_ptr(f->jit, f); + if (unlikely(handle == NULL)) { + // JIT has been reset, need to elaborate again + f->entry = jit_interp; + jit_interp(f, caller, args, tlab); + } + else + vhpi_call_foreign(handle, args, tlab); + + thread->anchor = NULL; +} + static void jit_ghdl_entry(jit_func_t *f, jit_anchor_t *caller, jit_scalar_t *args, tlab_t *tlab) { @@ -468,6 +486,25 @@ void jit_bind_foreign(jit_func_t *f, const char *spec, size_t length, assert(f->entry != jit_ghdl_entry); // Should only be called once f->entry = jit_ghdl_entry; } + else if (strcmp(p, "VHPI") == 0) { + const char *obj_lib = strtok(NULL, " "); + if (obj_lib == NULL) + jit_msg(NULL, DIAG_FATAL, "missing object library name"); + + const char *model = strtok(NULL, " "); + if (model == NULL) + jit_msg(NULL, DIAG_FATAL, "missing model name"); + + vhpiHandleT handle = vhpi_bind_foreign(obj_lib, model, where); + if (handle == NULL) + jit_msg(NULL, DIAG_FATAL, "foreign subprogram %s/%s not registered", + obj_lib, model); + + *jit_get_privdata_ptr(f->jit, f) = handle; + + assert(f->entry != jit_vhpi_entry); // Should only be called once + f->entry = jit_vhpi_entry; + } else if (strcmp(p, "INTERNAL") == 0) { p = strtok(NULL, " "); diff --git a/src/symbols.txt b/src/symbols.txt index 3cd3598b..6aea36fb 100644 --- a/src/symbols.txt +++ b/src/symbols.txt @@ -99,6 +99,7 @@ vhpi_enable_cb; vhpi_get; vhpi_get_cb_info; + vhpi_get_foreignf_info; vhpi_get_next_time; vhpi_get_next_time; vhpi_get_phys; @@ -113,6 +114,7 @@ vhpi_printf; vhpi_put_value; vhpi_register_cb; + vhpi_register_foreignf; vhpi_release_handle; vhpi_remove_cb; vhpi_scan; diff --git a/src/vhpi/vhpi-model.c b/src/vhpi/vhpi-model.c index 6aa5c1f6..75010634 100644 --- a/src/vhpi/vhpi-model.c +++ b/src/vhpi/vhpi-model.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2014-2022 Nick Gasson +// Copyright (C) 2014-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -154,6 +154,7 @@ typedef struct tag_typeDecl { vhpiBooleanT IsUnconstrained; bool homogeneous; bool wrapped; + uint8_t size; } c_typeDecl; typedef struct { @@ -243,8 +244,29 @@ typedef struct { DEF_CLASS(sigDecl, vhpiSigDeclK, objDecl.decl.object); typedef struct { - c_objDecl objDecl; - vhpiIntT Position; + c_vhpiObject object; + tree_t tree; + vhpiObjectListT Params; +} c_subpDecl; + +typedef struct { + c_subpDecl subpDecl; + c_typeDecl *ReturnType; + vhpiBooleanT IsPure; +} c_funcDecl; + +DEF_CLASS(funcDecl, vhpiFuncDeclK, subpDecl.object); + +typedef struct { + c_subpDecl subpDecl; +} c_procDecl; + +DEF_CLASS(procDecl, vhpiProcDeclK, subpDecl.object); + +typedef struct { + c_objDecl objDecl; + vhpiIntT Position; + unsigned argslot; } c_interfaceDecl; typedef struct { @@ -266,6 +288,20 @@ typedef struct { DEF_CLASS(genericDecl, vhpiGenericDeclK, interface.objDecl.decl.object); +typedef struct { + c_interfaceDecl interface; + vhpiModeT Mode; +} c_constParamDecl; + +DEF_CLASS(constParamDecl, vhpiConstParamDeclK, interface.objDecl.decl.object); + +typedef struct { + c_interfaceDecl interface; + vhpiModeT Mode; +} c_varParamDecl; + +DEF_CLASS(varParamDecl, vhpiVarParamDeclK, interface.objDecl.decl.object); + typedef struct { c_objDecl objDecl; vhpiBooleanT IsDeferred; @@ -423,6 +459,15 @@ typedef struct { DEF_CLASS(iterator, vhpiIteratorK, object); +typedef struct { + c_vhpiObject object; + vhpiForeignDataT data; + c_subpDecl *decl; + vhpiHandleT handle; +} c_foreignf; + +DEF_CLASS(foreignf, vhpiForeignfK, object); + #define HANDLE_BITS (sizeof(vhpiHandleT) * 8) #define HANDLE_MAX_INDEX ((UINT64_C(1) << (HANDLE_BITS / 2)) - 1) @@ -433,16 +478,19 @@ typedef struct { } handle_slot_t; typedef struct _vhpi_context { - c_tool *tool; - c_rootInst *root; - shash_t *strtab; - rt_model_t *model; - hash_t *objcache; - tree_t top; - jit_t *jit; - handle_slot_t *handles; - unsigned num_handles; - unsigned free_hint; + c_tool *tool; + c_rootInst *root; + shash_t *strtab; + rt_model_t *model; + hash_t *objcache; + tree_t top; + jit_t *jit; + handle_slot_t *handles; + unsigned num_handles; + unsigned free_hint; + vhpiObjectListT foreignfs; + jit_scalar_t *args; + tlab_t *tlab; } vhpi_context_t; static c_typeDecl *cached_typeDecl(type_t type, c_vhpiObject *obj); @@ -640,6 +688,9 @@ static c_objDecl *is_objDecl(c_vhpiObject *obj) case vhpiPortDeclK: case vhpiGenericDeclK: case vhpiConstDeclK: + case vhpiConstParamDeclK: + case vhpiVarParamDeclK: + case vhpiSigParamDeclK: return container_of(obj, c_objDecl, decl.object); default: return NULL; @@ -655,6 +706,20 @@ static c_objDecl *cast_objDecl(c_vhpiObject *obj) return od; } +static c_interfaceDecl *is_interfaceDecl(c_vhpiObject *obj) +{ + switch (obj->kind) { + case vhpiPortDeclK: + case vhpiGenericDeclK: + case vhpiConstParamDeclK: + case vhpiSigParamDeclK: + case vhpiVarParamDeclK: + return container_of(obj, c_interfaceDecl, objDecl.decl.object); + default: + return NULL; + } +} + static c_range *is_range(c_vhpiObject *obj) { switch (obj->kind) { @@ -666,6 +731,16 @@ static c_range *is_range(c_vhpiObject *obj) } } +static c_subpDecl *is_subpDecl(c_vhpiObject *obj) +{ + switch (obj->kind) { + case vhpiFuncDeclK: + case vhpiProcDeclK: + return container_of(obj, c_subpDecl, object); + default: + return NULL; + } +} static c_typeDecl *is_typeDecl(c_vhpiObject *obj) { switch (obj->kind) { @@ -935,6 +1010,12 @@ static void init_interfaceDecl(c_interfaceDecl *d, tree_t t, d->Position = Position; } +static void init_subpDecl(c_subpDecl *d, tree_t t) +{ + d->object.loc = *tree_loc(t); + d->tree = t ; +} + static void init_elemDecl(c_elemDecl *ed, tree_t t, c_typeDecl *Type, c_abstractRegion *ImmRegion) { @@ -964,6 +1045,7 @@ static void init_scalarTypeDecl(c_scalarTypeDecl *d, tree_t t, type_t type) { init_typeDecl(&(d->typeDecl), t, type); d->typeDecl.IsScalar = true; + d->typeDecl.size = type_bit_width(type) / 8; } static void init_compositeTypeDecl(c_compositeTypeDecl *d, tree_t t, type_t type) @@ -1261,6 +1343,17 @@ static bool init_iterator(c_iterator *it, vhpiOneToManyT type, c_vhpiObject *obj } } + c_subpDecl *subp = is_subpDecl(obj); + if (subp != NULL) { + switch (type) { + case vhpiParamDecls: + it->list = &(subp->Params); + return true; + default: + return false; + } + } + c_arrayTypeDecl *array = is_arrayTypeDecl(obj); if (array != NULL) { if (type == vhpiConstraints) { @@ -1632,6 +1725,9 @@ static void *vhpi_get_value_ptr(c_vhpiObject *obj) c_objDecl *decl = cast_objDecl(obj); assert(decl != NULL); + if (decl->decl.ImmRegion == NULL) + return NULL; + rt_scope_t *scope = vhpi_get_scope_abstractRegion(decl->decl.ImmRegion); if (*mptr_get(scope->privdata) == NULL) { vhpi_error(vhpiError, &(obj->loc), "%s has not been elaborated", @@ -2487,6 +2583,15 @@ const vhpiCharT *vhpi_get_str(vhpiStrPropertyT property, vhpiHandleT handle) } } + c_objDecl *od = is_objDecl(obj); + if (od != NULL) { + switch (property) { + case vhpiCaseNameP: + case vhpiNameP: return (vhpiStringT)istr(od->name); + default: goto unsupported; + } + } + unsupported: vhpi_error(vhpiError, &(obj->loc), "object does not have string property %s", vhpi_property_str(property)); @@ -2642,7 +2747,7 @@ int vhpi_get_value(vhpiHandleT expr, vhpiValueT *value_p) return -1; } - int size = 1, num_elems = td->numElems; + int size = td->size, num_elems = td->numElems; const unsigned char *value = NULL; switch (vhpi_get_prefix_kind(obj)) { case vhpiGenericDeclK: @@ -2675,9 +2780,32 @@ int vhpi_get_value(vhpiHandleT expr, vhpiValueT *value_p) } break; + case vhpiConstParamDeclK: + case vhpiVarParamDeclK: + { + vhpi_context_t *c = vhpi_context(); + assert(c->args != NULL); + + c_interfaceDecl *id = is_interfaceDecl(obj); + assert(id != NULL); + + if (td->wrapped) { + value = c->args[id->argslot].pointer; + num_elems = ffi_array_length(c->args[id->argslot + 2].integer); + } + else if (td->IsComposite) { + value = c->args[id->argslot].pointer; + num_elems = td->numElems; + } + else + value = (void *)&(c->args[id->argslot]); + } + break; + default: vhpi_error(vhpiError, &(obj->loc), "class kind %s cannot be used with " "vhpi_get_value", vhpi_class_str(obj->kind)); + return -1; } assert(td->IsComposite || num_elems == 1); @@ -2703,13 +2831,14 @@ int vhpi_get_value(vhpiHandleT expr, vhpiValueT *value_p) scalar = value[offset]; break; case vhpiEnumVal: -#define SIGNAL_READ_ENUM(type) \ - scalar = ((const type *)value)[offset] - FOR_ALL_SIZES(size, SIGNAL_READ_ENUM); +#define READ_ENUM(type) scalar = ((const type *)value)[offset] + FOR_ALL_SIZES(size, READ_ENUM); break; case vhpiIntVal: + scalar = ((const vhpiIntT *)value)[offset]; + break; case vhpiLongIntVal: - scalar = ((const int32_t *)value)[offset]; + scalar = ((const vhpiLongIntT *)value)[offset]; break; default: break; @@ -2789,13 +2918,13 @@ int vhpi_get_value(vhpiHandleT expr, vhpiValueT *value_p) value_p->numElems = num_elems; -#define SIGNAL_READ_ENUMV(type) do { \ +#define READ_ENUMV(type) do { \ const type *p = ((const type *)value) + offset; \ for (int i = 0; i < value_p->numElems; i++) \ value_p->value.enumvs[i] = *p++; \ } while (0) - FOR_ALL_SIZES(size, SIGNAL_READ_ENUMV); + FOR_ALL_SIZES(size, READ_ENUMV); return 0; } @@ -2833,6 +2962,24 @@ int vhpi_get_value(vhpiHandleT expr, vhpiValueT *value_p) return 0; } + case vhpiIntVecVal: + { + if (value_p->bufSize / sizeof(vhpiIntT) < num_elems) + return num_elems * sizeof(vhpiIntT); + + value_p->numElems = num_elems; + +#define READ_INTGS(type) do { \ + const type *p = ((const type *)value) + offset; \ + for (int i = 0; i < value_p->numElems; i++) \ + value_p->value.intgs[i] = *p++; \ + } while (0) + + FOR_ALL_SIZES(size, READ_INTGS); + + return 0; + } + default: vhpi_error(vhpiError, &(obj->loc), "unsupported format %d", value_p->format); @@ -2857,167 +3004,249 @@ int vhpi_put_value(vhpiHandleT handle, return 1; int offset = 0; - rt_signal_t *signal; - c_prefixedName *pn = is_prefixedName(obj); - if (pn != NULL) { - signal = vhpi_get_signal_prefixedName(pn); + rt_signal_t *signal = NULL; + c_typeDecl *td = NULL; + c_prefixedName *pn = NULL; + c_funcDecl *func = NULL; + c_varParamDecl *vpd = NULL; + if ((pn = is_prefixedName(obj))) { + if ((signal = vhpi_get_signal_prefixedName(pn)) == NULL) + return 1; + c_indexedName *in = is_indexedName(obj); if (in) offset = in->offset; + + td = pn->name.expr.Type; } + else if ((func = is_funcDecl(obj))) + td = func->ReturnType; + else if ((vpd = is_varParamDecl(obj))) + td = vpd->interface.objDecl.Type; else { c_objDecl *decl = cast_objDecl(obj); if (decl == NULL) return 1; - signal = vhpi_get_signal_objDecl(decl); + switch (obj->kind) { + case vhpiSigDeclK: + case vhpiPortDeclK: + if ((signal = vhpi_get_signal_objDecl(decl)) == NULL) + return 1; + break; + default: + break; + } + + td = decl->Type; } - if (signal == NULL) - return 1; + if (mode == vhpiSizeConstraint) { + if (func == NULL) { + vhpi_error(vhpiError, &(obj->loc), "vhpiSizeConstraint is only valid " + "for function result"); + return 1; + } + else if (!func->ReturnType->wrapped) { + vhpi_error(vhpiError, &(obj->loc), "function return type does not " + "have a size constraint"); + return 1; + } - rt_model_t *model = vhpi_context()->model; + vhpi_context_t *c = vhpi_context(); + c->args[0].pointer = tlab_alloc(c->tlab, value_p->numElems * td->size); + c->args[1].integer = 1; + c->args[2].integer = value_p->numElems; - if (!model_can_create_delta(model)) { - vhpi_error(vhpiError, &(obj->loc), "cannot create delta cycle " - "during current simulation phase"); - return 1; + return 0; } - switch (mode) { - case vhpiForcePropagate: - case vhpiDepositPropagate: - { - void *ext LOCAL = NULL, *ptr = NULL; - uint8_t byte; - union { - uint8_t uint8_t_val; - uint16_t uint16_t_val; - uint32_t uint32_t_val; - uint64_t uint64_t_val; - vhpiIntT vhpiIntT_val; - } scalar; - double real; - int num_elems = 0; - - switch (value_p->format) { - case vhpiLogicVal: - num_elems = 1; - byte = value_p->value.enumv; - ptr = &byte; - break; + void *ext LOCAL = NULL, *ptr = NULL; + uint8_t byte; + union { + uint8_t uint8_t_val; + uint16_t uint16_t_val; + uint32_t uint32_t_val; + uint64_t uint64_t_val; + int64_t signed_int; + } scalar = { .uint64_t_val = 0 }; + double real; + int num_elems = 0; - case vhpiSmallEnumVal: - num_elems = 1; - byte = value_p->value.smallenumv; - ptr = &byte; - break; + switch (value_p->format) { + case vhpiLogicVal: + num_elems = 1; + byte = value_p->value.enumv; + ptr = &byte; + break; - case vhpiEnumVal: - num_elems = 1; - ptr = &scalar; + case vhpiSmallEnumVal: + num_elems = 1; + byte = value_p->value.smallenumv; + ptr = &byte; + break; -#define SIGNAL_WRITE_ENUM(type) do { \ - scalar.type##_val = value_p->value.enumv; \ - } while (0) + case vhpiEnumVal: + num_elems = 1; + ptr = &scalar; - FOR_ALL_SIZES(signal_size(signal), SIGNAL_WRITE_ENUM); - break; +#define STORE_ENUM(type) do { \ + scalar.type##_val = value_p->value.enumv; \ + } while (0) - case vhpiCharVal: - num_elems = 1; - byte = value_p->value.ch; - ptr = &byte; - break; + FOR_ALL_SIZES(td->size, STORE_ENUM); + break; - case vhpiIntVal: - num_elems = 1; - scalar.vhpiIntT_val = value_p->value.intg; - ptr = &scalar; // Assume little endian - break; + case vhpiCharVal: + num_elems = 1; + byte = value_p->value.ch; + ptr = &byte; + break; - case vhpiRealVal: - num_elems = 1; - real = value_p->value.real; - ptr = ℜ - break; + case vhpiIntVal: + num_elems = 1; + scalar.signed_int = value_p->value.intg; + ptr = &scalar; // Assume little endian + break; - case vhpiLogicVecVal: - num_elems = value_p->bufSize / sizeof(vhpiEnumT); - ext = ptr = xmalloc(num_elems); - for (int i = 0; i < num_elems; i++) - ((uint8_t *)ext)[i] = value_p->value.enumvs[i]; - break; + case vhpiRealVal: + num_elems = 1; + real = value_p->value.real; + ptr = ℜ + break; - case vhpiSmallEnumVecVal: - num_elems = value_p->bufSize / sizeof(vhpiSmallEnumT); - ext = ptr = xmalloc(num_elems); - for (int i = 0; i < num_elems; i++) - ((uint8_t *)ext)[i] = value_p->value.smallenumvs[i]; - break; + case vhpiLogicVecVal: + num_elems = value_p->bufSize / sizeof(vhpiEnumT); + ext = ptr = xmalloc(num_elems); + for (int i = 0; i < num_elems; i++) + ((uint8_t *)ext)[i] = value_p->value.enumvs[i]; + break; - case vhpiEnumVecVal: - { - num_elems = value_p->bufSize / sizeof(vhpiEnumT); - uint8_t size = signal_size(signal); - ext = ptr = xmalloc_array(num_elems, size); + case vhpiSmallEnumVecVal: + num_elems = value_p->bufSize / sizeof(vhpiSmallEnumT); + ext = ptr = xmalloc(num_elems); + for (int i = 0; i < num_elems; i++) + ((uint8_t *)ext)[i] = value_p->value.smallenumvs[i]; + break; -#define SIGNAL_WRITE_ENUMV(type) do { \ - for (int i = 0; i < num_elems; i++) \ - ((type *)ext)[i] = value_p->value.enumvs[i]; \ - } while (0) + case vhpiEnumVecVal: + { + num_elems = value_p->bufSize / sizeof(vhpiEnumT); + ext = ptr = xmalloc_array(num_elems, td->size); - FOR_ALL_SIZES(size, SIGNAL_WRITE_ENUMV); - break; - } +#define STORE_ENUMV(type) do { \ + for (int i = 0; i < num_elems; i++) \ + ((type *)ext)[i] = value_p->value.enumvs[i]; \ + } while (0) - case vhpiStrVal: - num_elems = value_p->bufSize - 1; - ext = ptr = xmalloc(num_elems); - for (int i = 0; i < num_elems; i++) - ((vhpiCharT *)ext)[i] = value_p->value.str[i]; - break; + FOR_ALL_SIZES(td->size, STORE_ENUMV); + break; + } - case vhpiRealVecVal: - { - num_elems = value_p->bufSize / sizeof(vhpiRealT); - ext = ptr = xmalloc_array(num_elems, sizeof(double)); - for (int i = 0; i < num_elems; i++) - ((double *)ext)[i] = value_p->value.reals[i]; - break; - } + case vhpiStrVal: + num_elems = value_p->bufSize - 1; + ext = ptr = xmalloc(num_elems); + for (int i = 0; i < num_elems; i++) + ((vhpiCharT *)ext)[i] = value_p->value.str[i]; + break; - default: - vhpi_error(vhpiFailure, &(obj->loc), "value format " - "%d not supported in vhpi_put_value", - value_p->format); - return 1; - } + case vhpiRealVecVal: + { + num_elems = value_p->bufSize / sizeof(vhpiRealT); + ext = ptr = xmalloc_array(num_elems, sizeof(double)); + for (int i = 0; i < num_elems; i++) + ((double *)ext)[i] = value_p->value.reals[i]; + break; + } - if (offset + num_elems > signal_width(signal)) { - vhpi_error(vhpiError, &(obj->loc), - "too many values (%d) for signal with width %"PRIu32, - num_elems, signal_width(signal)); - return 1; - } + case vhpiIntVecVal: + { + num_elems = value_p->bufSize / sizeof(vhpiIntT); + ext = ptr = xmalloc_array(num_elems, sizeof(vhpiIntT)); - if (mode == vhpiForcePropagate) - force_signal(model, signal, ptr, offset, num_elems); - else - deposit_signal(model, signal, ptr, offset, num_elems); +#define STORE_INTGS(type) do { \ + for (int i = 0; i < num_elems; i++) \ + ((type *)ext)[i] = (int64_t)value_p->value.intgs[i]; \ + } while (0) - return 0; + FOR_ALL_SIZES(td->size, STORE_INTGS); + break; } - case vhpiRelease: - release_signal(model, signal, offset, signal_width(signal)); - return 0; - default: - vhpi_error(vhpiFailure, &(obj->loc), "mode %s not supported in " - "vhpi_put_value", vhpi_put_value_mode_str(mode)); + vhpi_error(vhpiFailure, &(obj->loc), "value format %d not supported " + "in vhpi_put_value", value_p->format); return 1; } + + if (signal != NULL) { + rt_model_t *model = vhpi_context()->model; + if (!model_can_create_delta(model)) { + vhpi_error(vhpiError, &(obj->loc), "cannot create delta cycle " + "during current simulation phase"); + return 1; + } + else if (offset + num_elems > signal_width(signal)) { + vhpi_error(vhpiError, &(obj->loc), + "too many values (%d) for signal with %d elements", + num_elems, signal_width(signal)); + return 1; + } + + switch (mode) { + case vhpiForcePropagate: + force_signal(model, signal, ptr, offset, num_elems); + return 0; + case vhpiDepositPropagate: + deposit_signal(model, signal, ptr, offset, num_elems); + return 0; + case vhpiRelease: + release_signal(model, signal, offset, signal_width(signal)); + return 0; + default: + break; + } + } + else if (func != NULL || vpd != NULL) { + vhpi_context_t *c = vhpi_context(); + assert(c->args != NULL); + + const int slot = vpd ? vpd->interface.argslot : 0; + + switch (mode) { + case vhpiForce: + case vhpiForcePropagate: + case vhpiDeposit: + case vhpiDepositPropagate: + if (td->IsScalar) { + c->args[slot].integer = scalar.uint64_t_val; + return 0; + } + else { + const int64_t length = td->wrapped + ? ffi_array_length(c->args[slot + 2].integer) + : td->numElems; + + if (offset + num_elems > length) { + vhpi_error(vhpiError, &(obj->loc), "too many values (%d) for " + "object with %"PRIi64" elements", num_elems, length); + return 1; + } + + memcpy(c->args[slot].pointer + offset * td->size, ext, + num_elems * td->size); + return 0; + } + case vhpiRelease: + return 0; // Specified to have no effect + default: + break; + } + } + + vhpi_error(vhpiFailure, &(obj->loc), "mode %s not supported in " + "vhpi_put_value", vhpi_put_value_mode_str(mode)); + return 1; } DLLEXPORT @@ -3113,9 +3342,49 @@ vhpiHandleT vhpi_create(vhpiClassKindT kind, } DLLEXPORT -int vhpi_get_foreignf_info(vhpiHandleT hdl, vhpiForeignDataT *foreignDatap) +int vhpi_get_foreignf_info(vhpiHandleT handle, vhpiForeignDataT *foreignDatap) { - VHPI_MISSING; + VHPI_TRACE("handle=%s", handle_pp(handle)); + + c_vhpiObject *obj = from_handle(handle); + if (obj == NULL) + return 1; + + c_foreignf *f = cast_foreignf(obj); + if (f == NULL) + return 1; + + *foreignDatap = f->data; + return 0; +} + +DLLEXPORT +vhpiHandleT vhpi_register_foreignf(vhpiForeignDataT *foreignDatap) +{ + VHPI_TRACE("kind=%d libraryName=%s modelName=%s", foreignDatap->kind, + foreignDatap->libraryName, foreignDatap->modelName); + + switch (foreignDatap->kind) { + case vhpiFuncF: + case vhpiProcF: + { + c_foreignf *f = new_object(sizeof(c_foreignf), vhpiForeignfK); + f->data = *foreignDatap; + + // Make a defensive copy of the passed-in strings + f->data.libraryName = (char *)new_string(foreignDatap->libraryName); + f->data.libraryName = (char *)new_string(foreignDatap->libraryName); + + vhpi_context_t *c = vhpi_context(); + APUSH(c->foreignfs, &(f->object)); + + return handle_for(&(f->object)); + } + + default: + vhpi_error(vhpiInternal, NULL, "foreign model kind not supported"); + return NULL; + } } DLLEXPORT @@ -3280,6 +3549,8 @@ static c_typeDecl *build_arrayTypeDecl(type_t type, tree_t decl, else td->ElemType = cached_typeDecl(elem, NULL); + td->composite.typeDecl.size = td->ElemType->size; + if (type_is_unconstrained(type)) { for (int i = 0; i < td->NumDimensions; i++) APUSH(td->Constraints, &(build_unconstrained()->range.object)); @@ -3527,6 +3798,9 @@ static c_typeDecl *build_typeDecl(type_t type, c_vhpiObject *obj) td->typeDecl.BaseType = cached_typeDecl(type_base_recur(type), NULL); td->isResolved = type_has_resolution(type); + td->typeDecl.IsScalar = td->typeDecl.BaseType->IsScalar; + td->typeDecl.IsComposite = td->typeDecl.BaseType->IsComposite; + unsigned nconstrs = type_constraints(type); if (nconstrs != 0) { assert(nconstrs == 1); @@ -3631,6 +3905,25 @@ static void build_genericDecl(tree_t generic, int pos, APUSH(region->decls, &(g->interface.objDecl.decl.object)); } +static c_constParamDecl *build_constParamDecl(tree_t param, int pos) +{ + c_constParamDecl *p = new_object(sizeof(c_constParamDecl), + vhpiConstParamDeclK); + init_interfaceDecl(&(p->interface), param, pos, NULL); + + p->Mode = mode_map[tree_subkind(param)]; + return p; +} + +static c_varParamDecl *build_varParamDecl(tree_t param, int pos) +{ + c_varParamDecl *p = new_object(sizeof(c_varParamDecl), vhpiVarParamDeclK); + init_interfaceDecl(&(p->interface), param, pos, NULL); + + p->Mode = mode_map[tree_subkind(param)]; + return p; +} + static void build_portDecl(tree_t port, int pos, c_abstractRegion *region) { @@ -3661,6 +3954,60 @@ static c_constDecl *build_constDecl(tree_t decl, c_abstractRegion *region) return cd; } +static void build_paramDecls(tree_t decl, int first, c_subpDecl *subp) +{ + const int nparams = tree_ports(decl); + for (int i = 0, slot = first; i < nparams; i++) { + tree_t p = tree_port(decl, i); + c_typeDecl *td = NULL; + switch (tree_class(p)) { + case C_CONSTANT: + { + c_constParamDecl *cpd = build_constParamDecl(p, i); + cpd->interface.argslot = slot; + APUSH(subp->Params, &(cpd->interface.objDecl.decl.object)); + td = cpd->interface.objDecl.Type; + } + break; + case C_VARIABLE: + { + c_varParamDecl *vpd = build_varParamDecl(p, i); + vpd->interface.argslot = slot; + APUSH(subp->Params, &(vpd->interface.objDecl.decl.object)); + td = vpd->interface.objDecl.Type; + } + break; + default: + fatal_at(tree_loc(p), "unsupported parameter class"); + } + + slot += td->wrapped ? 1 + 2*dimension_of(td->type) : 1; + } +} + +static c_funcDecl *build_funcDecl(tree_t decl) +{ + c_funcDecl *f = new_object(sizeof(c_funcDecl), vhpiFuncDeclK); + init_subpDecl(&(f->subpDecl), decl); + + build_paramDecls(decl, 1, &(f->subpDecl)); + + f->IsPure = !(tree_flags(decl) & TREE_F_IMPURE); + f->ReturnType = cached_typeDecl(type_result(tree_type(decl)), NULL); + + return f; +} + +static c_procDecl *build_procDecl(tree_t decl) +{ + c_procDecl *f = new_object(sizeof(c_funcDecl), vhpiProcDeclK); + init_subpDecl(&(f->subpDecl), decl); + + build_paramDecls(decl, 2, &(f->subpDecl)); + + return f; +} + static c_abstractRegion *build_blockStmt(tree_t t, c_abstractRegion *region) { c_blockStmt *bs = new_object(sizeof(c_blockStmt), vhpiBlockStmtK); @@ -3929,6 +4276,13 @@ static void vhpi_check_leaks(vhpi_context_t *c) void vhpi_context_free(vhpi_context_t *c) { + for (int i = 0; i < c->foreignfs.count; i++) { + c_foreignf *f = is_foreignf(c->foreignfs.items[i]); + assert(f != NULL); + drop_handle(c, f->handle); + } + ACLEAR(c->foreignfs); + if (opt_get_int(OPT_VHPI_DEBUG)) vhpi_check_leaks(c); @@ -3940,3 +4294,74 @@ void vhpi_context_free(vhpi_context_t *c) free(c->handles); free(c); } + +//////////////////////////////////////////////////////////////////////////////// +// Foreign function interface + +vhpiHandleT vhpi_bind_foreign(const char *obj_lib, const char *model, + tree_t where) +{ + vhpi_context_t *c = vhpi_context(); + for (int i = 0; i < c->foreignfs.count; i++) { + c_foreignf *f = cast_foreignf(c->foreignfs.items[i]); + if (strcmp(f->data.libraryName, obj_lib)) + continue; + else if (strcmp(f->data.modelName, model)) + continue; + + assert(tree_kind(where) == T_ATTR_SPEC); + tree_t sub = tree_ref(where); + + if (f->decl != NULL && f->decl->tree == sub) + return f->handle; + else if (f->decl != NULL) + jit_msg(NULL, DIAG_FATAL, "foreign subprogram %s/%s already bound " + "to %s", obj_lib, model, type_pp(tree_type(f->decl->tree))); + + switch (tree_kind(sub)) { + case T_FUNC_DECL: + f->decl = &(build_funcDecl(sub)->subpDecl); + break; + case T_PROC_DECL: + f->decl = &(build_procDecl(sub)->subpDecl); + break; + default: + jit_msg(NULL, DIAG_FATAL, "unsupported foreign subprogram"); + } + + return (f->handle = handle_for(&(f->object))); + } + + return NULL; +} + +void vhpi_call_foreign(vhpiHandleT handle, jit_scalar_t *args, tlab_t *tlab) +{ + c_vhpiObject *obj = from_handle(handle); + if (obj == NULL) + jit_msg(NULL, DIAG_FATAL, "called invalid foreign subprogram"); + + c_foreignf *f = is_foreignf(obj); + assert(f != NULL); + + void *orig_p0 = args[0].pointer; + + vhpi_context_t *c = vhpi_context(); + assert(c->args == NULL); + c->args = args; + c->tlab = tlab; + + vhpiHandleT subp = handle_for(&(f->decl->object)); + vhpiCbDataT data = { + .obj = subp + }; + (*f->data.execf)(&data); + + drop_handle(c, subp); + c->args = NULL; + c->tlab = NULL; + + if (f->decl->object.kind == vhpiFuncDeclK && args[0].pointer == orig_p0) + jit_msg(NULL, DIAG_FATAL, "foreign function %s did not return a value", + type_pp(tree_type(f->decl->tree))); +} diff --git a/src/vhpi/vhpi-util.c b/src/vhpi/vhpi-util.c index 14b10f29..4c55cfa1 100644 --- a/src/vhpi/vhpi-util.c +++ b/src/vhpi/vhpi-util.c @@ -318,6 +318,9 @@ vhpiFormatT vhpi_format_for_type(type_t type, const char **map_str) case T_REAL: return vhpiRealVecVal; + case T_INTEGER: + return vhpiIntVecVal; + default: break; } diff --git a/src/vhpi/vhpi-util.h b/src/vhpi/vhpi-util.h index 11765d4d..b2a197cf 100644 --- a/src/vhpi/vhpi-util.h +++ b/src/vhpi/vhpi-util.h @@ -19,6 +19,7 @@ #define _VHPI_UTIL_H #include "prim.h" +#include "jit/jit.h" #include "rt/rt.h" #ifdef __MINGW32__ @@ -51,6 +52,10 @@ bool vhpi_is_repetitive(vhpiEnumT reason); vhpiPhysT vhpi_phys_from_native(int64_t value); vhpiIntT vhpi_int_from_native(int64_t value); +vhpiHandleT vhpi_bind_foreign(const char *obj_lib, const char *model, + tree_t where); +void vhpi_call_foreign(vhpiHandleT handle, jit_scalar_t *args, tlab_t *tlab); + const char *vhpi_cb_reason_str(int reason); const char *vhpi_one_to_many_str(vhpiOneToManyT kind); const char *vhpi_one_to_one_str(vhpiOneToOneT kind); diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index a14ff86d..1e4a95b5 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -962,3 +962,4 @@ vhpi13 normal,vhpi issue873 normal array19 normal issue874 normal,2008 +vhpi14 normal,vhpi diff --git a/test/regress/vhpi14.vhd b/test/regress/vhpi14.vhd new file mode 100644 index 00000000..90dd19e8 --- /dev/null +++ b/test/regress/vhpi14.vhd @@ -0,0 +1,48 @@ +entity vhpi14 is +end entity; + +architecture test of vhpi14 is + function add2 (x : integer) return integer; + attribute foreign of add2 : function is "VHPI lib add2"; + + function popcount (x : bit_vector) return natural; + attribute foreign of popcount : function is "VHPI lib popcount"; + + type t_int_vec is array (natural range <>) of integer; + + procedure test1 (x : inout t_int_vec(1 to 4); y : in integer); + attribute foreign of test1 : procedure is "VHPI lib test1"; + + procedure test2 (x : inout t_int_vec; y : in integer); + attribute foreign of test2 : procedure is "VHPI lib test2"; + + function iota (n : natural) return t_int_vec; + attribute foreign of iota : function is "VHPI lib iota"; +begin + + p: process is + variable v1 : t_int_vec(1 to 4) := (1, 2, 3, 4); + begin + assert add2(1) = 3; + assert add2(-1) = 1; + assert add2(-10) = -8; + assert popcount("101") = 2; + assert popcount("10111") = 4; + + test1(v1, 5); + assert v1 = (6, 7, 8, 9); + + test2(v1, 1); + assert v1 = (7, 8, 9, 10); + + v1 := (-4, -3, -2, -1); + test2(v1, 2); + assert v1 = (-2, -1, 0, 1); + + assert iota(2) = (0, 1); + assert iota(4) = (0, 1, 2, 3); + + wait; + end process; + +end architecture; diff --git a/test/vhpi/Makemodule.am b/test/vhpi/Makemodule.am index bceee7f3..b0e51d0e 100644 --- a/test/vhpi/Makemodule.am +++ b/test/vhpi/Makemodule.am @@ -18,7 +18,8 @@ lib_vhpi_test_so_SOURCES = \ test/vhpi/vhpi11.c \ test/vhpi/issue762.c \ test/vhpi/vhpi12.c \ - test/vhpi/vhpi13.c + test/vhpi/vhpi13.c \ + test/vhpi/vhpi14.c lib_vhpi_test_so_CFLAGS = $(PIC_FLAG) -I$(top_srcdir)/src/vhpi $(AM_CFLAGS) lib_vhpi_test_so_LDFLAGS = -shared $(VHPI_LDFLAGS) $(AM_LDFLAGS) diff --git a/test/vhpi/vhpi14.c b/test/vhpi/vhpi14.c new file mode 100644 index 00000000..3b5cbf10 --- /dev/null +++ b/test/vhpi/vhpi14.c @@ -0,0 +1,225 @@ +// +// Copyright (C) 2024 Nick Gasson +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 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, see . +// + +#include "vhpi_test.h" + +#include +#include +#include +#include + +static void add2(const vhpiCbDataT *cb_data_p) +{ + vhpiHandleT p0 = vhpi_handle_by_index(vhpiParamDecls, cb_data_p->obj, 0); + check_error(); + fail_if(p0 == NULL); + fail_unless(vhpi_get(vhpiKindP, p0) == vhpiConstParamDeclK); + fail_unless(strcmp((char *)vhpi_get_str(vhpiNameP, p0), "X") == 0); + + vhpiValueT p0val = { .format = vhpiIntVal }; + fail_unless(vhpi_get_value(p0, &p0val) == 0); + check_error(); + + printf("add2 called p0=%p[%d]\n", p0, p0val.value.intg); + + vhpiValueT result = { + .format = vhpiIntVal, + .value = { .intg = p0val.value.intg + 2 } + }; + vhpi_put_value(cb_data_p->obj, &result, vhpiDeposit); + check_error(); + + vhpi_release_handle(p0); +} + +static void popcount(const vhpiCbDataT *cb_data_p) +{ + vhpiHandleT p0 = vhpi_handle_by_index(vhpiParamDecls, cb_data_p->obj, 0); + check_error(); + fail_if(p0 == NULL); + fail_unless(vhpi_get(vhpiKindP, p0) == vhpiConstParamDeclK); + + vhpiValueT arg = { + .format = vhpiBinStrVal, + .bufSize = 0, + .value.str = NULL + }; + int need = vhpi_get_value(p0, &arg); + check_error(); + + vhpi_printf("need %d bytes for p0 string", need); + + arg.value.str = malloc(need); + arg.bufSize = need; + fail_unless(vhpi_get_value(p0, &arg) == 0); + check_error(); + + vhpi_printf("argument %s", arg.value.str); + + vhpiValueT result = { + .format = vhpiIntVal, + .value = { .intg = 0 } + }; + + for (int i = 0; i < need; i++) { + if (arg.value.str[i] == '1') + result.value.intg++; + } + + vhpi_put_value(cb_data_p->obj, &result, vhpiDeposit); + check_error(); + + free(arg.value.str); + vhpi_release_handle(p0); +} + +static void test1(const vhpiCbDataT *cb_data_p) +{ + vhpiHandleT p0 = vhpi_handle_by_index(vhpiParamDecls, cb_data_p->obj, 0); + check_error(); + fail_if(p0 == NULL); + fail_unless(vhpi_get(vhpiKindP, p0) == vhpiVarParamDeclK); + + vhpiHandleT p1 = vhpi_handle_by_index(vhpiParamDecls, cb_data_p->obj, 1); + check_error(); + fail_if(p1 == NULL); + fail_unless(vhpi_get(vhpiKindP, p1) == vhpiConstParamDeclK); + + vhpiValueT p1_value = { .format = vhpiIntVal }; + fail_unless(vhpi_get_value(p1, &p1_value) == 0); + check_error(); + + vhpiValueT p0_value = { + .format = vhpiIntVecVal, + .bufSize = 0, + .value.intgs = NULL + }; + int need = vhpi_get_value(p0, &p0_value); + check_error(); + + vhpi_printf("need %d bytes for p0 array", need); + + p0_value.value.intgs = malloc(need * 2); + p0_value.bufSize = need; + fail_if(vhpi_get_value(p0, &p0_value) == -1); + check_error(); + + fail_unless(p0_value.numElems == 4); + + for (int i = 0; i < p0_value.numElems; i++) { + vhpi_printf("[%d] = %d + %d\n", i, p0_value.value.intgs[i], + p1_value.value.intg); + p0_value.value.intgs[i] += p1_value.value.intg; + } + + vhpi_put_value(p0, &p0_value, vhpiDeposit); + check_error(); + + fail_unless(vhpi_put_value(cb_data_p->obj, &p0_value, + vhpiSizeConstraint) == 1); + + p0_value.bufSize = need * 2; // Out of bounds + fail_if(vhpi_put_value(p0, &p0_value, vhpiDeposit) == 0); + + free(p0_value.value.intgs); + vhpi_release_handle(p0); + vhpi_release_handle(p1); +} + +static void iota(const vhpiCbDataT *cb_data_p) +{ + vhpiHandleT p0 = vhpi_handle_by_index(vhpiParamDecls, cb_data_p->obj, 0); + check_error(); + fail_if(p0 == NULL); + fail_unless(vhpi_get(vhpiKindP, p0) == vhpiConstParamDeclK); + fail_unless(strcmp((char *)vhpi_get_str(vhpiNameP, p0), "N") == 0); + + vhpiValueT p0_value = { .format = vhpiIntVal }; + fail_unless(vhpi_get_value(p0, &p0_value) == 0); + check_error(); + + vhpiValueT result = { + .format = vhpiIntVecVal, + .bufSize = p0_value.value.intg * sizeof(vhpiIntT), + .numElems = p0_value.value.intg, + }; + vhpi_put_value(cb_data_p->obj, &result, vhpiSizeConstraint); + check_error(); + + result.value.intgs = malloc(result.bufSize); + for (int i = 0; i < p0_value.value.intg; i++) + result.value.intgs[i] = i; + + vhpi_put_value(cb_data_p->obj, &result, vhpiDeposit); + check_error(); + + free(result.value.intgs); + vhpi_release_handle(p0); +} + +void vhpi14_startup(void) +{ + vhpiForeignDataT add2_data = { + .kind = vhpiFuncF, + .libraryName = "lib", + .modelName = "add2", + .execf = add2, + }; + vhpiHandleT h = vhpi_register_foreignf(&add2_data); + check_error(); + + vhpiForeignDataT popcount_data = { + .kind = vhpiFuncF, + .libraryName = "lib", + .modelName = "popcount", + .execf = popcount, + }; + vhpi_register_foreignf(&popcount_data); + check_error(); + + vhpiForeignDataT test1_data = { + .kind = vhpiFuncF, + .libraryName = "lib", + .modelName = "test1", + .execf = test1, + }; + vhpi_register_foreignf(&test1_data); + check_error(); + + vhpiForeignDataT test2_data = { + .kind = vhpiFuncF, + .libraryName = "lib", + .modelName = "test2", + .execf = test1, // Can reuse same code + }; + vhpi_register_foreignf(&test2_data); + check_error(); + + vhpiForeignDataT iota_data = { + .kind = vhpiFuncF, + .libraryName = "lib", + .modelName = "iota", + .execf = iota, + }; + vhpi_register_foreignf(&iota_data); + check_error(); + + vhpiForeignDataT check; + fail_if(vhpi_get_foreignf_info(h, &check)); + fail_unless(check.kind == vhpiFuncF); + fail_unless(strcmp(check.modelName, "add2") == 0); +} diff --git a/test/vhpi/vhpi_test.c b/test/vhpi/vhpi_test.c index 861a48ed..a490f343 100644 --- a/test/vhpi/vhpi_test.c +++ b/test/vhpi/vhpi_test.c @@ -42,6 +42,7 @@ static const vhpi_test_t tests[] = { { "issue762", issue762_startup }, { "vhpi12", vhpi12_startup }, { "vhpi13", vhpi13_startup }, + { "vhpi14", vhpi14_startup }, { NULL, NULL }, }; diff --git a/test/vhpi/vhpi_test.h b/test/vhpi/vhpi_test.h index 65cd821e..e9e2157d 100644 --- a/test/vhpi/vhpi_test.h +++ b/test/vhpi/vhpi_test.h @@ -41,6 +41,7 @@ void vhpi10_startup(void); void vhpi11_startup(void); void vhpi12_startup(void); void vhpi13_startup(void); +void vhpi14_startup(void); void issue744_startup(void); void issue762_startup(void); -- 2.39.2