Class: SOCMaker::SOCDef

Inherits:
CoreDef show all
Includes:
ERR, YAML_EXT
Defined in:
lib/soc_maker/soc_def.rb

Overview

This class represents a System-on-chip and derives the functionallity from SOCMaker::CoreDef. The two important fields are

  • #cores: holds all core-instances

  • #cons: holds all connections

In addition, the field @static is used to store static parameters, which are set for cores used in this SOC.

Instance Attribute Summary collapse

Attributes included from YAML_EXT

#src_dir

Attributes inherited from CoreDef

#author, #authormail, #date, #description, #functions, #hdlfiles, #id, #id_clean, #inst_parameters, #interfaces, #license, #licensefile, #name, #static_parameters, #toplevel, #vccmd

Instance Method Summary collapse

Methods included from YAML_EXT

#save_yaml

Methods included from ERR

#consistence_error, #consistence_error_if, #init_error, #init_error_if, #processing_error, #processing_error_if

Methods inherited from CoreDef

#add_interface, core_cnt, #dir_name, #generics, get_and_ensure_dst_dir!, #ifc_specification, #ports, #update_vcs

Constructor Details

#initialize(name, id, toplevel, optional = {}) ⇒ SOCDef

This constructor expects the name, an id, the toplevel-name as mandatory arguments. All other attributes can be privided as optional argument



100
101
102
103
104
105
106
# File 'lib/soc_maker/soc_def.rb', line 100

def initialize( name, id, toplevel, optional = {} )

  init_with( { 'name'     => name, 
               'id'       => id,
               'toplevel' => toplevel }.merge( optional ) )

end

Instance Attribute Details

#consObject

Hash of all connections. The structure is the following

@cons = { :connection_name_1 => {
            :mapping => [
                { :instance_1 => :interface_of_instance_1,  #\   dir=0
                  :instance_2 => :interface_of_instance_2,  #/
                  ...},
                { :instance_3 => :interface_of_instance_3,  #\   dir=1
                  :instance_4 => :interface_of_instance_4,  #/
                  ...}
              ] },

          :connection_name_2 => {
            :mapping => [
                { :instance_a => :interface_of_instance_a,  #\   dir=0
                  :instance_b => :interface_of_instance_b,  #/
                  ...},
                { :instance_c => :interface_of_instance_c,  #\   dir=1
                  :instance_d => :interface_of_instance_d,  #/
                  ...}
              ] },
               ....

              }

The important part is the mapping: it is a list with two entries. Ech entry is a Hash containing instance-interface assignments.

TODO: this needs to be re-designed, because it allowes only one core to be used in one connection-direction.



90
91
92
# File 'lib/soc_maker/soc_def.rb', line 90

def cons
  @cons
end

#coresObject

Hash of cores (core instances: SOCMaker::CoreInst)



53
54
55
# File 'lib/soc_maker/soc_def.rb', line 53

def cores
  @cores
end

#staticObject

Hash of all static parameters defined within this SOC



95
96
97
# File 'lib/soc_maker/soc_def.rb', line 95

def static
  @static
end

Instance Method Details

#==(o) ⇒ Object

Equality operator



801
802
803
804
805
806
807
# File 'lib/soc_maker/soc_def.rb', line 801

def ==(o)
  o.class   == self.class   &&
  o.cores   == self.cores   &&
  o.cons    == self.cons    &&
  o.static  == self.static  &&
  super( o )
end

#add_connection(*args) ⇒ Object

Method add/modify a connection. The number of arguments are variable and multiple entries can be provided as once.

args

number of arguments must be >3 and odd. The first argument defines

the connection name. The following arguments are treaded as pairs. Each pair consists of the instance name and the interface name.



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/soc_maker/soc_def.rb', line 316

def add_connection( *args )

    if args.size < 3 || args.size % 2 != 1
      processing_error "FATAL: wrong number of arguments: " +
                      "(#{args.size}) must be > = 3 and odd"
    end

    consistence_check


    con_name = args[0]
    args[ 1..-1].each_slice(2).to_a.each do |entry| 
      inst_name = entry[0]
      ifc_name  = entry[1]

      # get the core definition
      core_def = core_definition( inst_name ) or 
            consistence_error "Can't find definition of core #{inst_name}" 


      consistence_error_if ifc_in_use?( inst_name, ifc_name ), 
         "Interface #{ifc_name} of instance '#{inst_name}' is already in use " 


      consistence_error_if core_def.interfaces[ ifc_name ] == nil,
        "Interface '#{ifc_name}' dosn't exist in core '#{inst_name}' \n" +
        "The following interfaces do exist:  '#{core_def.interfaces.keys}'"  

      # get the interface specification
      ifc_spc = core_def.ifc_specification( ifc_name )

      # get the directions
      ifc_dir = core_def.interfaces[ ifc_name ].dir

      ifc_dir = ( ifc_dir + 1 ) % 2 if is_it_me?( inst_name )


      # create a new connection, if there is no one
      if @cons[ con_name ] == nil
        @cons[ con_name ] = { mapping: [ {},{} ] }
      end

      if ifc_spc.n_connections_ok?( ifc_dir, @cons[ con_name ][ :mapping ][ ifc_dir ].size+1 )
        @cons[ con_name ][ :mapping ][ ifc_dir ][ inst_name ] = ifc_name
      else
        consistence_error "Only #{ ifc_spc.multiplicity[ ifc_dir ] } " + 
                              "connections are allowed for direction #{ifc_dir}",
                              connection: con_name,
                              instance: inst_name,
                              interface: ifc_name

      end

    end


end

#add_core(id, inst_name) ⇒ Object

Add an instance to the SoC

id

the core ID, used to get the desired core from SOCMaker::Lib

inst_name

name of the instance



230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/soc_maker/soc_def.rb', line 230

def add_core( id, inst_name )

  consistence_error_if(
    inst_in_use?( inst_name ),
    "Instance name #{inst_name} is already in use"  )

  # check, if the core exits in our library
  #  if not: an error will be raised
  SOCMaker::lib.get_core( id )

  @cores[ inst_name ] = SOCMaker::CoreInst.new( id )
end

#all_core_idObject

Method to get all id’s of all cores, sub-socs (recursively)



783
784
785
# File 'lib/soc_maker/soc_def.rb', line 783

def all_core_id
  @cores.values.map{ |c| c.defn.all_core_id }.flatten! << @id
end

#all_static_parametersObject

Method to get all static parameters of all cores, sub-socs (recursively)



791
792
793
794
795
# File 'lib/soc_maker/soc_def.rb', line 791

def all_static_parameters
  tmp = { @id => @static }
  @cores.values.each { |v| tmp.merge!( v.defn.all_static_parameters ) }
  return tmp
end

#consistence_checkObject

Consistence check method:

  • ensures, that all cores are available and consistent

  • ensures, that all connections are possible



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
# File 'lib/soc_maker/soc_def.rb', line 143

def consistence_check
  super

  @cores.values.each do |inst|
    inst.consistence_check
  end


  @cons.each do |con_name, con_def|
    con_def[ :mapping ].each_with_index do |mapping,dir|
      mapping.each do |inst_name, ifc_name|
        if !is_it_me?( inst_name ) 
          consistence_error "#{inst_name} not does not exist in SOCDef #{@name}" if @cores[ inst_name ] == nil
        end

        core_def = core_definition( inst_name ) or 
              consistence_error "Can't find definition of core #{inst_name}" 

        consistence_error "Interface #{ifc_name} doesn exist in #{core_def.name}" if core_def.interfaces[ ifc_name ] == nil

        consistence_error "Wrong connection definition of #{inst_name}:#{ifc_name}",
          direction: core_def.interfaces[ ifc_name ].dir,
          localtion: dir,
          internal: is_it_me?( inst_name ) if !( 
              ( (core_def.interfaces[ ifc_name ].dir != dir) &&  is_it_me?( inst_name ) ) ||
              ( (core_def.interfaces[ ifc_name ].dir == dir) && !is_it_me?( inst_name ) ) )

      end
    end
  end



end

#core_definition(inst) ⇒ Object

Method to get the core definition.

inst

instance name (as symbol)

return

nil, if there is no core with the name and if it is not this SOC



291
292
293
294
295
296
297
298
299
300
# File 'lib/soc_maker/soc_def.rb', line 291

def core_definition( inst )
    if @cores[ inst ] != nil
      return @cores[ inst ].defn

    elsif is_it_me?( inst )
      return self
    else
      return nil
    end
end

#deploy(options = {}) ⇒ Object

Method, to deploy this SOC: It runs gen_toplevel and calls also super to copy files, which are added in addition to this SOC.

options

Common entries are coder and static:

coder must be a SOCMaker::Coder and static must be a hash of static parameters.



468
469
470
471
472
# File 'lib/soc_maker/soc_def.rb', line 468

def deploy( options = {}  )
  options = { coder: VHDLCoder.new  }.merge( options )
  gen_toplevel( options[ :coder ] )
  super( options )
end

#encode_with(coder) ⇒ Object

Encoder method (to yaml)

coder

An instance of the Psych::Coder to encode this class to a YAML file



113
114
115
116
117
118
119
# File 'lib/soc_maker/soc_def.rb', line 113

def encode_with( coder )
  init_error_if !coder.is_a?( Psych::Coder ), 
              'coder is not given as Psych::Coder'
  super coder
  %w[ cores cons static ].
    each { |v| coder[ v ] = instance_variable_get "@#{v}" }
end

#gen_toplevel(coder = VHDLCoder.new) ⇒ Object

Generate toplevel hdl file for this instance. This assumes, that this instance represents a SOC with further instances.

coder

An instance of the SOCMaker::HDLCoder, which is used to create the auto-generated HDL (optional). If no coder is given, a SOCMaker::VHDLCoder is used.



521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# File 'lib/soc_maker/soc_def.rb', line 521

def gen_toplevel( coder = VHDLCoder.new )


  #
  # Get filename
  #
  file_name = coder.filename( dir_name )
  dst_dir  = CoreDef::get_and_ensure_dst_dir!( dir_name )

  SOCMaker::logger.proc( "START of creating top-level '" + file_name + "'" )


  #
  # Create a unique list of cores and 
  # add for each core a component statement (vhdl only).
  # Even if there are multiple instances of a core,
  # we need to decalre it only once
  #
  @cores.values.uniq{|x| x.type }.each do |inst; spec|

    spec = SOCMaker::lib.get_core( inst.type  )
    processing_error "Can't find #{ inst.type } in SOC library" if !spec

    coder.add_core_component( inst.type, spec )
  end


  top_connections = {}

  #
  # Instanciate each core
  #
  @cores.each do |inst_name, inst|

    # get all connections of this instance, which are connected
    # to top and are single-entry connections
    top_cons_tmp = top_connections( inst_name )
    top_cons_tmp = top_cons_tmp.select{ |k,v|  v[:mapping][0].size == 1 && v[:mapping][1].size == 1 }
    top_cons_tmp.keys.each do |con_name|
      @cons[ con_name ][ :top_assigned ] = true
    end
    coder.add_core_instance( inst_name.to_s, inst, top_cons_tmp, @interfaces, self )
    top_connections.merge!( top_cons_tmp )
  end


  # create a list of all top-interfaces, which are directly connected
  # to cores
  top_connected_ifcs = []
  top_connections.values.each do |con|
    if con[ :mapping ][0].has_key?( @toplevel.to_sym )
      top_connected_ifcs << con[ :mapping ][0][ @toplevel.to_sym ]
    else
      top_connected_ifcs << con[ :mapping ][1][ @toplevel.to_sym ]
    end
  end


  # Iterate over all connections:
  #  - create signal instances
  #  - add assignments
  #
  @cons.each do |con_name, con_def|
    if not con_def[ :top_assigned]
      gen_toplevel_con(   con_name.to_s, 
                          con_def[ :mapping ][0], 
                          con_def[ :mapping ][1],
                          coder  )
    end
  end

  assign_unused_to_default( coder )

  coder.add_toplevel_sig( self, @toplevel, top_connected_ifcs )

  # 
  # Write content to the file
  #
  SOCMaker::logger.proc( "writing top-level" )
  File.open( File.join( dst_dir, file_name ), 'w' ) do |f| 
    f.write( coder.get_hdl_code( self, @toplevel ) )
  end
  SOCMaker::logger.proc( "END of creating top-level hdl code for #{@name}" )

  @cons.values.each{ |v| v[ :top_assigned ] = false }


end

#get_param(instance, param) ⇒ Object

Method to get an instance parameter

instance

name of the instance

param

name of the parameter



406
407
408
409
410
411
412
413
414
# File 'lib/soc_maker/soc_def.rb', line 406

def get_param( instance, param )

  # get instance
  core_inst = @cores[ instance ]
  consistence_error "Can't find '#{instance}' in SOC" if core_inst == nil
  param_val = core_inst.params[ param ]
  consistence_error "Can't find parameter '#{param}' in '#{instance}'" if param_val == nil
  return param_val
end

#get_sparam(core, param) ⇒ Object

Method to get a static parameter

instance

name of the instance

param

name of the parameter



449
450
451
452
453
454
455
# File 'lib/soc_maker/soc_def.rb', line 449

def get_sparam( core, param )
  consistence_error "Core '#{core}' does not exist in this SOC" if  @static[ core ] == nil

  consistence_error "Parameter '#{param}' does not exist for core '#{core}'" if @static[ core ][ param ] == nil

  return @static[ core ][ param ]
end

#ifc_in_use?(inst_name, ifc_name) ⇒ Boolean

Method to check, if the interface of an instance is already connected

inst_name

name of the instance

ifc_name

name of the interface

Returns:

  • (Boolean)


251
252
253
254
255
256
257
258
259
260
261
# File 'lib/soc_maker/soc_def.rb', line 251

def ifc_in_use?( inst_name, ifc_name )

  # go through all connections and check,
  # that non of the interfaces we want to connect is used
  @cons.each do |_con_name, con_def|
    return true if con_def[ :mapping ][ 0 ][ inst_name] == ifc_name 
    return true if con_def[ :mapping ][ 1 ][ inst_name] == ifc_name   
  end
  return false

end

#init_with(coder) ⇒ Object

Initialization method (from yaml)

coder

An instance of the Psych::Coder to init this class from a YAML file



127
128
129
130
131
132
133
134
# File 'lib/soc_maker/soc_def.rb', line 127

def init_with( coder )
  init_error_if !( coder.is_a?( Hash ) || coder.is_a?( Psych::Coder ) ), 
              'coder is not given as Hash neither as Psych::Coder'
  super coder
  @cores  = coder[ 'cores'  ] || {}
  @static = coder[ 'static' ] || {}
  @cons   = coder[ 'cons'   ] || {}
end

#inst_connections(inst_name) ⇒ Object

Method to get all connections of an instance

inst_name

Instance name (as symbol)

return

A connection hash



481
482
483
484
485
486
487
488
489
490
# File 'lib/soc_maker/soc_def.rb', line 481

def inst_connections( inst_name )
  connections = {};
  @cons.each do |con_name, con|
    if con[ :mapping ][ 0 ].select{ |k,v| k == inst_name }.size > 0 ||
       con[ :mapping ][ 1 ].select{ |k,v| k == inst_name }.size > 0
      connections[ con_name ] = con
    end
  end
  return connections
end

#inst_in_use?(inst_name) ⇒ Boolean

Check, if the instance name is already used within this SOC

Returns:

  • (Boolean)


198
199
200
# File 'lib/soc_maker/soc_def.rb', line 198

def inst_in_use?( inst_name )
  return ( @cores[ inst_name ] != nil or @cons[ inst_name ]  != nil )
end

#is_it_me?(inst_name) ⇒ Boolean

Method to check, if inst_name means this SOC. For connecting cores, the instance name is used. But If a toplevel connection to a core needs to be done, the SOC is identified by @toplevel (even if this is not an instance)

inst_name

name of instance

return

true if inst_name == @toplevel.to_sym, otherwise false

Returns:

  • (Boolean)


188
189
190
191
# File 'lib/soc_maker/soc_def.rb', line 188

def is_it_me?( inst_name )
  # TODO: is there a better way to identify self via @toplevel?
  inst_name == @toplevel.to_sym
end

#port(ifc_name, port_ref) ⇒ Object

Returns a port, identified by the interface and port name Note: this behaviour is exactly the same than of core_inst.port(…), but these ports are not evaluated.

ifc_name

name of the interface

port_ref

name of the port



751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
# File 'lib/soc_maker/soc_def.rb', line 751

def port( ifc_name, port_ref )

  ifc = @interfaces[ ifc_name ]

  # get interface specification
  ifc_spc = SOCMaker::lib.get_ifc( ifc.id )

  # get the port
  port_tmp = ifc.ports.select{ |k,v| v.spc_ref.to_sym == port_ref }

  # get the reference to the port definition
  defn_ref = port_tmp.first[1].spc_ref.to_sym

  _port_name = port_tmp.first[0].to_s

  if ifc_spc.ports[ defn_ref ][ :dir ] == 2
    _port_dir = 1 ^ ifc.dir
  else
    _port_dir = ifc_spc.ports[ defn_ref ][ :dir ] ^ ifc.dir
  end

  _port_default = ifc_spc.ports[ defn_ref ][ :default ] 

  _port_len = port_tmp.first[1].len

  return [ _port_name, { len: _port_len, dir: _port_dir, default: _port_default, ref: port_tmp.first[1].spc_ref } ]
end

#port_length(ifc_name, port_name, inst) ⇒ Object

Method to get the port length. All arguments as symbol.

ifc_name

name of the interface

port_name

name of the port

inst

name of the instance



271
272
273
274
275
276
277
278
279
280
# File 'lib/soc_maker/soc_def.rb', line 271

def port_length( ifc_name, port_name, inst )
  if @cores[ inst ] != nil
    return @cores[ inst ].port_length( ifc_name, port_name )
  elsif is_it_me?( inst )
    tmp =  @interfaces[ ifc_name ].ports.select{ |k,v| v.spc_ref == port_name.to_s }
    return tmp.values.first.len
  else
    return nil
  end 
end

#rm(inst_name) ⇒ Object

Method to remove a instance



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/soc_maker/soc_def.rb', line 205

def rm( inst_name )

  if @cores[ inst_name ] != nil 
    @cores.delete( inst_name )

    # remove all connection entries of this core
    @cons.each do |_con_name, con_def|
      con_def[ :mapping ][ 0 ].delete( inst_name ) if con_def[ :mapping ][ 0 ].has_key?( inst_name )
      con_def[ :mapping ][ 1 ].delete( inst_name ) if con_def[ :mapping ][ 1 ].has_key?( inst_name )
    end

  elsif @cons[ inst_name ]  != nil
    @cons.delete( inst_name )
  else
    consistence_error( "Can't remove instance #{inst_name}" )
  end
end

#set_param(instance, param, value) ⇒ Object

Method to set an instance parameter

instance

name of the instance

param

name of the parameter

vlue

value, which is set



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/soc_maker/soc_def.rb', line 382

def set_param( instance, param, value )

  # get instance
  core_inst = @cores[ instance ]
  consistence_error "Can't find '#{instance}' in SOC" if  core_inst == nil

  # get the core-definition
  core_def = SOCMaker::lib.get_core( core_inst.type  )

  # check if parameter exist
  if core_def.inst_parameters[ param ] != nil 
    core_inst.params[ param ] = value
  else
    consistence_error "Parameter '#{param}' not found in '#{core_def.name}'"
  end

end

#set_sparam(core, param, value) ⇒ Object

Method to set a static parameter

instance

name of the instance

param

name of the parameter

vlue

value, which is set



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/soc_maker/soc_def.rb', line 424

def set_sparam( core, param, value )

  #get instance

  # check, if we are instantiating this core
  processing_error_if( @cores.select{ |name,inst| inst.type == core }.size == 0,
    "Core '#{core}' is not instantiated in this SOC" )

  # get the core-definition
  core_def = SOCMaker::lib.get_core( core )

  # check if parameter exist
  processing_error_if( core_def.static_parameters.select{ |f,p| p.parameters[ param ] != nil }.size == 0,
      "Parameter '#{param}' not found in '#{core_def.name}'" )

  @static[ core ] ||= {}
  @static[ core ][ param ] = value
end

#to_sObject

Returns a string describing this instance



813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
# File 'lib/soc_maker/soc_def.rb', line 813

def to_s

  tmp = "_________ SOC #{@name}: _______\n"     + 
        super                                   +
        "\n__connections__\n"                     

  @cons.each do |_con_name, con_def|
    tmp += "#{_con_name}: #{con_def}\n"
  end 

  tmp += "\n__cores__\n" 
  @cores.each do |inst_name, inst|
    tmp += "#{inst_name}:\n#{inst}\n"
  end 


  tmp += "\n__interfaces__\n"
  @interfaces.each do |ifc_name, ifc|
    tmp += "#{ifc_name}:#{ifc.id}\n"
  end

  tmp += "'''''''''''''''''''''''''''''''''''\n"
  return tmp
end

#top_connections(inst_name) ⇒ Object

Method to get all connections which connected to the top

inst_name

Instance name (as symbol)

return

A connection hash



499
500
501
502
503
504
505
# File 'lib/soc_maker/soc_def.rb', line 499

def top_connections( inst_name )
  inst = inst_connections( inst_name )
  top  = inst_connections( toplevel.to_sym )

  tmp = inst.keys & top.keys
  return inst.select{ |k,v| tmp.include?( k ) }
end