Skip to content

Commit db16c32

Browse files
committed
merge branch 'tutorial'
2 parents bd06ebb + c60f295 commit db16c32

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3539
-14
lines changed

.github/workflows/tests.yml

+15-6
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ on:
1010
jobs:
1111

1212

13-
# GNU/Linux: VUnit Action (uses Docker image ghdl/vunit:llvm)
13+
# GNU/Linux: VUnit Action (uses Docker image ghdl/vunit:mcode)
1414

1515
lin-vunit:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- uses: actions/checkout@v2
19-
- uses: VUnit/[email protected]
19+
- uses: VUnit/vunit_action@master
20+
with:
21+
cmd: tdd/run.py
2022

2123

2224
# GNU/Linux: Custom Docker image
@@ -28,7 +30,13 @@ jobs:
2830
steps:
2931
- uses: actions/checkout@v2
3032
- run: docker build -t vunit/tdd - < .github/Dockerfile
31-
- run: docker run --rm -v $(pwd):/src -w /src -e CI vunit/tdd python3 -m pytest -v -s -ra test.py --color=yes
33+
- run: >-
34+
docker run --rm
35+
-v $(pwd):/src
36+
-w /src
37+
-e CI
38+
vunit/tdd
39+
python3 -m pytest -v -s -ra tdd/test_tdd.py tutorial/test_tutorial.py --color=yes
3240
3341
3442
# GNU/Linux: GHDL Action + custom Python packages
@@ -45,7 +53,7 @@ jobs:
4553
python-version: 3.8
4654
- run: |
4755
python -m pip install --progress-bar off pytest vunit_hdl
48-
python -m pytest -v -s -ra test.py --color=yes
56+
python -m pytest -v -s -ra tdd/test_tdd.py tutorial/test_tutorial.py --color=yes
4957
5058
5159
# Windows: MSYS2 Action + GHDL Action + custom Python packages
@@ -67,7 +75,7 @@ jobs:
6775
backend: llvm
6876
- run: |
6977
python -m pip install --progress-bar off pytest vunit_hdl
70-
python -m pytest -v -s -ra test.py --color=yes
78+
python -m pytest -v -s -ra tdd/test_tdd.py tutorial/test_tutorial.py --color=yes
7179
7280
7381
# Windows: standalone GHDL zipfile/tarball + custom Python packages
@@ -88,5 +96,6 @@ jobs:
8896
mv ../ghdl-tmp/GHDL/${WINDOWS_RELEASE}/ ../ghdl
8997
rm -rf ../ghdl-tmp ghdl.zip
9098
export PATH=$PATH:$(pwd)/../ghdl/bin
99+
91100
python -m pip install --progress-bar off pytest vunit_hdl
92-
python -m pytest -v -s -ra test.py --color=yes
101+
python -m pytest -v -s -ra tdd/test_tdd.py tutorial/test_tutorial.py --color=yes

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__/
2+
vunit_out/

run.py renamed to tdd/run.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/env python3
2+
13
"""VUnit run script."""
24

35
from pathlib import Path
File renamed without changes.
File renamed without changes.

test.py renamed to tdd/test_tdd.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import sys
22
from sys import executable, platform
3-
from os import environ
43
from pathlib import Path
54
from subprocess import check_call, STDOUT
65
from shutil import which
76
import unittest
8-
import pytest
97

108

119
class TestExamples(unittest.TestCase):
@@ -14,23 +12,21 @@ class TestExamples(unittest.TestCase):
1412
"""
1513

1614
def setUp(self):
17-
self.shell = [which('bash')] if platform == 'win32' else []
15+
self.shell = [which("bash")] if platform == "win32" else []
1816
self.root = Path(__file__).parent
1917

20-
print('\n::group::Log')
18+
print("\n::group::Log")
2119
sys.stdout.flush()
2220

2321
def tearDown(self):
24-
print('\n::endgroup::')
22+
print("\n::endgroup::")
2523
sys.stdout.flush()
2624

27-
2825
def _sh(self, args):
2926
check_call(self.shell + args, stderr=STDOUT)
3027

3128
def _py(self, args):
3229
check_call([executable] + args, stderr=STDOUT)
3330

34-
3531
def test_vunit_runpy(self):
36-
self._py([str(self.root / 'run.py'), '-v'])
32+
self._py([str(self.root / "run.py"), "-v"])

tutorial/common/src/incrementer.vhd

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
library ieee;
2+
use ieee.std_logic_1164.all;
3+
use ieee.numeric_std_unsigned.all;
4+
5+
use work.incrementer_pkg.all;
6+
7+
entity incrementer is
8+
generic(
9+
delay : positive := 1);
10+
port(
11+
clk : in std_logic;
12+
13+
input_tdata : in std_logic_vector;
14+
input_tvalid : in std_logic;
15+
input_tready : out std_logic;
16+
input_tlast : in std_logic := '1';
17+
18+
output_tdata : out std_logic_vector;
19+
output_tvalid : out std_logic := '0';
20+
output_tready : in std_logic := '1';
21+
output_tlast : out std_logic := '0';
22+
23+
ctrl_arready : out std_logic := '0';
24+
ctrl_arvalid : in std_logic := '0';
25+
ctrl_araddr : in std_logic_vector(7 downto 0) := x"00";
26+
27+
ctrl_rready : in std_logic := '0';
28+
ctrl_rvalid : out std_logic := '0';
29+
ctrl_rdata : out std_logic_vector(31 downto 0) := (others => '0');
30+
ctrl_rresp : out std_logic_vector(1 downto 0) := "00";
31+
32+
ctrl_awready : out std_logic := '0';
33+
ctrl_awvalid : in std_logic := '0';
34+
ctrl_awaddr : in std_logic_vector(7 downto 0) := x"00";
35+
36+
ctrl_wready : out std_logic := '0';
37+
ctrl_wvalid : in std_logic := '0';
38+
ctrl_wdata : in std_logic_vector(31 downto 0) := (others => '0');
39+
ctrl_wstrb : in std_logic_vector(3 downto 0) := x"0";
40+
41+
ctrl_bvalid : out std_logic := '0';
42+
ctrl_bready : in std_logic := '0';
43+
ctrl_bresp : out std_logic_vector(1 downto 0) := "00");
44+
begin
45+
assert input_tdata'length = output_tdata'length;
46+
end;
47+
48+
architecture a of incrementer is
49+
type state_t is (idle,
50+
writing,
51+
write_response,
52+
reading);
53+
54+
signal increment : std_logic_vector(input_tdata'length - 1 downto 0) := (0 => '1', others => '0');
55+
signal state : state_t := idle;
56+
signal addr : std_logic_vector(ctrl_araddr'range);
57+
begin
58+
main : process is
59+
type delay_line_t is array (natural range <>) of std_logic_vector(input_tdata'length + 1 downto 0);
60+
variable delay_line : delay_line_t(1 to delay + 1) := (others => (others => '0'));
61+
begin
62+
wait until rising_edge(clk);
63+
if not output_tvalid or output_tready then
64+
delay_line(1) := input_tlast & (input_tvalid and input_tready) & input_tdata;
65+
delay_line(2 to delay) := delay_line(1 to delay - 1);
66+
end if;
67+
output_tlast <= delay_line(delay)(input_tdata'length + 1);
68+
output_tvalid <= delay_line(delay)(input_tdata'length);
69+
output_tdata <= delay_line(delay)(input_tdata'length - 1 downto 0) + increment;
70+
end process main;
71+
72+
input_tready <= not output_tvalid or output_tready;
73+
74+
ctrl : process is
75+
constant axi_response_ok : std_logic_vector(1 downto 0) := "00";
76+
constant axi_response_decerr : std_logic_vector(1 downto 0) := "11";
77+
78+
-- Compare addresses of 32-bit words discarding byte address
79+
function cmp_word_address(byte_addr : std_logic_vector;
80+
word_addr : natural) return boolean is
81+
begin
82+
return to_integer(byte_addr(byte_addr'left downto 2)) = word_addr/4;
83+
end;
84+
85+
begin
86+
wait until rising_edge(clk);
87+
88+
ctrl_arready <= '0';
89+
ctrl_awready <= '0';
90+
ctrl_wready <= '0';
91+
ctrl_rvalid <= '0';
92+
ctrl_rdata <= (others => '0');
93+
94+
case state is
95+
when idle =>
96+
97+
if ctrl_arvalid then
98+
ctrl_arready <= '1';
99+
addr <= ctrl_araddr;
100+
state <= reading;
101+
102+
elsif ctrl_awvalid then
103+
ctrl_awready <= '1';
104+
addr <= ctrl_awaddr;
105+
state <= writing;
106+
end if;
107+
108+
when writing =>
109+
if ctrl_wvalid then
110+
ctrl_wready <= '1';
111+
112+
ctrl_bvalid <= '1';
113+
ctrl_bresp <= axi_response_ok;
114+
115+
state <= write_response;
116+
117+
-- Ignore byte write enable
118+
if cmp_word_address(addr, increment_reg_addr) then
119+
increment <= ctrl_wdata(increment'length - 1 downto 0);
120+
end if;
121+
end if;
122+
123+
when write_response =>
124+
if ctrl_bready then
125+
ctrl_bvalid <= '0';
126+
state <= idle;
127+
end if;
128+
129+
when reading =>
130+
if not ctrl_rvalid then
131+
ctrl_rvalid <= '1';
132+
ctrl_rresp <= axi_response_ok;
133+
134+
if cmp_word_address(addr, increment_reg_addr) then
135+
ctrl_rdata(increment'range) <= increment;
136+
else
137+
ctrl_rresp <= axi_response_decerr;
138+
end if;
139+
140+
elsif ctrl_rready then
141+
state <= idle;
142+
end if;
143+
end case;
144+
145+
end process ctrl;
146+
end;
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package incrementer_pkg is
2+
constant increment_reg_addr : natural := 0;
3+
end package incrementer_pkg;

tutorial/exercise_01/instructions.md

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Exercise 01
2+
## Purpose
3+
4+
* Learn how to write a `run.py` script
5+
* Learn how to compile your project
6+
* Learn how to make your existing testbench VUnit compliant
7+
* Learn how a testbench fails and how it passes
8+
9+
After this exercise you will be able to turn a non-VUnit testbench into a fully automated VUnit testbench.
10+
11+
More information on this topic can be found in our [user guide](http://vunit.github.io/user_guide.html) and in the run library [documentation](https://vunit.github.io/run/user_guide.html).
12+
13+
## Instructions
14+
15+
* Create a copy of `exercise_01/original` named `exercise_01/workspace`.
16+
* Open `exercise_01/workspace/run.py` in your text editor. Get acquainted with the content.
17+
* If you have **more than one** simulator on your path you need to select one of them. `<simulator>` = `ghdl`, `modelsim`, `rivierapro`, `activehdl` or `incisive`.
18+
``` console
19+
set VUNIT_SIMULATOR=<simulator>
20+
```
21+
* Run the script from the command line with the compile option:
22+
23+
``` console
24+
python run.py --compile
25+
```
26+
27+
All project files (just `src/counter.vhd`) and VUnit packages are found and compiled in dependency order.
28+
29+
* Re-run the compile command. Nothing is re-compiled because nothing has changed. You can always do a clean compile
30+
31+
``` console
32+
python run.py --compile --clean
33+
```
34+
35+
* Modify `run.py` to add a `tb_lib` library and add all source files in `test/` to that library
36+
37+
``` python
38+
tb_lib = prj.add_library("tb_lib")
39+
tb_lib.add_source_files(join(root, "test", "*.vhd"))
40+
```
41+
42+
* Re-compile:
43+
44+
``` console
45+
python run.py --compile
46+
```
47+
48+
Only `tb_counter` needs to be compiled.
49+
50+
* Open `test/tb_counter.vhd` and look at the five lines making this a VUnit testbench
51+
52+
``` vhdl
53+
-- Include VUnit functionality
54+
library vunit_lib;
55+
context vunit_lib.vunit_context;
56+
57+
-- The testbench is controlled through the runner_cfg generic
58+
runner_cfg : string;
59+
60+
-- Setup VUnit
61+
test_runner_setup(runner, runner_cfg);
62+
63+
-- This line will force the simulation to a stop
64+
test_runner_cleanup(runner);
65+
```
66+
67+
* Run `run.py` and watch it fail
68+
69+
``` console
70+
python run.py
71+
```
72+
73+
* Remove/fix/comment these lines, one by one, and run the script in between. Watch the different ways to fail
74+
75+
``` vhdl
76+
-- Different ways to fail
77+
assert count = 1;
78+
check_equal(count, 1);
79+
info("LSB of count = " & to_string(count(width)));
80+
wait for 1 hr;
81+
```
82+
83+
Failing on a testbench that is stuck requires the `test_runner_watchdog`. It's not required for a VUnit testbench but can save you from wasting a lot of simulation time.
84+
85+
``` vhdl
86+
test_runner_watchdog(runner, 1 ms);
87+
```
88+
89+
Full automation requires that all types of errors are detected and handled gracefully.
90+
* The previous errors caused an immediate stop of the simulation. Using VUnit checks you can control when the simulation stops. Run again to see multiple errors.
91+
92+
* Remove the remaining lines before `test_runner_cleanup` to make the testbench pass.

tutorial/exercise_01/original/run.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Import Python modules
2+
from vunit import VUnit
3+
from pathlib import Path
4+
5+
# Setup Python test runner project from command line arguments
6+
PRJ = VUnit.from_argv()
7+
8+
# Set the root to the directory of this script file
9+
ROOT = Path(__file__).resolve().parent
10+
11+
# Create and add VHDL Libraries to project
12+
LIB = PRJ.add_library("lib")
13+
14+
# Add all VHDL files to Libraries
15+
LIB.add_source_files(ROOT / "src" / "*.vhd")
16+
17+
# Run VUnit
18+
PRJ.main()

0 commit comments

Comments
 (0)