Skip to content

Commit

Permalink
add(examples/vhdl/external_buffer)
Browse files Browse the repository at this point in the history
  • Loading branch information
umarcor committed Jun 12, 2019
1 parent 3547b24 commit fa5d6ed
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 1 deletion.
52 changes: 52 additions & 0 deletions examples/vhdl/external_buffer/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com

"""
External Buffer
Interfacing with foreign languages (C) through VHPIDIRECT:
https://ghdl.readthedocs.io/en/latest/using/Foreign.html
An array of type ``uint8_t`` is allocated in a C application and some values
are written to the first ``1/3``positions. Then, the VHDL simulation is
executed, where the (external) array/buffer is used.
In the VHDL testbenches, two vector pointers are created, each of them using
a different access mechanism (``extfunc`` or ``extacc``). One of them is used to copy
the first ``1/3`` elements to positions ``[1/3, 2/3)``, while incrementing each value
by one. The second one is used to copy elements from ``[1/3, 2/3)`` to ``[2/3, 3/3)``,
while incrementing each value by two.
When the simulation is finished, the C application checks whether data was successfully
copied/modified. The content of the buffer is printed both before and after the
simulation.
"""

from vunit import VUnit
from os import popen
from os.path import join, dirname


src_path = join(dirname(__file__), 'src')

c_obj = join(src_path, 'main.o')
# Compile C application to an object
print(popen(' '.join([
'gcc', '-fPIC',
'-c', join(src_path, 'main.c'),
'-o', c_obj
])).read())

# Enable the external feature for strings
ui = VUnit.from_argv(vhdl_standard='2008', external={'string': True})

lib = ui.add_library('lib')
lib.add_source_files(join(src_path, '*.vhd'))

# Add the C object to the elaboration of GHDL
ui.set_sim_option('ghdl.elab_flags', ['-Wl,' + c_obj])

ui.main()
97 changes: 97 additions & 0 deletions examples/vhdl/external_buffer/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
External Buffer
Interfacing with foreign languages (C) through VHPIDIRECT:
https://ghdl.readthedocs.io/en/latest/using/Foreign.html
An array of type uint8_t is allocated and some values are written to the first 1/3
positions. Then, the VHDL simulation is executed, where the (external) array/buffer
is used. When the simulation is finished, the results are checked. The content of
the buffer is printed both before and after the simulation.
NOTE: This file is expected to be used along with tb_ext_byte_vector.vhd or tb_ext_string.vhd
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

extern int ghdl_main (int argc, char **argv);

uint8_t *D[1];
const uint32_t length = 5;

// Check procedure, to be executed when GHDL exits.
// The simulation is expected to copy the first 1/3 elements to positions [1/3, 2/3),
// while incrementing each value by one, and then copy elements from [1/3, 2/3) to
// [2/3, 3/3), while incrementing each value by two.
static void exit_handler(void) {
int i, j, z, k;
uint8_t expected, got;
k = 0;
for (j=0; j<3; j++) {
k += j;
for(i=0; i<length; i++) {
z = (length*j)+i;

expected = (i+1)*11 + k;
got = D[0][z];
if (expected != got) {
printf("check error %d: %d %d\n", z, expected, got);
exit(1);
}
printf("%d: %d\n", z, got);
}
}
free(D[0]);
}

// Main entrypoint of the application
int main(int argc, char **argv) {
// Allocate a buffer which is three times the number of values
// that we want to copy/modify
D[0] = (uint8_t *) malloc(3*length*sizeof(uint8_t));
if ( D[0] == NULL ) {
perror("execution of malloc() failed!\n");
return -1;
}
// Initialize the first 1/3 of the buffer
int i;
for(i=0; i<length; i++) {
D[0][i] = (i+1)*11;
}
// Print all the buffer
for(i=0; i<3*length; i++) {
printf("%d: %d\n", i, D[0][i]);
}

// Register a function to be called when GHDL exits
atexit(exit_handler);

// Start the simulation
return ghdl_main(argc, argv);
}

// External through access (mode<0)

void set_string_ptr(uint8_t id, uint8_t *p) {
//printf("C set_string_ptr(%d, %p)\n", id, p);
D[id] = p;
}

uintptr_t get_string_ptr(uint8_t id) {
//printf("C get_string_ptr(%d): %p\n", id, D[id]);
return (uintptr_t)D[id];
}

// External through functions (mode>0)

void write_char(uint8_t id, uint32_t i, uint8_t v ) {
//printf("C write_char(%d, %d): %d\n", id, i, v);
D[id][i] = v;
}

uint8_t read_char(uint8_t id, uint32_t i) {
//printf("C read_char(%d, %d): %d\n", id, i, D[id][i]);
return D[id][i];
}
54 changes: 54 additions & 0 deletions examples/vhdl/external_buffer/src/tb_ext_byte_vector.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this file,
-- You can obtain one at http://mozilla.org/MPL/2.0/.
--
-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com

-- NOTE: This file is expected to be used along with foreign languages (C)
-- through VHPIDIRECT: https://ghdl.readthedocs.io/en/latest/using/Foreign.html
-- See main.c for an example of a wrapper application.

--library vunit_lib;
--context vunit_lib.vunit_context;

library vunit_lib;
use vunit_lib.run_pkg.all;
use vunit_lib.logger_pkg.all;
use vunit_lib.byte_vector_ptr_pkg.all;

entity tb_external_byte_vector is
generic ( runner_cfg : string );
end entity;

architecture tb of tb_external_byte_vector is

constant block_len : natural := 5;

constant ebuf: byte_vector_ptr_t := new_byte_vector_ptr( 3*block_len, extfnc); -- external through VHPIDIRECT functions 'read_char' and 'write_char'
constant abuf: byte_vector_ptr_t := new_byte_vector_ptr( 3*block_len, extacc); -- external through access (requires VHPIDIRECT function 'get_string_ptr')

begin

main: process
variable val, ind: integer;
begin
test_runner_setup(runner, runner_cfg);
info("Init test");
for x in 0 to block_len-1 loop
val := get(ebuf, x) + 1;
ind := block_len+x;
set(ebuf, ind, val);
info("SET " & to_string(ind) & ": " & to_string(val));
end loop;
for x in block_len to 2*block_len-1 loop
val := get(abuf, x) + 2;
ind := block_len+x;
set(abuf, ind, val);
info("SET " & to_string(ind) & ": " & to_string(val));
end loop;
info("End test");
test_runner_cleanup(runner);
wait;
end process;

end architecture;
54 changes: 54 additions & 0 deletions examples/vhdl/external_buffer/src/tb_ext_string.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this file,
-- You can obtain one at http://mozilla.org/MPL/2.0/.
--
-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com

-- NOTE: This file is expected to be used along with foreign languages (C)
-- through VHPIDIRECT: https://ghdl.readthedocs.io/en/latest/using/Foreign.html
-- See main.c for an example of a wrapper application.

--library vunit_lib;
--context vunit_lib.vunit_context;

library vunit_lib;
use vunit_lib.run_pkg.all;
use vunit_lib.logger_pkg.all;
use vunit_lib.string_ptr_pkg.all;

entity tb_external_string is
generic ( runner_cfg : string );
end entity;

architecture tb of tb_external_string is

constant block_len : natural := 5;

constant ebuf: string_ptr_t := new_string_ptr( 3*block_len, extfnc); -- external through VHPIDIRECT functions 'read_char' and 'write_char'
constant abuf: string_ptr_t := new_string_ptr( 3*block_len, extacc); -- external through access (requires VHPIDIRECT function 'get_string_ptr')

begin

main: process
variable val, ind: integer;
begin
test_runner_setup(runner, runner_cfg);
info("Init test");
for x in 1 to block_len loop
val := get(ebuf, x) + 1;
ind := block_len+x;
set(ebuf, ind, val);
info("SET " & to_string(ind) & ": " & to_string(val));
end loop;
for x in block_len+1 to 2*block_len loop
val := get(abuf, x) + 2;
ind := block_len+x;
set(abuf, ind, val);
info("SET " & to_string(ind) & ": " & to_string(val));
end loop;
info("End test");
test_runner_cleanup(runner);
wait;
end process;

end architecture;
9 changes: 8 additions & 1 deletion vunit/test/acceptance/test_external_run_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import sys
from vunit import ROOT
from vunit.builtins import VHDL_PATH
from vunit.test.common import has_simulator, check_report, simulator_is
from vunit.test.common import has_simulator, check_report, simulator_is, simulator_check


def simulator_supports_verilog():
Expand Down Expand Up @@ -115,6 +115,13 @@ def test_vhdl_array_axis_vcs_example_project(self):
def test_vhdl_axi_dma_example_project(self):
self.check(join(ROOT, "examples", "vhdl", "axi_dma", "run.py"))

@unittest.skipIf(
simulator_check(lambda simclass: not simclass.supports_vhpi()),
"This simulator/backend does not support interfacing with external C code"
)
def test_vhdl_external_buffer_project(self):
self.check(join(ROOT, "examples", "vhdl", "external_buffer", "run.py"))

def test_vhdl_user_guide_example_project(self):
self.check(join(ROOT, "examples", "vhdl", "user_guide", "run.py"), exit_code=1)
check_report(self.report_file,
Expand Down

0 comments on commit fa5d6ed

Please sign in to comment.