[3/3] Support up to N external memory models by umarcor · Pull Request #470 · VUnit/vunit

It seems you have reformatted many files making it harder to review.

You can check the first 11 commits: https://github.com/VUnit/vunit/pull/470/files/b6bca4993ad78c66618767f76a60739372b3e086. That's before I switched to integer_vector_ptr and before I switched the format of function signatures.

Also I do not think the integer_vector_ptr should be changed at all. It it has nothing to do with the memory model except that it is being used as a storage medium.

The memory model is exclusive to verification components. After I implemented it for the memory model only (first 11 commits), I realized that:

  • Most of the changes did only affect p_data, and the procedures/functions defined for it.
  • Having an externally defined array can be useful to share data between the testbench and the software app, without requiring VCs.
  • VCs require to use VHDL2008, but the external feature is possible with earlier versions of the standard. Implementing it in integer_vector_ptr allows to use the feature with VHDL 1993 too.

With my latest proposal, integer_vector_ptr can be used in six different contexts:

  • Default memory model.
  • Memory model bind to helper functions defined in C.
  • Memory model bind to an array defined in C, through an VHDL access type.
  • General purpose internal array.
  • General purpose array bind to helper functions.
  • General purpose array bind to an array defined in C, through an VHDL access type.

Helper C functions (read_byte, write_byte) are useful to implement communication with external processes, be it through pipes, sockets, gRPC, etc. Binding the array through an access type reduces the overhead, as assignments and reads are direct.

For example, in #465 an array is defined in C and pkg_c.vhd is used to bind helper functions. Since VCs are AXI Streams, no memory model is used explicitly in the testbench. However, the code in pkg_c.vhd is equivalent to extmem_pkg (renamed to ext_pkg in the latest commits). So, implementing the external feature in integer_vector_ptr allows the user not to provide pkg_c, but to use new_integer_vector_ptr directly.

The integer_vector_ptr is not even used to store bytes but integers. It is a general purpose data structure that has no benefit of reading or writing bytes in an external object file. It is in the memory model that this flexibility must be added.

Yet, I did not see where in the memory model is a array of bytes/characters defined. It seems that a integer type is used to store data. This is because memory.p_data is of type integer_vector_ptr.

In the simplest case just add a natural range 0 to 1 to the memory_t where 0 means use integer_vector_ptr and 1 means use external read_byte/write_byte calls.

That was the first approach. But instead of a natural range 0 to 1, I used field p_id of type integer. -1 meant 'use integer_vector_ptr', and /=-1 meant 'use external read_byte/write_byte' calls. Note that:

  • A boolean field is not enough. We need to pass a parameter to the C functions to tell which memory (among multiple possibly mapped buffers) we want to access, along with the address and the value.
    • Now, I use a integer type to represent three models: default (zero), external through funcs (negative) and external through access (positive). It is possible to use multiple positive models, multiple negative models and a single default model at the same time.
  • The negative external model (calling external read_byte and write_byte) is the most general, but not the best approach. If the externally defined buffer is indeed a C buffer, it is possible to bind it to an VHDL type explicitly. This allows VUnit to read/write as if it was a 'local' or 'shared' variable, i.e., without calling any external function.