diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ee356561..5b48fea2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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: @@ -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: diff --git a/.gitignore b/.gitignore index 476f7820..54be6705 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,9 @@ *.lst *.o *.so +*.vpi +ent* +tb* +!*.vhd +!*.vhdl +doc/_build/* diff --git a/doc/vpi/examples/index.rst b/doc/vpi/examples/index.rst index df8af1ba..bf9b4cc0 100644 --- a/doc/vpi/examples/index.rst +++ b/doc/vpi/examples/index.rst @@ -1,4 +1,7 @@ Examples ######## -TBC +.. toctree:: + + quickstart + list diff --git a/doc/vpi/examples/list.rst b/doc/vpi/examples/list.rst new file mode 100644 index 00000000..7f1857c7 --- /dev/null +++ b/doc/vpi/examples/list.rst @@ -0,0 +1,5 @@ +List +#### + +:cosimtree:`list ` +**************************** diff --git a/doc/vpi/examples/quickstart.rst b/doc/vpi/examples/quickstart.rst new file mode 100644 index 00000000..e8bda852 --- /dev/null +++ b/doc/vpi/examples/quickstart.rst @@ -0,0 +1,23 @@ +Quickstart +########## + +:cosimtree:`helloworld ` +*************************************************** + +:cosimtree:`begin_end ` +************************************************* + +:cosimtree:`access ` +******************************************* + +:cosimtree:`access_port ` +***************************************************** + +:cosimtree:`timestep ` +*********************************************** + + + + + + diff --git a/vpi/README.md b/vpi/README.md new file mode 100644 index 00000000..73142b34 --- /dev/null +++ b/vpi/README.md @@ -0,0 +1,16 @@ +## VPI examples + +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 +```bash +sh run.sh +``` + diff --git a/vpi/common_vpi.h b/vpi/common_vpi.h new file mode 100644 index 00000000..056a5e49 --- /dev/null +++ b/vpi/common_vpi.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +/* + * 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; + 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); +} + diff --git a/vpi/list/ent.vhd b/vpi/list/ent.vhd new file mode 100644 index 00000000..28d8c87f --- /dev/null +++ b/vpi/list/ent.vhd @@ -0,0 +1,24 @@ +library ieee; +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; diff --git a/vpi/list/run.sh b/vpi/list/run.sh new file mode 100755 index 00000000..4be9f56d --- /dev/null +++ b/vpi/list/run.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +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 diff --git a/vpi/list/tb.vhd b/vpi/list/tb.vhd new file mode 100644 index 00000000..b66336be --- /dev/null +++ b/vpi/list/tb.vhd @@ -0,0 +1,50 @@ +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 : 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 + 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; + + diff --git a/vpi/list/vpi.c b/vpi/list/vpi.c new file mode 100644 index 00000000..eaec3686 --- /dev/null +++ b/vpi/list/vpi.c @@ -0,0 +1,61 @@ +#include +#include +#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 +}; diff --git a/vpi/quickstart/access/run.sh b/vpi/quickstart/access/run.sh new file mode 100755 index 00000000..49ee29ac --- /dev/null +++ b/vpi/quickstart/access/run.sh @@ -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 diff --git a/vpi/quickstart/access/tb.vhd b/vpi/quickstart/access/tb.vhd new file mode 100644 index 00000000..f08a6214 --- /dev/null +++ b/vpi/quickstart/access/tb.vhd @@ -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; + + diff --git a/vpi/quickstart/access/vpi.c b/vpi/quickstart/access/vpi.c new file mode 100644 index 00000000..aca04184 --- /dev/null +++ b/vpi/quickstart/access/vpi.c @@ -0,0 +1,53 @@ +#include +#include +#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 +}; diff --git a/vpi/quickstart/access_port/run.sh b/vpi/quickstart/access_port/run.sh new file mode 100755 index 00000000..49ee29ac --- /dev/null +++ b/vpi/quickstart/access_port/run.sh @@ -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 diff --git a/vpi/quickstart/access_port/tb.vhd b/vpi/quickstart/access_port/tb.vhd new file mode 100644 index 00000000..aabda177 --- /dev/null +++ b/vpi/quickstart/access_port/tb.vhd @@ -0,0 +1,27 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity tb is + port(nibble1,nibble2 : in unsigned(3 downto 0); + sum : out unsigned(3 downto 0)); + +end tb; + +architecture behavioral of tb is + begin + + process + + begin + + wait for 10 ns; + wait; + + end process; + + sum <= nibble1 + nibble2; + +end architecture behavioral; + + diff --git a/vpi/quickstart/access_port/vpi.c b/vpi/quickstart/access_port/vpi.c new file mode 100644 index 00000000..aca04184 --- /dev/null +++ b/vpi/quickstart/access_port/vpi.c @@ -0,0 +1,53 @@ +#include +#include +#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 +}; diff --git a/vpi/quickstart/begin_end/ent.vhd b/vpi/quickstart/begin_end/ent.vhd new file mode 100644 index 00000000..81bcf3a7 --- /dev/null +++ b/vpi/quickstart/begin_end/ent.vhd @@ -0,0 +1,7 @@ + +entity ent is +end entity ent; + +architecture behavioral of ent is +begin +end architecture behavioral; diff --git a/vpi/quickstart/begin_end/run.sh b/vpi/quickstart/begin_end/run.sh new file mode 100755 index 00000000..1b3b4a2f --- /dev/null +++ b/vpi/quickstart/begin_end/run.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +set -e + +cd "$(dirname $0)" + +PATH=$PATH:$(pwd) + +echo "Analyze ent.vhd" +ghdl -a ent.vhd + +echo "Elaborate tb" +ghdl -e ent + +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 ent" +ghdl -r ent --vpi=./vpi.vpi diff --git a/vpi/quickstart/begin_end/vpi.c b/vpi/quickstart/begin_end/vpi.c new file mode 100644 index 00000000..0d762083 --- /dev/null +++ b/vpi/quickstart/begin_end/vpi.c @@ -0,0 +1,32 @@ +#include +#include +#include "common_vpi.h" + +PLI_INT32 start_cb(p_cb_data data){ + (void) data; + printf("Start of simulation \n"); + return 0; +} + +PLI_INT32 end_cb(p_cb_data data){ + (void) data; + printf("End of simulation \n"); + return 0; +} + + +void entry_point_cb() { + + // cbStartOfSimulation is a callback executed at the beginning + // of the simulation + register_cb(start_cb, cbStartOfSimulation, -1); + + // cbStartOfSimulation is a callback executed at the beginning + // of the simulation + register_cb(end_cb, cbEndOfSimulation, -1); +} + +void (*vlog_startup_routines[]) () = { + entry_point_cb, + 0 +}; diff --git a/vpi/quickstart/helloworld/ent.vhd b/vpi/quickstart/helloworld/ent.vhd new file mode 100644 index 00000000..06044f1e --- /dev/null +++ b/vpi/quickstart/helloworld/ent.vhd @@ -0,0 +1,8 @@ + +entity ent is +end entity ent; + +architecture behavioral of ent is +begin +end architecture behavioral; + diff --git a/vpi/quickstart/helloworld/run.sh b/vpi/quickstart/helloworld/run.sh new file mode 100755 index 00000000..1b3b4a2f --- /dev/null +++ b/vpi/quickstart/helloworld/run.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +set -e + +cd "$(dirname $0)" + +PATH=$PATH:$(pwd) + +echo "Analyze ent.vhd" +ghdl -a ent.vhd + +echo "Elaborate tb" +ghdl -e ent + +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 ent" +ghdl -r ent --vpi=./vpi.vpi diff --git a/vpi/quickstart/helloworld/vpi.c b/vpi/quickstart/helloworld/vpi.c new file mode 100644 index 00000000..81403c35 --- /dev/null +++ b/vpi/quickstart/helloworld/vpi.c @@ -0,0 +1,51 @@ +#include +#include + +/* + * Delay callback + */ +PLI_INT32 delay_ro_cb(p_cb_data data){ + (void) data; + printf("Hello world! \n"); + + //vpi_control with vpiFinish asks the simulator to terminate + //the second argument is the return value + vpi_control(vpiFinish, 0); + return 0; +} + +/* + * Main entry point + */ +void entry_point_cb() { + + // VPI callback structure + s_cb_data cbData; + // VPI simulation time structure + s_vpi_time simuTime; + + // The callback is executed after a delay + cbData.reason = cbAfterDelay; + cbData.cb_rtn = delay_ro_cb; + cbData.user_data = 0; + cbData.value = 0; + + // the callback is executed after a delay of 0 + cbData.time = &simuTime; + simuTime.type = vpiSimTime; + simuTime.high = 0; + simuTime.low = 0; + + // the callback is registered with vpi_register_cb + vpi_register_cb(&cbData); + +} + +/* + * vlog_startup_routines[] is a list of entry points that are + * called when the VPI plugin is loaded by the simulator + */ +void (*vlog_startup_routines[]) () = { + entry_point_cb, + 0 +}; diff --git a/vpi/quickstart/timestep/ent.vhd b/vpi/quickstart/timestep/ent.vhd new file mode 100644 index 00000000..06044f1e --- /dev/null +++ b/vpi/quickstart/timestep/ent.vhd @@ -0,0 +1,8 @@ + +entity ent is +end entity ent; + +architecture behavioral of ent is +begin +end architecture behavioral; + diff --git a/vpi/quickstart/timestep/run.sh b/vpi/quickstart/timestep/run.sh new file mode 100755 index 00000000..1b3b4a2f --- /dev/null +++ b/vpi/quickstart/timestep/run.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +set -e + +cd "$(dirname $0)" + +PATH=$PATH:$(pwd) + +echo "Analyze ent.vhd" +ghdl -a ent.vhd + +echo "Elaborate tb" +ghdl -e ent + +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 ent" +ghdl -r ent --vpi=./vpi.vpi diff --git a/vpi/quickstart/timestep/vpi.c b/vpi/quickstart/timestep/vpi.c new file mode 100644 index 00000000..21d242b0 --- /dev/null +++ b/vpi/quickstart/timestep/vpi.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include "common_vpi.h" + +#define STOP_ITERATION 10 + +uint32_t iteration = 0; + +/* In this example the simulation time is advanced from + * VPI. This is accomplished registering a chain of callbacks. + */ + +// register a cbAfterDelay callback that executes delay_ro_cb after 1 timestep +PLI_INT32 rw_cb(p_cb_data); + +// register a cbAfterDelay callback that executes delay_rw_cb after 0 timestep +PLI_INT32 ro_cb(p_cb_data); + +// register a cbReadWriteSynch callback that execute rw_cb after 0 timestep +PLI_INT32 delay_rw_cb(p_cb_data); + +// register a cbReadOnlySynch callback that executes ro_cb after 0 timestep +PLI_INT32 delay_ro_cb(p_cb_data); + +PLI_INT32 rw_cb(p_cb_data data){ + (void) data; + + printf("Timestep %d \n", iteration); + + // all write accesses to signals must go HERE + + if(iteration < STOP_ITERATION) { + + // change the last parameter to modify the + // simulation delay induced by the iteration + register_cb(delay_ro_cb, cbAfterDelay, 1); + } else { + + // if the chain is executed STOP_ITERATION times, the simulation is stopped + vpi_control(vpiFinish, 0); + } + + iteration++; + return 0; +} + +PLI_INT32 ro_cb(p_cb_data data){ + (void) data; + register_cb(delay_rw_cb, cbAfterDelay, 0); + return 0; +} + +PLI_INT32 delay_rw_cb(p_cb_data data){ + (void) data; + register_cb(rw_cb, cbReadWriteSynch, 0); + return 0; +} + +PLI_INT32 delay_ro_cb(p_cb_data data){ + (void) data; + register_cb(ro_cb, cbReadOnlySynch, 0); + return 0; +} + +void entry_point_cb() { + register_cb(delay_ro_cb, cbAfterDelay, 0); +} + +void (*vlog_startup_routines[]) () = { + entry_point_cb, + 0 +};