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.
- checksums.yaml +7 -0
- data/assets/Nexys4DDR_Master.xdc +255 -0
- data/assets/fifo.vhd +109 -0
- data/assets/flag_buf.vhd +45 -0
- data/assets/mod_m_counter.vhd +36 -0
- data/assets/slow_ticker.vhd +49 -0
- data/assets/uart.vhd +69 -0
- data/assets/uart_bus_master.vhd +256 -0
- data/assets/uart_rx.vhd +90 -0
- data/assets/uart_tx.vhd +102 -0
- data/bin/reggae +6 -0
- data/lib/reggae.rb +3 -0
- data/lib/reggae/ast.rb +72 -0
- data/lib/reggae/code.rb +55 -0
- data/lib/reggae/compiler.rb +110 -0
- data/lib/reggae/parser.rb +281 -0
- data/lib/reggae/pretty_printer.rb +107 -0
- data/lib/reggae/version.rb +3 -0
- data/lib/reggae/vhdl_generator.rb +973 -0
- data/lib/reggae/visitor.rb +107 -0
- data/tests/regmap.sexp +139 -0
- metadata +64 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative 'code'
|
2
|
+
require_relative 'visitor'
|
3
|
+
module Reggae
|
4
|
+
|
5
|
+
class PrettyPrinter < Visitor
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@indent=-2
|
9
|
+
end
|
10
|
+
|
11
|
+
def inc str=nil
|
12
|
+
say(str) if str
|
13
|
+
@indent+=2
|
14
|
+
end
|
15
|
+
|
16
|
+
def dec
|
17
|
+
@indent-=2
|
18
|
+
end
|
19
|
+
|
20
|
+
def say str
|
21
|
+
puts " "*@indent+str.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit mm
|
25
|
+
inc
|
26
|
+
mm.accept(self,nil)
|
27
|
+
dec
|
28
|
+
end
|
29
|
+
|
30
|
+
def visitMemoryMap mm,args=nil
|
31
|
+
inc "MemoryMap"
|
32
|
+
say mm.name
|
33
|
+
mm.parameters.accept(self,nil)
|
34
|
+
mm.zones.each{|zone| zone.accept(self,nil)}
|
35
|
+
dec
|
36
|
+
end
|
37
|
+
|
38
|
+
def visitParameters params,args=nil
|
39
|
+
inc "Parameters"
|
40
|
+
params.bus.accept(self,nil)
|
41
|
+
params.range.accept(self,nil)
|
42
|
+
dec
|
43
|
+
end
|
44
|
+
|
45
|
+
def visitBus bus,args=nil
|
46
|
+
inc "Bus"
|
47
|
+
say bus.frequency
|
48
|
+
say bus.address_size
|
49
|
+
say bus.data_size
|
50
|
+
dec
|
51
|
+
end
|
52
|
+
|
53
|
+
def visitRange range,args=nil
|
54
|
+
inc "Range"
|
55
|
+
say range.from
|
56
|
+
say range.to
|
57
|
+
dec
|
58
|
+
end
|
59
|
+
|
60
|
+
def visitZone zone,args=nil
|
61
|
+
inc "Zone"
|
62
|
+
say zone.name
|
63
|
+
zone.range.accept(self)
|
64
|
+
zone.registers.each{|reg| reg.accept(self)}
|
65
|
+
zone.subzones.each{|subzone| subzone.accept(self)}
|
66
|
+
dec
|
67
|
+
end
|
68
|
+
|
69
|
+
def visitSubzone zone,args=nil
|
70
|
+
inc "Subzone"
|
71
|
+
say zone.name
|
72
|
+
zone.range.accept(self)
|
73
|
+
zone.registers.each{|reg| reg.accept(self)}
|
74
|
+
dec
|
75
|
+
end
|
76
|
+
|
77
|
+
def visitRegister reg,args=nil
|
78
|
+
inc "Register"
|
79
|
+
say reg.name
|
80
|
+
say reg.address
|
81
|
+
say reg.init
|
82
|
+
reg.bits.each{|bit| bit.accept(self)}
|
83
|
+
reg.bitfields.each{|bitfield| bitfield.accept(self)}
|
84
|
+
|
85
|
+
dec
|
86
|
+
end
|
87
|
+
|
88
|
+
def visitBit bit,args=nil
|
89
|
+
inc "Bit"
|
90
|
+
say bit.position
|
91
|
+
say bit.name
|
92
|
+
say bit.purpose
|
93
|
+
say bit.toggle
|
94
|
+
dec
|
95
|
+
end
|
96
|
+
|
97
|
+
def visitBitfield bitfield,args=nil
|
98
|
+
inc "Bitfield"
|
99
|
+
say bitfield.position
|
100
|
+
say bitfield.name
|
101
|
+
say bitfield.purpose
|
102
|
+
say bitfield.toggle
|
103
|
+
dec
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,973 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require_relative 'code'
|
4
|
+
|
5
|
+
module Reggae
|
6
|
+
|
7
|
+
class VHDLGenerator < Visitor
|
8
|
+
|
9
|
+
def initialize options
|
10
|
+
super()
|
11
|
+
@options=options
|
12
|
+
@verbose=options[:verbose]
|
13
|
+
@vhdl_entity_arch=nil
|
14
|
+
@vhdl_pkg=nil
|
15
|
+
@vhdl_files=[]
|
16
|
+
end
|
17
|
+
|
18
|
+
#.......................VHDL.......................
|
19
|
+
def generate_from model
|
20
|
+
@dest_dir=$working_dir #+"/src"
|
21
|
+
if !Dir.exists?(@dest_dir)
|
22
|
+
FileUtils.mkdir(@dest_dir)
|
23
|
+
end
|
24
|
+
begin
|
25
|
+
inc
|
26
|
+
model.accept(self,nil)
|
27
|
+
#gen_testbench
|
28
|
+
gen_compile_script
|
29
|
+
gen_synthesis_script
|
30
|
+
gen_ruby_sw_if(model) if @options[:gen_ruby]
|
31
|
+
dec
|
32
|
+
rescue Exception => e
|
33
|
+
puts e.backtrace
|
34
|
+
puts e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def now
|
39
|
+
time = Time.new
|
40
|
+
time.ctime
|
41
|
+
end
|
42
|
+
|
43
|
+
def header
|
44
|
+
code=Code.new
|
45
|
+
code << "-"*80
|
46
|
+
code << "-- Generated automatically by Reggae compiler "
|
47
|
+
code << "-- (c) Jean-Christophe Le Lann - 2011"
|
48
|
+
code << "-- date : #{now}"
|
49
|
+
code << "-"*80
|
50
|
+
#code.newline
|
51
|
+
code << "library ieee,std;"
|
52
|
+
code << "use ieee.std_logic_1164.all;"
|
53
|
+
code << "use ieee.numeric_std.all;"
|
54
|
+
code.newline
|
55
|
+
code
|
56
|
+
end
|
57
|
+
|
58
|
+
def clock_and_reset
|
59
|
+
code=Code.new
|
60
|
+
code << "reset_n : in std_logic;"
|
61
|
+
code << "clk : in std_logic;"
|
62
|
+
code << "sreset : in std_logic;"
|
63
|
+
code
|
64
|
+
end
|
65
|
+
|
66
|
+
def visitMemoryMap mm,args=nil
|
67
|
+
inc "MemoryMap"
|
68
|
+
@model_name=mm.name
|
69
|
+
@addr_size=mm.parameters.bus.address_size
|
70
|
+
@data_size=mm.parameters.bus.data_size
|
71
|
+
|
72
|
+
mm.parameters.accept(self,nil)
|
73
|
+
mm.zones.each{|zone| zone.accept(self,nil)}
|
74
|
+
if @options[:gen_system]
|
75
|
+
gen_system(mm)
|
76
|
+
unless @options[:include_uart]
|
77
|
+
puts "WARNING : no uart included. -u to add it if wanted."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
dec
|
81
|
+
end
|
82
|
+
|
83
|
+
def visitZone zone,args=nil
|
84
|
+
inc "Zone"
|
85
|
+
gen_ip_pkg(zone)
|
86
|
+
gen_ip_regif(zone)
|
87
|
+
gen_ip_entity_arch(zone)
|
88
|
+
if @options[:gen_xdc]
|
89
|
+
gen_xdc(zone)
|
90
|
+
end
|
91
|
+
dec
|
92
|
+
end
|
93
|
+
|
94
|
+
def gen_ip_pkg zone
|
95
|
+
filename=zone.name.to_s+"_pkg.vhd"
|
96
|
+
puts " - code for IP package"+(" "+filename).rjust(38,'.')
|
97
|
+
code=Code.new
|
98
|
+
code << header
|
99
|
+
code << "package #{zone.name}_regif_pkg is"
|
100
|
+
code.indent=2
|
101
|
+
zone.registers.each do |reg|
|
102
|
+
code.newline
|
103
|
+
code << "type #{reg.name}_reg is record"
|
104
|
+
code.indent=4
|
105
|
+
tmp_h={}
|
106
|
+
reg.bits.each do |bit|
|
107
|
+
tmp_h[bit.name]="std_logic;"
|
108
|
+
end
|
109
|
+
reg.bitfields.each do |bitf|
|
110
|
+
min,max=bitf.position.minmax
|
111
|
+
nb_bits=(max-min)
|
112
|
+
tmp_h[bitf.name]="std_logic_vector(#{nb_bits} downto 0);"
|
113
|
+
end
|
114
|
+
max_justify=tmp_h.keys.map(&:size).max
|
115
|
+
tmp_h.each do |name,type|
|
116
|
+
code << "#{name.to_s.ljust(max_justify,' ')} : #{type}"
|
117
|
+
end
|
118
|
+
code.indent=2
|
119
|
+
code << "end record;"
|
120
|
+
code.newline
|
121
|
+
code << "constant #{reg.name.upcase}_INIT: #{reg.name}_reg :=("
|
122
|
+
code.indent=4
|
123
|
+
tmp_h={}
|
124
|
+
init_reg=reg.init.to_i(16).to_s(2).rjust(@data_size,'0')
|
125
|
+
|
126
|
+
reg.bits.each do |bit|
|
127
|
+
pos=bit.position
|
128
|
+
init_value=init_reg[@data_size-pos-1]
|
129
|
+
tmp_h[bit.name]="'#{init_value}'"
|
130
|
+
end
|
131
|
+
|
132
|
+
reg.bitfields.each do |bitf|
|
133
|
+
min,max=bitf.position.minmax
|
134
|
+
size=(max-min)+1
|
135
|
+
bitf.position
|
136
|
+
range=(@data_size-max-1)..(@data_size-min-1)
|
137
|
+
init_value=init_reg[range]
|
138
|
+
init_value=init_value.rjust(size,'0')
|
139
|
+
tmp_h[bitf.name]="\"#{init_value}\""
|
140
|
+
end
|
141
|
+
|
142
|
+
max_justify=tmp_h.keys.map(&:size).max
|
143
|
+
tmp_h.each do |name,value|
|
144
|
+
code << "#{name.to_s.ljust(max_justify,' ')} => #{value},"
|
145
|
+
end
|
146
|
+
code.indent=2
|
147
|
+
|
148
|
+
code << ");"
|
149
|
+
end
|
150
|
+
|
151
|
+
code.newline
|
152
|
+
code << "type registers_type is record"
|
153
|
+
code.indent=4
|
154
|
+
max_just=zone.registers.map{|reg| reg.name.size}.max
|
155
|
+
zone.registers.each do |reg|
|
156
|
+
code << "#{reg.name.to_s.ljust(max_just,' ')} : #{reg.name}_reg; -- #{reg.address}"
|
157
|
+
end
|
158
|
+
code.indent=2
|
159
|
+
code << "end record;"
|
160
|
+
|
161
|
+
code.newline
|
162
|
+
code << "constant REGS_INIT : registers_type :=("
|
163
|
+
code.indent=4
|
164
|
+
max_just=zone.registers.map{|reg| reg.name.size}.max
|
165
|
+
zone.registers.each do |reg|
|
166
|
+
code << "#{reg.name.to_s.ljust(max_just,' ')} => #{reg.name.upcase}_INIT,"
|
167
|
+
end
|
168
|
+
code.indent=2
|
169
|
+
code << ");"
|
170
|
+
code.newline
|
171
|
+
|
172
|
+
code << "--sampling values from IPs"
|
173
|
+
code << "type sampling_type is record"
|
174
|
+
code.indent=4
|
175
|
+
zone.registers.each do |reg|
|
176
|
+
if reg.sampling
|
177
|
+
reg.bits.each do |bit|
|
178
|
+
type="std_logic"
|
179
|
+
code << "#{reg.name.to_s}_#{bit.name} : #{type};"
|
180
|
+
end
|
181
|
+
reg.bitfields.each do |bitf|
|
182
|
+
min,max=bitf.position.minmax
|
183
|
+
nb_bits=(max-min)
|
184
|
+
type="std_logic_vector(#{nb_bits} downto 0)"
|
185
|
+
code << "#{reg.name.to_s}_#{bitf.name} : #{type};"
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
code.indent=2
|
191
|
+
code << "end record;"
|
192
|
+
|
193
|
+
code.indent=0
|
194
|
+
code.newline
|
195
|
+
code << "end package;"
|
196
|
+
@vhdl_files << vhdl="#{@dest_dir}/#{zone.name}_regif_pkg.vhd"
|
197
|
+
code.save_as(vhdl,verbose=false)
|
198
|
+
end
|
199
|
+
|
200
|
+
def gen_ip_regif zone
|
201
|
+
filename=zone.name.to_s+"_regif.vhd"
|
202
|
+
puts " - code for IP register interface "+(" "+filename).rjust(26,'.')
|
203
|
+
code=Code.new
|
204
|
+
code << header
|
205
|
+
code << "use work.#{zone.name}_pkg.all;"
|
206
|
+
code.newline
|
207
|
+
code << "entity #{zone.name}_reg is"
|
208
|
+
code.indent=2
|
209
|
+
code << "port("
|
210
|
+
code.indent=4
|
211
|
+
code << clock_and_reset
|
212
|
+
code << "ce : in std_logic;"
|
213
|
+
code << "we : in std_logic;"
|
214
|
+
code << "address : in unsigned(#{@addr_size-1} downto 0);"
|
215
|
+
code << "datain : in std_logic_vector(#{@data_size-1} downto 0);"
|
216
|
+
code << "dataout : out std_logic_vector(#{@data_size-1} downto 0);"
|
217
|
+
code << "registers : out registers_type;"
|
218
|
+
code << "sampling : in sampling_type;"
|
219
|
+
code.indent=2
|
220
|
+
code << ");"
|
221
|
+
code.indent=0
|
222
|
+
code << "end #{zone.name}_reg;"
|
223
|
+
code.newline
|
224
|
+
code << "architecture RTL of #{zone.name}_reg is"
|
225
|
+
code.newline
|
226
|
+
code.indent=2
|
227
|
+
code << "--interface"
|
228
|
+
code << "signal regs : registers_type;"
|
229
|
+
code.newline
|
230
|
+
code << "--addresses are declared here to avoid VHDL93 error /locally static/"
|
231
|
+
tmp_h={}
|
232
|
+
zone.registers.each do |reg|
|
233
|
+
addr_vhdl=reg.address.to_i(16)
|
234
|
+
addr_vhdl_b2=addr_vhdl.to_s(2).rjust(@addr_size,'0')
|
235
|
+
nb_digits_hex=(@addr_size/4.0).ceil
|
236
|
+
addr_vhdl=addr_vhdl.to_s.rjust(nb_digits_hex,'0')
|
237
|
+
addr_name="ADDR_#{reg.name.upcase}"
|
238
|
+
tmp_code=": unsigned(#{@addr_size-1} downto 0) := \"#{addr_vhdl_b2}\";-- 0x#{addr_vhdl};"
|
239
|
+
tmp_h[addr_name]=tmp_code
|
240
|
+
end
|
241
|
+
max_length_addr_name=(tmp_h.keys.max_by{|e|e.size}||[]).size
|
242
|
+
#pp tmp_h
|
243
|
+
|
244
|
+
tmp_h.each do |addr,kode|
|
245
|
+
code << "constant #{addr.ljust(max_length_addr_name,' ')} #{kode}"
|
246
|
+
end
|
247
|
+
code.newline
|
248
|
+
code << "--application signals"
|
249
|
+
code << declare_application_sampling_signals(zone)
|
250
|
+
code.indent=0
|
251
|
+
code.newline
|
252
|
+
code << "begin"
|
253
|
+
code.newline
|
254
|
+
code.indent=2
|
255
|
+
#---- write process
|
256
|
+
code << "write_reg_p : process(reset_n,clk)"
|
257
|
+
code << "begin"
|
258
|
+
code.indent=4
|
259
|
+
code << "if reset_n='0' then"
|
260
|
+
code.indent=6
|
261
|
+
code << "regs <= REGS_INIT;"
|
262
|
+
code.indent=4
|
263
|
+
code << "elsif rising_edge(clk) then"
|
264
|
+
code.indent=6
|
265
|
+
code << "if ce='1' then"
|
266
|
+
code.indent=8
|
267
|
+
code << "if we='1' then"
|
268
|
+
code.indent=10
|
269
|
+
code << "case address is"
|
270
|
+
code.indent=12
|
271
|
+
zone.registers.each do |reg|
|
272
|
+
if reg.writable
|
273
|
+
code << "when ADDR_#{reg.name.upcase} =>"
|
274
|
+
code.indent=14
|
275
|
+
code << gen_reg_write(reg)
|
276
|
+
code.indent=12
|
277
|
+
end
|
278
|
+
end
|
279
|
+
code << "when others =>"
|
280
|
+
code.indent=14
|
281
|
+
code << "null;"
|
282
|
+
code.indent=12
|
283
|
+
code.indent=10
|
284
|
+
code << "end case;"
|
285
|
+
code.indent=8
|
286
|
+
code << "end if;";
|
287
|
+
code.indent=6
|
288
|
+
code << "else --no bus preemption => sampling or toggle"
|
289
|
+
code << "--sampling"
|
290
|
+
code.indent=8
|
291
|
+
no_sampling=zone.registers.collect{|r| r.sampling}
|
292
|
+
no_sampling=zone.registers.collect{|r| r.sampling}.compact.empty?
|
293
|
+
if no_sampling
|
294
|
+
code << "null; --no_sampling"
|
295
|
+
else
|
296
|
+
zone.registers.each do |reg|
|
297
|
+
code.indent=8
|
298
|
+
if reg.sampling
|
299
|
+
code << gen_reg_sampling(reg)
|
300
|
+
end
|
301
|
+
code.indent=6
|
302
|
+
end
|
303
|
+
end
|
304
|
+
code << "--toggling"
|
305
|
+
zone.registers.each do |reg|
|
306
|
+
code.indent=8
|
307
|
+
|
308
|
+
toggling_bits=reg.bits.collect{|bit| bit.toggle}.compact
|
309
|
+
if toggling_bits
|
310
|
+
code << gen_reg_toggle(reg)
|
311
|
+
end
|
312
|
+
code.indent=6
|
313
|
+
end
|
314
|
+
code.indent=6
|
315
|
+
code << "end if;"
|
316
|
+
code.indent=4
|
317
|
+
code << "end if;"
|
318
|
+
code.indent=2
|
319
|
+
code << "end process;"
|
320
|
+
#-----END write process
|
321
|
+
code.newline
|
322
|
+
code << "read_reg_p: process(reset_n,clk)"
|
323
|
+
code << "begin"
|
324
|
+
code.indent=4
|
325
|
+
code << "if reset_n='0' then"
|
326
|
+
code.indent=6
|
327
|
+
code << "dataout <= (others=>'0');"
|
328
|
+
code.indent=4
|
329
|
+
code << "elsif rising_edge(clk) then"
|
330
|
+
code.indent=6
|
331
|
+
# code << "dataout <= (others=>'0');"
|
332
|
+
code << "if ce='1' then"
|
333
|
+
code.indent=8
|
334
|
+
code << "if we='0' then"
|
335
|
+
code.indent=10
|
336
|
+
code << "dataout <= (others=>'0');"
|
337
|
+
code << "case address is"
|
338
|
+
code.indent=12
|
339
|
+
zone.registers.each do |reg|
|
340
|
+
code << gen_reg_read(reg)
|
341
|
+
end
|
342
|
+
code << "when others=>"
|
343
|
+
code.indent=14
|
344
|
+
code << "dataout <= (others=>'0');"
|
345
|
+
code.indent=12
|
346
|
+
code.indent=10
|
347
|
+
code << "end case;"
|
348
|
+
code.indent=8
|
349
|
+
code << "end if;"
|
350
|
+
code.indent=6
|
351
|
+
code << "end if;"
|
352
|
+
code.indent=4
|
353
|
+
code << "end if;"
|
354
|
+
code.indent=2
|
355
|
+
code << "end process;"
|
356
|
+
#-----
|
357
|
+
code << "registers <= regs;"
|
358
|
+
code.indent=0
|
359
|
+
code.newline
|
360
|
+
#code << gen_vivadohls_instances(zone)
|
361
|
+
code << "end RTL;"
|
362
|
+
filename="#{@dest_dir}/#{zone.name}_regif.vhd"
|
363
|
+
code.save_as filename,verbose=false
|
364
|
+
@vhdl_files << filename
|
365
|
+
code
|
366
|
+
end
|
367
|
+
|
368
|
+
def gen_ip_entity_arch zone
|
369
|
+
filename=zone.name.to_s+".vhd"
|
370
|
+
puts " - code for IP "+(" "+filename).rjust(45,'.')
|
371
|
+
code=Code.new
|
372
|
+
code << header
|
373
|
+
code << "use work.#{zone.name}_pkg.all;"
|
374
|
+
code.newline
|
375
|
+
code << "entity #{zone.name} is"
|
376
|
+
code.indent=2
|
377
|
+
code << "port("
|
378
|
+
code.indent=4
|
379
|
+
code << clock_and_reset
|
380
|
+
code << "ce : in std_logic;"
|
381
|
+
code << "we : in std_logic;"
|
382
|
+
code << "address : in unsigned(#{@addr_size-1} downto 0);"
|
383
|
+
code << "datain : in std_logic_vector(#{@data_size-1} downto 0);"
|
384
|
+
code << "dataout : out std_logic_vector(#{@data_size-1} downto 0);"
|
385
|
+
code.indent=2
|
386
|
+
code << ");"
|
387
|
+
code.indent=0
|
388
|
+
code << "end #{zone.name};"
|
389
|
+
code.newline
|
390
|
+
code << "architecture RTL of #{zone.name} is"
|
391
|
+
code.newline
|
392
|
+
code.indent=2
|
393
|
+
code << "--interface"
|
394
|
+
code << "signal regs : registers_type;"
|
395
|
+
code << "signal sampling : sampling_type;"
|
396
|
+
code.newline
|
397
|
+
code.indent=0
|
398
|
+
code << "begin"
|
399
|
+
code.newline
|
400
|
+
code.indent=2
|
401
|
+
code << gen_regif_instance(zone)
|
402
|
+
code.newline
|
403
|
+
code << gen_vivadohls_instances(zone)
|
404
|
+
code.newline
|
405
|
+
code.indent=0
|
406
|
+
code << "end RTL;"
|
407
|
+
filename="#{@dest_dir}/#{zone.name}.vhd"
|
408
|
+
code.save_as filename,verbose=false
|
409
|
+
if @options[:show_code]
|
410
|
+
puts code.finalize
|
411
|
+
end
|
412
|
+
@vhdl_files << filename
|
413
|
+
code
|
414
|
+
end
|
415
|
+
|
416
|
+
def writing_process mm
|
417
|
+
code=Code.new
|
418
|
+
code << "-- writing process"
|
419
|
+
code << "write_proc: process(clk)"
|
420
|
+
code << "begin"
|
421
|
+
code.indent=2
|
422
|
+
code << "if reset_n='0' then"
|
423
|
+
code.indent=4
|
424
|
+
code << "regs <= REGS_INIT;"
|
425
|
+
code.indent=2
|
426
|
+
code << "elsif rising_edge(clk) then"
|
427
|
+
code.indent=4
|
428
|
+
code << "if sreset='1' then"
|
429
|
+
code.indent=6
|
430
|
+
code << "regs <= REGS_INIT;"
|
431
|
+
code.indent=4
|
432
|
+
code << "else"
|
433
|
+
code.indent=6
|
434
|
+
code << "if ce='1' and we='1' then "
|
435
|
+
|
436
|
+
code.indent=8
|
437
|
+
code << "case address is "
|
438
|
+
code.indent=10
|
439
|
+
for zone in mm.zones
|
440
|
+
code << "--zone #{zone.name.to_s.upcase.center(40,'-')}"
|
441
|
+
for reg in zone.registers
|
442
|
+
name="#{zone.name.upcase}_#{reg.name.upcase}"
|
443
|
+
code << "when ADDR_#{zone.name.upcase}_#{reg.name.upcase} =>"
|
444
|
+
code.indent=12
|
445
|
+
code << "reg(#{name}) <= datain;"
|
446
|
+
code.indent=10
|
447
|
+
end
|
448
|
+
end
|
449
|
+
code << "when others => null;"
|
450
|
+
code.indent=8
|
451
|
+
code << "end case;"
|
452
|
+
code.indent=6
|
453
|
+
code << "end if;"
|
454
|
+
code.indent=4
|
455
|
+
code << "end if;"
|
456
|
+
code.indent=2
|
457
|
+
code << "end if;"
|
458
|
+
code.indent=0
|
459
|
+
code << "end process;"
|
460
|
+
code
|
461
|
+
end
|
462
|
+
|
463
|
+
def gen_regif_instance zone
|
464
|
+
code=Code.new
|
465
|
+
code << "regif_inst : entity work.#{zone.name}_reg"
|
466
|
+
code.indent=2
|
467
|
+
code << "port map("
|
468
|
+
code.indent=4
|
469
|
+
code << "reset_n => reset_n,"
|
470
|
+
code << "clk => clk,"
|
471
|
+
code << "sreset => sreset,"
|
472
|
+
code << "ce => ce,"
|
473
|
+
code << "we => we,"
|
474
|
+
code << "address => address,"
|
475
|
+
code << "datain => datain,"
|
476
|
+
code << "dataout => dataout,"
|
477
|
+
code << "registers => regs,"
|
478
|
+
code << "sampling => sampling"
|
479
|
+
code.indent=2
|
480
|
+
code << ");"
|
481
|
+
code.indent=0
|
482
|
+
code
|
483
|
+
end
|
484
|
+
|
485
|
+
# in sexp, we can instanciate components
|
486
|
+
# WARNING : default clk and reset are hardcoded here !!!!
|
487
|
+
def gen_vivadohls_instances zone
|
488
|
+
code=Code.new
|
489
|
+
if zone.instances.any?
|
490
|
+
zone.instances.each do |inst|
|
491
|
+
@vhdl_files << inst.name.to_s+".vhd"
|
492
|
+
code << "inst_#{inst.name} : entity work.#{inst.name}"
|
493
|
+
code.indent=2
|
494
|
+
code << "port map("
|
495
|
+
code.indent=4
|
496
|
+
if @options[:from_vivado_hls]
|
497
|
+
code << "ap_clk => clk,"
|
498
|
+
else #standard mode
|
499
|
+
code << "reset_n => reset_n,"
|
500
|
+
code << "clk => clk,"
|
501
|
+
end
|
502
|
+
inst.mapping.each do |cnx|
|
503
|
+
if cnx.formal.is_a? Input
|
504
|
+
sig="regs."+cnx.actual.name.to_s+"."+cnx.actual.field.to_s
|
505
|
+
else
|
506
|
+
sig="sampling.#{cnx.actual.name}_#{cnx.actual.field}"
|
507
|
+
end
|
508
|
+
code << "#{cnx.formal.name} => #{sig},"
|
509
|
+
end
|
510
|
+
code.indent=2
|
511
|
+
code << ");"
|
512
|
+
code.indent=0
|
513
|
+
end
|
514
|
+
code.newline
|
515
|
+
end
|
516
|
+
|
517
|
+
code
|
518
|
+
end
|
519
|
+
|
520
|
+
def declare_application_sampling_signals zone
|
521
|
+
code=Code.new
|
522
|
+
tmp_h={}
|
523
|
+
zone.registers.each do |reg|
|
524
|
+
if reg.sampling
|
525
|
+
reg.bits.each do |bit|
|
526
|
+
sig_name="#{reg.name}_#{bit.name}"
|
527
|
+
tmp_h[sig_name]="std_logic;"
|
528
|
+
end
|
529
|
+
reg.bitfields.each do |bitfield|
|
530
|
+
min,max=bitfield.position.minmax
|
531
|
+
nb_bits=(max-min)
|
532
|
+
sig_name="#{reg.name}_#{bitfield.name}"
|
533
|
+
tmp_h[sig_name]="std_logic_vector(#{nb_bits} downto 0);"
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
max_size=(tmp_h.keys.max_by{|e|e.size} || []).size
|
538
|
+
tmp_h.each do |signame,decl|
|
539
|
+
code << "signal #{signame.ljust(max_size,' ')} : #{decl}"
|
540
|
+
end
|
541
|
+
code
|
542
|
+
end
|
543
|
+
|
544
|
+
def gen_reg_write reg
|
545
|
+
code=Code.new
|
546
|
+
reg.bits.each do |bit|
|
547
|
+
code << "regs.#{reg.name}.#{bit.name} <= datain(#{bit.position});"
|
548
|
+
end
|
549
|
+
reg.bitfields.each do |bitfield|
|
550
|
+
min,max=bitfield.position.minmax
|
551
|
+
code << "regs.#{reg.name}.#{bitfield.name} <= datain(#{max} downto #{min});"
|
552
|
+
end
|
553
|
+
if code.empty?
|
554
|
+
code << "regs.#{reg.name} <= datain;"
|
555
|
+
end
|
556
|
+
code
|
557
|
+
end
|
558
|
+
|
559
|
+
def gen_reg_read reg
|
560
|
+
code=Code.new
|
561
|
+
code << "when ADDR_#{reg.name.upcase} =>"
|
562
|
+
code.indent=2
|
563
|
+
reg.bits.each do |bit|
|
564
|
+
code << "dataout(#{bit.position}) <= regs.#{reg.name}.#{bit.name};"
|
565
|
+
end
|
566
|
+
reg.bitfields.each do |bitfield|
|
567
|
+
min,max=bitfield.position.minmax
|
568
|
+
code << "dataout(#{max} downto #{min}) <= regs.#{reg.name}.#{bitfield.name};"
|
569
|
+
end
|
570
|
+
code.indent=0
|
571
|
+
code
|
572
|
+
end
|
573
|
+
|
574
|
+
def gen_reg_sampling reg
|
575
|
+
code=Code.new
|
576
|
+
reg.bits.each do |bit|
|
577
|
+
sig="sampling.#{reg.name}_#{bit.name}"
|
578
|
+
code << "regs.#{reg.name}.#{bit.name} <= #{sig};"
|
579
|
+
end
|
580
|
+
reg.bitfields.each do |bitfield|
|
581
|
+
sig="sampling.#{reg.name}_#{bitfield.name}"
|
582
|
+
code << "regs.#{reg.name}.#{bitfield.name} <= #{sig};"
|
583
|
+
end
|
584
|
+
code
|
585
|
+
end
|
586
|
+
|
587
|
+
def gen_reg_toggle reg
|
588
|
+
code=Code.new
|
589
|
+
reg.bits.each do |bit|
|
590
|
+
if bit.toggle
|
591
|
+
bit_init="'0'"
|
592
|
+
code << "regs.#{reg.name}.#{bit.name} <= #{bit_init};"
|
593
|
+
end
|
594
|
+
end
|
595
|
+
reg.bitfields.each do |bitfield|
|
596
|
+
if bitfield.toggle
|
597
|
+
min,max=bitfield.position.minmax
|
598
|
+
bitfield_init="0"*((max-min)+1)
|
599
|
+
code << "regs.#{reg.name}.#{bitfield.name} <= \"#{bitfield_init}\";"
|
600
|
+
end
|
601
|
+
end
|
602
|
+
code
|
603
|
+
end
|
604
|
+
|
605
|
+
#================= System stuff ================
|
606
|
+
def gen_system mm
|
607
|
+
filename="#{@dest_dir}/#{mm.name}.vhd"
|
608
|
+
generate_assets
|
609
|
+
puts " - code for complete system "+(" "+filename).rjust(32,".")
|
610
|
+
code=Code.new
|
611
|
+
code << header
|
612
|
+
code << "entity #{mm.name} is"
|
613
|
+
code.indent=2
|
614
|
+
code << "port("
|
615
|
+
code.indent=4
|
616
|
+
code << "reset_n : in std_logic;"
|
617
|
+
code << "clk : in std_logic;"
|
618
|
+
code << "rx : in std_logic;"
|
619
|
+
code << "tx : out std_logic;"
|
620
|
+
code << "leds : out std_logic_vector(15 downto 0)"
|
621
|
+
code.indent=2
|
622
|
+
code << ");"
|
623
|
+
code.indent=0
|
624
|
+
code << "end entity;"
|
625
|
+
code.newline
|
626
|
+
|
627
|
+
code << gen_system_architecture(mm)
|
628
|
+
puts code.finalize if @options[:show_code]
|
629
|
+
code.save_as(filename,verbose=false)
|
630
|
+
@vhdl_files << filename
|
631
|
+
end
|
632
|
+
|
633
|
+
def generate_assets
|
634
|
+
puts " - code for assets"+"<8 vhdl files>".rjust(42,'.')
|
635
|
+
dir=__dir__
|
636
|
+
assets_dir=dir+"/../../assets/"
|
637
|
+
#vhdl_assets=Dir["#{assets_dir}/*.vhd"]
|
638
|
+
vhdl_assets=[
|
639
|
+
"mod_m_counter.vhd",
|
640
|
+
"fifo.vhd",
|
641
|
+
"flag_buf.vhd",
|
642
|
+
"uart_rx.vhd",
|
643
|
+
"uart_tx.vhd",
|
644
|
+
"uart.vhd",
|
645
|
+
"slow_ticker.vhd",
|
646
|
+
"uart_bus_master.vhd",
|
647
|
+
"bram_xilinx.vhd",
|
648
|
+
]
|
649
|
+
dest_dir=$working_dir+"/assets"
|
650
|
+
if !Dir.exists?(dest_dir)
|
651
|
+
FileUtils.mkdir(dest_dir)
|
652
|
+
end
|
653
|
+
vhdl_assets.each do |file|
|
654
|
+
FileUtils.cp(assets_dir+file,dest_dir)
|
655
|
+
@vhdl_files << "../assets/"+File.basename(file)
|
656
|
+
end
|
657
|
+
|
658
|
+
end
|
659
|
+
|
660
|
+
def gen_synthesis_script
|
661
|
+
dest_dir=$working_dir #+"/synthesis"
|
662
|
+
filename="#{dest_dir}/synthesis.tcl"
|
663
|
+
if !Dir.exists?(dest_dir)
|
664
|
+
FileUtils.mkdir(dest_dir)
|
665
|
+
end
|
666
|
+
code=Code.new
|
667
|
+
code << "# =====FPGA device : Artix7 in Nexys4DDR"
|
668
|
+
code << "set partname \"xc7a100tcsg324-1\""
|
669
|
+
code << "set xdc_constraints \"./Nexys4DDR_Master.xdc\""
|
670
|
+
code.newline
|
671
|
+
code << "# =====Define output directory"
|
672
|
+
code << "set outputDir ./SYNTH_OUTPUTS"
|
673
|
+
code << "file mkdir $outputDir"
|
674
|
+
code.newline
|
675
|
+
code << "# =====Setup design sources and constraints"
|
676
|
+
code << "read_vhdl [ glob ../assets/*.vhd]"
|
677
|
+
code << "read_vhdl [ glob ../src/*.vhd]"
|
678
|
+
code << "read_xdc $xdc_constraints"
|
679
|
+
code.newline
|
680
|
+
code << "synth_design -top #{@model_name} -part $partname"
|
681
|
+
code << "write_checkpoint -force $outputDir/post_synth.dcp"
|
682
|
+
code << "report_timing_summary -file $outputDir/post_synth_timing_summary.rpt"
|
683
|
+
code << "report_utilization -file $outputDir/post_synth_util.rpt"
|
684
|
+
code << "opt_design"
|
685
|
+
code << "# reportCriticalPaths $outputDir/post_opt_critpath_report.csv"
|
686
|
+
code << "place_design"
|
687
|
+
code << "# report_clock_utilization -file $outputDir/clock_util.rpt"
|
688
|
+
code << "#"
|
689
|
+
code << "write_checkpoint -force $outputDir/post_place.dcp"
|
690
|
+
code << "report_utilization -file $outputDir/post_place_util.rpt"
|
691
|
+
code << "report_timing_summary -file $outputDir/post_place_timing_summary.rpt"
|
692
|
+
code.newline
|
693
|
+
code << "# ====== run the router, write the post-route design checkpoint, report the routing"
|
694
|
+
code << "# status, report timing, power, and DRC, and finally save the Verilog netlist."
|
695
|
+
code << "#"
|
696
|
+
code << "route_design"
|
697
|
+
code << "write_checkpoint -force $outputDir/post_route.dcp"
|
698
|
+
code << "report_route_status -file $outputDir/post_route_status.rpt"
|
699
|
+
code << "report_timing_summary -file $outputDir/post_route_timing_summary.rpt"
|
700
|
+
code << "report_power -file $outputDir/post_route_power.rpt"
|
701
|
+
code << "report_drc -file $outputDir/post_imp_drc.rpt"
|
702
|
+
code << "# write_verilog -force $outputDir/cpu_impl_netlist.v -mode timesim -sdf_anno true"
|
703
|
+
code.newline
|
704
|
+
code << "# ====== generate a bitstream"
|
705
|
+
code << "write_bitstream -force $outputDir/top.bit"
|
706
|
+
code << "exit"
|
707
|
+
code.save_as(filename,verbose=false)
|
708
|
+
|
709
|
+
dir=__dir__
|
710
|
+
assets_dir=dir+"/../../assets/"
|
711
|
+
file="Nexys4DDR_Master.xdc"
|
712
|
+
FileUtils.cp(assets_dir+file,dest_dir)
|
713
|
+
end
|
714
|
+
|
715
|
+
def gen_compile_script
|
716
|
+
dest_dir=$working_dir+"/src"
|
717
|
+
if !Dir.exists?(dest_dir)
|
718
|
+
FileUtils.mkdir(dest_dir)
|
719
|
+
end
|
720
|
+
File.open("#{dest_dir}/compile.x",'w') do |f|
|
721
|
+
f.puts "echo \"=> cleaning\""
|
722
|
+
f.puts "rm -rf *.o"
|
723
|
+
f.puts
|
724
|
+
f.puts "echo \"=> analyzing VHDL files\""
|
725
|
+
@vhdl_files.each do |vhdl|
|
726
|
+
f.puts "echo \"=> analyzing #{vhdl}\""
|
727
|
+
f.puts "ghdl -a #{vhdl}"
|
728
|
+
end
|
729
|
+
f.puts "echo \"=> elaboration\""
|
730
|
+
top=File.basename(@vhdl_files.last,".vhd")
|
731
|
+
f.puts "ghdl -e #{top}"
|
732
|
+
#f.puts "echo \"=> running simulation
|
733
|
+
#f.puts "ghdl -r #{top} --wave=#{top}.ghw"
|
734
|
+
#f.puts "echo \"=> viewing waveforms\""
|
735
|
+
#f.puts "gtkwave #{top}.ghw #{top}.sav"
|
736
|
+
puts "=> compile script"+" compile.x".rjust(45,'.')
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
def gen_system_architecture mm
|
741
|
+
code=Code.new
|
742
|
+
code << "architecture rtl of #{mm.name} is"
|
743
|
+
code.indent=2
|
744
|
+
code << "-- bus"
|
745
|
+
code << "signal ce : std_logic;"
|
746
|
+
code << "signal we : std_logic;"
|
747
|
+
code << "signal address : unsigned(#{@addr_size-1} downto 0);"
|
748
|
+
code << "signal datain : std_logic_vector(#{@data_size-1} downto 0);"
|
749
|
+
code << "signal dataout : std_logic_vector(#{@data_size-1} downto 0);"
|
750
|
+
code << "--"
|
751
|
+
code << "signal sreset : std_logic;"
|
752
|
+
code << "-- debug"
|
753
|
+
code << "signal slow_clk,slow_tick : std_logic;"
|
754
|
+
code.newline
|
755
|
+
code.indent=0
|
756
|
+
code << "begin"
|
757
|
+
code.indent=2
|
758
|
+
code.newline
|
759
|
+
code << instance_of_uart_bus_master if @options[:include_uart]
|
760
|
+
code << ip_instanciations(mm)
|
761
|
+
code << debug_stuff
|
762
|
+
code.newline
|
763
|
+
code.indent=0
|
764
|
+
code << "end;"
|
765
|
+
code
|
766
|
+
end
|
767
|
+
|
768
|
+
def instance_of_uart_bus_master
|
769
|
+
code=Code.new
|
770
|
+
code << "-- ============== UART as Master of bus !========="
|
771
|
+
code << "uart_master : entity work.uart_bus_master"
|
772
|
+
code << " generic map (DATA_WIDTH => #{@data_size})"
|
773
|
+
code << " port map("
|
774
|
+
code << " reset_n => reset_n,"
|
775
|
+
code << " clk => clk,"
|
776
|
+
code << " -- UART --"
|
777
|
+
code << " rx => rx,"
|
778
|
+
code << " tx => tx,"
|
779
|
+
code << " -- Bus --"
|
780
|
+
code << " ce => ce,"
|
781
|
+
code << " we => we,"
|
782
|
+
code << " address => address,"
|
783
|
+
code << " datain => datain,"
|
784
|
+
code << " dataout => dataout"
|
785
|
+
code << " );"
|
786
|
+
code
|
787
|
+
end
|
788
|
+
|
789
|
+
def ip_instanciations mm
|
790
|
+
code=Code.new
|
791
|
+
mm.zones.each do |zone|
|
792
|
+
code.newline
|
793
|
+
code << "-- "+zone.name.to_s.center(46,"=")
|
794
|
+
code << "inst_#{zone.name} : entity work.#{zone.name}"
|
795
|
+
code.indent=2
|
796
|
+
code << "port map ("
|
797
|
+
code.indent=4
|
798
|
+
code << "reset_n => reset_n,"
|
799
|
+
code << "clk => clk,"
|
800
|
+
code << "sreset => sreset,"
|
801
|
+
code << "ce => ce,"
|
802
|
+
code << "we => we,"
|
803
|
+
code << "address => address,"
|
804
|
+
code << "datain => datain,"
|
805
|
+
code << "dataout => dataout"
|
806
|
+
code.indent=2
|
807
|
+
code << ");"
|
808
|
+
code.indent=0
|
809
|
+
end
|
810
|
+
code
|
811
|
+
end
|
812
|
+
|
813
|
+
def debug_stuff
|
814
|
+
code=Code.new
|
815
|
+
code.newline
|
816
|
+
code << "-- =================== DEBUG ===================="
|
817
|
+
code << "ticker : entity work.slow_ticker(rtl)"
|
818
|
+
code << " port map("
|
819
|
+
code << " reset_n => reset_n,"
|
820
|
+
code << " fast_clk => clk,"
|
821
|
+
code << " slow_clk => slow_clk,"
|
822
|
+
code << " slow_tick => slow_tick"
|
823
|
+
code << " );"
|
824
|
+
code << "leds <= \"000000000000000\" & slow_clk;"
|
825
|
+
code
|
826
|
+
end
|
827
|
+
|
828
|
+
|
829
|
+
|
830
|
+
def gen_xdc zone
|
831
|
+
dest_dir=$working_dir #+"/synthesis"
|
832
|
+
if !Dir.exists?(dest_dir)
|
833
|
+
FileUtils.mkdir(dest_dir)
|
834
|
+
end
|
835
|
+
puts "==> generating XDC constraints file for Artix7"
|
836
|
+
code=Code.new
|
837
|
+
code
|
838
|
+
code << "## clock signal"
|
839
|
+
code << "set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_35"
|
840
|
+
code << "create_clock -add -name clk -period 10.00 -waveform {0 5} [get_ports {clk}];"
|
841
|
+
code.newline
|
842
|
+
pinout_str=%Q(
|
843
|
+
J15,L16,M13,R15,R17,T18,U18,R13,T8,U8,R16,T13,H6,U12,U11,V10,H17,K15,J13,N14,R18,V17,U17,U16,\
|
844
|
+
V16,T15,U14,T16,V15,V14,V12,V11,R12,M16,N15,G14,R11,N16,T10,R10,K16,K13,P15,T11,L18,H15,J17,J18,\
|
845
|
+
T9,J14,P14,T14,K2,U13,C12,N17,M18,P17,M17,P18,C17,D18,E18,G17,D17,E17,F18,G18,D14,F16,G16,H14,E16,\
|
846
|
+
F13,G13,H16,K1,F6,J2,G6,E7,J3,J4,E6,H4,H1,G1,G3,H2,G4,G2,F3,A14,A13,A16,A15,B17,B16,A18,B18,A3,B4,\
|
847
|
+
C5,A4,C6,A5,B6,A6,B7,C7,D7,D8,B11,B12,E2,A1,B1,C1,C2,E1,F1,D2,E15,F14,F15,D15,B13,C16,C14,C15,D13,\
|
848
|
+
B14,J5,H5,F5,A11,D12,C4,D4,D3,E5,F4,B2,C9,A9,B3,D9,C10,C11,D10,B9,A10,A8,D5,B8,K17,K18,L14,M14,L13\
|
849
|
+
)
|
850
|
+
pinout=pinout_str.strip.split(',').map(&:strip)
|
851
|
+
ports=[
|
852
|
+
{:reset_n => 1},
|
853
|
+
{:ce => 1},
|
854
|
+
{:we => 1},
|
855
|
+
{:address => @addr_size},
|
856
|
+
{:datain => @data_size},
|
857
|
+
{:dataout => @data_size},
|
858
|
+
]
|
859
|
+
mapping={}
|
860
|
+
ports.each do |porth|
|
861
|
+
name,size=porth.to_a.first
|
862
|
+
for i in 0..size-1
|
863
|
+
name=name.to_s
|
864
|
+
pname=(size==1)? name : name+"[#{i}]"
|
865
|
+
mapping[pname]=pinout.shift
|
866
|
+
code << "set_property -dict { PACKAGE_PIN #{mapping[pname]} IOSTANDARD LVCMOS33 } [get_ports { #{pname} }];"
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
file=code.save_as("#{dest_dir}/artix7.xdc",false)
|
871
|
+
puts " - mapped #{mapping.size} ports to pinout .................... #{file}"
|
872
|
+
end
|
873
|
+
|
874
|
+
def gen_ruby_sw_if mm
|
875
|
+
dest_dir=$working_dir+"/esw"
|
876
|
+
if !Dir.exists?(dest_dir)
|
877
|
+
FileUtils.mkdir(dest_dir)
|
878
|
+
end
|
879
|
+
words_in_name=mm.name.to_s.split("_")
|
880
|
+
filename=words_in_name.map(&:downcase).join("_")+".rb"
|
881
|
+
puts "=> generating Ruby sw"+(" "+filename).rjust(41,'.')
|
882
|
+
code=Code.new
|
883
|
+
code << "require 'pp'"
|
884
|
+
code << "require 'rubyserial'"
|
885
|
+
code.newline
|
886
|
+
classname=words_in_name.map(&:capitalize).join
|
887
|
+
code << "class #{classname}"
|
888
|
+
code.indent=2
|
889
|
+
code.newline
|
890
|
+
code << "def initialize"
|
891
|
+
code.indent=4
|
892
|
+
code << "@serial=Serial.new '/dev/ttyUSB1',19200,8"
|
893
|
+
code.indent=2
|
894
|
+
code << "end"
|
895
|
+
code.newline
|
896
|
+
code << "def to_byte_array num,bytes"
|
897
|
+
code.indent=2
|
898
|
+
code << "(bytes - 1).downto(0).collect{|byte|((num >> (byte * 8)) & 0xFF)}"
|
899
|
+
code.indent=0
|
900
|
+
code << "end"
|
901
|
+
code.newline
|
902
|
+
code << "def byte_array_to_int ary"
|
903
|
+
code << " val=0"
|
904
|
+
code << " nb_bytes=ary.size"
|
905
|
+
code << " ary.each_with_index{|b,i|"
|
906
|
+
code << " pow256=256**(nb_bytes-1-i)"
|
907
|
+
code << " val+=b*pow256"
|
908
|
+
code << " }"
|
909
|
+
code << " val"
|
910
|
+
code << "end"
|
911
|
+
code.newline
|
912
|
+
code << "def read_reg address"
|
913
|
+
code.indent=4
|
914
|
+
code << "# b4 b3 b2 b1 b0"
|
915
|
+
code << "@serial.write [ 0x10].pack(\"C*\")"
|
916
|
+
code << "@serial.write [ address].pack(\"C*\")"
|
917
|
+
code << "bytes=[]"
|
918
|
+
code << "for i in 0..3"
|
919
|
+
code.indent=6
|
920
|
+
code << "byte=nil"
|
921
|
+
code << "byte=@serial.getbyte until byte"
|
922
|
+
code << "bytes << byte"
|
923
|
+
code.indent=4
|
924
|
+
code << "end"
|
925
|
+
code << "bytes # array of bytes"
|
926
|
+
code << "byte_array_to_int(bytes)"
|
927
|
+
code.indent=2
|
928
|
+
code << "end"
|
929
|
+
code.newline
|
930
|
+
code << "def write_reg addr,data"
|
931
|
+
code.indent=4
|
932
|
+
code << "# b4 b3 b2 b1 b0"
|
933
|
+
code << "@serial.write [ 0x11].pack(\"C*\")"
|
934
|
+
code << "@serial.write [ addr].pack(\"C*\")"
|
935
|
+
code << "bytes=to_byte_array(data,4)"
|
936
|
+
code << "@serial.write bytes.unpack(\"C*\")"
|
937
|
+
code.indent=2
|
938
|
+
code << "end"
|
939
|
+
code.newline
|
940
|
+
mm.zones.each do |zone|
|
941
|
+
code << "# === #{zone.name} ==="
|
942
|
+
zone.registers.each do |reg|
|
943
|
+
code << "def #{zone.name}_#{reg.name}"
|
944
|
+
code.indent=4
|
945
|
+
code << "read_reg #{reg.address}"
|
946
|
+
code.indent=2
|
947
|
+
code << "end"
|
948
|
+
code.newline
|
949
|
+
code << "def #{zone.name}_#{reg.name}=val"
|
950
|
+
code.indent=4
|
951
|
+
code << "write_reg #{reg.address},val"
|
952
|
+
code.indent=2
|
953
|
+
code << "end"
|
954
|
+
code.newline
|
955
|
+
end
|
956
|
+
end
|
957
|
+
code.indent=0
|
958
|
+
code << "end"
|
959
|
+
code.newline
|
960
|
+
code << "if $PROGRAM_NAME==__FILE__"
|
961
|
+
code.indent=2
|
962
|
+
code << "#{classname.downcase}=#{classname}.new"
|
963
|
+
code << "#{classname}."
|
964
|
+
code.indent=0
|
965
|
+
code << "end"
|
966
|
+
puts code.finalize
|
967
|
+
filename="#{dest_dir}/#{filename}"
|
968
|
+
code.save_as(filename,verbose=false)
|
969
|
+
end
|
970
|
+
|
971
|
+
end
|
972
|
+
|
973
|
+
end
|