vhdl_doctest 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +9 -0
- data/Guardfile +13 -0
- data/LICENSE +22 -0
- data/README.md +70 -0
- data/Rakefile +2 -0
- data/bin/vhdl_doctest +19 -0
- data/examples/alu.vhd +55 -0
- data/lib/vhdl_doctest/dut.rb +72 -0
- data/lib/vhdl_doctest/port.rb +33 -0
- data/lib/vhdl_doctest/test_case.rb +26 -0
- data/lib/vhdl_doctest/test_file.rb +227 -0
- data/lib/vhdl_doctest/test_runner.rb +47 -0
- data/lib/vhdl_doctest/types/std_logic.rb +20 -0
- data/lib/vhdl_doctest/types/std_logic_vector.rb +21 -0
- data/lib/vhdl_doctest/types.rb +16 -0
- data/lib/vhdl_doctest/version.rb +3 -0
- data/lib/vhdl_doctest.rb +11 -0
- data/spec/dut_spec.rb +25 -0
- data/spec/port_spec.rb +35 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/test_runner_spec.rb +46 -0
- data/vhdl_doctest.gemspec +20 -0
- metadata +111 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
require 'guard-notifier-git_auto_commit'
|
5
|
+
|
6
|
+
guard 'rspec', :version => 2 do
|
7
|
+
watch(%r{^spec/.+_spec\.rb$})
|
8
|
+
watch(%r{^lib/vhdl_doctest/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
9
|
+
watch('spec/spec_helper.rb') { "spec" }
|
10
|
+
end
|
11
|
+
|
12
|
+
notification :git_auto_commit
|
13
|
+
notification :notifysend
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 tomykaira
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# VhdlDoctest
|
2
|
+
|
3
|
+
This is a simple doctest-like test runner for VHDL.
|
4
|
+
|
5
|
+
## Dependency
|
6
|
+
|
7
|
+
This uses GHDL for compiling and running VHDL. Please download from [GHDL Main/Home Page](http://ghdl.free.fr), and install.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
$ gem install vhdl_doctest
|
12
|
+
|
13
|
+
Install GHDL from [GHDL Main/Home Page](http://ghdl.free.fr).
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
White parameterized test in your VHDL file. Current version is only for combination circuit.
|
18
|
+
|
19
|
+
entity alu is
|
20
|
+
|
21
|
+
port (
|
22
|
+
a, b : in std_logic_vector(31 downto 0);
|
23
|
+
control : in std_logic_vector(2 downto 0);
|
24
|
+
output : out std_logic_vector(31 downto 0);
|
25
|
+
zero : out std_logic);
|
26
|
+
|
27
|
+
end alu;
|
28
|
+
|
29
|
+
For this module, you can write the following test in the same file. You should not miss `-- TEST` and `-- /TEST`. Space after `--` is necessary.
|
30
|
+
|
31
|
+
-- TEST
|
32
|
+
-- a |b |control b|output|zero
|
33
|
+
-- 18 |9 |000 |0 |1
|
34
|
+
-- | |001 |27 |0
|
35
|
+
-- | |010 |27 |0
|
36
|
+
-- | |110 |9 |0
|
37
|
+
-- | |111 |0 |1
|
38
|
+
-- 18 |18 |111 |0 |1
|
39
|
+
-- 18 |19 | |1 |0
|
40
|
+
-- 18 |100 | |1 |0
|
41
|
+
-- /TEST
|
42
|
+
|
43
|
+
- Each field should be separated with `|`.
|
44
|
+
- If the header column includes space followed by "b" or "h" or "x", that column's values are interpreted as binary (for "b") or "hex" (for "h" or "x"). The default interpretation is decimal.
|
45
|
+
- Blank field inherits the value of the previous row.
|
46
|
+
- Edge cases are not supported. If you find out a buggy case, tell me through [ITS in github](https://github.com/tomykaira/vhdl_doctest/issues).
|
47
|
+
|
48
|
+
This generates code like this.
|
49
|
+
|
50
|
+
a <= "00000000000000000000000000010010";
|
51
|
+
b <= "00000000000000000000000000001001";
|
52
|
+
control <= "000";wait for 10 ns;
|
53
|
+
assert output = "00000000000000000000000000000000" and zero = '1' report "FAILED: a = 18, b = 9, control = 0 expected to output = 0, zero = 1, but output = " & to_string(output) & ", zero = " & to_string(zero) & "" severity warning;
|
54
|
+
|
55
|
+
CAUTION: a file can have only one TEST block.
|
56
|
+
|
57
|
+
## Issues And Features
|
58
|
+
|
59
|
+
If you found a bug (or unexpected movement), let me know. Please attach your vhd file (as far as possible), and describe your intention precisely.
|
60
|
+
|
61
|
+
Any feature request is welcome. I appreciate if it have an example, or a test case.
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
1. Fork it
|
66
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
67
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
68
|
+
4. Write tests with rspec for your changes
|
69
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/vhdl_doctest
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
unless ARGV.size == 1 && File.file?(ARGV[0])
|
5
|
+
puts "Usage: vhdl_doctest VHDL_PATH"
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
# resolve bin path, ignoring symlinks
|
10
|
+
require "pathname"
|
11
|
+
bin_file = Pathname.new(__FILE__).realpath
|
12
|
+
|
13
|
+
# add self to libpath
|
14
|
+
$:.unshift File.expand_path("../../lib", bin_file)
|
15
|
+
|
16
|
+
require "vhdl_doctest"
|
17
|
+
test = VhdlDoctest::DUT.parse(ARGV[0]).test_file
|
18
|
+
|
19
|
+
VhdlDoctest::TestRunner.new(STDOUT, ARGV[0], test).run
|
data/examples/alu.vhd
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
library IEEE;
|
2
|
+
use IEEE.STD_LOGIC_1164.all;
|
3
|
+
use IEEE.STD_LOGIC_ARITH.all;
|
4
|
+
use IEEE.STD_LOGIC_UNSIGNED.all;
|
5
|
+
|
6
|
+
-- TEST
|
7
|
+
-- a |b |control b|output|zero
|
8
|
+
-- 18 |9 |000 |0 |1
|
9
|
+
-- | |001 |27 |0
|
10
|
+
-- | |010 |27 |0
|
11
|
+
-- | |110 |9 |0
|
12
|
+
-- | |111 |0 |1
|
13
|
+
-- 18 |18 |111 |0 |1
|
14
|
+
-- 18 |19 | |1 |0
|
15
|
+
-- 18 |100 | |1 |0
|
16
|
+
-- /TEST
|
17
|
+
|
18
|
+
entity alu is
|
19
|
+
|
20
|
+
port (
|
21
|
+
a, b : in std_logic_vector(31 downto 0);
|
22
|
+
control : in std_logic_vector(2 downto 0);
|
23
|
+
output : out std_logic_vector(31 downto 0);
|
24
|
+
zero : out std_logic);
|
25
|
+
|
26
|
+
end alu;
|
27
|
+
|
28
|
+
architecture behave of alu is
|
29
|
+
|
30
|
+
signal bb : std_logic_vector(31 downto 0);
|
31
|
+
signal c : std_logic;
|
32
|
+
signal o0, o1, o2, o3 : std_logic_vector(31 downto 0);
|
33
|
+
|
34
|
+
signal out_buf : std_logic_vector(31 downto 0);
|
35
|
+
|
36
|
+
begin -- behave
|
37
|
+
|
38
|
+
c <= control(2);
|
39
|
+
|
40
|
+
bb <= not b when control(2) = '1' else b;
|
41
|
+
|
42
|
+
o0 <= a and bb;
|
43
|
+
o1 <= a or b;
|
44
|
+
o2 <= a + bb + c;
|
45
|
+
o3 <= x"0000000" & "000" & o2(31);
|
46
|
+
|
47
|
+
out_buf <= o0 when control(1 downto 0) = "00" else
|
48
|
+
o1 when control(1 downto 0) = "01" else
|
49
|
+
o2 when control(1 downto 0) = "10" else
|
50
|
+
o3 when control(1 downto 0) = "11";
|
51
|
+
|
52
|
+
output <= out_buf;
|
53
|
+
zero <= '1' when out_buf = x"00000000" else '0';
|
54
|
+
|
55
|
+
end behave;
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module VhdlDoctest
|
2
|
+
class DUT
|
3
|
+
def self.parse(path)
|
4
|
+
entity, ports, cases = DoctestParser.new(path).parse
|
5
|
+
new(entity, ports, cases)
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :entity, :ports, :cases
|
9
|
+
def initialize(entity, ports, cases)
|
10
|
+
@entity, @ports, @cases = entity, ports, cases
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_file
|
14
|
+
TestFile.new(@entity, @ports, @cases)
|
15
|
+
end
|
16
|
+
|
17
|
+
class DoctestParser
|
18
|
+
def initialize(path)
|
19
|
+
@vhdl = File.read(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse
|
23
|
+
entity = @vhdl.match(/entity\s+(.*)\s+is/)[1]
|
24
|
+
ports = extract_ports
|
25
|
+
cases = extract_test_cases
|
26
|
+
|
27
|
+
[entity, ports, cases.map{ |c| TestCase.new(Hash[ports.zip(c)]) }]
|
28
|
+
end
|
29
|
+
|
30
|
+
def extract_ports
|
31
|
+
return @ports if @ports
|
32
|
+
@ports = []
|
33
|
+
definitions = @vhdl.match(/entity.*is\s+port\s*\((.*)\);\s*end/m)[1]
|
34
|
+
definitions.split("\n").each do |l|
|
35
|
+
names, attributes = l.strip.gsub(/;$/, '').split(":")
|
36
|
+
next unless attributes
|
37
|
+
destination, type = attributes.strip.split(' ', 2)
|
38
|
+
names.split(',').each do |name|
|
39
|
+
@ports << Port.new(name.strip, destination.to_sym, VhdlDoctest::Types.parse(type))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@ports
|
43
|
+
end
|
44
|
+
|
45
|
+
def extract_test_cases
|
46
|
+
definitions = @vhdl.match(/-- TEST\n(.*)-- \/TEST/m)[1]
|
47
|
+
header, *body = definitions.split("\n").map { |l| l[3..-1].split("|").map(&:strip) }
|
48
|
+
|
49
|
+
header.each_with_index do |h, idx|
|
50
|
+
radix = 10
|
51
|
+
if h.include?(' ')
|
52
|
+
case h[-1]
|
53
|
+
when 'b'
|
54
|
+
radix = 2
|
55
|
+
when 'h', 'x'
|
56
|
+
radix = 16
|
57
|
+
end
|
58
|
+
end
|
59
|
+
prev = ''
|
60
|
+
body.each do |l|
|
61
|
+
if l[idx].empty?
|
62
|
+
l[idx] = prev
|
63
|
+
else
|
64
|
+
prev = l[idx] = l[idx].to_i(radix)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
body
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module VhdlDoctest
|
2
|
+
class Port
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, direction, type)
|
6
|
+
@name, @direction, @type = name, direction, type
|
7
|
+
end
|
8
|
+
|
9
|
+
def port_definition
|
10
|
+
"#@name : #@direction #{@type.to_vhdl}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def signal_definition
|
14
|
+
"signal #@name : #{@type.to_vhdl};"
|
15
|
+
end
|
16
|
+
|
17
|
+
def mapping
|
18
|
+
"#@name => #@name"
|
19
|
+
end
|
20
|
+
|
21
|
+
def assignment(value)
|
22
|
+
"#@name <= #{@type.format(value)};"
|
23
|
+
end
|
24
|
+
|
25
|
+
def equation(value)
|
26
|
+
"#@name = #{@type.format(value)}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def in?
|
30
|
+
@direction == :in
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module VhdlDoctest
|
2
|
+
class TestCase
|
3
|
+
def initialize(mapping)
|
4
|
+
@in_mapping, @out_mapping = mapping.partition{ |port, _| port.in? }
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_vhdl
|
8
|
+
stimuli.join("\n") + "wait for 10 ns;\n" + assertion
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def stimuli
|
13
|
+
@in_mapping.map do |port, value|
|
14
|
+
port.assignment(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def assertion
|
19
|
+
cond = @out_mapping.map { |port, value| port.equation(value) }.join(" and ")
|
20
|
+
inputs = @in_mapping.map { |port, value| "#{port.name} = #{value}" }.join(", ")
|
21
|
+
expected = @out_mapping.map { |port, value| "#{port.name} = #{value}" }.join(", ")
|
22
|
+
actual = @out_mapping.map { |port, value| "#{port.name} = \" & to_string(#{port.name}) & \"" }.join(", ")
|
23
|
+
%Q{assert #{ cond } report "FAILED: #{inputs} expected to #{expected}, but #{actual}" severity warning;}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
module VhdlDoctest
|
2
|
+
class TestFile
|
3
|
+
attr_reader :cases
|
4
|
+
|
5
|
+
def initialize(dut_entity, ports, cases)
|
6
|
+
@dut_entity = dut_entity
|
7
|
+
@ports = ports
|
8
|
+
@cases = cases
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_name
|
12
|
+
"testbench_" + @dut_entity
|
13
|
+
end
|
14
|
+
|
15
|
+
def path
|
16
|
+
if @path
|
17
|
+
@path
|
18
|
+
else
|
19
|
+
raise "This test file is not yet instantiated"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create(dir)
|
24
|
+
@path = File.join(dir, test_name + ".vhd")
|
25
|
+
File.open(@path, 'w') do |f|
|
26
|
+
f << header + "\n\n"
|
27
|
+
f << "architecture sim of #{test_name} is\n"
|
28
|
+
f << dut_component
|
29
|
+
f << signals
|
30
|
+
f << UTILS
|
31
|
+
f << "begin"
|
32
|
+
f << dut_instantiation
|
33
|
+
f << testcases
|
34
|
+
f << "end sim;\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def header
|
40
|
+
%Q{-- This file is generated by vhdl_doctest
|
41
|
+
library IEEE;
|
42
|
+
use IEEE.STD_LOGIC_1164.all;
|
43
|
+
use IEEE.STD_LOGIC_ARITH.all;
|
44
|
+
use IEEE.STD_LOGIC_UNSIGNED.all;
|
45
|
+
|
46
|
+
entity #{ test_name } is
|
47
|
+
end #{ test_name };
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def dut_component
|
52
|
+
str = "component #{@dut_entity} port("
|
53
|
+
str << @ports.map { |port| port.port_definition }.join(";\n")
|
54
|
+
str << ");\nend component;"
|
55
|
+
end
|
56
|
+
|
57
|
+
def signals
|
58
|
+
@ports.map(&:signal_definition).join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
def dut_instantiation
|
62
|
+
%Q{
|
63
|
+
dut : #{ @dut_entity } port map (
|
64
|
+
#{ @ports.map(&:mapping).join(", ") }
|
65
|
+
);
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def testcases
|
70
|
+
%Q{
|
71
|
+
process begin
|
72
|
+
#{@cases.map(&:to_vhdl).join("\n\n")}
|
73
|
+
wait;
|
74
|
+
end process;
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
UTILS = %Q{
|
79
|
+
function to_string (value : STD_ULOGIC) return STRING;
|
80
|
+
function to_string (value : STD_ULOGIC_VECTOR) return STRING;
|
81
|
+
function to_string (value : STD_LOGIC_VECTOR) return STRING;
|
82
|
+
|
83
|
+
alias TO_BSTRING is TO_STRING [STD_ULOGIC_VECTOR return STRING];
|
84
|
+
alias TO_BINARY_STRING is TO_STRING [STD_ULOGIC_VECTOR return STRING];
|
85
|
+
function TO_OSTRING (VALUE : STD_ULOGIC_VECTOR) return STRING;
|
86
|
+
alias TO_OCTAL_STRING is TO_OSTRING [STD_ULOGIC_VECTOR return STRING];
|
87
|
+
function TO_HSTRING (VALUE : STD_ULOGIC_VECTOR) return STRING;
|
88
|
+
alias TO_HEX_STRING is TO_HSTRING [STD_ULOGIC_VECTOR return STRING];
|
89
|
+
|
90
|
+
alias TO_BSTRING is TO_STRING [STD_LOGIC_VECTOR return STRING];
|
91
|
+
alias TO_BINARY_STRING is TO_STRING [STD_LOGIC_VECTOR return STRING];
|
92
|
+
function TO_OSTRING (VALUE : STD_LOGIC_VECTOR) return STRING;
|
93
|
+
alias TO_OCTAL_STRING is TO_OSTRING [STD_LOGIC_VECTOR return STRING];
|
94
|
+
function TO_HSTRING (VALUE : STD_LOGIC_VECTOR) return STRING;
|
95
|
+
alias TO_HEX_STRING is TO_HSTRING [STD_LOGIC_VECTOR return STRING];
|
96
|
+
|
97
|
+
type char_indexed_by_MVL9 is array (STD_ULOGIC) of CHARACTER;
|
98
|
+
constant MVL9_to_char : char_indexed_by_MVL9 := "UX01ZWLH-";
|
99
|
+
constant NUS : STRING(2 to 1) := (others => ' '); -- null STRING
|
100
|
+
|
101
|
+
-----------------------------------------------------------------------------
|
102
|
+
-- New string functions for vhdl-200x fast track
|
103
|
+
-----------------------------------------------------------------------------
|
104
|
+
function to_string (value : STD_ULOGIC) return STRING is
|
105
|
+
variable result : STRING (1 to 1);
|
106
|
+
begin
|
107
|
+
result (1) := MVL9_to_char (value);
|
108
|
+
return result;
|
109
|
+
end function to_string;
|
110
|
+
-------------------------------------------------------------------
|
111
|
+
-- TO_STRING (an alias called "to_bstring" is provide)
|
112
|
+
-------------------------------------------------------------------
|
113
|
+
function to_string (value : STD_ULOGIC_VECTOR) return STRING is
|
114
|
+
alias ivalue : STD_ULOGIC_VECTOR(1 to value'length) is value;
|
115
|
+
variable result : STRING(1 to value'length);
|
116
|
+
begin
|
117
|
+
if value'length < 1 then
|
118
|
+
return NUS;
|
119
|
+
else
|
120
|
+
for i in ivalue'range loop
|
121
|
+
result(i) := MVL9_to_char(iValue(i));
|
122
|
+
end loop;
|
123
|
+
return result;
|
124
|
+
end if;
|
125
|
+
end function to_string;
|
126
|
+
|
127
|
+
-------------------------------------------------------------------
|
128
|
+
-- TO_HSTRING
|
129
|
+
-------------------------------------------------------------------
|
130
|
+
function to_hstring (value : STD_ULOGIC_VECTOR) return STRING is
|
131
|
+
constant ne : INTEGER := (value'length+3)/4;
|
132
|
+
variable pad : STD_ULOGIC_VECTOR(0 to (ne*4 - value'length) - 1);
|
133
|
+
variable ivalue : STD_ULOGIC_VECTOR(0 to ne*4 - 1);
|
134
|
+
variable result : STRING(1 to ne);
|
135
|
+
variable quad : STD_ULOGIC_VECTOR(0 to 3);
|
136
|
+
begin
|
137
|
+
if value'length < 1 then
|
138
|
+
return NUS;
|
139
|
+
else
|
140
|
+
if value (value'left) = 'Z' then
|
141
|
+
pad := (others => 'Z');
|
142
|
+
else
|
143
|
+
pad := (others => '0');
|
144
|
+
end if;
|
145
|
+
ivalue := pad & value;
|
146
|
+
for i in 0 to ne-1 loop
|
147
|
+
quad := To_X01Z(ivalue(4*i to 4*i+3));
|
148
|
+
case quad is
|
149
|
+
when x"0" => result(i+1) := '0';
|
150
|
+
when x"1" => result(i+1) := '1';
|
151
|
+
when x"2" => result(i+1) := '2';
|
152
|
+
when x"3" => result(i+1) := '3';
|
153
|
+
when x"4" => result(i+1) := '4';
|
154
|
+
when x"5" => result(i+1) := '5';
|
155
|
+
when x"6" => result(i+1) := '6';
|
156
|
+
when x"7" => result(i+1) := '7';
|
157
|
+
when x"8" => result(i+1) := '8';
|
158
|
+
when x"9" => result(i+1) := '9';
|
159
|
+
when x"A" => result(i+1) := 'A';
|
160
|
+
when x"B" => result(i+1) := 'B';
|
161
|
+
when x"C" => result(i+1) := 'C';
|
162
|
+
when x"D" => result(i+1) := 'D';
|
163
|
+
when x"E" => result(i+1) := 'E';
|
164
|
+
when x"F" => result(i+1) := 'F';
|
165
|
+
when "ZZZZ" => result(i+1) := 'Z';
|
166
|
+
when others => result(i+1) := 'X';
|
167
|
+
end case;
|
168
|
+
end loop;
|
169
|
+
return result;
|
170
|
+
end if;
|
171
|
+
end function to_hstring;
|
172
|
+
|
173
|
+
-------------------------------------------------------------------
|
174
|
+
-- TO_OSTRING
|
175
|
+
-------------------------------------------------------------------
|
176
|
+
function to_ostring (value : STD_ULOGIC_VECTOR) return STRING is
|
177
|
+
constant ne : INTEGER := (value'length+2)/3;
|
178
|
+
variable pad : STD_ULOGIC_VECTOR(0 to (ne*3 - value'length) - 1);
|
179
|
+
variable ivalue : STD_ULOGIC_VECTOR(0 to ne*3 - 1);
|
180
|
+
variable result : STRING(1 to ne);
|
181
|
+
variable tri : STD_ULOGIC_VECTOR(0 to 2);
|
182
|
+
begin
|
183
|
+
if value'length < 1 then
|
184
|
+
return NUS;
|
185
|
+
else
|
186
|
+
if value (value'left) = 'Z' then
|
187
|
+
pad := (others => 'Z');
|
188
|
+
else
|
189
|
+
pad := (others => '0');
|
190
|
+
end if;
|
191
|
+
ivalue := pad & value;
|
192
|
+
for i in 0 to ne-1 loop
|
193
|
+
tri := To_X01Z(ivalue(3*i to 3*i+2));
|
194
|
+
case tri is
|
195
|
+
when o"0" => result(i+1) := '0';
|
196
|
+
when o"1" => result(i+1) := '1';
|
197
|
+
when o"2" => result(i+1) := '2';
|
198
|
+
when o"3" => result(i+1) := '3';
|
199
|
+
when o"4" => result(i+1) := '4';
|
200
|
+
when o"5" => result(i+1) := '5';
|
201
|
+
when o"6" => result(i+1) := '6';
|
202
|
+
when o"7" => result(i+1) := '7';
|
203
|
+
when "ZZZ" => result(i+1) := 'Z';
|
204
|
+
when others => result(i+1) := 'X';
|
205
|
+
end case;
|
206
|
+
end loop;
|
207
|
+
return result;
|
208
|
+
end if;
|
209
|
+
end function to_ostring;
|
210
|
+
|
211
|
+
function to_string (value : STD_LOGIC_VECTOR) return STRING is
|
212
|
+
begin
|
213
|
+
return to_string (to_stdulogicvector (value));
|
214
|
+
end function to_string;
|
215
|
+
|
216
|
+
function to_hstring (value : STD_LOGIC_VECTOR) return STRING is
|
217
|
+
begin
|
218
|
+
return to_hstring (to_stdulogicvector (value));
|
219
|
+
end function to_hstring;
|
220
|
+
|
221
|
+
function to_ostring (value : STD_LOGIC_VECTOR) return STRING is
|
222
|
+
begin
|
223
|
+
return to_ostring (to_stdulogicvector (value));
|
224
|
+
end function to_ostring;
|
225
|
+
}
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module VhdlDoctest
|
2
|
+
class TestRunner
|
3
|
+
def initialize(out, dut_path, test_file)
|
4
|
+
@out = out
|
5
|
+
@dut_path = File.expand_path(dut_path)
|
6
|
+
@test_file = test_file
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
create_files
|
11
|
+
run_test
|
12
|
+
report_result
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def create_files
|
17
|
+
require 'tmpdir'
|
18
|
+
@dir = Dir.mktmpdir
|
19
|
+
@test_file.create(@dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_test
|
23
|
+
@result = ""
|
24
|
+
|
25
|
+
sh = File.join(@dir, "run.sh")
|
26
|
+
File.open(sh, 'w') do |f|
|
27
|
+
f << "#!/bin/sh -e\n\n"
|
28
|
+
f << "cd #{@dir}\n"
|
29
|
+
f << "ghdl -a --ieee=synopsys -fexplicit --warn-default-binding --warn-binding --warn-library --warn-body --warn-specs --warn-unused #{@dut_path} #{@test_file.path}\n"
|
30
|
+
f << "ghdl -e -Plibs/unisim --ieee=synopsys -fexplicit #{@test_file.test_name}\n"
|
31
|
+
f << "ghdl -r #{@test_file.test_name} --vcd=out.vcd --stop-time=10ms\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
IO.popen("sh #{sh} 2>&1") do |f|
|
35
|
+
output = f.read
|
36
|
+
@result << output
|
37
|
+
@out << output
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def report_result
|
42
|
+
failures = @result.split("\n").select { |l| l.include?('FAILED') }
|
43
|
+
@out << "\n#{@test_file.cases.size} examples, #{failures.size} failures\n"
|
44
|
+
@out << "\nTest directory: #{@dir}\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module VhdlDoctest::Types
|
2
|
+
class StdLogic
|
3
|
+
def to_vhdl
|
4
|
+
'std_logic'
|
5
|
+
end
|
6
|
+
|
7
|
+
def format(v)
|
8
|
+
if [0, 1].include? v.to_i
|
9
|
+
%Q{'#{v.to_i}'}
|
10
|
+
else
|
11
|
+
# TODO: define error class
|
12
|
+
raise "unacceptable value error #{v}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse(str)
|
17
|
+
new if str.strip == 'std_logic'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module VhdlDoctest::Types
|
2
|
+
class StdLogicVector
|
3
|
+
def initialize(length)
|
4
|
+
@length = length
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_vhdl
|
8
|
+
"std_logic_vector(#{@length-1} downto 0)"
|
9
|
+
end
|
10
|
+
|
11
|
+
def format(v)
|
12
|
+
'"' + v.to_s(2).rjust(@length, '0')+ '"'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse(str)
|
16
|
+
if str.strip.match(/\Astd_logic_vector\s*\((\d+)\s+downto\s+0\)\Z/i)
|
17
|
+
new($1.to_i + 1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'vhdl_doctest/types/std_logic'
|
2
|
+
require 'vhdl_doctest/types/std_logic_vector'
|
3
|
+
|
4
|
+
module VhdlDoctest
|
5
|
+
module Types
|
6
|
+
def self.parse(str)
|
7
|
+
Types.constants.each do |c|
|
8
|
+
klass = const_get("#{c}")
|
9
|
+
next unless klass.respond_to?(:parse)
|
10
|
+
if result = klass.parse(str)
|
11
|
+
return result
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/vhdl_doctest.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "vhdl_doctest/version"
|
2
|
+
require "vhdl_doctest/test_runner"
|
3
|
+
require "vhdl_doctest/port"
|
4
|
+
require "vhdl_doctest/types"
|
5
|
+
require "vhdl_doctest/test_file"
|
6
|
+
require "vhdl_doctest/test_case"
|
7
|
+
require "vhdl_doctest/dut"
|
8
|
+
|
9
|
+
module VhdlDoctest
|
10
|
+
# Your code goes here...
|
11
|
+
end
|
data/spec/dut_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module VhdlDoctest
|
4
|
+
sample_vhdl = 'examples/alu.vhd'
|
5
|
+
describe DUT do
|
6
|
+
subject { DUT.parse(sample_vhdl) }
|
7
|
+
|
8
|
+
its(:entity) { should == "alu" }
|
9
|
+
it { should have(5).ports }
|
10
|
+
it { should have(8).cases }
|
11
|
+
|
12
|
+
its('test_file.test_name') { should == 'testbench_alu' }
|
13
|
+
its(:test_file) { should have(8).cases }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DUT::DoctestParser do
|
17
|
+
describe 'ports' do
|
18
|
+
subject { described_class.new(sample_vhdl).extract_ports }
|
19
|
+
|
20
|
+
it { should have(5).items }
|
21
|
+
its('first.port_definition') { should == "a : in std_logic_vector(31 downto 0)" }
|
22
|
+
its('last.port_definition') { should == "zero : out std_logic" }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/port_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module VhdlDoctest
|
4
|
+
describe Port do
|
5
|
+
subject { Port.new('test', :in, Types::StdLogic.new) }
|
6
|
+
|
7
|
+
its(:mapping){ should == "test => test" }
|
8
|
+
|
9
|
+
describe '#assignment' do
|
10
|
+
subject { port.assignment(value) }
|
11
|
+
|
12
|
+
describe 'std_logic' do
|
13
|
+
let(:port) { Port.new('test', :in, Types::StdLogic.new) }
|
14
|
+
|
15
|
+
context 'value = 0' do
|
16
|
+
let(:value) { 0 }
|
17
|
+
it { should == "test <= '0';" }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'value = 1' do
|
21
|
+
let(:value) { 1 }
|
22
|
+
it { should == "test <= '1';" }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
describe 'std_logic_vector(8)' do
|
26
|
+
let(:port) { Port.new('test', :in, Types::StdLogicVector.new(8)) }
|
27
|
+
|
28
|
+
context 'value = 0' do
|
29
|
+
let(:value) { 0 }
|
30
|
+
it { should == 'test <= "00000000";' }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'vhdl_doctest'
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
config.filter_run :focus
|
13
|
+
|
14
|
+
# Run specs in random order to surface order dependencies. If you find an
|
15
|
+
# order dependency and want to debug it, you can fix the order by providing
|
16
|
+
# the seed, which is printed after each run.
|
17
|
+
# --seed 1234
|
18
|
+
config.order = 'random'
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module VhdlDoctest
|
4
|
+
describe TestRunner do
|
5
|
+
let(:dut_file) { "examples/alu.vhd/" }
|
6
|
+
describe "#run" do
|
7
|
+
context "3 successful examples" do
|
8
|
+
let(:out) { StringIO.new }
|
9
|
+
let(:file) { test_file([[18, 9, 0, 0, 1], [18, 18, 7, 0, 1], [18, 19, 7, 1, 0]]) }
|
10
|
+
subject { out.rewind; out.read }
|
11
|
+
before { described_class.new(out, dut_file, file).run }
|
12
|
+
|
13
|
+
it { should match "3 examples, 0 failures" }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "1 successful examples" do
|
17
|
+
let(:out) { StringIO.new }
|
18
|
+
let(:file) { test_file([[18, 9, 0, 0, 1]]) }
|
19
|
+
subject { out.rewind; out.read }
|
20
|
+
before { described_class.new(out, dut_file, file).run }
|
21
|
+
|
22
|
+
it { should match "1 examples, 0 failures" }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "1 failing examples" do
|
26
|
+
let(:out) { StringIO.new }
|
27
|
+
let(:file) { test_file([[18, 9, 0, 0, 0]]) }
|
28
|
+
subject { out.rewind; out.read }
|
29
|
+
before { described_class.new(out, dut_file, file).run }
|
30
|
+
|
31
|
+
it { should match "1 examples, 1 failures" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_file(cases)
|
36
|
+
ports = [
|
37
|
+
Port.new("a", :in, Types::StdLogicVector.new(32)),
|
38
|
+
Port.new("b", :in, Types::StdLogicVector.new(32)),
|
39
|
+
Port.new("control", :in, Types::StdLogicVector.new(3)),
|
40
|
+
Port.new("output", :out, Types::StdLogicVector.new(32)),
|
41
|
+
Port.new("zero", :out, Types::StdLogic.new)
|
42
|
+
]
|
43
|
+
TestFile.new("alu", ports, cases.map{ |c| TestCase.new(Hash[ports.zip(c)]) })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/vhdl_doctest/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["tomykaira"]
|
6
|
+
gem.email = ["tomykaira@gmail.com"]
|
7
|
+
gem.description = %q{Run parameterized test for VHDL written in doctest-like format.}
|
8
|
+
gem.summary = %q{Run parameterized test for VHDL written in doctest-like format.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "vhdl_doctest"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = VhdlDoctest::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rspec', '~> 2.11'
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vhdl_doctest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- tomykaira
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.11'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.11'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Run parameterized test for VHDL written in doctest-like format.
|
47
|
+
email:
|
48
|
+
- tomykaira@gmail.com
|
49
|
+
executables:
|
50
|
+
- vhdl_doctest
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- Guardfile
|
57
|
+
- LICENSE
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/vhdl_doctest
|
61
|
+
- examples/alu.vhd
|
62
|
+
- lib/vhdl_doctest.rb
|
63
|
+
- lib/vhdl_doctest/dut.rb
|
64
|
+
- lib/vhdl_doctest/port.rb
|
65
|
+
- lib/vhdl_doctest/test_case.rb
|
66
|
+
- lib/vhdl_doctest/test_file.rb
|
67
|
+
- lib/vhdl_doctest/test_runner.rb
|
68
|
+
- lib/vhdl_doctest/types.rb
|
69
|
+
- lib/vhdl_doctest/types/std_logic.rb
|
70
|
+
- lib/vhdl_doctest/types/std_logic_vector.rb
|
71
|
+
- lib/vhdl_doctest/version.rb
|
72
|
+
- spec/dut_spec.rb
|
73
|
+
- spec/port_spec.rb
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
- spec/test_runner_spec.rb
|
76
|
+
- vhdl_doctest.gemspec
|
77
|
+
homepage: ''
|
78
|
+
licenses: []
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
hash: 39814003
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
hash: 39814003
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Run parameterized test for VHDL written in doctest-like format.
|
107
|
+
test_files:
|
108
|
+
- spec/dut_spec.rb
|
109
|
+
- spec/port_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
- spec/test_runner_spec.rb
|