HDLRuby 3.0.0 → 3.1.0

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,533 @@
1
+ require 'std/sequencer'
2
+
3
+ module HDLRuby::High::Std
4
+
5
+ ##
6
+ # Standard HDLRuby::High library: sequencer function generator.
7
+ # The idea is to be able to write sw-like sequential code.
8
+ #
9
+ ########################################################################
10
+
11
+
12
+
13
+ # Describes a sequencer function definition.
14
+ #
15
+ # NOTE: like with ruby, functions does not have types for their arguments,
16
+ # their are set when the function is called.
17
+ # This is handle by the eigen functions (see SequencerFunctionE).
18
+ class SequencerFunctionT
19
+ # The name of the function.
20
+ attr_reader :name
21
+
22
+ # The body of the function.
23
+ attr_reader :body
24
+
25
+ # The stack overflow code of the function if any.
26
+ attr_reader :overflow
27
+
28
+ # Creates a new sequencer function named +name+, with stack size +depth+
29
+ # executing code given by +ruby_block+. Additionaly a HDLRuby block
30
+ # +overflow+ can be added to be executed when a stack overflow occured.
31
+ #
32
+ # NOTE: if +depth+ is nil it will be automatically computed at call time.
33
+ def initialize(name, depth = nil, overflow = nil, &ruby_block)
34
+ @name = name.to_sym
35
+ @body = ruby_block
36
+ @depth = depth ? depth.to_i : nil
37
+ @overflow = overflow ? overflow.to_proc : nil
38
+ end
39
+
40
+ # Call the function with arguments +args+.
41
+ def call(*args)
42
+ # Check if there are extra arguments, they are used for configuring
43
+ # the stack.
44
+ if args.size > @body.arity then
45
+ # The first extra argument is the depth of the stack for this
46
+ # specific call.
47
+ @depth = args.delete_at(@body.arity)
48
+ end
49
+
50
+ # Specialize the function with the types of the arguments.
51
+ # (the result is the eigen function of funcI).
52
+ funcE = SequencerFunctionE.new(self, args.map {|arg| arg.type })
53
+ # Check if it is a recursion.
54
+ funcI = SequencerFunctionI.recursion(funcE)
55
+ if funcI then
56
+ # puts "Recursive call"
57
+ # Recursion, set the size of the stack.
58
+ funcI.make_depth(@depth)
59
+ # Call the function.
60
+ st_call = funcI.recurse_call(*args)
61
+ # adds the return address.
62
+ depth = funcI.depth
63
+ stack_ptr = funcI.stack_ptr
64
+ # st_call.gotos << proc do
65
+ old_code = st_call.code
66
+ st_call.code = proc do
67
+ old_code.call
68
+ HDLRuby::High.top_user.instance_exec do
69
+ # hprint("returning with stack_ptr=",stack_ptr,"\n")
70
+ hif(stack_ptr <= depth) do
71
+ # hprint("poking recursive return value at idx=",funcI.returnIdx," with value=",st_call.value+1,"\n")
72
+ funcI.poke(funcI.returnIdx,st_call.value + 1)
73
+ end
74
+ end
75
+ end
76
+ else
77
+ # puts "First call"
78
+ # No recursion, create an instance of the function
79
+ funcI = SequencerFunctionI.new(funcE)
80
+ # Call the function.
81
+ st_call = funcI.first_call(*args)
82
+ # Build the function... Indeed after the call, that
83
+ # allows to avoid one state.
84
+ st_func = funcI.build
85
+ # adds the return value.
86
+ # st_call.gotos << proc do
87
+ old_code = st_call.code
88
+ st_call.code = proc do
89
+ old_code.call
90
+ HDLRuby::High.top_user.instance_exec do
91
+ # hprint("poking return value at idx=",funcI.returnIdx," with value=",st_func.value+1,"\n")
92
+ funcI.poke(funcI.returnIdx,st_func.value + 1)
93
+ end
94
+ end
95
+ end
96
+ # Return the created funcI return value.
97
+ return funcI.return_value
98
+ end
99
+ end
100
+
101
+ # Describes a sequencer eigen function.
102
+ # Here, an eigen function is a function definition specilized with
103
+ # arguments types.
104
+ class SequencerFunctionE
105
+
106
+ attr_reader :funcT
107
+
108
+ ## Creates a new eigen function with function type +funcT+ and arguments
109
+ # types +argTs+.
110
+ def initialize(funcT, argTs)
111
+ @funcT = funcT
112
+ @argTs = argTs.to_a
113
+ end
114
+
115
+ ## Gets the name of the function.
116
+ def name
117
+ @funcT.name
118
+ end
119
+
120
+ ## Gets the body of the function.
121
+ def body
122
+ @funcT.body
123
+ end
124
+
125
+ ## Gets the stack overflow code of the function.
126
+ def overflow
127
+ @funcT.overflow
128
+ end
129
+
130
+ # Iterates over the argument types.
131
+ #
132
+ # Returns an enumerator if no ruby block is given.
133
+ def each_argT(&ruby_block)
134
+ # No ruby block? Return an enumerator.
135
+ return to_enum(:each_argT) unless ruby_block
136
+ # A ruby block? Apply it on each agument type.
137
+ @argTs.each(&ruby_block)
138
+ end
139
+
140
+ # Comparison of eigen functions.
141
+ def ==(obj)
142
+ # Is obj an eigen function?
143
+ return false unless obj.is_a?(SequencerFunctionE)
144
+ # Has obj the same function type?
145
+ return false unless self.funcT == obj.funcT
146
+ # Has obj the same argument types?
147
+ return obj.each_argT.zip(self.each_argT).all? {|t0,t1| t0 == t1 }
148
+ end
149
+ end
150
+
151
+
152
+ # Describes a sequencer function instance.
153
+ class SequencerFunctionI
154
+ @@current_stack = [] # The stack of current function instance.
155
+
156
+ # Get the function instance currently processing.
157
+ def self.current
158
+ @@current_stack[-1]
159
+ end
160
+
161
+ # The eigen function.
162
+ attr_reader :funcE
163
+
164
+ # The return index in the stacks.
165
+ attr_reader :returnIdx
166
+
167
+ # The stack pointer register.
168
+ attr_reader :stack_ptr
169
+
170
+ # The depth of the stack.
171
+ attr_reader :depth
172
+
173
+ # Creates a new instance of function from +funcE+ eigen function,
174
+ # and possible default stack depth +depth+.
175
+ def initialize(funcE)
176
+ # Sets the eigen function.
177
+ @funcE = funcE
178
+ # Initialize the depth.
179
+ # At first, no recursion is assumed, hence the depth is 1.
180
+ @depth = 1
181
+ # Create the table of signal stacks (by name).
182
+ # For further updating.
183
+ @stack_sigs = [] # Signal stacks
184
+ # Signal stacks pointer.
185
+ stack_ptr = nil
186
+ depth = @depth
187
+ HDLRuby::High.cur_system.open do
188
+ stack_ptr = bit[depth.width].inner(HDLRuby.uniq_name(:stack_ptr) => 0)
189
+ end
190
+ @stack_ptr = stack_ptr
191
+ # Create the stack for the returns.
192
+ # @returnIdx = self.make_stack(bit[SequencerT.current.size.width])
193
+ @returnIdx = self.make_stack(bit[8])
194
+ # Create the stacks for the arguments.
195
+ @funcE.each_argT { |argT| self.make_stack(argT) }
196
+ # @argsIdx = @returnIdx + 2
197
+ @argsIdx = @returnIdx + 1
198
+
199
+ # Create the return value, however, at first their type is unknown
200
+ # to set it as a simple bit.
201
+ # The type of the return value is built when calling make_return.
202
+ # @returnValIdx = self.make_stack(bit[1])
203
+ # puts "@returnValIdx=#{@returnValIdx}"
204
+ returnValue = nil
205
+ name = @funcE.name
206
+ HDLRuby::High.cur_system.open do
207
+ returnValue = bit[1].inner(
208
+ HDLRuby.uniq_name("#{name}_return"))
209
+ end
210
+ @returnValue = returnValue
211
+
212
+ # Initialize the state where the initial function call will be.
213
+ @state = nil
214
+ end
215
+
216
+ ## Give access to the return value.
217
+ #
218
+ # NOTE: is automatically called when within an expression.
219
+ def to_expr
220
+ return self.return_value
221
+ end
222
+
223
+ # There is actually recurse, compute the depth: if +depth+ is given
224
+ # as argument, this is the depth, otherwise compute it from the
225
+ # bit width of the argument.
226
+ #
227
+ # NOTE: uses the heuristic that the depth is more or less equal to the
228
+ # bit width of the largest argument type.
229
+ def make_depth(depth)
230
+ if depth then
231
+ @depth = depth
232
+ else
233
+ # There is no default depth, use the heuristic that the
234
+ # depth is more or less equal to the bit width of the
235
+ # largest argument type.
236
+ @depth = @funcE.each_argT.map {|t| t.width }.max
237
+ end
238
+ # Resize the stackes according to the depth.
239
+ @stack_sigs.each do |sig|
240
+ sig.type.instance_variable_set(:@range,0..@depth-1)
241
+ end
242
+ @stack_ptr.type.instance_variable_set(:@range,(@depth+1).width-1..0)
243
+ end
244
+
245
+
246
+
247
+ # Builds the code of the function.
248
+ # Returns the last state of the buit function, will serve
249
+ # for computing the return state of the first call.
250
+ def build
251
+ # Saves the current function to detect recursion.
252
+ @@current_stack.push(self)
253
+
254
+ # Get the body.
255
+ body = @funcE.body
256
+
257
+ # Create a state starting the function.
258
+ SequencerT.current.step
259
+
260
+ # Get the arguments.
261
+ args = (@argsIdx...(@argsIdx+body.arity)).map {|idx| self.peek(idx) }
262
+ # Place the body.
263
+ # SequencerT.current.instance_exec(*args,&body)
264
+ HDLRuby::High.top_user.instance_exec(*args,&body)
265
+ # # Free the stack of current frame.
266
+ # Moved to return...
267
+ # self.pop_all
268
+
269
+ # Create a state for returning.
270
+ st = self.make_return
271
+
272
+ # The function is built, remove it from recursion detection..
273
+ @@current_stack.pop
274
+
275
+ return st
276
+ end
277
+
278
+ # Call the function with arguments +args+ for the first time.
279
+ def first_call(*args)
280
+ # # Create a state for the call.
281
+ # call_state = SequencerT.current.step
282
+
283
+ # Push a new frame.
284
+ self.push_all
285
+
286
+ # Adds the arguments and the return state to the current stack frame.
287
+ args.each_with_index { |arg,i| self.poke(@argsIdx + i,arg) }
288
+ # The return is set afterward when the end of the function is
289
+ # known, since the return position for the first call is just
290
+ # after it.
291
+ # self.poke(@returnIdx,call_state.value + 1)
292
+
293
+ # Create a state for the call.
294
+ call_state = SequencerT.current.step
295
+
296
+
297
+ # Get the state value of the function: it is the state
298
+ # following the first function call.
299
+ func_state_value = call_state.value + 1
300
+ # Do the call.
301
+ call_state.gotos << proc do
302
+ HDLRuby::High.top_user.instance_exec do
303
+ next_state_sig <= func_state_value
304
+ end
305
+ end
306
+
307
+ # Sets the state of the first function call.
308
+ @state = call_state
309
+
310
+ # Return the state for inserting the push of the return state.
311
+ return call_state
312
+ end
313
+
314
+ # Call the function with arguments +args+ for recursion.
315
+ def recurse_call(*args)
316
+ # # create a state for the call.
317
+ # call_state = SequencerT.current.step
318
+
319
+ # Get the variables for handling the stack overflow.
320
+ stack_ptr = @stack_ptr
321
+ depth = @depth
322
+ argsIdx = @argsIdx
323
+ this = self
324
+
325
+ # Adds the argument to the stack if no overflow.
326
+ HDLRuby::High.top_user.hif(stack_ptr < depth) do
327
+ # hprint("stack_ptr=",stack_ptr," depth=",depth,"\n")
328
+ # Adds the arguments and the return state to the current stack frame.
329
+ # Since not pushed the stack yet for not loosing the previous
330
+ # arguments, add +1 to the offset when poking the new arguments.
331
+ # args.each_with_index { |arg,i| self.poke(@argsIdx + i,arg,1) }
332
+ args.each_with_index { |arg,i| this.poke(argsIdx + i,arg,1) }
333
+ end
334
+
335
+ # Push a new frame.
336
+ self.push_all
337
+
338
+ # create a state for the call.
339
+ call_state = SequencerT.current.step
340
+
341
+ # Prepare the handling of overflow
342
+ call_state_value = call_state.value
343
+ overflow = @funcE.overflow
344
+ if overflow then
345
+ HDLRuby::High.top_user.hif(stack_ptr > depth) do
346
+ HDLRuby::High.top_user.instance_exec(&overflow)
347
+ end
348
+ end
349
+
350
+ # Get the state value of the function: it is the state
351
+ # following the first function call.
352
+ func_state_value = @state.value + 1
353
+ # Do the call.
354
+ call_state.gotos << proc do
355
+ HDLRuby::High.top_user.instance_exec do
356
+ hif(stack_ptr <= depth) do
357
+ next_state_sig <= func_state_value
358
+ end
359
+ helse do
360
+ # Overflow! Skip the call.
361
+ next_state_sig <= call_state_value + 1
362
+ # if overflow then
363
+ # # There is some overflow code to execute.
364
+ # HDLRuby::High.top_user.instance_exec(&overflow)
365
+ # end
366
+ end
367
+ end
368
+ end
369
+
370
+ return call_state
371
+ end
372
+
373
+ # Methods for handling the recursions and stacks.
374
+
375
+
376
+ ## Check if the current function call with eigen +funcE+ would be
377
+ # recursive or not.
378
+ def self.recursion(funcE)
379
+ # puts "recursion with funcE=#{funcE}"
380
+ return @@current_stack.find {|funcI| funcI.funcE == funcE }
381
+ end
382
+
383
+ ## Create a stack for elements of types +typ+.
384
+ def make_stack(typ)
385
+ # Create the signal array representing the stack.
386
+ depth = @depth
387
+ # puts "make stack with @depth=#{@depth}"
388
+ stack_sig = nil
389
+ name = @funcE.name
390
+ HDLRuby::High.cur_system.open do
391
+ stack_sig = typ[-depth].inner(
392
+ HDLRuby.uniq_name("#{name}_stack"))
393
+ end
394
+ # Add it to the list of stacks to handle.
395
+ @stack_sigs << stack_sig
396
+
397
+ # Returns the index of the newly created stack.
398
+ return @stack_sigs.size-1
399
+ end
400
+
401
+ ## Pushes a new frame to the top of the stacks.
402
+ def push_all
403
+ # HDLRuby::High.cur_system.hprint("push_all\n")
404
+ @stack_ptr <= @stack_ptr + 1
405
+ end
406
+
407
+ ## Remove the top frame from the stacks.
408
+ def pop_all
409
+ # HDLRuby::High.cur_system.hprint("pop_all\n")
410
+ @stack_ptr <= @stack_ptr -1
411
+ end
412
+
413
+ ## Get a value from the top of stack number +idx+
414
+ # If +off+ is the offeset in the stack.
415
+ def peek(idx, off = 0)
416
+ return @stack_sigs[idx][@stack_ptr-1+off]
417
+ end
418
+
419
+ ## Sets value +val+ to the top of stack number +idx+.
420
+ # If +off+ is the offeset in the stack.
421
+ def poke(idx,val, off = 0)
422
+ # puts "idx=#{idx} val=#{val} sig=#{@stack_sigs[idx].name}"
423
+ @stack_sigs[idx][@stack_ptr-1+off] <= val
424
+ end
425
+
426
+ ## Access the return value signal.
427
+ def return_value
428
+ # return @stack_sigs[@returnValIdx][@stack_ptr-1]
429
+ @returnValue
430
+ end
431
+
432
+ ## Creates a return point with value +val+.
433
+ # Returns the created state.
434
+ #
435
+ # NOTE: when val is nil, no return value is provided.
436
+ def make_return(val = nil)
437
+ SequencerT.current.step
438
+ # puts "make_return with val=#{val}"
439
+ # Update the type of the return value.
440
+ if val then
441
+ # Update the type.
442
+ @returnValue.instance_variable_set(:@type, @returnValue.type.resolve(val.to_expr.type))
443
+ # Sets the return value if any.
444
+ self.return_value <= val
445
+ end
446
+ # Create the state for the return command.
447
+ state = SequencerT.current.step
448
+ # Get the return state value.
449
+ # ret_state_value = self.peek(@returnIdx, HDLRuby::High.top_user.mux(@stack_ptr < @depth,-1,0))
450
+ # Peek before the stack pointer value to account from the fact that
451
+ # the pop is performed beforehand.
452
+ ret_state_value = self.peek(@returnIdx, HDLRuby::High.top_user.mux(@stack_ptr < @depth,0,+1))
453
+ # Return.
454
+ this = self
455
+ state.gotos << proc do
456
+ HDLRuby::High.top_user.instance_exec do
457
+ # Set the next state.
458
+ next_state_sig <= ret_state_value
459
+ # # Pop must be place after setting the return state.
460
+ # this.pop_all
461
+ end
462
+ end
463
+ # Pop (done at clock edge, hence before the update of the state).
464
+ old_code = state.code
465
+ state.code = proc do
466
+ old_code.call
467
+ HDLRuby::High.top_user.instance_exec do
468
+ this.pop_all
469
+ end
470
+ end
471
+
472
+ return state
473
+ end
474
+ end
475
+
476
+
477
+
478
+
479
+ ## Remplement make_inners of block to support declaration within function.
480
+
481
+
482
+ class HDLRuby::High::Block
483
+ alias_method :old_make_inners, :make_inners
484
+
485
+ def make_inners(typ,*names)
486
+ if SequencerFunctionI.current then
487
+ unames = names.map {|name| HDLRuby.uniq_name(name) }
488
+ HDLRuby::High.cur_scope.make_inners(typ, *unames)
489
+ names.zip(unames).each do |name,uname|
490
+ HDLRuby::High.space_reg(name) { send(uname) }
491
+ end
492
+ else
493
+ self.old_make_inners(typ,*names)
494
+ end
495
+ end
496
+ end
497
+
498
+
499
+
500
+
501
+ # Declares a sequencer function named +name+ using +ruby_block+ as body.
502
+ # You can specify a stack depth with +depth+ argument and a HDLRuby
503
+ # block to execute in case of stack overflow with the +overflow+ argument.
504
+ def sdef(name, depth=nil, overflow = nil, &ruby_block)
505
+ # Create the function.
506
+ funcT = SequencerFunctionT.new(name,depth,overflow,&ruby_block)
507
+ # Register it for calling.
508
+ if HDLRuby::High.in_system? then
509
+ define_singleton_method(name.to_sym) do |*args|
510
+ funcT.call(*args)
511
+ end
512
+ else
513
+ define_method(name.to_sym) do |*args|
514
+ funcT.call(*args)
515
+ end
516
+ end
517
+ # Return the create function.
518
+ funcT
519
+ end
520
+
521
+ # Returns value +val+ from a sequencer function.
522
+ def sreturn(val)
523
+ # HDLRuby::High.top_user.hprint("sreturn\n")
524
+ # Get the top function.
525
+ funcI = SequencerFunctionI.current
526
+ unless funcI then
527
+ raise "Cannot return since outside a function."
528
+ end
529
+ # Applies the return on it.
530
+ funcI.make_return(val)
531
+ end
532
+
533
+ end
@@ -10,3 +10,4 @@ require 'std/decoder.rb'
10
10
  require 'std/fsm.rb'
11
11
  require 'std/sequencer.rb'
12
12
  require 'std/sequencer_sync.rb'
13
+ require 'std/sequencer_func.rb'
@@ -1,3 +1,3 @@
1
1
  module HDLRuby
2
- VERSION = "3.0.0"
2
+ VERSION = "3.1.0"
3
3
  end