Skip to content

Commit 752415e

Browse files
committed
extend(examples/vhdl/array_axis_vcs): integration of foreign C declarations with GHDL
1 parent d492353 commit 752415e

7 files changed

Lines changed: 287 additions & 4 deletions

File tree

examples/vhdl/array_axis_vcs/run.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55
# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com
66

7+
import os
78
from os.path import join, dirname
89
from vunit import VUnit
910

@@ -21,4 +22,10 @@
2122

2223
# vu.set_sim_option('modelsim.init_files.after_load',['runall_addwave.do'])
2324

25+
c_obj = join(root, 'src', 'test', 'main.o')
26+
27+
print(os.popen('gcc -fPIC -rdynamic -c '+join(root, 'src/**/*.c')+' -o '+c_obj).read())
28+
29+
vu.set_objects([c_obj])
30+
2431
vu.main()
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <stdint.h>
4+
5+
extern int ghdl_main (int argc, char **argv);
6+
7+
int32_t *V[2];
8+
uint32_t length = 100;
9+
10+
// get_param is used by GHDL to retrieve parameter values (integers).
11+
uint32_t get_param(uint32_t w) {
12+
uint32_t o = 0;
13+
switch(w) {
14+
case 0 : // buffer length
15+
o = length;
16+
break;;
17+
case 1 : // data width (in bits)
18+
o = 8*sizeof(int32_t);
19+
break;;
20+
case 2 : // fifo depth
21+
o = 5;
22+
break;;
23+
}
24+
printf("get_p(%d): %d\n", w, (int)o);
25+
return o;
26+
}
27+
28+
// get_addr is used by GHDL to retrieve pointers to the base addresses of the buffers.
29+
uintptr_t get_addr(uint32_t w) {
30+
//printf("get_addr(%d): %p\n", w, (void *)V[w]o);
31+
return (uintptr_t)V[w];
32+
}
33+
34+
// check evaluates if the result produced by the UUT is equivalent to some other softwre procedure.
35+
int check(int32_t *I, int32_t *O, uint32_t l) {
36+
int i;
37+
for ( i=0 ; i<l ; i++ ) {
38+
if ( I[i] != O[i] ) {
39+
printf("check failed! %d: %d %d\n", i, I[i], O[i]);
40+
return -1;
41+
}
42+
}
43+
printf("check successful\n");
44+
return 0;
45+
}
46+
47+
// main is the entrypoint to the application.
48+
int main(int argc, char **argv) {
49+
50+
// Optionally, some of the CLI arguments can be processed by the software app and others forwarded to GHDL.
51+
int gargc = argc;
52+
char **gargv = argv;
53+
54+
// Allocate the memory buffers that are to be shared between the software and the simulation.
55+
int i;
56+
for (i=0 ; i<2 ; i++) {
57+
V[i] = (int32_t *) malloc(length*sizeof(uint32_t));
58+
if ( V[i] == NULL ) {
59+
perror("execution of malloc() failed!\n");
60+
return -1;
61+
}
62+
}
63+
64+
// Initialize one of the buffers with random data.
65+
for (i=0 ; i<length ; i++) {
66+
V[0][i]=i*100+rand()/(RAND_MAX/100);
67+
}
68+
69+
// Execute the simulation to let the UUT copy the data from one buffer to another.
70+
ghdl_main(gargc, gargv);
71+
72+
// Check that the UUT did what it was expected to do.
73+
printf("> Call 'check'\n");
74+
if ( check(V[0], V[1], length) != 0 ) {
75+
printf("check failed!\n");
76+
return -1;
77+
}
78+
79+
// Free the allocated memory, since we don't need it anymore.
80+
free(V[0]);
81+
free(V[1]);
82+
83+
return 0;
84+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-- This Source Code Form is subject to the terms of the Mozilla Public
2+
-- License, v. 2.0. If a copy of the MPL was not distributed with this file,
3+
-- You can obtain one at http://mozilla.org/MPL/2.0/.
4+
--
5+
-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com
6+
7+
package pkg_c is
8+
9+
function get_param(f: integer) return integer;
10+
attribute foreign of get_param :
11+
function is "VHPIDIRECT get_param";
12+
13+
constant stream_length : integer := get_param(0);
14+
15+
type buffer_t is array(integer range 0 to stream_length-1) of integer;
16+
type buffer_p is access buffer_t;
17+
18+
impure function get_addr(f: integer) return buffer_p;
19+
attribute foreign of get_addr :
20+
function is "VHPIDIRECT get_addr";
21+
22+
type buffet_t_prot is protected
23+
procedure init ( i: integer );
24+
procedure set ( i: integer; v: integer);
25+
impure function get (i: integer) return integer;
26+
end protected buffet_t_prot;
27+
28+
end pkg_c;
29+
30+
package body pkg_c is
31+
function get_param(f: integer) return integer is begin
32+
assert false report "VHPI" severity failure;
33+
end get_param;
34+
35+
impure function get_addr(f: integer) return buffer_p is begin
36+
assert false report "VHPI" severity failure;
37+
end get_addr;
38+
39+
type buffet_t_prot is protected body
40+
variable var: buffer_p;
41+
procedure init ( i: integer ) is begin
42+
var := get_addr(i);
43+
end procedure;
44+
procedure set ( i: integer; v: integer ) is begin
45+
var(i) := v;
46+
end procedure;
47+
impure function get ( i: integer ) return integer is begin
48+
return var(i);
49+
end get;
50+
end protected body buffet_t_prot;
51+
end pkg_c;
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
-- This Source Code Form is subject to the terms of the Mozilla Public
2+
-- License, v. 2.0. If a copy of the MPL was not distributed with this file,
3+
-- You can obtain one at http://mozilla.org/MPL/2.0/.
4+
--
5+
-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com
6+
7+
-- This testbench is a Minimum Working Example (MWE) of VUnit's resources to read/write data from a buffer
8+
-- allocated in a foreign C application, and to verify AXI4-Stream components. Data is sent to an AXI4-Stream
9+
-- Slave. The AXI4-Stream Slave is expected to be connected to an AXI4-Stream Master either directly or
10+
-- (preferredly) through a FIFO, thus composing a loopback. Therefore, as data is pushed to the AXI4-Stream
11+
-- Slave interface, the output is read from the AXI4-Stream Master interface and it is saved back to the buffer
12+
-- shared with the software application.
13+
14+
library ieee;
15+
context ieee.ieee_std_context;
16+
17+
library vunit_lib;
18+
context vunit_lib.vunit_context;
19+
context vunit_lib.vc_context;
20+
21+
use work.pkg_c.all;
22+
23+
entity tb_c_axis_loop is
24+
generic (
25+
runner_cfg : string;
26+
tb_path : string
27+
);
28+
end entity;
29+
30+
architecture tb of tb_c_axis_loop is
31+
-- Simulation constants
32+
33+
constant clk_period : time := 20 ns;
34+
constant data_width : natural := get_param(1);
35+
constant fifo_depth : natural := get_param(2);
36+
37+
-- AXI4Stream Verification Components
38+
39+
constant m_axis : axi_stream_master_t := new_axi_stream_master(data_length => data_width);
40+
constant s_axis : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width);
41+
42+
-- tb signals and variables
43+
44+
signal clk, rst, rstn : std_logic := '0';
45+
signal start, sent, saved : boolean := false;
46+
shared variable ibuffer, obuffer: buffet_t_prot;
47+
48+
begin
49+
50+
clk <= not clk after (clk_period/2);
51+
rstn <= not rst;
52+
53+
main: process
54+
procedure run_test is begin
55+
info("Init test");
56+
wait until rising_edge(clk); start <= true;
57+
wait until rising_edge(clk); start <= false;
58+
wait until (sent and saved and rising_edge(clk));
59+
info("Test done");
60+
end procedure;
61+
begin
62+
test_runner_setup(runner, runner_cfg);
63+
while test_suite loop
64+
if run("test") then
65+
ibuffer.init(0);
66+
obuffer.init(1);
67+
rst <= '1';
68+
wait for 15*clk_period;
69+
rst <= '0';
70+
run_test;
71+
end if;
72+
end loop;
73+
test_runner_cleanup(runner);
74+
wait;
75+
end process;
76+
77+
--
78+
79+
stimuli: process
80+
variable last : std_logic;
81+
begin
82+
sent <= false;
83+
wait until start and rising_edge(clk);
84+
85+
for y in 0 to stream_length-1 loop
86+
wait until rising_edge(clk);
87+
push_axi_stream(net, m_axis, std_logic_vector(to_signed(ibuffer.get(y), data_width)) , tlast => '0');
88+
end loop;
89+
90+
info("m_I sent!");
91+
92+
wait until rising_edge(clk);
93+
sent <= true;
94+
wait;
95+
end process;
96+
97+
save: process
98+
variable o : std_logic_vector(31 downto 0);
99+
variable last : std_logic:='0';
100+
begin
101+
saved <= false;
102+
wait until start and rising_edge(clk);
103+
wait for 50*clk_period;
104+
105+
for y in 0 to stream_length-1 loop
106+
pop_axi_stream(net, s_axis, tdata => o, tlast => last);
107+
obuffer.set(y,to_integer(signed(o)));
108+
end loop;
109+
110+
info("m_O read!");
111+
112+
wait until rising_edge(clk);
113+
saved <= true;
114+
wait;
115+
end process;
116+
117+
--
118+
119+
uut_vc: entity work.tb_vc_axis_loop
120+
generic map (
121+
m_axis => m_axis,
122+
s_axis => s_axis,
123+
data_width => data_width,
124+
fifo_depth => fifo_depth
125+
)
126+
port map (
127+
clk => clk,
128+
rstn => rstn
129+
);
130+
131+
end architecture;

examples/vhdl/array_axis_vcs/src/test/tb_py_axis_loop.vhd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ architecture tb of tb_py_axis_loop is
3232

3333
-- Simulation constants
3434

35-
constant clk_period : time := 20 ns;
35+
constant clk_period : time := 20 ns;
3636
constant data_width : natural := 32;
3737

3838
-- AXI4Stream Verification Components
@@ -42,8 +42,8 @@ architecture tb of tb_py_axis_loop is
4242

4343
-- tb signals and variables
4444

45-
signal clk, rst, rstn : std_logic := '0';
46-
signal start, sent, saved : boolean := false;
45+
signal clk, rst, rstn : std_logic := '0';
46+
signal start, sent, saved : boolean := false;
4747
shared variable m_I, m_O : array_t;
4848

4949
begin

examples/vhdl/array_axis_vcs/src/test/tb_vc_axis_loop.vhd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
-- This Source Code Form is subject to the terms of the Mozilla Public
2+
-- License, v. 2.0. If a copy of the MPL was not distributed with this file,
3+
-- You can obtain one at http://mozilla.org/MPL/2.0/.
4+
--
5+
-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com
6+
17
library ieee;
28
context ieee.ieee_std_context;
39

vunit/test/acceptance/test_external_run_scripts.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import sys
1717
from vunit import ROOT
1818
from vunit.builtins import VHDL_PATH
19-
from vunit.test.common import has_simulator, check_report, simulator_is
19+
from vunit.test.common import has_simulator, check_report, simulator_is, simulator_check
2020

2121

2222
def simulator_supports_verilog():
@@ -109,6 +109,10 @@ def test_vhdl_json4vhdl_example_project(self):
109109
def test_vhdl_array_example_project(self):
110110
self.check(join(ROOT, "examples", "vhdl", "array", "run.py"))
111111

112+
@unittest.skipIf(
113+
simulator_check(lambda simclass: not simclass.supports_vhpi()),
114+
"This simulator/backend does not support interfacing with external C code"
115+
)
112116
def test_vhdl_array_axis_vcs_example_project(self):
113117
self.check(join(ROOT, "examples", "vhdl", "array_axis_vcs", "run.py"))
114118

0 commit comments

Comments
 (0)