-
Notifications
You must be signed in to change notification settings - Fork 266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[1/3] Add external models to string_ptr; add byte_vector* as an alias #507
Conversation
@kraigher, I think that the most important part to understand this PR is https://github.com/dbhi/vunit/blob/ext-strings/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd#L56-L154. Precisely, https://github.com/dbhi/vunit/blob/ext-strings/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd#L65-L72: type ptr_storage is record
id : integer;
ptr : integer;
eptr : integer;
ids : storage_vector_access_t;
ptrs : vava_t;
eptrs : evava_t;
end record;
When the user uses As a result, each time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pull request does not contain any testing that the external mechanism works creating a technical debt.
The fact that all the existing tests pass is a proof that Nonetheless, the I must ask for some trust here. |
If the example serves as the test of id != 0 I would like to include it in this PR. |
It is added now.
|
523d5df
to
5a27188
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have looked through this again and found some issues. I will look further after they are fixed. Sorry for this taking such a long time but you are trying to change a core data type in VUnit which requires a lot of care.
43e7f86
to
351b301
Compare
@kraigher, I think I addressed all of the suggested changes. Let me know when you have another look at it. |
Ok I will look again. By the way I though the integer vector length -> len revert would become a separate PR. It would have been cleaner as that could be merged on its own. |
I BTW, regarding this PR, do you think it is worth having separated |
07420e0
to
fa5d6ed
Compare
b175a0a
to
ed75c87
Compare
|
||
impure function new_string_ptr ( | ||
length : natural := 0; | ||
mode : storage_mode_t := internal; | ||
id : integer := 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume id=0 means automatic id assignment. This is assumed with internal string. Maybe an check is needed that is it 0 when it cannot be a custom id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. Note that this id
is NOT the same id
that was in the codebase until 6 months ago. That was renamed to ref
: https://github.com/VUnit/vunit/blob/master/vunit/vhdl/data_types/src/string_ptr_pkg.vhd#L20 and https://github.com/VUnit/vunit/blob/master/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd#L21.
The mechanism to share buffers between C and VHDL is a char* []
, i.e. an array of pointers to an arrays of chars. This id
is used to decide which of those arrays of chars corresponds to a string
in VHDL. See examples/vhdl/external_buffer/src/main.c
:
- If mode is extacc, VHDL will execute
get_string_ptr(id)
to retrieve that pointer address. - If mode is extfnc, VHDL will pass the id in each call to
write_char
orread_char
.
In this example, using write_char
(mode extfnc) is equivalent to VHDL writing directly with mode extacc. However, this is only because of the specific C implementation of write_char
. In a practical example write_char
is expected to do some more complex task, such as sending data through HTTP. That's where the id is handy.
When mode is internal, field id
is not used at all.
Nonetheless, ref
still exists. Each string, no matter the mode, has a ref; it is assigned automaticallly, as it has always been. See:
vunit/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd
Lines 131 to 158 in ed75c87
case mode is | |
when internal => | |
st.ids(st.id) := ( | |
id => st.ptr, | |
mode => internal, | |
length => 0 | |
); | |
reallocate_ptrs(st.ptrs, st.ptr); | |
st.ptrs(st.ptr) := new vec_t'(1 to length => value); | |
st.ptr := st.ptr + 1; | |
when extacc => | |
st.ids(st.id) := ( | |
id => st.eptr, | |
mode => extacc, | |
length => length | |
); | |
reallocate_eptrs(st.eptrs, st.eptr); | |
st.eptrs(st.eptr) := get_ptr(id); | |
st.eptr := st.eptr + 1; | |
when extfnc => | |
st.ids(st.id) := ( | |
id => id, | |
mode => extfnc, | |
length => length | |
); | |
end case; | |
st.id := st.id + 1; | |
return st.id-1; |
Let's suppose we want to use four strings:
- internal
- extacc with id 0
- internal
- extfnc with id 1
st.ids st.ptr st.eptr
string0 0 0
string1 1 0
string2 2 1
string3 3
So,
st.ids
is an array ofref
that contains four entries, one for each string.st.ptr
is the array of refs/pointers to internal strings (this is the element that has always existed, everything else is wrapped around it), and contains two entries.st.eptr
is the array of refs/pointers to extacc strings (id
is used to get_string_ptr once only).
As a result:
st.ids(0).id
is 0
(index to read/write from/to st.ptr)
st.ids(1).id
is 0
(index to read/write from/to st.eptr)
st.ids(2).id
is 1
(index to read/write from/to st.ptr)
st.ids(3).id
is 1
(id to read/write through read_char and write_char)
Maybe I am overusing the name id
? Currently, it is:
- A parameter for extacc to get the pointer from C.
- A parameter for extfnc to pass to read_char/write_char.
- The name of the field
st.ids
, which contains a number (id) for each string. - The name of field
st.ids().id
which contains a number representing either:- The corresponding index in
st.ptr
. - The corresponding index in
st.eptr
. - The
id
parameter used when creating an extfnc string.
- The corresponding index in
EDIT
After reading this, I think that it might be easier to understand #507 (comment). I feel that it should be possible to merge st.ptr
and st.eptr
in a single array. But VHDL seems to fight against me doing it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I understand a bit better, but it still means it is user error to call the function with internal and an explicit id either by name of position?
-- Case 1
new_string_ptr(length, mode => internal, id => 1) -- Not valid
-- Case 2
new_string_ptr(length, mode => internal, id => 0) -- Also not valid
-- Case 3
new_string_ptr(length, mode => internal) -- Valid
Maybe it is not so important to check "Case 2" but "Case 1" could easily give an error which might help debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I'd say that such a test existed: id /= 0 is not supported with mode internal. Maybe I unwantedly dropped it during some rebase/modification. Let me check.
BTW, I'm also understanding and having a look at the add_builtins comment above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added an assertion for case 1. Note that case 3 defaults to case 2. Therefore, we have no mechanism to tell them apart. Should the default of id be some non-supported value, we could identify case 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now:
- I moved
index_t
totypes_pkg
. - I renamed
id
fields toeid
andidx
, to make it easier to tell them apart. eid
is now of typeindex_t
and it defaults to-1
.- Assertions are added to detect cases 1 and 2, and also when extacc or extfnc are used without providing
eid
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kraigher, shall I add tests for these assertions? If so, do they fit in vunit/vhdl/data_types/test/tb_byte_vector_ptr.vhd?
1143bb3
to
614bdf1
Compare
a2879d0
to
fe81505
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with these changes now
🚀🚀🚀 However, let's not merge for now. After lunch, I will re-read all the comments in this issue again, to ensure that me/we are not missing anything. I will also add some further tests (e.g., to use different Thanks for your support @go2sh. Much appreciated. |
Awesome work in total (your PR series), this will rock. A did a quick test on my setup and seams to work fine, but I haven't checked the code in detail. Right now I use your code to get comfortable with VPI etc. |
I added four new files to the @kraigher, if you are ok with the status of this PR, we can merge it and move forward. |
I am ok with this. |
Now that this is merged, will In that PR, I modify |
The second part of this PR is still pending: #476. Here,
Note that there are other PRs in the series (such as #470 and #568). However, any of those are to be evaluated after your contributions. This is because those higher level features might be adapted/improved based on #522. |
The modifications to integer_vector_ptr were really only to provide consistency between the types. It's not required. I'll probably get it up and running on top of this PR and then update integer_vector_ptr after #476 |
Coming from #476. Related to #470.
In this PR, external modes are added to
string_ptr.vhd
through VHPIDIRECT (see https://ghdl.readthedocs.io/en/latest/using/Foreign.html). With 'external mode' we mean:get
/set
callback functions defined in C (or any C-alike compatible language) to interact with strings (byte vectors).char* []
) can be shared between VHDL an any C-alike compatible language.The usage is as follows:
new_string_ptr(len, mode, eid, value)
creates a new array of lengthlen
with modemode
and identifierid
.mode = internal
(default), the array is allocated in the testbench and it is initialized withvalue
.mode = extfnc
, the array is not allocated. When values areget
/set
, VHPIDIRECT functions/proceduresread_char
/write_char
are called.mode = extacc
, the array is not allocated. A pointer is created and the value is retrieved through VHPIDIRECT functionget_string_ptr
. The returned value must be a pointer to an array of typeuint8_t
(size of 1 byte). When data isget
/set
, content is directly modified in the shared memory.Notes:
mode /= internal
,value
is not used andlen
is used to tell VHDL the maximum size of the external buffer. Currently, this is a fixed value, i.e., there is no 'synchronization' of this value.mode /= internal
,eid
is used to decide which of the shared buffers to use.In order to support using
string_ptr
with or without VHPIDIRECT, two different implementations of the external resources are provided:external_string_pkg-vhpi.vhd
, declares the functions/procedures as external; therefore, C implementations must be provided.external_string_pkg-novhpi.vhd
does not declare the functions/procedures as external; C implementations are not required, but it is not possible to create vectors withmode /= internal
(an assertion of level error is raised).The user needs to select whether to use external C objects. Furthermore, alternative implementions of
*vhpi.pkg
sources might be provided. This is not expected to be required for most use cases, but it is supported. This is achieved through a newexternal
parameter tofrom_args
andfrom_argv
:None
(defaults to False){'string': False}
{'string': True}
{'string': ['path/to/custom/file']}
deallocate
,reallocate
andresize
are not implemented for external modes (mode /= internal
). For the caseextacc
, it would be easy to implement it. Since the 'connection' is a pointer, it is possible to usemalloc
ormmap
in the C implementation. Coherently, it would be possible to update the length in the VHDL model according to the actual size in the C app.However, when
extfnc
, it can be complex or not possible. Sinceread_char
/write_char
can be used to interact with external services/processes through pipes, sockets, packets, messages, etc., the implementation is likely to be very specific. Nonetheless, in this example the implementation can be the same for both modes.byte_vector
is added as an alias ofstring
.