Skip to content
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

Various VPI examples #19

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ jobs:
vhpidirect/arrays/intvector,
vhpidirect/arrays/logicvector,
vhpidirect/arrays/matrices,
vpi/quickstart/helloworld,
vpi/quickstart/begin_end,
vpi/quickstart/access,
vpi/quickstart/access_port,
vpi/quickstart/timestep,
vpi/list
]
runs-on: ubuntu-latest
env:
Expand Down Expand Up @@ -77,7 +83,13 @@ jobs:
#vhpidirect/shared/shghdl, ! dlfcn.h is not available on win
vhpidirect/arrays/intvector,
vhpidirect/arrays/logicvector,
vhpidirect/arrays/matrices,
vhpidirect/arrays/matrices
#vpi/quickstart/helloworld, ! VPI plugins needs their folder to be added to PATH
#vpi/quickstart/begin_end, ! VPI plugins needs their folder to be added to PATH
#vpi/quickstart/access, ! VPI plugins needs their folder to be added to PATH
#vpi/quickstart/access_port, ! VPI plugins needs their folder to be added to PATH
#vpi/quickstart/timestep, ! VPI plugins needs their folder to be added to PATH
#vpi/list ! VPI plugins needs their folder to be added to PATH
]
runs-on: windows-latest
env:
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
*.lst
*.o
*.so
*.vpi
ent*
tb*
!*.vhd
!*.vhdl
doc/_build/*
5 changes: 4 additions & 1 deletion doc/vpi/examples/index.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
Examples
########

TBC
.. toctree::

quickstart
list
5 changes: 5 additions & 0 deletions doc/vpi/examples/list.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
List
####

:cosimtree:`list <vpi/list>`
****************************
23 changes: 23 additions & 0 deletions doc/vpi/examples/quickstart.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Quickstart
##########

:cosimtree:`helloworld <vpi/quickstart/helloworld>`
***************************************************

:cosimtree:`begin_end <vpi/quickstart/begin_end>`
*************************************************

:cosimtree:`access <vpi/quickstart/access>`
*******************************************

:cosimtree:`access_port <vpi/quickstart/access_port>`
*****************************************************

:cosimtree:`timestep <vpi/quickstart/timestep>`
***********************************************






16 changes: 16 additions & 0 deletions vpi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## VPI examples
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be written in RST and located in subdir doc. The motivation to use RST is that we might want to add labels for cross-referencing these examples. The location is because we are keeping docs separate from code sources.

Moreover, each example should have a header of the appropriate level. This is necessary for the TOC.

In VHPIDIRECT, examples are grouped (Quick start, Arrays, Other projects...). You might want to add all your examples to a section named "Quick Start", to use/create some other section, or to not create any section yet.

Copy link
Author

@bellaz89 bellaz89 Apr 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am ok with this. However, I just did a similar thing as in ./systemc/README.md . If you think it is unnecessary I will remove that, but having a small overview of the folder is helpful for people that just want to explore the repo without directly using the documentation. See https://github.com/bellaz89/ghdl-cosim/tree/master/vpi

Copy link
Member

@umarcor umarcor Apr 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, the existence of ./systemc/README.md is a kind of "artifact". Repo https://github.com/ghdl/ghdl-systemc-fosdem15 was created some weeks ago, due to a user's demand. The name was not correct, and it was renamed to https://github.com/ghdl/ghdl-systemc-fosdem16 a few days later. Then, we discussed about widening the scope to "co-simulation", and this repo was forked from there. We considered to rename the other once again, but we thought it would be too much trouble. Nonetheless, the content of the "old" repo was moved to subdir ./systemc and the README remains. The plan is to add it to the docs (as other examples), once we guess how to make it fit.

If you think it is unnecessary I will remove that, but having a small overview of the folder is helpful for people that just want to explore the repo without directly using the documentation.

Honestly, I had not thought about it, but it sounds as a good idea! From this point of view:

  • The docs need not to link this file, but headers should link to the corresponding subdirs.
  • This file should contain one or multiple links to the docs, where these very brief descriptions are extended.
  • Do we want a single vpi/README.md? Two (vpi/quickstart/README.md and vpi/list/README.md)? Or all of them?


The directory contains some VPI usage examples:

1. [quickstart/helloworld](./quickstart/helloworld/) minimal VPI code example that runs a simulation, prints a message and exits.
2. [quickstart/begin_end](./quickstart/begin_end/) VPI code example that executes a callback at the begin and the end of a simulation.
3. [quickstart/access](./quickstart/access/) signal read/write example on an adder component using `vpi_put_value` and `vpi_get_value`.
4. [quickstart/access_port](./quickstart/access_port/) toplevel ports read/write example on an adder component using `vpi_put_value` and `vpi_get_value`.
5. [quickstart/timestep](./quickstart/timestep/) shows how to run a simulation for an arbitrary number of timesteps.
6. [list](./list/) example on signal hierarchy iteration using `vpi_iterate`and `vpi_scan`.

to run a test, just go in the directory and execute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment applies to all the examples in this repo. It would better fit either as an admonition in the main page, or in the README.

It can be a separate PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should go in the main README, along with an explanation about "groups of examples" (see #19 (comment)).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now included in #21.

```bash
sh run.sh
```

31 changes: 31 additions & 0 deletions vpi/common_vpi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once
#include <inttypes.h>
#include <vpi_user.h>

/*
* register_cb is a wrapper function around vpi_register_cb
* to simplify the the callback registering process
*/
void register_cb(PLI_INT32(*f)(p_cb_data),
PLI_INT32 reason,
int64_t cycles){

s_cb_data cbData;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this deserves a reference/cite, either here or in the docs. Where should users search for the definition of s_cb_data? In vpi_user.h? The standard? Some book?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now explained in #21.

s_vpi_time simuTime;
if (cycles < 0){
cbData.time = NULL;
} else {
cbData.time = &simuTime;
simuTime.type = vpiSimTime;
simuTime.high = (PLI_INT32) (cycles >> 32);
simuTime.low = (PLI_INT32) (cycles & 0xFFFFFFFF);
}

cbData.reason = reason;
cbData.cb_rtn = f;
cbData.user_data = 0;
cbData.value = 0;

vpi_register_cb(&cbData);
}

24 changes: 24 additions & 0 deletions vpi/list/ent.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
library ieee;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment about duplicated files above.

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- ent is a 4 bit adder

entity ent is
port(nibble1, nibble2 : in unsigned(3 downto 0);
sum : out unsigned(3 downto 0);
carry_out : out std_logic);

end entity ent;

architecture behavioral of ent is

signal temp : unsigned(4 downto 0);

begin

temp <= ("0" & nibble1) + nibble2;
sum <= temp(3 downto 0);
carry_out <= temp(4);

end architecture behavioral;
22 changes: 22 additions & 0 deletions vpi/list/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment about duplicated files above.


set -e

cd "$(dirname $0)"

PATH=$PATH:$(pwd)

echo "Analyze ent.vhd and tb.vhd"
ghdl -a ent.vhd tb.vhd

echo "Elaborate tb"
ghdl -e tb

echo "Compile vpi.c"
ghdl --vpi-compile gcc -c vpi.c -I./../ -o vpi.o

echo "Link vpi.o"
ghdl --vpi-link gcc vpi.o -o vpi.vpi

echo "Execute tb"
ghdl -r tb --vpi=./vpi.vpi
50 changes: 50 additions & 0 deletions vpi/list/tb.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
library ieee;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment about duplicated files above.

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb is
end tb;

architecture behavioral of tb is

signal nibble1, nibble2 : unsigned(3 downto 0);
signal sum_0, sum_1, sum_2, sum_3 : unsigned(3 downto 0);
signal carry_out_0, carry_out_1, carry_out_2, carry_out_3: std_logic;

begin

ent_0: entity work.ent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels not to be valid VHDL. Is it on purpose?

Anyway, correct or purposely incorrect, I'd suggest to use a for generate instead:

    i_ent: for I in 0 to 3 generate
      w_ent: entity work.ent 
      port map (nibble1 => nibble1,
                nibble2 => nibble2,
                sum => sum,
                carry_out => carry_out);
   end generate;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script is running, the CI completes without any error, and GHDL doesn't pop out even a Warning. Therefore I think it is valid VHDL.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprising... I would expect sum and carry_out to collide, since four different drivers are connected to the same signal. I simulated it and, when one of the instances drives a different value, sum and/or carry_out are X. @tgingold, should this produce a warning?

Anyway, I think we should build a proper adder chain to sum two 4*4=16bit numbers.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, now I see that you were talking about a multiple-driving issue. I agree that it is strange that GHDL doesn't catch it and I will fix that. Please could you be a bit more descriptive and giving the code lines where the problem is next time?

Copy link
Member

@umarcor umarcor May 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you be a bit more descriptive and giving the code lines where the problem is next time?

Sure! Sorry about it. I was convinced that you had done it on purpose, because you only wanted to explore the hierarchy without caring about the content.

I will take care of fixing this source if you want; so you can focus on VPI-specific issues which I'm less comfortable with.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Equivalent source included in #21.

port map (nibble1 => nibble1,
nibble2 => nibble2,
sum => sum_0,
carry_out => carry_out_0);

ent_1: entity work.ent
port map (nibble1 => nibble1,
nibble2 => nibble2,
sum => sum_1,
carry_out => carry_out_1);

ent_2: entity work.ent
port map (nibble1 => nibble1,
nibble2 => nibble2,
sum => sum_2,
carry_out => carry_out_2);

ent_3: entity work.ent
port map (nibble1 => nibble1,
nibble2 => nibble2,
sum => sum_3,
carry_out => carry_out_3);
process

begin

wait for 10 ns;
wait;

end process;

end architecture behavioral;


61 changes: 61 additions & 0 deletions vpi/list/vpi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include <vpi_user.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that all VHDL sources are the same for all the examples. Do you think that it can be a common pattern for all VPI examples? Or do you expect other features to require different VHDL sources?

I ask it because it seems that the 4-7 examples that are part of this PR differ on the vpi.c file and the compilation commands only.

Copy link
Author

@bellaz89 bellaz89 Apr 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is that to demonstrate very basic usage of VPI, the VHDL component under test do not matter so much. So, for helloworld I can, for example, just put a complete void top module without any signals, because at the end the VPI code is interacting with the simulator but not with the simulation. If you think it is better, I can try to make the ent.vhd and tb.vhd even more concise in each test.

Copy link
Member

@umarcor umarcor Apr 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This. In VHPIDIRECT examples, many C sources are common but VHDL sources need to be different. Here, as you said, VHDL sources are almost irrelevant (as long as they contain the minimum features that we want to test). Hence, instead of making ent.vhd and tb.vhd more concise in each test, I would suggest to make them general enough so that they can used in any VPI example (last structure in #19 (comment)). If some example in the future requires some weird feature in VHDL, we will consider adding different VHDL sources to the corresponding subdir. But, for now, it seems unnecessary.

#include <stdio.h>
#include "common_vpi.h"

void print_net_in_module(vpiHandle module_handle) {
char* module_name = vpi_get_str(vpiName, module_handle);
vpiHandle net_handle;

printf(" Signals of %s: \n", module_name);
vpiHandle net_iterator = vpi_iterate(vpiNet,module_handle);
if(net_iterator){
while(net_handle = vpi_scan(net_iterator)){
char* net_full_name = vpi_get_str(vpiFullName, net_handle);
printf(" %s \n", net_full_name);
vpi_free_object(net_handle);
}
}
}

void print_signals(){

vpiHandle top_mod_iterator;
vpiHandle top_mod_handle;

top_mod_iterator = vpi_iterate(vpiModule,NULL);

top_mod_handle = vpi_scan(top_mod_iterator);
while(top_mod_handle) {
print_net_in_module(top_mod_handle);
vpiHandle module_iterator = vpi_iterate(vpiModule,top_mod_handle);
if (module_iterator){
vpiHandle module_handle;
module_handle = vpi_scan(module_iterator);
while (module_handle) {
print_net_in_module(module_handle);
vpi_free_object(module_handle);
module_handle = vpi_scan(module_iterator);
}
}
vpi_free_object(top_mod_handle);
top_mod_handle = vpi_scan(top_mod_iterator);
}
}

PLI_INT32 start_cb(p_cb_data data){
(void) data;

printf("List of simulation signals: \n");
print_signals();

return 0;
}

void entry_point_cb() {
register_cb(start_cb, cbStartOfSimulation, -1);
}

void (*vlog_startup_routines[]) () = {
entry_point_cb,
0
};
22 changes: 22 additions & 0 deletions vpi/quickstart/access/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env sh

set -e

cd "$(dirname $0)"

PATH=$PATH:$(pwd)

echo "Analyze tb.vhd"
ghdl -a tb.vhd

echo "Elaborate tb"
ghdl -e tb

echo "Compile vpi.c"
ghdl --vpi-compile gcc -c vpi.c -I./../../ -o vpi.o

echo "Link vpi.o"
ghdl --vpi-link gcc vpi.o -o vpi.vpi

echo "Execute tb"
ghdl -r tb --vpi=./vpi.vpi
27 changes: 27 additions & 0 deletions vpi/quickstart/access/tb.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb is
end tb;

architecture behavioral of tb is

signal nibble1, nibble2, sum : unsigned(3 downto 0);

begin

process

begin

wait for 10 ns;
wait;

end process;

sum <= nibble1 + nibble2;

end architecture behavioral;


53 changes: 53 additions & 0 deletions vpi/quickstart/access/vpi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <vpi_user.h>
#include <stdio.h>
#include "common_vpi.h"

PLI_INT32 start_cb(p_cb_data data){
(void) data;

// VPI value structure
s_vpi_value val;

// type of data format that is passed to the runtime.
val.format = vpiBinStrVal;
val.value.str = "0101";
printf("set %s in tb.nibble1 \n", val.value.str);

//vpi_handle_by_name returns an arbitrary port/signal in the
//simulation hierarchy
vpiHandle nibble1 = vpi_handle_by_name("tb.nibble1", NULL);

//vpi_put_value set the value to the passed signal handle
vpi_put_value(nibble1, &val, NULL, vpiNoDelay);

val.value.str = "0011";
printf("set %s in tb.nibble2 \n", val.value.str);
vpiHandle nibble2 = vpi_handle_by_name("tb.nibble2", NULL);
vpi_put_value(nibble2, &val, NULL, vpiNoDelay);

return 0;
}

PLI_INT32 end_cb(p_cb_data data){
(void) data;
s_vpi_value val;

val.format = vpiBinStrVal;
vpiHandle sum = vpi_handle_by_name("tb.sum", NULL);

//vpi_get_value reads the value from the passed signal handle
vpi_get_value(sum, &val);
printf("get %s from tb.sum \n", val.value.str);

return 0;
}

void entry_point_cb() {
register_cb(start_cb, cbStartOfSimulation, -1);
register_cb(end_cb, cbEndOfSimulation, -1);
}

void (*vlog_startup_routines[]) () = {
entry_point_cb,
0
};
Loading