Skip to content

New feature proposal: a codified dynamic type system #521

@bradleyharden

Description

@bradleyharden

I mentioned this on Gitter, but I think it might be best to create an issue to consolidate the discussion and make sure comments don't get lost.

I would like to develop a generic dynamic typing system. I'm drawing inspiration from the queue_element_type_t and trying to generalize and expose that as a codified interface. My goal is to provide a generic type mark that contains everything you need to know to encode and decode a value of that type. What I'm aiming for is something like this

type type_class_t is (scalar_type, array_type, variable_type);

type type_t is record
  p_name  : string;
  p_class : type_class_t;
  p_size  : real;
end record;

For scalar types, size represents the number of bytes to encode a value. For array types, size represents the number of bytes to encode a single element of the array. Variable types have undetermined size. We can provide a function to determine the encoded length of any type

function code_length(
  typ    : type_t;
  length : natural := 0)
  return integer
is
begin
  case type_class(typ) is
    when scalar_type =>
      return size(typ);
    when array_type =>
      return integer(ceil(size(typ) * real(length)));
    when variable_type =>
      return -1;
  end case;
end;

However, I will probably end up using a numerator and a denominator instead of a real, since the size is always a rational number. That way we can use integer division to calculate the size.

My initial thought was that you could declare all types as constants of this record type. However, to use it as a type mark would require that you always encode the entire record with the encoded data. Instead, it would be easier to implement it as

type type_t is record
  p_id : integer;
end record;

Then, we can use the type ID as an index into an array of string_ptr for getting the type name, an array of type_class_t for getting the type class, and an array of real for getting the size. This is the same strategy as msg_type_t.

Next, we can define an element

type element_t is record
  p_type     : type_t;
  p_range    : range_t;
  p_code     : string;
end record;

I'm not sure whether to keep the range as a range_t, i.e. bit_vector, or to store it as an encoded range. It probably makes more sense to store it as string(1 to 9). The simulator's internal representation of range_t will need to be at least 9 bytes anyway, and then the size doesn't vary based on the range.

You could convert between element_t and all other types with the functions to_element and from_element, which would essentially be light wrappers around encode and decode. The aliases for from_element could also be to_std_logic_vector, etc. to mimic a real type conversion.

With this record, we can reformulate queue_pkg to push and pop elements. The string representation inside the queue wouldn't change, but every push and pop would be forced through push_element and pop_element.

In fact, that brings up a separate point. Why is queue_t implemented with a single, variable length string? Why not implement it as a variable length integer_vector and treat each integer as a string_ptr to an element? Then you never have to use push_variable_string and you also know the how many elements are in the queue at any moment. To me, that seems like a much better way to implement it. Is there some reason the current implementation was chosen instead?

Anyway, element_t is great for pushing things around internally, using unconstrained function inputs and return values, but it isn't easy for users at the top level. There, I think it makes more sense to give them

type element_ptr_t is record
  p_type  : type_t;
  p_range : string_ptr;
  p_code  : string_ptr;
end record;

I'm not sure whether to use separate string pointers for the range and code here, or to combine them into a single string_ptr. Either way, this record is of fixed size and can be easily manipulated at the top level.

With element_t and element_ptr_t you could perform type conversions of the underlying coded data. To facilitate that, I would like to change the encoding of all base types to use the standard binary representation. For example, instead of using 'T' and 'F' for a boolean, use character'val(0) and character'val(1) That way, you could easily convert between boolean and a unit8 type. Or you could convert a real type into an array of 4 uint16 types. To accomplish this with real, we could use the to_real function from float_pkg, rather than the custom implementation right now.

Next, you can define array_t as an array of element_t, where array_t stores each element internally as a pointer to an encoded string. In effect, element_t acts as the container that @kraigher mentioned one time on Gitter when discussing arrays of arbitrary type.

Finally, you could define

type dynamic_t is record
  p_type  : type_t;
  p_array : array_t;
end record;

Using this, users could dynamically create a new type_t and append elements to the dynamic_t to define their own containers for custom records.

Also, dict_t could be modified to accept element_t and dynamic_t as keys and values. You could also create a list_t based on array_t with a focus on list operations rather than a general array type, like integer_array_t. I think there are many different directions you could take this.

Separately, I also have an idea for a bit_array_t. Its purpose would be to store elements of arbitrary bit width. Internally, the data would be stored as one large string. Each bit_array would store a range_t that indicates the size of each element. The get and set functions would pull out a sub string and decode it to set/get the desired bits. You could even change the range_t dynamically to change your view of the bit_array. This is similar to read_word and write_word in memory_t, but not restricted to 8-bit boundaries. @umarcor might be interested in this as the basis for co-simulation. I think it would be the most flexible option.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions