VHPIDIRECT Demo

The following sources show how to pass different types of data to/from VHDL and C through VHPIDIRECT.

Note

ghdl.h is a reference of GHDL’s ABI, which can be imported to easily convert data types. However, the ABI is not settled, so it might change without prior notice.

#!/usr/bin/env sh

cd "$(dirname $0)"

set -e

ghdl -a -O0 -g tb.vhd
ghdl -e -O0 -g -Wl,-I./ -Wl,main.c tb &&
./tb
#valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt ./tb &&
#cat valgrind-out.txt | grep -A 4 "LEAK SUMMARY"
use std.textio.line;

library ieee;
use ieee.std_logic_1164.all;

entity tb is
end;

architecture arch of tb is

  type rec_t is record
    r_char: character;
    r_int : integer;
  end record;

  type enum_t is (standby, start, busy, done);

  type int_vec_t is array(natural range <>) of integer;
  type real_vec_t is array(natural range <>) of real;
  type bool_vec_t is array(natural range <>) of boolean;
  type time_vec_t is array(natural range <>) of time;
  type rec_vec_t is array(natural range <>) of rec_t;
  type enum_vec_t is array(natural range <>) of enum_t;

  type real_2vec_t is array (natural range <>, natural range <>) of real;

  type int_2vec_t is array(natural range <>, natural range <>) of integer;
  type int_3vec_t is array(natural range <>, natural range <>, natural range <>) of integer;
begin
  process

    procedure testCinterface(
      v_logic     : std_logic   := 'H';
      v_ulogic    : std_ulogic  := 'Z';
      v_char      : character   := 'k';
      v_int       : integer     := -6;
      v_nat       : natural     := 9;
      v_pos       : positive    := 3;
      v_real      : real        := 3.34;
      v_bool      : boolean     := true;
      v_bit       : bit         := '1';
      v_time      : time        := 20 ns;
      v_rec       : rec_t       := ('y', 5);
      v_enum      : enum_t      := busy;
      v_str       : string      := "hellostr";
      v_vec_int   : int_vec_t   := (11, 22, 33, 44, 55);
      v_vec_real  : real_vec_t  := (0.5, 1.75, 3.33, -0.125, -0.67, -2.21);
      v_vec_bool  : bool_vec_t  := (false, true, true, false);
      v_vec_bit   : bit_vector  := ('1', '0', '1', '0');
      v_vec_time  : time_vec_t  := (1 ns, 50 ps, 1.34 us);
      v_vec_rec   : rec_vec_t   := (('x', 17),('y', 25));
      v_vec_enum  : enum_vec_t  := (start, busy, standby);
      v_2vec_real : real_2vec_t := ((0.1, 0.25, 0.5),(3.33, 4.25, 5.0));
      v_mat_int   : int_2vec_t  := ((11, 22, 33), (44, 55, 66));
      v_3d_int   : int_3vec_t   := ( ((11, 22, 33), (44, 55, 66)), ((77, 88, 99), (110, 121, 132)) )
    ) is
    begin assert false report "VHPIDIRECT testCinterface" severity failure; end;
    attribute foreign of testCinterface : procedure is "VHPIDIRECT testCinterface";

    function getString return string is
    begin assert false report "VHPIDIRECT getString" severity failure; end;
    attribute foreign of getString : function is "VHPIDIRECT getString";

    function getIntVec return int_vec_t is
    begin assert false report "VHPIDIRECT getIntVec" severity failure; end;
    attribute foreign of getIntVec : function is "VHPIDIRECT getIntVec";

    function getIntMat return int_2vec_t is
    begin assert false report "VHPIDIRECT getIntMat" severity failure; end;
    attribute foreign of getIntMat : function is "VHPIDIRECT getIntMat";

    function getInt3d return int_3vec_t is
    begin assert false report "VHPIDIRECT getInt3d" severity failure; end;
    attribute foreign of getInt3d : function is "VHPIDIRECT getInt3d";

    function getLine return line is
    begin assert false report "VHPIDIRECT getLine" severity failure; end;
    attribute foreign of getLine : function is "VHPIDIRECT getLine";

    constant g_str: string := getString;
    constant g_int_vec: int_vec_t := getIntVec;
    constant g_int_mat: int_2vec_t := getIntMat;
    constant g_int_3d: int_3vec_t := getInt3d;

    variable g_line: line := getLine;

    function getLogicValue(logic : std_logic) return integer is
    begin assert false report "VHPIDIRECT getLogicValue" severity failure; end;
    attribute foreign of getLogicValue : function is "VHPIDIRECT getLogicIntValue";

    function getUlogicValue(logic : std_ulogic) return integer is
    begin assert false report "VHPIDIRECT getUlogicValue" severity failure; end;
    attribute foreign of getUlogicValue : function is "VHPIDIRECT getLogicIntValue";

    function getBitValue(bitVal : bit) return integer is
    begin assert false report "VHPIDIRECT getBitValue" severity failure; end;
    attribute foreign of getBitValue : function is "VHPIDIRECT getLogicIntValue";

    procedure freeCPointers is
    begin assert false report "VHPIDIRECT freeCPointers" severity failure; end;
    attribute foreign of freeCPointers : procedure is "VHPIDIRECT freePointers";
    
    variable spareInt: integer;
  begin

    testCinterface(
      v_logic     => 'H',
      v_ulogic    => 'Z',
      v_char      => 'k',
      v_int       => -6,
      v_nat       => 9,
      v_pos       => 3,
      v_real      => 3.34,
      v_bool      => true,
      v_bit       => '1',
      v_time      => 20 ns,
      v_rec       => ('y', 5),
      v_enum      => busy,
      v_str       => "hellostr",
      v_vec_int   => (11, 22, 33, 44, 55),
      v_vec_real  => (0.5, 1.75, 3.33, -0.125, -0.67, -2.21),
      v_vec_bool  => (false, true, true, false),
      v_vec_bit   => ('1', '0', '1', '0'),
      v_vec_time  => (1 ns, 50 ps, 1.34 us),
      v_vec_rec   => (('x', 17),('y', 25)),
      v_vec_enum  => (start, busy, standby),
      v_2vec_real => ((0.1, 0.25, 0.5),(3.33, 4.25, 5.0)),
      v_mat_int   =>  ((11, 22, 33), (44, 55, 66)),
      v_3d_int    => ( ((11, 22, 33), (44, 55, 66)), ((77, 88, 99), (110, 121, 132)) )
    );

    report "g_str'length: " & integer'image(g_str'length) severity note;
    if g_str'length /= 0 then
      report "g_str: " & g_str severity note;
    end if;
    report "string: " & getString severity note;--g_str results from calling getString(), calling it again means a malloc'd pointer can be lost.

    report "g_int_vec'length: " & integer'image(g_int_vec'length) severity note;
    for x in g_int_vec'range loop
      report integer'image(x) & ": " & integer'image(g_int_vec(x)) severity note;
      assert g_int_vec(x) = 11*(x+1) severity warning;
    end loop;

    report "g_line: " & g_line.all severity note;
    report "getLine: " & getLine.all severity note;--g_line results from calling getLine(), calling it again means a malloc'd pointer can be lost.
    assert getLine.all = "HELLO WORLD" severity failure;

    assert 0 = getLogicValue('U') severity error;
    assert 1 = getLogicValue('X') severity error;
    assert 2 = getLogicValue('0') severity error;
    assert 3 = getLogicValue('1') severity error;
    assert 4 = getLogicValue('Z') severity error;
    assert 5 = getLogicValue('W') severity error;
    assert 6 = getLogicValue('L') severity error;
    assert 7 = getLogicValue('H') severity error;
    assert 8 = getLogicValue('-') severity error;

    assert 0 = getUlogicValue('U') severity error;
    assert 1 = getUlogicValue('X') severity error;
    assert 2 = getUlogicValue('0') severity error;
    assert 3 = getUlogicValue('1') severity error;
    assert 4 = getUlogicValue('Z') severity error;
    assert 5 = getUlogicValue('W') severity error;
    assert 6 = getUlogicValue('L') severity error;
    assert 7 = getUlogicValue('H') severity error;
    assert 8 = getUlogicValue('-') severity error;

    assert 0 = getBitValue('0') severity error;
    assert 1 = getBitValue('1') severity error;
    
    spareInt := 0;
    report "g_int_mat'length: " & integer'image(g_int_mat'length) severity note;
    for i in g_int_mat'range(1) loop
      for j in g_int_mat'range(2) loop
        spareInt := spareInt + 1;
        report "Asserting Mat [" & integer'image(i) & "," & integer'image(j) & "]: " & integer'image(g_int_mat(i, j)) severity note;
        assert g_int_mat(i, j) = 11*spareInt severity error;
      end loop ;
    end loop ;

    spareInt := 0;
    report "g_int_3d'length: " & integer'image(g_int_3d'length) severity note;
    for i in g_int_3d'range(1) loop
      for j in g_int_3d'range(2) loop
        for k in g_int_3d'range(3) loop
          spareInt := spareInt + 1;
          report "Asserting 3D [" & integer'image(i) & "," & integer'image(j) & "," & integer'image(k) & "]: " & integer'image(g_int_3d(i, j, k)) severity note;
          assert g_int_3d(i, j, k) = 11*spareInt severity error;
          end loop;
      end loop ;
    end loop ;

    freeCPointers;
    report "No errors/failures. Concluding testbench." severity note;
    wait;
  end process;
end;
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>

#include <ghdl.h>

typedef struct rec_t {
  char    r_char;
  int32_t r_int;
} rec_t;

typedef enum {standby, start, busy, done} enum_t;

int32_t* vec;
bounds_t* vec_bounds;
int32_t* mat;
bounds_t* mat_bounds;
int32_t* d3_ptr;
bounds_t* d3_bounds;
int* len;
int* len2;
int* len3;
bounds_t* string_bounds;
ghdl_AccNaturalDimArr_t* line;

int getFlatArrayIndex(int* dimIndex, int* lens, int dims){
  if(dims == 1){
    return dimIndex[0];
  }
  else{
    return dimIndex[dims-1] + (lens[dims-1]*getFlatArrayIndex(dimIndex, lens, dims-1));
  }
}

void testCinterface(
  char     v_logic,
  char     v_ulogic,
  char     v_char,
  int32_t  v_int,
  uint32_t v_nat,
  uint32_t v_pos,
  double   v_real,
  bool     v_bool,
  bool     v_bit,
  int64_t  v_time,
  rec_t*   v_rec,
  uint8_t  v_enum,
  ghdl_NaturalDimArr_t* v_str,
  ghdl_NaturalDimArr_t* v_vec_int,
  ghdl_NaturalDimArr_t* v_vec_real,
  ghdl_NaturalDimArr_t* v_vec_bool,
  ghdl_NaturalDimArr_t* v_vec_bit,
  ghdl_NaturalDimArr_t* v_vec_phy,
  ghdl_NaturalDimArr_t* v_vec_rec,
  ghdl_NaturalDimArr_t* v_vec_enum,
  ghdl_NaturalDimArr_t* v_2vec_real,
  ghdl_NaturalDimArr_t* v_mat_int,
  ghdl_NaturalDimArr_t* v_3d_int
) {
  assert(v_logic == HDL_H);
  printf("v_logic  : %c\n", HDL_LOGIC_STATE[v_logic]);

  assert(v_ulogic == HDL_Z);
  printf("v_ulogic : %c\n", HDL_LOGIC_STATE[v_ulogic]);

  assert(v_char == 'k');
  printf("v_char : %c\n", v_char);

  assert(v_int == -6);
  printf("v_int  : %d\n", v_int);

  assert(v_nat == 9);
  printf("v_nat  : %d\n", v_nat);

  assert(v_pos == 3);
  printf("v_pos  : %d\n", v_pos);

  assert(v_real == 3.34);
  printf("v_real : %f\n", v_real);

  assert(v_bool == true);
  printf("v_bool : %d\n", v_bool);

  assert(v_bit == true);
  printf("v_bit  : %d\n", v_bit);

  assert(v_time == 20e6);
  printf("v_time : %ld\n", v_time);

  assert(v_rec != NULL);
  assert(v_rec->r_char == 'y');
  assert(v_rec->r_int == 5);
  printf("v_rec  : %p %c %d\n", v_rec, v_rec->r_char, v_rec->r_int);

  assert(v_enum == busy);
  printf("v_enum : %d %d\n", v_enum, busy);

  char* str = ghdlToString(v_str);
  printf("v_str  : %p '%s' [%ld]\n", v_str->array, str, strlen(str));
  free(str);

  len = malloc(2 * sizeof(int));

  int32_t* vec_int;
  ghdlToArray(v_vec_int, (void**)&vec_int, len, 1);
  assert(vec_int[0] == 11);
  assert(vec_int[1] == 22);
  assert(vec_int[2] == 33);
  assert(vec_int[3] == 44);
  assert(vec_int[4] == 55);
  printf("v_vec_int  : %p [%d]\n", vec_int, len[0]);

  double* vec_real;
  ghdlToArray(v_vec_real, (void**)&vec_real, len, 1);
  assert(vec_real[0] == 0.5);
  assert(vec_real[1] == 1.75);
  assert(vec_real[2] == 3.33);
  assert(vec_real[3] == -0.125);
  assert(vec_real[4] == -0.67);
  assert(vec_real[5] == -2.21);
  printf("v_vec_real : %p [%d]\n", vec_real, len[0]);

  bool* vec_bool;
  ghdlToArray(v_vec_bool, (void**)&vec_bool, len, 1);
  assert(vec_bool[0] == 0);
  assert(vec_bool[1] == 1);
  assert(vec_bool[2] == 1);
  assert(vec_bool[3] == 0);
  printf("v_vec_bool : %p [%d]\n", vec_bool, len[0]);

  bool* vec_bit;
  ghdlToArray(v_vec_bit, (void**)&vec_bit, len, 1);
  assert(vec_bit[0] == 1);
  assert(vec_bit[1] == 0);
  assert(vec_bit[2] == 1);
  assert(vec_bit[3] == 0);
  printf("v_vec_bit  : %p [%d]\n", vec_bit, len[0]);

  int64_t* vec_phy;
  ghdlToArray(v_vec_phy, (void**)&vec_phy, len, 1);
  assert(vec_phy[0] == 1e6);
  assert(vec_phy[1] == 50e3);
  assert(vec_phy[2] == 1.34e9);
  printf("v_vec_phy  : %p [%d]\n", vec_phy, len[0]);

  rec_t* vec_rec;
  ghdlToArray(v_vec_rec, (void**)&vec_rec, len, 1);
  assert(vec_rec[0].r_char == 'x');
  assert(vec_rec[0].r_int == 17);
  assert(vec_rec[1].r_char == 'y');
  assert(vec_rec[1].r_int == 25);
  printf("v_vec_rec  : %p [%d]\n", vec_rec, len[0]);

  uint8_t* vec_enum;
  ghdlToArray(v_vec_enum, (void**)&vec_enum, len, 1);
  assert(vec_enum[0] == start);
  assert(vec_enum[1] == busy);
  assert(vec_enum[2] == standby);
  printf("v_vec_enum : %p [%d]\n", vec_enum, len[0]);

  double* vec2_real_base;
  ghdlToArray(v_2vec_real, (void**)&vec2_real_base, len, 2);
  double (*vec2_real)[len[0]] = (double(*)[len[0]])vec2_real_base;
  assert(vec2_real[0][0] == 0.1);
  assert(vec2_real[0][1] == 0.25);
  assert(vec2_real[0][2] == 0.5);
  assert(vec2_real[1][0] == 3.33);
  assert(vec2_real[1][1] == 4.25);
  assert(vec2_real[1][2] == 5.0);
  printf("v_2vec_real : %p [%d, %d]\n", vec_enum, len[1], len[0]);

  printf("\nVerify GHDL Matrix in C\n");
  printAttributes(v_mat_int, 2);
  len2 = malloc(2 * sizeof(int));

  int32_t* mat_int;
  ghdlToArray(v_mat_int, (void**)&mat_int, len2, 2);
  for (int i = 0; i < len2[0]; i++)
  {
    for (int j = 0; j < len2[1]; j++)
    { 
      int ind[] = {i, j};
      int flatIndex = getFlatArrayIndex(ind, len2, 2);
      printf("C assert: %d == (val: %d) @ [%d,%d](%d)\n", 11*(flatIndex+1), mat_int[flatIndex], i, j, flatIndex);
      assert(mat_int[flatIndex] == 11*(flatIndex+1));
    }
  }
  printf("v_mat_int  : %p [%d,%d]\n\n", mat_int, len2[0], len2[1]);

  printf("\nVerify the 3D GHDL array in C\n");
  printAttributes(v_3d_int, 3);
  len3 = malloc(3 * sizeof(int));

  int32_t* d3_int;
  ghdlToArray(v_3d_int, (void**)&d3_int, len3, 3);
  for(int i = 0; i < len3[0]; i++)
  {
    for (int j = 0; j < len3[1]; j++)
    {
      for (int k = 0; k < len3[2]; k++)
      { 
        int ind[] = {i, j, k};
        int flatIndex = getFlatArrayIndex(ind, len3, 3);
        printf("C assert: %d == (val: %d) @ [%d,%d,%d](%d)\n", 11*(flatIndex+1), d3_int[flatIndex], i, j, k, flatIndex);
        assert(d3_int[flatIndex] == 11*(flatIndex+1));
      }
    }
  }
  printf("v_3d_int  : %p [%d,%d,%d]\n\n", d3_int, len3[0], len3[1], len3[2]);

  printf("end testCinterface\n\n");
}

void freePointers(){
  free(vec);
  free(vec_bounds);
  free(mat);
  free(mat_bounds);
  free(d3_ptr);
  free(d3_bounds);
  free(string_bounds);
  free(line);
  free(len);
  free(len2);
  free(len3);
}

void getString(ghdl_NaturalDimArr_t* ptr) {
  if(string_bounds != NULL){//this handles a second call//this handles a second call
    free(string_bounds);
  }
  *ptr = ghdlFromString("HELLO WORLD");
  string_bounds = ptr->bounds;
}

void getIntVec(ghdl_NaturalDimArr_t *ptr) {//Notice how similar this is to getIntMat() which is supposedly a 2D array (it is also actually just a flat (1D) array)
  vec = malloc(2*3*sizeof(int32_t));
  int32_t len[1] = {2*3};
  int x, y;
  for ( x=0 ; x<2 ; x++ ) {
    for ( y=0 ; y<3 ; y++ ) {
      int flatIndex = x*3+y;
      vec[flatIndex] = 11*(flatIndex+1);
    }
  }
  
  *ptr = ghdlFromPointer((void *)vec, len, 1); 
  vec_bounds = ptr->bounds; 
  assert(ptr->array == vec);

  printf("\n1D Array values [%d]:\n", len[0]);
  for ( x=0 ; x<len[0] ; x++ ) {
    printf("[%d]: %d VS %d\n", x, vec[x], ((int32_t*)ptr->array)[x]);
    assert(vec[x] == ((int32_t*)ptr->array)[x]);
  }
  
}

void getIntMat(ghdl_NaturalDimArr_t* ptr){
  mat = malloc(2*3*sizeof(int32_t));
  int32_t len[2] = {2, 3};
  int x, y, ind[2];
  for ( x=0 ; x<len[0] ; x++ ) {
    ind[0] = x;
    for ( y=0 ; y<len[1] ; y++ ) {
      ind[1] = y;
      int flatIndex = getFlatArrayIndex(ind, len, 2);
      mat[flatIndex] = 11*(flatIndex+1);
    }
  }
  
  *ptr = ghdlFromPointer((void *)mat, len, 2);
  mat_bounds = ptr->bounds;
  printf("\n2D Array values [%d,%d]:\n", len[0], len[1]);
  for ( x=0 ; x<len[0] ; x++ ) {
    ind[0] = x;
    for ( y=0 ; y<len[1] ; y++ ) {
      ind[1] = y;
      int flatIndex = getFlatArrayIndex(ind, len, 2);
      printf("mat[%d][%d] = %d\t", x, y, mat[flatIndex]);
      assert(mat[flatIndex] == ((uint32_t*)ptr->array)[flatIndex]);
    }
    printf("\n");
  }
  
}

void getInt3d(ghdl_NaturalDimArr_t* ptr){
  int32_t d3[2][4][3];
  int32_t len[3] = {2, 4, 3};
  int x, y, z, ind[3];
  for ( x=0 ; x<len[0] ; x++ ) {
    ind[0] = x;
    for ( y=0 ; y<len[1] ; y++ ) {
      ind[1] = y;
      for ( z=0 ; z<len[2] ; z++ ) {
        ind[2] = z;
        int flatIndex = getFlatArrayIndex(ind, len, 3);
        d3[x][y][z] = 11*(flatIndex+1);
      }
    }
  }
  
  *ptr = ghdlFromArray((void *)d3, len, 3, sizeof(int32_t));
  d3_ptr = ptr->array;
  d3_bounds = ptr->bounds;
  printf("\n3D Array values [%d,%d,%d]:\n", len[0], len[1], len[2]);
  for ( x=0 ; x<len[0] ; x++ ) {
    ind[0] = x;
    for ( y=0 ; y<len[1] ; y++ ) {
      ind[1] = y;
      for ( z=0 ; z<len[2] ; z++ ) {
        ind[2] = z;
        int flatIndex = getFlatArrayIndex(ind, len, 3);
        printf("d3[%d][%d][%d] = %d\t", x, y, z, d3[x][y][z]);
        assert(d3[x][y][z] == ((uint32_t*)ptr->array)[flatIndex]);
      }
      printf("\n");
    }
    printf("\n");
  }
}

ghdl_AccNaturalDimArr_t* getLine() {
  if(line != NULL){//this handles a second call
    free(line);
  }
  line = ghdlAccFromString("HELLO WORLD");
  return line;
}

int getLogicIntValue(char logic){
  return 0 + logic;
}
#ifndef GHDL_TYPES_H
#define GHDL_TYPES_H

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

// Range/bounds of a dimension of an unconstrained array with dimensions of type 'natural'
typedef struct {
  int32_t left;
  int32_t right;
  int32_t dir;
  int32_t len;
} bounds_t;

// Unconstrained array with dimensions of type 'natural'
typedef struct {
  void* array;
  bounds_t* bounds;
} ghdl_NaturalDimArr_t;

// Access to an unconstrained array with 1 dimension of type 'natural'
typedef struct {
  bounds_t range;
  uint8_t array[];
} ghdl_AccNaturalDimArr_t;

/*
*  Print custom types
*/

void printAttributes(ghdl_NaturalDimArr_t* ptr, int dims) {
  printf("array: %p\n", ptr->array);
  printf("bounds: %p\n", ptr->bounds);
  int i;
  for(i = 0; i < dims; i++){
    printf("bounds[%d].left: %d\n", i+1, ptr->bounds[i].left);
    printf("bounds[%d].right: %d\n", i+1, ptr->bounds[i].right);
    printf("bounds[%d].dir: %d\n", i+1, ptr->bounds[i].dir);
    printf("bounds[%d].len: %d\n", i+1, ptr->bounds[i].len);
  }
}

/*
*  Convert a fat pointer of an unconstrained string, to a (null terminated) C string
*/

// @umarcor
char* ghdlToString(ghdl_NaturalDimArr_t* ptr) {
  assert(ptr != NULL);
  assert(ptr->bounds != NULL);
  int len = ptr->bounds[0].len;
  char* str = malloc(sizeof(char) * len + 1);
  strncpy(str, ptr->array, len);
  str[len] = '\0';
  return str;
}

// In the prototype, Bradley declares a value instead of a reference. Why?

// @bradleyharden
/*
char* ghdl_array_to_string(array_t array) {
  // Add a null character, because GHDL strings are not null-terminated
  char *string = malloc(array.range->len + 1);
  strncpy(string, array.array, array.range->len);
  string[array.range->len] = '\0';
  return string;
}
*/

/*
*  Convert a fat pointer of an uncontrained array with (up to 3) dimensions of type 'natural', to C types
*/

void ghdlToArray(ghdl_NaturalDimArr_t* ptr, void** vec, int* len, int num) {
  assert(ptr != NULL);
  assert(ptr->bounds != NULL);
  *vec = ptr->array;

  for (int i = 0; i < num; i++)
  {
    len[i] = ptr->bounds[num-i-1].len;
  }
}

/*
*  Convert a (null terminated) C string, to a fat pointer of an unconstrained string
*/

// @umarcor
/*
ghdl_NaturalDimArr_t* ghdlFromString(char* str) {
  uint32_t len = strlen(str);
  ghdl_NaturalDimArr_t* ptr = malloc(sizeof(ghdl_NaturalDimArr_t));
  ptr->array = malloc(sizeof(char) * len);
  strncpy((char*)(ptr->array), str, len);
  ptr->bounds = malloc(sizeof(bounds_t));
  bounds_t* b = ptr->bounds;
  b->dim_1.left = 1;
  b->dim_1.right = len;
  b->dim_1.dir = 0;
  b->dim_1.len = len;
  return ptr;
}
*/

// Again, the prototype I had (above) returns a reference instead of a value (Bradley's below)

// @bradleyharden
ghdl_NaturalDimArr_t ghdlFromString(char *string) {
  bounds_t *range = malloc(sizeof(bounds_t));
  assert(range != NULL);
  uint32_t len = strlen(string);
  range->left = 1;
  range->right = len;
  range->dir = 0;
  range->len = len;
  // Don't bother copying the string, because GHDL will do that anyway
  return (ghdl_NaturalDimArr_t){.array=string, .bounds=range};
}

// @RocketRoss
/*
*   Helper to setup the bounds_t for ghdlFromArray
*/

void ghdlSetRange(bounds_t* r, int len, bool reversed){
  if(!reversed){//to
    r->left = 0;
    r->right = len-1;
    r->dir = 0;
    r->len = len;
  }
  else{//downto
    r->left = len-1;
    r->right = 0;
    r->dir = 1;
    r->len = len;
  }
}

// @RocketRoss
/*
*  Convert C types representing an unconstrained array with a dimension of type 'natural', to a fat pointer
*/

ghdl_NaturalDimArr_t ghdlFromPointer(void* vec, int* len, int dims) {//handled malloc'd pointer in freeCPointers()
  bounds_t* b = malloc(sizeof(bounds_t)*dims);
  assert(b != NULL);

  for (int i = 0; i < dims; i++)
  {
    ghdlSetRange(b+i, len[i], false);
  }
  
  void *a = vec;
  return (ghdl_NaturalDimArr_t){.array= a, .bounds=b};
}

ghdl_NaturalDimArr_t ghdlFromArray(void* vec, int* len, int dims, int sizeOfDataType) {//handled malloc'd pointer in freeCPointers()
  bounds_t* b = malloc(sizeof(bounds_t)*dims);
  int totalSize = 1;
  for (int i = 0; i < dims; i++)
  {
    totalSize *= len[i];
    ghdlSetRange(b+i, len[i], false);
  }

  void *a = malloc(sizeOfDataType * totalSize);
  memcpy(a, vec, sizeOfDataType * totalSize);

  return (ghdl_NaturalDimArr_t){.array= a, .bounds=b};
}

/*
*  Convert an access to an unconstrained string, to a (null terminated) C string
*/

char* ghdlAccToString(ghdl_AccNaturalDimArr_t *line) {//TODO Test //TODO handle malloc'd pointer
  // Add a null character, because GHDL strings are not null-terminated
  char *string = malloc(line->range.len + 1);
  strncpy(string, line->array, line->range.len);
  string[line->range.len] = '\0';
}

/*
*  Convert C types representing an unconstrained array with a dimension of type 'natural', to an access
*/

// TODO: support 2 and 3 dimensions
ghdl_AccNaturalDimArr_t* ghdlAccFromArray(uint32_t length, size_t bytes) {//TODO handle malloc'd pointer
  ghdl_AccNaturalDimArr_t *access = malloc(sizeof(ghdl_AccNaturalDimArr_t) + length * bytes);
  assert(access != NULL);
  access->range.left = 0;
  access->range.right = length - 1;
  access->range.dir = 0;
  access->range.len = length;
  return access;
}

/*
*  Convert a (null terminated) C string, to an access to an unconstrained string
*/

/*
// @umarcor
ghdl_AccNaturalDimArr_t* ghdlLineFromString(char *str) {
  uint32_t len = strlen(str);
  ghdl_AccNaturalDimArr_t *line = malloc(sizeof(ghdl_AccNaturalDimArr_t) + sizeof(char) * len);
  line->bounds.left = 1;
  line->bounds.right = len;
  line->bounds.dir = 0;
  line->bounds.len = len;
  strncpy(line->array, str, len);
  return line;
}
*/

// @bradleyharden
ghdl_AccNaturalDimArr_t* ghdlAccFromString(char *string) {
  uint32_t length = strlen(string);
  ghdl_AccNaturalDimArr_t *line = ghdlAccFromArray(length, 1);
  // New access objects default to numbering from 0,
  // but VHDL strings must be numbered from 1
  line->range.left++;
  line->range.right++;
  // Don't copy the null termination
  strncpy(line->array, string, length);
  return line;
}

/*  
*   Handle C char for the appropriate values in std_ulogic and std_logic.
*/

// @RocketRoss
static const char HDL_LOGIC_STATE[] = { 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'};

enum HDL_LOGIC_CHAR {
HDL_U = 0,
HDL_X = 1,
HDL_0 = 2,
HDL_1 = 3,
HDL_Z = 4,
HDL_W = 5,
HDL_L = 6,
HDL_H = 7,
HDL_D = 8,
};

#endif