reggae_eda 0.0.6

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.
@@ -0,0 +1,102 @@
1
+ -- pong p.chu code.
2
+ library ieee;
3
+ use ieee.std_logic_1164.all;
4
+ use ieee.numeric_std.all;
5
+ entity uart_tx is
6
+ generic(
7
+ DBIT : integer := 8; -- # data bits
8
+ SB_TICK : integer := 16 -- # ticks for stop bits
9
+ );
10
+ port(
11
+ clk, reset_n : in std_logic;
12
+ tx_start : in std_logic;
13
+ s_tick : in std_logic;
14
+ din : in std_logic_vector(7 downto 0);
15
+ tx_done_tick : out std_logic;
16
+ tx : out std_logic
17
+ );
18
+ end uart_tx;
19
+
20
+ architecture arch of uart_tx is
21
+ type state_type is (idle, start, data, stop);
22
+ signal state_reg, state_next : state_type;
23
+ signal s_reg, s_next : unsigned(3 downto 0);
24
+ signal n_reg, n_next : unsigned(2 downto 0);
25
+ signal b_reg, b_next : std_logic_vector(7 downto 0);
26
+ signal tx_reg, tx_next : std_logic;
27
+ begin
28
+ -- FSMD state & data registers
29
+ process(clk, reset_n)
30
+ begin
31
+ if reset_n = '0' then
32
+ state_reg <= idle;
33
+ s_reg <= (others => '0');
34
+ n_reg <= (others => '0');
35
+ b_reg <= (others => '0');
36
+ tx_reg <= '1';
37
+ elsif (clk'event and clk = '1') then
38
+ state_reg <= state_next;
39
+ s_reg <= s_next;
40
+ n_reg <= n_next;
41
+ b_reg <= b_next;
42
+ tx_reg <= tx_next;
43
+ end if;
44
+ end process;
45
+ -- next-state logic & data path functional units/routing
46
+ process(state_reg, s_reg, n_reg, b_reg, s_tick,
47
+ tx_reg, tx_start, din)
48
+ begin
49
+ state_next <= state_reg;
50
+ s_next <= s_reg;
51
+ n_next <= n_reg;
52
+ b_next <= b_reg;
53
+ tx_next <= tx_reg;
54
+ tx_done_tick <= '0';
55
+ case state_reg is
56
+ when idle =>
57
+ tx_next <= '1';
58
+ if tx_start = '1' then
59
+ state_next <= start;
60
+ s_next <= (others => '0');
61
+ b_next <= din;
62
+ end if;
63
+ when start =>
64
+ tx_next <= '0';
65
+ if (s_tick = '1') then
66
+ if s_reg = 15 then
67
+ state_next <= data;
68
+ s_next <= (others => '0');
69
+ n_next <= (others => '0');
70
+ else
71
+ s_next <= s_reg + 1;
72
+ end if;
73
+ end if;
74
+ when data =>
75
+ tx_next <= b_reg(0);
76
+ if (s_tick = '1') then
77
+ if s_reg = 15 then
78
+ s_next <= (others => '0');
79
+ b_next <= '0' & b_reg(7 downto 1);
80
+ if n_reg = (DBIT-1) then
81
+ state_next <= stop;
82
+ else
83
+ n_next <= n_reg + 1;
84
+ end if;
85
+ else
86
+ s_next <= s_reg + 1;
87
+ end if;
88
+ end if;
89
+ when stop =>
90
+ tx_next <= '1';
91
+ if (s_tick = '1') then
92
+ if s_reg = (SB_TICK-1) then
93
+ state_next <= idle;
94
+ tx_done_tick <= '1';
95
+ else
96
+ s_next <= s_reg + 1;
97
+ end if;
98
+ end if;
99
+ end case;
100
+ end process;
101
+ tx <= tx_reg;
102
+ end arch;
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/reggae'
3
+
4
+ reggae=Reggae::Compiler.new
5
+ reggae.analyze_options ARGV
6
+ reggae.compile
@@ -0,0 +1,3 @@
1
+ require_relative 'reggae/compiler'
2
+ module Reggae
3
+ end
@@ -0,0 +1,72 @@
1
+ module Reggae
2
+
3
+ module Visitable
4
+ def accept(visitor, arg=nil)
5
+ name = self.class.name.split(/::/)[1]
6
+ visitor.send("visit#{name}".to_sym, self ,arg) # Metaprograming !
7
+ end
8
+ end
9
+
10
+ class MemoryMap < Struct.new(:name,:parameters,:zones,:comments)
11
+ include Visitable
12
+ end
13
+
14
+ class Comment < Struct.new(:txt)
15
+ include Visitable
16
+ end
17
+
18
+ class Parameters < Struct.new(:bus,:range)
19
+ include Visitable
20
+ end
21
+
22
+ class Bus < Struct.new(:frequency,:address_size,:data_size)
23
+ include Visitable
24
+ end
25
+
26
+ class Range < Struct.new(:from,:to)
27
+ include Visitable
28
+ end
29
+
30
+ class Zone < Struct.new(:name,:range,:registers,:subzones,:instances)
31
+ include Visitable
32
+ end
33
+
34
+ class Instance < Struct.new(:name,:mapping)
35
+ include Visitable
36
+ end
37
+
38
+ class Connect < Struct.new(:formal,:actual)
39
+ include Visitable
40
+ end
41
+
42
+ class Input < Struct.new(:name)
43
+ include Visitable
44
+ end
45
+
46
+ class Output < Struct.new(:name)
47
+ include Visitable
48
+ end
49
+
50
+ class RegSig < Struct.new(:name,:field)
51
+ include Visitable
52
+ end
53
+
54
+ class Subzone < Zone
55
+ include Visitable
56
+ end
57
+
58
+ class Register < Struct.new(:name,:address,:init,:sampling,:writable,:bits,:bitfields)
59
+ include Visitable
60
+ def bit(n)
61
+ @bits.find{|bit| bit.position==n}
62
+ end
63
+ end
64
+
65
+ class Bit < Struct.new(:position,:name,:purpose,:toggle)
66
+ include Visitable
67
+ end
68
+
69
+ class Bitfield < Bit
70
+ include Visitable
71
+ end
72
+ end
@@ -0,0 +1,55 @@
1
+ class Code
2
+
3
+ attr_accessor :indent,:code
4
+
5
+ def initialize indent=0
6
+ @code=[]
7
+ @indent=indent
8
+ end
9
+
10
+ def empty?
11
+ @code.size==0
12
+ end
13
+
14
+ def <<(str)
15
+ if str.is_a? Code
16
+ str.code.each do |line|
17
+ @code << " "*@indent+line
18
+ end
19
+ elsif str.nil?
20
+ else
21
+ @code << " "*@indent+str
22
+ end
23
+ end
24
+
25
+ def finalize dot=false
26
+ if dot
27
+ return @code.join('\n')
28
+ end
29
+ @code.join("\n")
30
+ end
31
+
32
+ def newline
33
+ @code << " "
34
+ end
35
+
36
+ def save_as filename,verbose=true
37
+ str=self.finalize
38
+ if filename.end_with? ".vhd"
39
+ str=clean_vhdl(str)
40
+ end
41
+ File.open(filename,'w'){|f| f.puts(str)}
42
+ puts "saved code in file #{filename}" if verbose
43
+ return filename
44
+ end
45
+
46
+ def size
47
+ @code.size
48
+ end
49
+
50
+ def clean_vhdl str
51
+ str1=str.gsub(/\;\s*\)/,")")
52
+ str2=str1.gsub(/\,\s*\)/,")")
53
+ end
54
+
55
+ end
@@ -0,0 +1,110 @@
1
+ require 'pp'
2
+ require 'optparse'
3
+ require_relative 'parser'
4
+ require_relative 'pretty_printer'
5
+ require_relative 'vhdl_generator'
6
+ require_relative 'version'
7
+
8
+ module Reggae
9
+
10
+ class Compiler
11
+
12
+ attr_accessor :ast
13
+
14
+ def initialize
15
+ puts "Reggae generator #{VERSION}"
16
+ @options={}
17
+ end
18
+
19
+ def analyze_options args
20
+ args << "-h" if args.empty?
21
+
22
+ opt_parser = OptionParser.new do |opts|
23
+ opts.banner = "Usage: reggae <filename.sexp>"
24
+
25
+ opts.on("-v", "--version", "Prints version") do
26
+ puts VERSION
27
+ abort
28
+ end
29
+
30
+ opts.on("-h", "--help", "Prints this help") do
31
+ puts
32
+ puts "Generates an IP-based system, from its memory-map expressed in s-expressions."
33
+ puts
34
+ puts "Author mail: jean-christophe.le_lann@ensta-bretagne.fr"
35
+ puts
36
+ @options[:show_help]=true
37
+ puts opts
38
+ abort
39
+ end
40
+
41
+ opts.on("-s", "--system", "Generates a top-level system") do
42
+ @options[:gen_system]=true
43
+ end
44
+
45
+ opts.on("-d","Shows VHDL generated in the terminal,during generation") do
46
+ @options[:show_code]=true
47
+ end
48
+
49
+ opts.on("-u", "--include_uart", "Generates an UART Master in the system top-level") do
50
+ @options[:include_uart]=true
51
+ end
52
+
53
+ opts.on("--gen_ruby", "Generates Ruby code to interact with the system from a PC host") do
54
+ @options[:gen_ruby]=true
55
+ end
56
+
57
+ opts.on("-x", "--gen_xdc", "Generates a Xilinx XDC constraint file for Artix7 FPGA (IP only)") do
58
+ @options[:gen_xdc]=true
59
+ end
60
+
61
+ opts.on("--from_vivado_hls", "Indicates that the sexp file is generated from VHDL_WRAP tuned for VivadoHLS") do
62
+ @options[:from_vivado_hls]=true
63
+ end
64
+ end
65
+
66
+ begin
67
+ opt_parser.parse!(args)
68
+ rescue Exception => e
69
+ puts e
70
+ #puts e.backtrace
71
+ exit
72
+ end
73
+ @filename = ARGV.pop
74
+ $dirname = File.dirname(@filename) if @filename
75
+ unless @filename or @options[:show_help]
76
+ puts "Need to specify a filename to process"
77
+ #exit
78
+ end
79
+ end
80
+
81
+ def compile
82
+ if @options.any?
83
+ puts
84
+ puts "running with the following options :"
85
+ pp @options
86
+ puts
87
+ end
88
+ @ast=parse(@filename)
89
+ #pretty_print
90
+ generate_vhdl
91
+ end
92
+
93
+ def parse filename
94
+ puts "=> parsing #{filename}"
95
+ $working_dir=Dir.pwd
96
+ @ast=Reggae::Parser.new.parse(filename)
97
+ end
98
+
99
+ def pretty_print
100
+ puts "=> pretty print..."
101
+ Reggae::Visitor.new.visit(ast)
102
+ end
103
+
104
+ def generate_vhdl
105
+ puts "=> generating VHDL..."
106
+ Reggae::VHDLGenerator.new(@options).generate_from(ast)
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,281 @@
1
+ require 'sxp'
2
+ require 'pp'
3
+
4
+ require_relative 'ast'
5
+
6
+ class Array
7
+ def header
8
+ self.first
9
+ end
10
+ end
11
+
12
+ module Reggae
13
+
14
+ class Parser
15
+
16
+ def parse filename
17
+ str=IO.read(filename)
18
+ str=fix_sexpistol_bug(str)
19
+ mm_a=SXP.read(str)
20
+ parseMemoryMap(mm_a)
21
+ end
22
+
23
+ def fix_sexpistol_bug str
24
+ s1=str.gsub(/0x(\w+)/,'\1') #0x....
25
+ s2=s1.gsub(/(\d+)\.\.(\d+)/,'\1 \2') #range
26
+ end
27
+
28
+ def parseMemoryMap ary
29
+ mm=MemoryMap.new(nil,nil,[])
30
+ ary.shift
31
+ mm.name=ary.shift
32
+ while ary.any?
33
+ case h=ary.first.header
34
+ when :comment
35
+ mm.comments ||=[]
36
+ mm.comments << parseComment(ary.shift)
37
+ when :parameters
38
+ mm.parameters=parseParameters(ary.shift)
39
+ when :zone
40
+ mm.zones << parseZone(ary.shift)
41
+ else
42
+ raise "error.expecting 'zone' or 'parameters'. got a '#{h}'"
43
+ end
44
+ end
45
+ mm
46
+ end
47
+
48
+ def parseComment ary
49
+ comment=Comment.new(nil)
50
+ ary.shift
51
+ comment.txt=ary.shift
52
+ comment
53
+ end
54
+
55
+ def parseParameters ary
56
+ param=Parameters.new(nil,nil)
57
+ ary.shift
58
+ while ary.any?
59
+ case h=ary.first.header
60
+ when :bus
61
+ param.bus=parseBus(ary.shift)
62
+ when :range
63
+ param.range=parseRange(ary.shift)
64
+ else
65
+ raise "error.expecting 'bus' or 'range'. Got a '#{h}'"
66
+ end
67
+ end
68
+ @param=param
69
+ param
70
+ end
71
+
72
+ def parseBus ary
73
+ bus=Bus.new(nil,nil,nil)
74
+ ary.shift
75
+ while ary.any?
76
+ case h=ary.first.header
77
+ when :frequency
78
+ bus.frequency=ary.first.last.to_i
79
+ when :address_size
80
+ bus.address_size=ary.first.last.to_i
81
+ when :data_size
82
+ bus.data_size=ary.first.last.to_i
83
+ else
84
+ raise "error during parseBus. Expecting 'frequency','address_size' or 'data_size'. Got '#{h}'"
85
+ end
86
+ ary.shift
87
+ end
88
+ bus
89
+ end
90
+
91
+ def parseHexa sym
92
+ "0x#{sym}"
93
+ end
94
+
95
+ def parseRange ary
96
+ rg=Range.new(nil,nil)
97
+ rg.from=parseHexa(ary[1])
98
+ rg.to=parseHexa(ary[2])
99
+ rg
100
+ end
101
+
102
+ def parseZone ary
103
+ zone=Zone.new(nil,nil,[],[],[])
104
+ ary.shift
105
+ zone.name=ary.shift
106
+ while ary.any?
107
+ case h=ary.first.header
108
+ when :range
109
+ zone.range=parseRange(ary.shift)
110
+ when :register
111
+ zone.registers << parseRegister(ary.shift)
112
+ when :subzone
113
+ zone.subzones << parseSubZone(ary.shift)
114
+ when :instance
115
+ zone.instances << parseInstance(ary.shift)
116
+ else
117
+ raise "error during parseZone.Expecting 'range' or 'register'. Got '#{h}'"
118
+ end
119
+ end
120
+ zone
121
+ end
122
+
123
+ def parseInstance ary
124
+ inst=Instance.new(nil,[])
125
+ ary.shift
126
+ inst.name=ary.shift
127
+ while ary.any?
128
+ case h=ary.first.header
129
+ when :connect
130
+ inst.mapping << parseConnect(ary.shift)
131
+ else
132
+ raise "error during parseZone.Expecting 'connect'. Got '#{h}'"
133
+ end
134
+ end
135
+ inst
136
+ end
137
+
138
+ def parseConnect ary
139
+ map=Connect.new(nil,nil)
140
+ ary.shift
141
+ map.formal=parseFormalIO(ary.shift)
142
+ map.actual=parseRegSig(ary.shift)
143
+ map
144
+ end
145
+
146
+ def parseFormalIO ary
147
+ dir=ary.shift
148
+ case dir
149
+ when :input
150
+ return Input.new(ary.shift)
151
+ when :output
152
+ return Output.new(ary.shift)
153
+ else
154
+ raise "error in formal io : #{ary}"
155
+ end
156
+ end
157
+
158
+ def parseRegSig ary
159
+ sig=RegSig.new(nil,nil)
160
+ ary.shift #register
161
+ sig.name=ary.shift
162
+ sig.field=ary.shift
163
+ sig
164
+ end
165
+
166
+ def parseSubZone ary
167
+ zone=Subzone.new(nil,nil,[],[])
168
+ ary.shift
169
+ zone.name=ary.shift
170
+ while ary.any?
171
+ case h=ary.first.header
172
+ when :range
173
+ zone.range=parseRange(ary.shift)
174
+ when :register
175
+ zone.registers << parseRegister(ary.shift)
176
+ when :subzone
177
+ zone.subzones << parseSubZone(ary.shift)
178
+ else
179
+ raise "error during parseZone.Expecting 'range' or 'register'. Got '#{h}'"
180
+ end
181
+ end
182
+ zone
183
+ end
184
+
185
+ def parseRegister ary
186
+ reg=Register.new(nil,nil,nil,nil,true,[],[])
187
+ ary.shift
188
+ reg.name=ary.shift
189
+ while ary.any?
190
+ case h=ary.first.header
191
+ when :address
192
+ reg.address=parseAddress(ary.shift)
193
+ when :init
194
+ reg.init=parseInit(ary.shift)
195
+ when :sampling
196
+ reg.sampling=parseSampling(ary.shift)
197
+ when :writable
198
+ reg.writable=parseWritable(ary.shift)
199
+ when :bit
200
+ reg.bits << parseBit(ary.shift)
201
+ when :bitfield
202
+ reg.bitfields << parseBitfield(ary.shift)
203
+ else
204
+ raise "Error during parseRegister"
205
+ end
206
+ end
207
+ if reg.bits.empty? and reg.bitfields.empty?
208
+ bus_size=@param.bus.data_size
209
+ position=[bus_size-1,0]
210
+ reg.bitfields << Bitfield.new(position,name=:value,nil,nil)
211
+ end
212
+ reg
213
+ end
214
+
215
+ def parseAddress ary
216
+ parseHexa(ary.last)
217
+ end
218
+
219
+ def parseInit ary
220
+ parseHexa(ary.last)
221
+ end
222
+
223
+ def parseSampling ary
224
+ ary.shift
225
+ ary.shift==:true
226
+ end
227
+
228
+ def parseWritable ary
229
+ ary.shift
230
+ ary.shift==:true
231
+ end
232
+
233
+ def parseBit ary
234
+ bit=Bit.new(nil,nil,nil,nil)
235
+ ary.shift
236
+ bit.position=ary.shift.to_i
237
+ while ary.any?
238
+ case h=ary.first.header
239
+ when :name
240
+ bit.name=parseName(ary.shift)
241
+ when :purpose
242
+ bit.purpose=parsePurpose(ary.shift)
243
+ when :toggle
244
+ bit.toggle=parseToggle(ary.shift)
245
+ end
246
+ end
247
+ bit
248
+ end
249
+
250
+ def parseName ary
251
+ ary[1]
252
+ end
253
+
254
+ def parsePurpose ary
255
+ ary[1]
256
+ end
257
+
258
+ def parseToggle ary
259
+ ary[1]==:true
260
+ end
261
+
262
+ def parseBitfield ary
263
+ bf=Bitfield.new(nil,nil,nil,nil)
264
+ ary.shift
265
+ bf.position=[]
266
+ bf.position << ary.shift.to_s.to_i
267
+ bf.position << ary.shift.to_s.to_i
268
+ while ary.any?
269
+ case h=ary.first.header
270
+ when :name
271
+ bf.name=parseName(ary.shift)
272
+ when :purpose
273
+ bf.purpose=parsePurpose(ary.shift)
274
+ when :toggle
275
+ bf.toggle=parseToggle(ary.shift)
276
+ end
277
+ end
278
+ bf
279
+ end
280
+ end #parser
281
+ end #module