From 97c153804969d09474fa9c753c5e3d256ac4f97e Mon Sep 17 00:00:00 2001 From: umarcor Date: Sat, 6 Apr 2019 01:51:11 +0200 Subject: [PATCH 1/2] style(examples/vhdl/array_axis_vcs): add tb_vc_axis_loop.vhd to be used by both tb_py_* and tb_c_* --- .../{tb_axis_loop.vhd => tb_py_axis_loop.vhd} | 91 ++++++------------- .../src/test/tb_vc_axis_loop.vhd | 78 ++++++++++++++++ 2 files changed, 105 insertions(+), 64 deletions(-) rename examples/vhdl/array_axis_vcs/src/test/{tb_axis_loop.vhd => tb_py_axis_loop.vhd} (68%) create mode 100644 examples/vhdl/array_axis_vcs/src/test/tb_vc_axis_loop.vhd diff --git a/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd b/examples/vhdl/array_axis_vcs/src/test/tb_py_axis_loop.vhd similarity index 68% rename from examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd rename to examples/vhdl/array_axis_vcs/src/test/tb_py_axis_loop.vhd index 301c8a38c..767656cc7 100644 --- a/examples/vhdl/array_axis_vcs/src/test/tb_axis_loop.vhd +++ b/examples/vhdl/array_axis_vcs/src/test/tb_py_axis_loop.vhd @@ -18,7 +18,7 @@ library vunit_lib; context vunit_lib.vunit_context; context vunit_lib.vc_context; -entity tb_axis_loop is +entity tb_py_axis_loop is generic ( runner_cfg : string; tb_path : string; @@ -27,7 +27,7 @@ entity tb_axis_loop is ); end entity; -architecture tb of tb_axis_loop is +architecture tb of tb_py_axis_loop is -- Simulation constants @@ -38,22 +38,19 @@ architecture tb of tb_axis_loop is constant master_axi_stream : axi_stream_master_t := new_axi_stream_master(data_length => data_width); constant slave_axi_stream : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width); - - -- Signals to/from the UUT from/to the verification components - - signal m_valid, m_ready, m_last, s_valid, s_ready, s_last : std_logic; - signal m_data, s_data : std_logic_vector(data_length(master_axi_stream)-1 downto 0); + constant m_axis : axi_stream_master_t := new_axi_stream_master(data_length => data_width); + constant s_axis : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width); -- tb signals and variables signal clk, rst, rstn : std_logic := '0'; constant m_I : integer_array_t := load_csv(tb_path & csv_i); constant m_O : integer_array_t := new_2d(width(m_I), height(m_I), data_width, true); - signal start, done, saved : boolean := false; + signal start, sent, saved : boolean := false; begin - clk <= not clk after clk_period/2; + clk <= not clk after (clk_period/2); rstn <= not rst; main: process @@ -61,7 +58,7 @@ begin info("Init test"); wait until rising_edge(clk); start <= true; wait until rising_edge(clk); start <= false; - wait until (done and saved and rising_edge(clk)); + wait until (sent and saved and rising_edge(clk)); info("Test done"); end procedure; begin @@ -78,12 +75,13 @@ begin wait; end process; +-- + stimuli: process variable last : std_logic; begin + sent <= false; wait until start and rising_edge(clk); - done <= false; - wait until rising_edge(clk); info("Sending m_I of size " & to_string(height(m_I)) & "x" & to_string(width(m_I)) & " to UUT..."); @@ -91,29 +89,30 @@ begin for x in 0 to width(m_I)-1 loop wait until rising_edge(clk); if x = width(m_I)-1 then last := '1'; else last := '0'; end if; - push_axi_stream(net, master_axi_stream, std_logic_vector(to_signed(get(m_I, x, y), data_width)) , tlast => last); + push_axi_stream(net, m_axis, std_logic_vector(to_signed(get(m_I, x, y), data_width)) , tlast => last); end loop; end loop; info("m_I sent!"); wait until rising_edge(clk); - done <= true; + sent <= true; + wait; end process; save: process variable o : std_logic_vector(31 downto 0); variable last : std_logic:='0'; begin - wait until start and rising_edge(clk); saved <= false; + wait until start and rising_edge(clk); wait for 50*clk_period; info("Receiving m_O of size " & to_string(height(m_O)) & "x" & to_string(width(m_O)) & " from UUT..."); for y in 0 to height(m_O)-1 loop for x in 0 to width(m_O)-1 loop - pop_axi_stream(net, slave_axi_stream, tdata => o, tlast => last); + pop_axi_stream(net, s_axis, tdata => o, tlast => last); if (x = width(m_O)-1) and (last='0') then error("Something went wrong. Last misaligned!"); end if; @@ -130,57 +129,21 @@ begin wait until rising_edge(clk); saved <= true; + wait; end process; -- - vunit_axism: entity vunit_lib.axi_stream_master - generic map ( - master => master_axi_stream - ) - port map ( - aclk => clk, - tvalid => m_valid, - tready => m_ready, - tdata => m_data, - tlast => m_last - ); - - vunit_axiss: entity vunit_lib.axi_stream_slave - generic map ( - slave => slave_axi_stream - ) - port map ( - aclk => clk, - tvalid => s_valid, - tready => s_ready, - tdata => s_data, - tlast => s_last - ); - --- - - uut: entity work.axis_buffer - generic map ( - data_width => data_width, - fifo_depth => 4 - ) - port map ( - s_axis_clk => clk, - s_axis_rstn => rstn, - s_axis_rdy => m_ready, - s_axis_data => m_data, - s_axis_valid => m_valid, - s_axis_strb => "1111", - s_axis_last => m_last, - - m_axis_clk => clk, - m_axis_rstn => rstn, - m_axis_valid => s_valid, - m_axis_data => s_data, - m_axis_rdy => s_ready, - m_axis_strb => open, - m_axis_last => s_last - ); + uut_vc: entity work.tb_vc_axis_loop + generic map ( + m_axis => m_axis, + s_axis => s_axis, + data_width => data_width, + fifo_depth => 4 + ) + port map ( + clk => clk, + rstn => rstn + ); end architecture; diff --git a/examples/vhdl/array_axis_vcs/src/test/tb_vc_axis_loop.vhd b/examples/vhdl/array_axis_vcs/src/test/tb_vc_axis_loop.vhd new file mode 100644 index 000000000..f2913444f --- /dev/null +++ b/examples/vhdl/array_axis_vcs/src/test/tb_vc_axis_loop.vhd @@ -0,0 +1,78 @@ +-- 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 + +library ieee; +context ieee.ieee_std_context; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; + +entity tb_vc_axis_loop is + generic ( + m_axis : axi_stream_master_t; + s_axis : axi_stream_slave_t; + data_width : natural; + fifo_depth : natural + ); + port ( + clk, rstn: in std_logic + ); +end entity; + +architecture vc of tb_vc_axis_loop is + + signal m_valid, m_ready, m_last, s_valid, s_ready, s_last : std_logic; + signal m_data, s_data : std_logic_vector(data_length(m_axis)-1 downto 0); + +begin + + vunit_axism: entity vunit_lib.axi_stream_master + generic map ( + master => m_axis) + port map ( + aclk => clk, + tvalid => m_valid, + tready => m_ready, + tdata => m_data, + tlast => m_last); + + vunit_axiss: entity vunit_lib.axi_stream_slave + generic map ( + slave => s_axis) + port map ( + aclk => clk, + tvalid => s_valid, + tready => s_ready, + tdata => s_data, + tlast => s_last); + +-- + + uut: entity work.axis_buffer + generic map ( + data_width => data_width, + fifo_depth => fifo_depth + ) + port map ( + s_axis_clk => clk, + s_axis_rstn => rstn, + s_axis_rdy => m_ready, + s_axis_data => m_data, + s_axis_valid => m_valid, + s_axis_strb => "1111", + s_axis_last => m_last, + + m_axis_clk => clk, + m_axis_rstn => rstn, + m_axis_valid => s_valid, + m_axis_data => s_data, + m_axis_rdy => s_ready, + m_axis_strb => open, + m_axis_last => s_last + ); + +end architecture; From acbbd1c0d714c069955afe7b9d3dcb2c1d099843 Mon Sep 17 00:00:00 2001 From: umarcor Date: Sat, 6 Apr 2019 01:51:18 +0200 Subject: [PATCH 2/2] extend(examples/vhdl/array_axis_vcs): integration of foreign C declarations with GHDL --- examples/vhdl/array_axis_vcs/run.py | 23 ++- examples/vhdl/array_axis_vcs/src/test/main.c | 87 ++++++++++++ .../vhdl/array_axis_vcs/src/test/pkg_c.vhd | 81 +++++++++++ examples/vhdl/array_axis_vcs/src/test/stubs.c | 13 ++ .../src/test/tb_c_axis_loop.vhd | 133 ++++++++++++++++++ 5 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 examples/vhdl/array_axis_vcs/src/test/main.c create mode 100644 examples/vhdl/array_axis_vcs/src/test/pkg_c.vhd create mode 100644 examples/vhdl/array_axis_vcs/src/test/stubs.c create mode 100644 examples/vhdl/array_axis_vcs/src/test/tb_c_axis_loop.vhd diff --git a/examples/vhdl/array_axis_vcs/run.py b/examples/vhdl/array_axis_vcs/run.py index 3fe728b27..2169c1dcf 100644 --- a/examples/vhdl/array_axis_vcs/run.py +++ b/examples/vhdl/array_axis_vcs/run.py @@ -17,6 +17,7 @@ """ from pathlib import Path +from os import popen from vunit import VUnit VU = VUnit.from_argv() @@ -24,8 +25,28 @@ SRC_PATH = Path(__file__).parent / "src" -VU.add_library("lib").add_source_files([SRC_PATH / "*.vhd", SRC_PATH / "**" / "*.vhd"]) +LIB = vu.add_library("lib") +LIB.add_source_files([SRC_PATH / "*.vhd", SRC_PATH / "**" / "*.vhd"]) # vu.set_sim_option('modelsim.init_files.after_load',['runall_addwave.do']) +C_NOBJ = join(src_path, "test", "stubs.o") +C_OBJ = join(src_path, "test", "main.o") +print( + popen( + "gcc -fPIC -rdynamic -c " + join(src_path, "**", "stubs.c") + " -o " + C_NOBJ + ).read() +) +print( + popen( + "gcc -fPIC -rdynamic -c " + join(src_path, "**", "main.c") + " -o " + C_OBJ + ).read() +) + +for tb in lib.get_test_benches(pattern="*tb_py_*", allow_empty=False): + tb.set_sim_option("ghdl.elab_flags", ["-Wl," + C_NOBJ], overwrite=False) + +for tb in lib.get_test_benches(pattern="*tb_c_*", allow_empty=False): + tb.set_sim_option("ghdl.elab_flags", ["-Wl," + C_OBJ], overwrite=False) + VU.main() diff --git a/examples/vhdl/array_axis_vcs/src/test/main.c b/examples/vhdl/array_axis_vcs/src/test/main.c new file mode 100644 index 000000000..b1806f18f --- /dev/null +++ b/examples/vhdl/array_axis_vcs/src/test/main.c @@ -0,0 +1,87 @@ +#include +#include +#include + +extern int ghdl_main (int argc, char **argv); + +uint8_t *V[2]; +uint32_t length = 100; + +// get_param is used by GHDL to retrieve parameter values (integers). +uint32_t get_param(uint32_t w) { + uint32_t o = 0; + switch(w) { + case 0 : // buffer length + o = length; + break;; + case 1 : // data width (in bits) + o = 8*sizeof(int32_t); + break;; + case 2 : // fifo depth + o = 5; + break;; + } + printf("get_p(%d): %d\n", w, (int)o); + return o; +} + +void write_byte ( uint8_t id, uint32_t i, uint8_t v ) { + V[id][i] = v; +} + +uint8_t read_byte ( uint8_t id, uint32_t i ) { + return V[id][i]; +} + +// check evaluates if the result produced by the UUT is equivalent to some other softwre procedure. +int check(int32_t *I, int32_t *O, uint32_t l) { + int i; + for ( i=0 ; i Call 'check'\n"); + if ( check((int32_t*)V[0], (int32_t*)V[1], length) != 0 ) { + printf("check failed!\n"); + return -1; + } + + // Free the allocated memory, since we don't need it anymore. + free(V[0]); + free(V[1]); + + return 0; +} diff --git a/examples/vhdl/array_axis_vcs/src/test/pkg_c.vhd b/examples/vhdl/array_axis_vcs/src/test/pkg_c.vhd new file mode 100644 index 000000000..e567f5ef6 --- /dev/null +++ b/examples/vhdl/array_axis_vcs/src/test/pkg_c.vhd @@ -0,0 +1,81 @@ +-- 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 + +library ieee; +context ieee.ieee_std_context; + +package pkg_c is + + function get_param(f: integer) return integer; + attribute foreign of get_param : function is "VHPIDIRECT get_param"; + + procedure write_byte ( id, i, v: integer ) ; + attribute foreign of write_byte : procedure is "VHPIDIRECT write_byte"; + + impure function read_byte ( id, i: integer ) return integer; + attribute foreign of read_byte : function is "VHPIDIRECT read_byte"; + + type memory_t is record + -- Private + p_id: integer; + end record; + + impure function new_memory(id: integer := -1) return memory_t; + procedure write_word ( memory: memory_t; i: natural; w: std_logic_vector ); + impure function read_word ( memory: memory_t; i, bytes_per_word: integer ) return std_logic_vector; + + procedure write_byte ( memory: memory_t; i: integer; v: std_logic_vector(7 downto 0) ); + impure function read_byte ( memory: memory_t; i: integer ) return std_logic_vector; + +end pkg_c; + +package body pkg_c is + + -- VHPI + + function get_param(f: integer) return integer is begin + assert false report "VHPI" severity failure; + end function; + + procedure write_byte ( id, i, v: integer ) is begin + assert false report "VHPI" severity failure; + end procedure; + + impure function read_byte ( id, i: integer ) return integer is begin + assert false report "VHPI" severity failure; + end function; + + -- VHDL + + procedure write_byte ( memory: memory_t; i: integer; v: std_logic_vector(7 downto 0) ) is + begin + write_byte(memory.p_id, i, to_integer(unsigned(v))); + end procedure; + + impure function read_byte ( memory: memory_t; i: integer ) return std_logic_vector is begin + return std_logic_vector(to_unsigned(read_byte(memory.p_id, i), 8)); + end function; + + impure function new_memory(id: integer := -1) return memory_t is begin + return (p_id => id); + end; + + procedure write_word ( memory: memory_t; i: natural; w: std_logic_vector ) is begin + for idx in 0 to w'length/8-1 loop + write_byte(memory, i + idx, w(8*idx+7 downto 8*idx)); + end loop; + end procedure; + + impure function read_word ( memory: memory_t; i, bytes_per_word: integer ) return std_logic_vector is + variable tmp: std_logic_vector(31 downto 0); + begin + for idx in 0 to bytes_per_word-1 loop + tmp(8*idx+7 downto 8*idx) := read_byte(memory, i + idx); + end loop; + return tmp; + end function; + +end pkg_c; diff --git a/examples/vhdl/array_axis_vcs/src/test/stubs.c b/examples/vhdl/array_axis_vcs/src/test/stubs.c new file mode 100644 index 000000000..af5fbb066 --- /dev/null +++ b/examples/vhdl/array_axis_vcs/src/test/stubs.c @@ -0,0 +1,13 @@ +#include + +extern int ghdl_main (int argc, char **argv); + +// main is the entrypoint to the application. +int main(int argc, char **argv) { + + printf("This is an example of how to wrap a GHDL + VUnit simulation in a C application.\n"); + + ghdl_main(argc, argv); + + return 0; +} \ No newline at end of file diff --git a/examples/vhdl/array_axis_vcs/src/test/tb_c_axis_loop.vhd b/examples/vhdl/array_axis_vcs/src/test/tb_c_axis_loop.vhd new file mode 100644 index 000000000..4ec135652 --- /dev/null +++ b/examples/vhdl/array_axis_vcs/src/test/tb_c_axis_loop.vhd @@ -0,0 +1,133 @@ +-- 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 + +-- This testbench is a Minimum Working Example (MWE) of VUnit's resources to read/write data from a buffer +-- allocated in a foreign C application, and to verify AXI4-Stream components. Data is sent to an AXI4-Stream +-- Slave. The AXI4-Stream Slave is expected to be connected to an AXI4-Stream Master either directly or +-- (preferredly) through a FIFO, thus composing a loopback. Therefore, as data is pushed to the AXI4-Stream +-- Slave interface, the output is read from the AXI4-Stream Master interface and it is saved back to the buffer +-- shared with the software application. + +library ieee; +context ieee.ieee_std_context; + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.vc_context; + +use work.pkg_c; +use work.pkg_c.all; + +entity tb_c_axis_loop is + generic ( + runner_cfg : string; + tb_path : string + ); +end entity; + +architecture tb of tb_c_axis_loop is + -- Simulation constants + + constant clk_period : time := 20 ns; + constant stream_length : integer := get_param(0); + constant data_width : natural := get_param(1); + constant fifo_depth : natural := get_param(2); + + -- AXI4Stream Verification Components + + constant m_axis : axi_stream_master_t := new_axi_stream_master(data_length => data_width); + constant s_axis : axi_stream_slave_t := new_axi_stream_slave(data_length => data_width); + + constant ibuffer: pkg_c.memory_t := pkg_c.new_memory(0); + constant obuffer: pkg_c.memory_t := pkg_c.new_memory(1); + + -- tb signals and variables + + signal clk, rst, rstn : std_logic := '0'; + signal start, sent, saved : boolean := false; + +begin + + clk <= not clk after (clk_period/2); + rstn <= not rst; + + main: process + procedure run_test is begin + info("Init test"); + wait until rising_edge(clk); start <= true; + wait until rising_edge(clk); start <= false; + wait until (sent and saved and rising_edge(clk)); + info("Test done"); + end procedure; + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("test") then + rst <= '1'; + wait for 15*clk_period; + rst <= '0'; + run_test; + end if; + end loop; + test_runner_cleanup(runner); + wait; + end process; + +-- + + stimuli: process + variable last : std_logic; + begin + sent <= false; + wait until start and rising_edge(clk); + + for y in 0 to stream_length-1 loop + wait until rising_edge(clk); + push_axi_stream(net, m_axis, pkg_c.read_word(ibuffer, 4*y, 4) , tlast => '0'); + end loop; + + info("m_I sent!"); + + wait until rising_edge(clk); + sent <= true; + wait; + end process; + + save: process + variable o : std_logic_vector(31 downto 0); + variable last : std_logic:='0'; + begin + saved <= false; + wait until start and rising_edge(clk); + wait for 50*clk_period; + + for y in 0 to stream_length-1 loop + pop_axi_stream(net, s_axis, tdata => o, tlast => last); + pkg_c.write_word(obuffer, 4*y, o); + end loop; + + info("m_O read!"); + + wait until rising_edge(clk); + saved <= true; + wait; + end process; + +-- + + uut_vc: entity work.tb_vc_axis_loop + generic map ( + m_axis => m_axis, + s_axis => s_axis, + data_width => data_width, + fifo_depth => fifo_depth + ) + port map ( + clk => clk, + rstn => rstn + ); + +end architecture;