vhdl_tb 0.4 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/tbgen +4 -4
- data/bin/vhdl_tb +2 -2
- data/lib/ast.rb +26 -0
- data/lib/compiler.rb +127 -0
- data/lib/generic_lexer.rb +62 -0
- data/lib/generic_parser.rb +25 -0
- data/lib/lexer.rb +31 -0
- data/lib/parser.rb +183 -0
- data/lib/template.tb.vhd +5 -3
- data/lib/token.rb +54 -0
- metadata +9 -3
- data/lib/vhdl_tb.rb +0 -105
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99115f46e648d9a330a16e6998507fe532bbaac7
|
4
|
+
data.tar.gz: 4a7ca042f8bbe236adc0cec08c0ba820e7d26c89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ccce1103568033abcdaa16ac33666e4b73c82f007550607c5b79564cb64ef24dae46d3f258963a3a5aa34263a9180298875f49539a6ba507025c70aad5fd1a94
|
7
|
+
data.tar.gz: 5d1f555f4908071b4360c906209dfa90ae2d99ef0d7f5b2dd22788780331a0be2b53db73e76aad1ff39f5a56e364da6d3f78dfeb59f4fb170e9ffb85da6d06e4
|
data/bin/tbgen
CHANGED
data/bin/vhdl_tb
CHANGED
data/lib/ast.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module VHDL_TB
|
2
|
+
Root=Struct.new(:design_units)
|
3
|
+
Entity=Struct.new(:name,:generics,:ports)
|
4
|
+
Generic=Struct.new(:name,:type,:init)
|
5
|
+
Input=Struct.new(:name,:type)
|
6
|
+
Output=Struct.new(:name,:type)
|
7
|
+
Architecture=Struct.new(:name,:entity)
|
8
|
+
|
9
|
+
Identifier=Struct.new(:tok) do
|
10
|
+
def to_s
|
11
|
+
"#{self.tok}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
IntLit=Struct.new(:tok) do
|
16
|
+
def to_s
|
17
|
+
"#{self.tok}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
VectorType=Struct.new(:name,:lhs,:dir,:rhs) do
|
22
|
+
def to_s
|
23
|
+
"#{self.name}(#{self.lhs} #{self.dir} #{self.rhs})"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/compiler.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'pp'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
require_relative 'ast'
|
6
|
+
require_relative 'parser'
|
7
|
+
|
8
|
+
module VHDL_TB
|
9
|
+
|
10
|
+
TestBench=Struct.new(:name)
|
11
|
+
|
12
|
+
class Compiler
|
13
|
+
|
14
|
+
VERSION = "0.6.3"
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
#puts __dir__
|
18
|
+
banner
|
19
|
+
@engine=ERB.new(IO.read "#{__dir__}/template.tb.vhd")
|
20
|
+
@parser=Parser.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def banner
|
24
|
+
puts "-- "+"="*65
|
25
|
+
puts "-- VHDL testbench generator -- Jean-Christophe Le Lann 2017-2018"
|
26
|
+
puts "-- "+"="*65
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def analyze_options args
|
31
|
+
args << "-h" if args.empty?
|
32
|
+
|
33
|
+
opt_parser = OptionParser.new do |opts|
|
34
|
+
opts.banner = "Usage: vhdl_tb (or tbgen) <filename>"
|
35
|
+
|
36
|
+
opts.on("-v", "--version", "Prints version") do |n|
|
37
|
+
puts VERSION
|
38
|
+
abort
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-h", "--help", "Prints this help") do
|
42
|
+
puts "Generates testbench in VHDL, from a given file containing an Entity-Architecture couple."
|
43
|
+
puts
|
44
|
+
puts opts
|
45
|
+
abort
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
opt_parser.parse!(args)
|
51
|
+
@args=args
|
52
|
+
|
53
|
+
rescue Exception => e
|
54
|
+
puts e
|
55
|
+
#puts e.backtrace
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def generate entity_filename=@args.first
|
61
|
+
@symtable=[]
|
62
|
+
@symtable << "clk"
|
63
|
+
@symtable << "reset_n"
|
64
|
+
@symtable << "sreset"
|
65
|
+
begin
|
66
|
+
analyze(entity_filename)
|
67
|
+
tb_txt=@engine.result(binding)
|
68
|
+
tb_filename="#{@tb.name}.vhd"
|
69
|
+
File.open(tb_filename,'w'){|f| f.puts tb_txt}
|
70
|
+
puts "testbench generated : #{tb_filename}"
|
71
|
+
rescue Exception => e
|
72
|
+
abort
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def analyze entity_filename
|
77
|
+
puts "analyzing VHDL file : #{entity_filename}"
|
78
|
+
|
79
|
+
root=Parser.new.parse entity_filename
|
80
|
+
|
81
|
+
@entity=root.design_units.find{|du| du.class==Entity}
|
82
|
+
puts "entity found : #{@entity.name} (#{@entity.ports.size} ports)"
|
83
|
+
|
84
|
+
@arch=root.design_units.find{|du| du.is_a? Architecture}
|
85
|
+
check
|
86
|
+
# prepare ERB through instance variables
|
87
|
+
@max_length=@entity.ports.map{|p| p.name.val.size}.max
|
88
|
+
@ports=@entity.ports.map{|p| " "*10+"#{e=p.name.val.ljust(@max_length)} => #{e}"}.join(",\n")
|
89
|
+
if gs=(@entity.generics)
|
90
|
+
generics=gs.map{|g| " "*10+"#{g.name} => #{g.init}"}
|
91
|
+
@generics="generic map(\n#{generics.join(",\n")})"
|
92
|
+
end
|
93
|
+
@tb=TestBench.new(@entity.name.val+"_tb")
|
94
|
+
end
|
95
|
+
|
96
|
+
def check
|
97
|
+
print "checking".ljust(20)
|
98
|
+
errors =[]
|
99
|
+
|
100
|
+
if @entity.ports.size==0
|
101
|
+
errors << "0 ports found for #{@entity.name}"
|
102
|
+
end
|
103
|
+
|
104
|
+
if @arch.entity.val!=@entity.name.val
|
105
|
+
errors << "wrong entity-architecture pair : entity is -->#{@entity.name}<-- vs arch #{@arch.name} of -->#{@arch.entity.name}<--"
|
106
|
+
end
|
107
|
+
|
108
|
+
if errors.any?
|
109
|
+
puts ": nok"
|
110
|
+
puts "\nchecks failed due to the following errors :"
|
111
|
+
errors.each{|e| puts "- ERROR : #{e}"}
|
112
|
+
raise
|
113
|
+
else
|
114
|
+
puts ": ok"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
if $PROGRAM_NAME == __FILE__
|
124
|
+
raise "you need to provide a VHDL file that contains the entity to test" if ARGV.size==0
|
125
|
+
filename=ARGV[0]
|
126
|
+
VHDL_TB.Compiler.new.generate(filename)
|
127
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
require_relative 'token'
|
4
|
+
|
5
|
+
class GenericLexer
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@rules = []
|
9
|
+
@rules << [:newline,/[\n]/]
|
10
|
+
end
|
11
|
+
|
12
|
+
def ignore pattern
|
13
|
+
@rules << [:skip,pattern]
|
14
|
+
end
|
15
|
+
|
16
|
+
def keyword str
|
17
|
+
@rules.unshift [str.to_sym,/#{str}\b/]
|
18
|
+
end
|
19
|
+
|
20
|
+
def token hash
|
21
|
+
token,pattern=*hash.to_a.flatten
|
22
|
+
@rules << [token, pattern]
|
23
|
+
end
|
24
|
+
|
25
|
+
def open code
|
26
|
+
@ssc = StringScanner.new code
|
27
|
+
@line=0
|
28
|
+
end
|
29
|
+
|
30
|
+
def next_token
|
31
|
+
return [nil,nil,nil] if @ssc.empty?
|
32
|
+
tok = get_token
|
33
|
+
return (tok.is_a? :skip) ? next_token : tok
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_token
|
37
|
+
linecol=position()
|
38
|
+
@rules.each do |rule, regexp|
|
39
|
+
val = @ssc.scan(regexp)
|
40
|
+
return Token.new([rule, val, linecol]) if val
|
41
|
+
end
|
42
|
+
raise "lexing error line #{linecol.first} around : ...'#{@ssc.peek(5)}'... "
|
43
|
+
end
|
44
|
+
|
45
|
+
def position
|
46
|
+
if @ssc.bol?
|
47
|
+
@line+=1
|
48
|
+
@old_pos=@ssc.pos
|
49
|
+
end
|
50
|
+
[@line,@ssc.pos-@old_pos+1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def tokenize code
|
54
|
+
open(code)
|
55
|
+
tokens=[]
|
56
|
+
tokens << next_token() while not @ssc.eos?
|
57
|
+
# while not @ssc.eos?
|
58
|
+
# tokens << (p next_token)
|
59
|
+
# end
|
60
|
+
tokens
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class GenericParser
|
2
|
+
|
3
|
+
def acceptIt
|
4
|
+
tok=tokens.shift
|
5
|
+
puts "consuming #{tok.val} (#{tok.kind})" if @verbose
|
6
|
+
tok
|
7
|
+
end
|
8
|
+
|
9
|
+
def showNext k=1
|
10
|
+
tokens[k-1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def expect kind
|
14
|
+
if (actual=showNext.kind)!=kind
|
15
|
+
abort "ERROR at #{showNext.pos}. Expecting #{kind}. Got #{actual}"
|
16
|
+
else
|
17
|
+
return acceptIt()
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def more?
|
22
|
+
!tokens.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/lib/lexer.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'generic_lexer'
|
2
|
+
|
3
|
+
class Lexer < GenericLexer
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
ignore /\s+/
|
7
|
+
#....keywords
|
8
|
+
token :entity => /entity/
|
9
|
+
token :is => /is/
|
10
|
+
token :generic => /generic/
|
11
|
+
token :port => /port/
|
12
|
+
token :in => /in/
|
13
|
+
token :to => /to/
|
14
|
+
token :downto => /downto/
|
15
|
+
token :out => /out/
|
16
|
+
token :end => /end/
|
17
|
+
token :comment => /--(.*)$/
|
18
|
+
token :vassign => /\:\=/
|
19
|
+
token :lparen => /\(/
|
20
|
+
token :rparen => /\)/
|
21
|
+
token :semicolon=> /\;/
|
22
|
+
token :colon => /\:/
|
23
|
+
token :dot => /\./
|
24
|
+
token :comma => /\,/
|
25
|
+
token :architecture => /architecture/
|
26
|
+
token :of => /of/
|
27
|
+
token :id => /[a-zA-Z_][a-zA-Z0-9_]*/i
|
28
|
+
token :int_lit => /[0-9]+/
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
require_relative 'generic_parser'
|
2
|
+
require_relative 'ast'
|
3
|
+
require_relative 'lexer'
|
4
|
+
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
module VHDL_TB
|
8
|
+
|
9
|
+
class Parser < GenericParser
|
10
|
+
attr_accessor :lexer,:tokens
|
11
|
+
attr_accessor :basename,:filename
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@lexer=Lexer.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse filename
|
18
|
+
str=IO.read(filename)
|
19
|
+
begin
|
20
|
+
@tokens=lexer.tokenize(str)
|
21
|
+
@tokens=tokens.select{|t| t.class==Token} # filters [nil,nil,nil]
|
22
|
+
@tokens=tokens.reject{|tok| tok.is_a? [:comment,:newline]}
|
23
|
+
rescue Exception=>e
|
24
|
+
puts e
|
25
|
+
puts "an error occured during LEXICAL analysis. Sorry. Aborting."
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
root=Root.new([])
|
29
|
+
begin
|
30
|
+
consume_to :entity
|
31
|
+
root.design_units << entity=parse_entity
|
32
|
+
|
33
|
+
consume_to :architecture
|
34
|
+
root.design_units << archi=parse_architecture
|
35
|
+
|
36
|
+
rescue Exception => e
|
37
|
+
#puts e.backtrace
|
38
|
+
puts e
|
39
|
+
puts "an error occured during SYNTACTIC analysis (around line #{showNext.pos.first}). Sorry. Aborting."
|
40
|
+
raise
|
41
|
+
end
|
42
|
+
return root
|
43
|
+
end
|
44
|
+
|
45
|
+
def consume_to token_kind
|
46
|
+
while showNext && showNext.kind!=token_kind
|
47
|
+
acceptIt
|
48
|
+
end
|
49
|
+
if showNext.nil?
|
50
|
+
raise "cannot find token '#{token_kind}'"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_entity
|
55
|
+
entity=Entity.new(nil,[],[])
|
56
|
+
expect :entity
|
57
|
+
entity.name=expect :id
|
58
|
+
expect :is
|
59
|
+
if showNext.is_a? :generic
|
60
|
+
entity.generics=parse_generics
|
61
|
+
end
|
62
|
+
if showNext.is_a? :port
|
63
|
+
entity.ports=parse_ports
|
64
|
+
end
|
65
|
+
expect :end
|
66
|
+
return entity
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_generics
|
70
|
+
generics=[]
|
71
|
+
expect :generic
|
72
|
+
expect :lparen
|
73
|
+
while showNext.is_not_a? :rparen
|
74
|
+
generics << parse_generic
|
75
|
+
if showNext.is_a? :semicolon
|
76
|
+
acceptIt
|
77
|
+
end
|
78
|
+
end
|
79
|
+
expect :rparen
|
80
|
+
expect :semicolon
|
81
|
+
generics.flatten!
|
82
|
+
generics
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_generic
|
86
|
+
ids=[]
|
87
|
+
ids << expect(:id)
|
88
|
+
while showNext.is_a? :comma
|
89
|
+
acceptIt
|
90
|
+
ids << expect(:id)
|
91
|
+
end
|
92
|
+
expect :colon
|
93
|
+
type=parse_type
|
94
|
+
if showNext.is_a? :vassign
|
95
|
+
acceptIt
|
96
|
+
expr=parse_expression
|
97
|
+
end
|
98
|
+
ids.map{|id| Generic.new(id,type,expr)}
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_ports
|
102
|
+
ports=[]
|
103
|
+
expect :port
|
104
|
+
expect :lparen
|
105
|
+
while showNext.is_not_a? :rparen
|
106
|
+
ports << parse_io
|
107
|
+
if showNext.is_a? :semicolon
|
108
|
+
acceptIt
|
109
|
+
end
|
110
|
+
end
|
111
|
+
expect :rparen
|
112
|
+
expect :semicolon
|
113
|
+
ports.flatten!
|
114
|
+
ports
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_io
|
118
|
+
ids=[]
|
119
|
+
ids << expect(:id)
|
120
|
+
while showNext.is_a? :comma
|
121
|
+
acceptIt
|
122
|
+
ids << expect(:id)
|
123
|
+
end
|
124
|
+
expect :colon
|
125
|
+
if showNext.is_a? [:in,:out]
|
126
|
+
dir=acceptIt
|
127
|
+
dir=dir.kind
|
128
|
+
end
|
129
|
+
type=parse_type
|
130
|
+
ids.map{|id| dir==:in ? Input.new(id,type) : Output.new(id,type)}
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_type
|
134
|
+
type=Identifier.new
|
135
|
+
type.tok=expect(:id)
|
136
|
+
if showNext.is_a? :lparen
|
137
|
+
acceptIt
|
138
|
+
name=type.tok
|
139
|
+
type=VectorType.new
|
140
|
+
type.name=name
|
141
|
+
type.lhs=parse_expression
|
142
|
+
if showNext.is_a? [:downto,:to]
|
143
|
+
type.dir=acceptIt
|
144
|
+
end
|
145
|
+
type.rhs=parse_expression
|
146
|
+
expect :rparen
|
147
|
+
end
|
148
|
+
type
|
149
|
+
end
|
150
|
+
|
151
|
+
def parse_expression
|
152
|
+
parse_term
|
153
|
+
end
|
154
|
+
|
155
|
+
def parse_term
|
156
|
+
if showNext.is_a? [:int_lit,:id]
|
157
|
+
case showNext.kind
|
158
|
+
when :int_lit
|
159
|
+
return IntLit.new(acceptIt)
|
160
|
+
when :id
|
161
|
+
return Identifier.new(acceptIt)
|
162
|
+
else
|
163
|
+
puts "cannot parse term"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def parse_architecture
|
169
|
+
archi=Architecture.new
|
170
|
+
expect :architecture
|
171
|
+
archi.name=expect(:id)
|
172
|
+
expect :of
|
173
|
+
archi.entity=expect(:id)
|
174
|
+
archi
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
if $PROGRAM_NAME==__FILE__
|
180
|
+
filename=ARGV.first
|
181
|
+
parser=VHDL_TB::Parser.new
|
182
|
+
parser.parse filename
|
183
|
+
end
|
data/lib/template.tb.vhd
CHANGED
@@ -27,7 +27,7 @@ architecture bhv of <%=@tb.name%> is
|
|
27
27
|
end procedure;
|
28
28
|
|
29
29
|
<%=@entity.ports.collect do |port|
|
30
|
-
" signal #{port.name.
|
30
|
+
" signal #{port.name.val.ljust(@max_length)} : #{port.type}" if not @symtable.include?(port.name)
|
31
31
|
end.compact.join(";\n")%>;
|
32
32
|
|
33
33
|
begin
|
@@ -42,8 +42,10 @@ begin
|
|
42
42
|
-- Design Under Test
|
43
43
|
--------------------------------------------------------------------
|
44
44
|
dut : entity work.<%=@entity.name%>(<%=@arch.name%>)
|
45
|
-
|
46
|
-
|
45
|
+
<%=@generics%>
|
46
|
+
port map (
|
47
|
+
<%=@ports%>
|
48
|
+
);
|
47
49
|
|
48
50
|
--------------------------------------------------------------------
|
49
51
|
-- sequential stimuli
|
data/lib/token.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
class Token
|
2
|
+
attr_accessor :kind,:val,:pos
|
3
|
+
def initialize tab
|
4
|
+
@kind,@val,@pos=*tab
|
5
|
+
end
|
6
|
+
|
7
|
+
def is_a? kind
|
8
|
+
case kind
|
9
|
+
when Symbol
|
10
|
+
return @kind==kind
|
11
|
+
when Array
|
12
|
+
for sym in kind
|
13
|
+
return true if @kind==sym
|
14
|
+
end
|
15
|
+
return false
|
16
|
+
else
|
17
|
+
raise "wrong type during lookahead"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_not_a? kind
|
22
|
+
case kind
|
23
|
+
when Symbol
|
24
|
+
return @kind!=kind
|
25
|
+
when Array
|
26
|
+
ret=true
|
27
|
+
for sym in kind
|
28
|
+
ret=false if @kind==sym
|
29
|
+
end
|
30
|
+
return ret
|
31
|
+
else
|
32
|
+
raise "wrong type during lookahead"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# def accept visitor
|
37
|
+
# end
|
38
|
+
|
39
|
+
def self.create str
|
40
|
+
Token.new [:id,str,[0,0]]
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
val
|
45
|
+
end
|
46
|
+
|
47
|
+
def clone
|
48
|
+
Token.new([@kind,@val,@pos])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ONE = Token.new [:int_lit,'1',['na','na']]
|
53
|
+
ZERO = Token.new [:int_lit,'0',['na','na']]
|
54
|
+
DUMMY= Token.new [:id ,'' ,['na','na']]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vhdl_tb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean-Christophe Le Lann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A simple testbench generator for VHDL
|
14
14
|
email: jean-christophe.le_lann@ensta-bretagne.fr
|
@@ -20,8 +20,14 @@ extra_rdoc_files: []
|
|
20
20
|
files:
|
21
21
|
- bin/tbgen
|
22
22
|
- bin/vhdl_tb
|
23
|
+
- lib/ast.rb
|
24
|
+
- lib/compiler.rb
|
25
|
+
- lib/generic_lexer.rb
|
26
|
+
- lib/generic_parser.rb
|
27
|
+
- lib/lexer.rb
|
28
|
+
- lib/parser.rb
|
23
29
|
- lib/template.tb.vhd
|
24
|
-
- lib/
|
30
|
+
- lib/token.rb
|
25
31
|
- tests/circuit.vhd
|
26
32
|
homepage: http://rubygems.org/gems/vhdl_tb
|
27
33
|
licenses:
|
data/lib/vhdl_tb.rb
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
require 'erb'
|
2
|
-
require 'pp'
|
3
|
-
require 'optparse'
|
4
|
-
|
5
|
-
Entity = Struct.new("Entity",:name,:ports)
|
6
|
-
Architecture = Struct.new("Architecture",:name,:entity)
|
7
|
-
Port = Struct.new("Port",:name,:dir,:type)
|
8
|
-
Testbench = Struct.new("Testbench",:name)
|
9
|
-
|
10
|
-
class VhdlTb
|
11
|
-
|
12
|
-
VERSION = "0.4"
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
#puts __dir__
|
16
|
-
banner
|
17
|
-
@engine=ERB.new(IO.read "#{__dir__}/template.tb.vhd")
|
18
|
-
end
|
19
|
-
|
20
|
-
def banner
|
21
|
-
puts "-- "+"="*60
|
22
|
-
puts "-- VHDL testbench generator -- Jean-Christophe Le Lann 2017"
|
23
|
-
puts "-- "+"="*60
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def analyze_options args
|
28
|
-
args << "-h" if args.empty?
|
29
|
-
|
30
|
-
opt_parser = OptionParser.new do |opts|
|
31
|
-
opts.banner = "Usage: vhdl_tb (or tbgen) <filename>"
|
32
|
-
|
33
|
-
opts.on("-v", "--version", "Prints version") do |n|
|
34
|
-
puts VERSION
|
35
|
-
abort
|
36
|
-
end
|
37
|
-
|
38
|
-
opts.on("-h", "--help", "Prints this help") do
|
39
|
-
puts "Generates testbench in VHDL, from a given file containing an Entity-Architecture couple."
|
40
|
-
puts
|
41
|
-
puts opts
|
42
|
-
abort
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
begin
|
47
|
-
opt_parser.parse!(args)
|
48
|
-
@args=args
|
49
|
-
|
50
|
-
rescue Exception => e
|
51
|
-
puts e
|
52
|
-
#puts e.backtrace
|
53
|
-
exit
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def generate entity_filename=@args.first
|
58
|
-
@symtable=[]
|
59
|
-
@symtable << "clk"
|
60
|
-
@symtable << "reset_n"
|
61
|
-
@symtable << "sreset"
|
62
|
-
analyze(entity_filename)
|
63
|
-
tb_txt=@engine.result(binding)
|
64
|
-
tb_filename="#{@tb.name}.vhd"
|
65
|
-
File.open(tb_filename,'w'){|f| f.puts tb_txt}
|
66
|
-
puts "testbench generated : #{tb_filename}"
|
67
|
-
end
|
68
|
-
|
69
|
-
def analyze entity_filename
|
70
|
-
puts "analyzing VHDL file : #{entity_filename}"
|
71
|
-
code=IO.read(entity_filename)
|
72
|
-
regexp_entity=/entity\s+(\w+)\s+is\s+(generic\(.*\)\;\s+)?port\s*\(\s*(.*)\)\s*\;\s*end\s+/im
|
73
|
-
entity_matched = regexp_entity.match(code)
|
74
|
-
name,generics,iotext=*entity_matched.captures
|
75
|
-
|
76
|
-
ioregexp=/(\w+)\s*:\s*(\w+)\s+(.*)\s*;?/ix
|
77
|
-
iotab= iotext.scan(ioregexp)
|
78
|
-
io= to_port(iotab)
|
79
|
-
puts "entity found : #{name} (#{io.size} ports)"
|
80
|
-
@entity=Entity.new(name,io)
|
81
|
-
|
82
|
-
regexp_arch=/architecture\s+(\w+)\s+of\s+(\w+)/im
|
83
|
-
arch_matched = regexp_arch.match(code)
|
84
|
-
arch_name,entity_name=*arch_matched.captures
|
85
|
-
@arch=Architecture.new(arch_name,entity_name)
|
86
|
-
@tb=Testbench.new(@entity.name+"_tb")
|
87
|
-
end
|
88
|
-
|
89
|
-
def to_port iotab
|
90
|
-
@max_length=0
|
91
|
-
iotab.collect do |io|
|
92
|
-
name,dir,type=io
|
93
|
-
type=type.split(";")[0] #suppress comments
|
94
|
-
@max_length=name.size if name.size>@max_length
|
95
|
-
Port.new(name,dir,type)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
if $PROGRAM_NAME == __FILE__
|
102
|
-
raise "you need to provide a VHDL file that contains the entity to test" if ARGV.size==0
|
103
|
-
filename=ARGV[0]
|
104
|
-
VhdlTb.new.generate(filename)
|
105
|
-
end
|