Class: HDLRuby::High::Std::FsmT

Inherits:
Object
  • Object
show all
Includes:
HScope_missing
Defined in:
lib/HDLRuby/std/fsm.rb

Overview

Describes a high-level fsm type.

Defined Under Namespace

Classes: State

Constant Summary

Constants included from Hmissing

Hmissing::High, Hmissing::NAMES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from HScope_missing

#h_missing, #method_missing

Methods included from Hmissing

#method_missing

Constructor Details

#initialize(name, *options) ⇒ FsmT

Creates a new fsm type with +name+. +options+ allows to specify the type of fsm: synchronous (default) / asynchronous and mono-front(default) / dual front



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/HDLRuby/std/fsm.rb', line 36

def initialize(name,*options)
    # Check and set the name
    @name = name.to_sym
    # Check and set the type of fsm depending of the options.
    @dual = false
    @type = :sync
    options.each do |opt|
        case opt
        when :sync,:synchronous then
            @type = :sync
        when :async, :asynchronous then
            @type = :async
        when :dual then
            @dual = true
        else
            raise AnyError, "Invalid option for a fsm: :#{type}"
        end
    end

    # Initialize the internals of the FSM.


    # Initialize the environment for building the FSM

    # The main states.
    @states = []

    # The extra synchronous states.
    @extra_syncs = []
    # The extra asynchronous states.
    @extra_asyncs = []

    # The default code of the operative part.
    @default_codes = []

    # The current and next state signals
    @cur_state_sig = nil
    @next_state_sig = nil

    # The event synchronizing the fsm
    @mk_ev = proc { $clk.posedge }

    # The reset check.
    @mk_rst = proc { $rst }

    # The code executed in case of reset.
    # (By default, nothing).
    @reset_sync  = nil
    @reset_async = nil

    # Creates the namespace to execute the fsm block in.
    @namespace = Namespace.new(self)

    # Generates the function for setting up the fsm
    # provided there is a name.
    obj = self # For using the right self within the proc
    HDLRuby::High.space_reg(@name) do |&ruby_block|
        if ruby_block then
            # Builds the fsm.
            obj.build(&ruby_block)
        else
            # Return the fsm as is.
            return obj
        end
    end unless name.empty?

end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class HDLRuby::High::HScope_missing

Instance Attribute Details

#cur_state_sigObject

The current and next state signals.



30
31
32
# File 'lib/HDLRuby/std/fsm.rb', line 30

def cur_state_sig
  @cur_state_sig
end

#nameObject (readonly)

The name of the FSM type.



20
21
22
# File 'lib/HDLRuby/std/fsm.rb', line 20

def name
  @name
end

#namespaceObject (readonly)

The namespace associated with the FSM



23
24
25
# File 'lib/HDLRuby/std/fsm.rb', line 23

def namespace
  @namespace
end

#next_state_sigObject

The current and next state signals.



30
31
32
# File 'lib/HDLRuby/std/fsm.rb', line 30

def next_state_sig
  @next_state_sig
end

#reset_asyncObject (readonly)

The reset codes for the synchronous and the asynchronous operative parts of the fsm



27
28
29
# File 'lib/HDLRuby/std/fsm.rb', line 27

def reset_async
  @reset_async
end

#reset_syncObject (readonly)

The reset codes for the synchronous and the asynchronous operative parts of the fsm



27
28
29
# File 'lib/HDLRuby/std/fsm.rb', line 27

def reset_sync
  @reset_sync
end

#work_stateObject

The current and next state signals.



30
31
32
# File 'lib/HDLRuby/std/fsm.rb', line 30

def work_state
  @work_state
end

Instance Method Details

#async(name, &ruby_block) ⇒ Object

Declares an extra asynchronous code to execute for state +name+.



398
399
400
401
402
403
404
405
406
407
# File 'lib/HDLRuby/std/fsm.rb', line 398

def async(name, &ruby_block)
    # Create the resulting state.
    result = State.new
    result.name = name.to_sym
    result.code = ruby_block
    # Add it to the lis of extra synchronous states.
    @extra_asyncs << result
    # Return it
    return result
end

#build(&ruby_block) ⇒ Object

builds the fsm by executing +ruby_block+.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/HDLRuby/std/fsm.rb', line 105

def build(&ruby_block)
    # Use local variable for accessing the attribute since they will
    # be hidden when opening the sytem.
    states = @states
    namespace = @namespace
    this   = self
    mk_ev  = @mk_ev
    mk_rst = @mk_rst
    type = @type
    dual = @dual
    extra_syncs   = @extra_syncs
    extra_asyncs  = @extra_asyncs
    default_codes = @default_codes

    return_value = nil

    # Enters the current system
    HDLRuby::High.cur_system.open do
        sub do
            HDLRuby::High.space_push(namespace)
            # Execute the instantiation block
            return_value =HDLRuby::High.top_user.instance_exec(&ruby_block)

            # Expands the extra state processing so that al all the
            # parts of the state machine are in par (clear synthesis).
            [extra_syncs,extra_asyncs].each do |extras|
                # Set the values of the extra states from their name.
                extras.each do |extra|
                    st = states.find {|st| st.name == extra.name }
                    unless st then
                        raise "Unknown state name: #{extra.name}"
                    end
                    extra.value = st.value
                end
                # Fills the holes in the extra syncs and asyncs.
                if extras.any? then
                    # Sort by value in a new array using counter sort.
                    results = [ nil ] * states.size
                    extras.each {|st| results[st.value] = st }
                    # Fill the whole with empty states.
                    results.map!.with_index do |st,i| 
                        unless st then
                            st = State.new
                            st.value = i
                            st.code = proc {}
                        end
                        st
                    end
                    # Replace the content of extras
                    extras.clear
                    results.each {|st| extras << st }
                end
            end

            # Create the state register.
            name = HDLRuby.uniq_name
            # Declare the state register.
            this.cur_state_sig = [states.size.width].inner(name)
            # Declare the next state wire.
            name = HDLRuby.uniq_name
            this.next_state_sig = [states.size.width].inner(name)

            # Create the fsm code

            # Control part: update of the state.
            par(mk_ev.call) do
                hif(mk_rst.call) do
                    # Reset: current state is to put to 0.
                    this.cur_state_sig <= 0
                end
                helse do
                    # No reset: current state is updated with
                    # next state value.
                    this.cur_state_sig <= this.next_state_sig
                end
            end

            # Operative main-part: one case per state.
            # (clock-dependent if synchronous mode).
            if type == :sync then
                # Synchronous case.
                event = mk_ev.call
                event = event.invert if dual
            else
                # Asynchronous case: no event required.
                event = []
            end
            # The process
            par(*event) do
                # The operative code.
                oper_code =  proc do
                    # The default code.
                    default_codes.each(&:call)
                    # Depending on the state.
                    hcase(this.cur_state_sig)
                    states.each do |st|
                        # Register the working state (for the gotos)
                        this.work_state = st
                        hwhen(st.value) do
                            # Generate the content of the state.
                            st.code.call
                        end
                    end
                end
                # Is there reset code?
                if type == :sync and this.reset_sync then
                    # Yes in case of synchronous fsm,
                    # use it before the operative code.
                    hif(mk_rst.call) do
                        this.reset_sync.call
                    end
                    helse(&oper_code)
                elsif type == :async and this.reset_async then
                    # Yes in case of asynchronous fsm,
                    # use it before the operative code.
                    hif(mk_rst.call) do
                        this.reset_async.call
                    end
                    helse(&oper_code)
                else
                    # Use only the operative code.
                    oper_code.call
                end
            end

            # Control part: computation of the next state.
            # (clock-independent)
            hcase(this.cur_state_sig)
            states.each do |st|
                hwhen(st.value) do
                    if st.gotos.any? then
                        # Gotos were present, use them.
                        st.gotos.each(&:call)
                    else
                        # No gotos, by default the next step is
                        # current + 1
                        # this.next_state_sig <= mux(mk_rst.call , 0, this.cur_state_sig + 1)
                        this.next_state_sig <=  this.cur_state_sig + 1
                    end
                end
            end
            # By default set the next state to 0.
            helse do
                this.next_state_sig <= 0
            end

            # Operative additional parts.
            # Extra synchronous operative part.
            if extra_syncs.any? then
                event = mk_ev.call
                event = event.invert if @dual
                # The extra code.
                par(*event) do
                    # Build the extra synchronous part.
                    sync_code = proc do
                        hcase(this.cur_state_sig)
                        extra_syncs.each do |st|
                            hwhen(st.value) do
                                # Generate the content of the state.
                                st.code.call
                            end
                        end
                    end
                    # Place it.
                    if this.reset_sync then
                        # There some synchronous reset code, use
                        # it.
                        hif(mk_rst.call) do
                            this.reset_sync.call
                        end
                        helse(&sync_code)
                    else
                        # No syncrhonous code, place the extra
                        # synchronous states as is.
                        sync_code.call
                    end
                end
            end

            # Extra asynchronous operative part.
            if extra_asyncs.any? then
                par do
                    # Build the extra synchronous part.
                    async_code = proc do
                        hcase(this.cur_state_sig)
                        extra_asyncs.each do |st|
                            hwhen(st.value) do
                                # Generate the content of the state.
                                st.code.call
                            end
                        end
                    end
                    # Place it with possible reset.
                    if this.reset_async then
                        # There some synchronous reset code, use
                        # it.
                        hif(mk_rst.call) do
                            this.reset_async.call
                        end
                        helse(&sync_code)
                    else
                        # No syncrhonous code, place the extra
                        # synchronous states as is.
                        sync_code.call
                    end
                end
            end

            HDLRuby::High.space_pop
        end
    end

    return return_value
end

#default(&ruby_block) ⇒ Object

Adds a default operative code.



366
367
368
# File 'lib/HDLRuby/std/fsm.rb', line 366

def default(&ruby_block)
    @default_codes << ruby_block
end

#for_event(event = nil, &ruby_block) ⇒ Object

Sets the event synchronizing the fsm.



324
325
326
327
328
329
330
331
332
# File 'lib/HDLRuby/std/fsm.rb', line 324

def for_event(event = nil,&ruby_block)
    if event then
        # An event is passed as argument, use it.
        @mk_ev = proc { event.to_event }
    else
        # No event given, use the ruby_block as event generator.
        @mk_ev = ruby_block
    end
end

#for_reset(reset = nil, &ruby_block) ⇒ Object

Sets the reset.



335
336
337
338
339
340
341
342
343
# File 'lib/HDLRuby/std/fsm.rb', line 335

def for_reset(reset = nil,&ruby_block)
    if reset then
        # An reset is passed as argument, use it.
        @mk_rst = proc { reset.to_expr }
    else
        # No reset given, use the ruby_block as event generator.
        @mk_rst = ruby_block
    end
end

#goto(*args) ⇒ Object

Sets the next state. Arguments can be:

+name+: the name of the next state. +expr+, +names+: an expression with the list of the next statements in order of the value of the expression, the last one being necesserily the default case.



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
# File 'lib/HDLRuby/std/fsm.rb', line 415

def goto(*args)
    # Make reference to the fsm attributes.
    next_state_sig = @next_state_sig
    states = @states
    # Add the code of the goto to the working state.
    @work_state.gotos << proc do
        # Depending on the first argument type.
        unless args[0].is_a?(Symbol) then
            # expr + names arguments.
            # Get the predicate
            pred = args.shift
            # hif or hcase?
            if args.size <= 2 then
                # 2 or less cases, generate an hif
                arg = args.shift
                hif(pred) do
                    next_state_sig <=
                        (states.detect { |st| st.name == arg }).value
                end
                arg = args.shift
                if arg then
                    # There is an else.
                    helse do
                        next_state_sig <=
                        (states.detect { |st| st.name == arg }).value
                    end
                end
            else
                # More than 2, generate a hcase
                hcase (pred)
                args[0..-2].each.with_index do |arg,i|
                    # Ensure the argument is a symbol.
                    arg = arg.to_sym
                    # Make the when statement.
                    hwhen(i) do
                        next_state_sig <= 
                        (states.detect { |st| st.name == arg }).value
                    end
                end
                # The last name is the default case.
                # Ensure it is a symbol.
                arg = args[-1].to_sym
                # Make the default statement.
                helse do
                    next_state_sig <= 
                        (states.detect { |st| st.name == arg }).value
                end
            end
        else
            # single name argument, check it.
            raise AnyError, "Invalid argument for a goto: if no expression is given only a single name can be used." if args.size > 1
            # Ensure the name is a symbol.
            name = args[0].to_sym
            # Get the state with name.
            next_state_sig <= (states.detect { |st| st.name == name }).value
        end
    end
end

#reset(type = @type, &ruby_block) ⇒ Object

Adds a code to be executed in case of reset. +type+ indicates if it is the synchronous part or the asynchronous part that is to reset.



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/HDLRuby/std/fsm.rb', line 348

def reset(type = @type,&ruby_block)
    if type == :sync or type == :synchronous then
        # Reset of the synchronous part.
        if @reset_sync then 
            raise AnyError.new("Reset of the synchronous part already declared.")
        end
        @reset_sync = ruby_block
    elsif type == :async or type == :asynchronous then
        # Reset if the asynchronous part.
        if @reset_async then
            raise AnyError.new("Reset of the asynchronosu part already declared.")
        end
    else
        raise AnyError.new("Invalid fsm type for declaring a reset code: #{type}")
    end
end

#state(name = :"", &ruby_block) ⇒ Object

Declares a new state with +name+ and executing +ruby_block+.



371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/HDLRuby/std/fsm.rb', line 371

def state(name = :"", &ruby_block)
    # Create the resulting state
    result = State.new
    # Its value is the current number of states
    result.value = @states.size
    result.name = name.to_sym
    result.code = ruby_block
    result.gotos = []
    # Add it to the list of states.
    @states << result
    # Return it.
    return result
end

#sync(name, &ruby_block) ⇒ Object

Declares an extra synchronous code to execute for state +name+.



386
387
388
389
390
391
392
393
394
395
# File 'lib/HDLRuby/std/fsm.rb', line 386

def sync(name, &ruby_block)
    # Create the resulting state.
    result = State.new
    result.name = name.to_sym
    result.code = ruby_block
    # Add it to the lis of extra synchronous states.
    @extra_syncs << result
    # Return it
    return result
end