Class: HDLRuby::High::Std::FsmT
- Inherits:
-
Object
- Object
- HDLRuby::High::Std::FsmT
- 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
Instance Attribute Summary collapse
-
#cur_state_sig ⇒ Object
The current and next state signals.
-
#name ⇒ Object
readonly
The name of the FSM type.
-
#namespace ⇒ Object
readonly
The namespace associated with the FSM.
-
#next_state_sig ⇒ Object
The current and next state signals.
-
#reset_async ⇒ Object
readonly
The reset codes for the synchronous and the asynchronous operative parts of the fsm.
-
#reset_sync ⇒ Object
readonly
The reset codes for the synchronous and the asynchronous operative parts of the fsm.
-
#work_state ⇒ Object
The current and next state signals.
Instance Method Summary collapse
-
#async(name, &ruby_block) ⇒ Object
Declares an extra asynchronous code to execute for state +name+.
-
#build(&ruby_block) ⇒ Object
builds the fsm by executing +ruby_block+.
-
#default(&ruby_block) ⇒ Object
Adds a default operative code.
-
#for_event(event = nil, &ruby_block) ⇒ Object
Sets the event synchronizing the fsm.
-
#for_reset(reset = nil, &ruby_block) ⇒ Object
Sets the reset.
-
#goto(*args) ⇒ Object
Sets the next state.
-
#initialize(name, *options) ⇒ FsmT
constructor
Creates a new fsm type with +name+.
-
#reset(type = @type, &ruby_block) ⇒ Object
Adds a code to be executed in case of reset.
-
#state(name = :"", &ruby_block) ⇒ Object
Declares a new state with +name+ and executing +ruby_block+.
-
#sync(name, &ruby_block) ⇒ Object
Declares an extra synchronous code to execute for state +name+.
Methods included from HScope_missing
Methods included from Hmissing
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,*) # Check and set the name @name = name.to_sym # Check and set the type of fsm depending of the options. @dual = false @type = :sync .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_sig ⇒ Object
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 |
#name ⇒ Object (readonly)
The name of the FSM type.
20 21 22 |
# File 'lib/HDLRuby/std/fsm.rb', line 20 def name @name end |
#namespace ⇒ Object (readonly)
The namespace associated with the FSM
23 24 25 |
# File 'lib/HDLRuby/std/fsm.rb', line 23 def namespace @namespace end |
#next_state_sig ⇒ Object
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_async ⇒ Object (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_sync ⇒ Object (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_state ⇒ Object
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 |