origen 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2b03ec3627ba26fc085e2a5302472d6bf13522bb
4
- data.tar.gz: e915a39f3b40a64aa4255b13293570b37abdd765
3
+ metadata.gz: 7f8ba64d3b0ecc75adf1a8d9e494db97b7367cc5
4
+ data.tar.gz: 6552aebba7e09bf8ad59ea4d5cd75f75395c6adf
5
5
  SHA512:
6
- metadata.gz: 958952409ba581f177774cc2dbe23eb42cc018fdf64eca2faa1708f7356c73beebc886678c4cb0fb6e8d7fac726f1fc20979cd7e233ced7d848a7724a8fd92bb
7
- data.tar.gz: 5ad6fb24cdf7c84f05bf7cc854c917e63e86c29eb133eee45f6e5ced7ac870ca108be1a0dcc4456af8f0d7f9696cecfe9103a4a76632ffc88eaf7a2a73360cab
6
+ metadata.gz: 3093953c422bf77ff2bc60af0878aab13f49a0f1b6911555a6ee7958b3ce5bed3bf5266c157d6ace7ef8c2af98f8d0d7cff6d7454cdca9118db3a79cdaf3bb67
7
+ data.tar.gz: 7a54ad39e2cb1c0c5ba1c9b86fc420c620e0aee9a2b8d8e6f937181a5307fed6bc53b3aae69c948afc191a59cac8562d98ee18dc6f564d31d6398c8805fc4024
data/config/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Origen
2
2
  MAJOR = 0
3
- MINOR = 11
3
+ MINOR = 12
4
4
  BUGFIX = 0
5
5
  DEV = nil
6
6
 
@@ -79,6 +79,34 @@ module Origen
79
79
  send(@reset)
80
80
  end
81
81
 
82
+ # Returns the drive cycle wave assigned to the pin based on the currently enabled timeset,
83
+ # or nil if none is set.
84
+ # Note that if a timeset is set then all pins will always return a wave as they will pick
85
+ # up a default waveform if none is explicitly assigned to it.
86
+ def drive_wave(code = nil)
87
+ if t = dut.current_timeset
88
+ # Cache this for performance since potentially this is something that could be called on
89
+ # every cycle in some applications
90
+ @drive_waves ||= {}
91
+ @drive_waves[t.id] ||= {}
92
+ @drive_waves[t.id][code] ||= dut.current_timeset.send(:wave_for, self, type: :drive, code: code)
93
+ end
94
+ end
95
+
96
+ # Returns the compare cycle wave assigned to the pin based on the currently enabled timeset,
97
+ # or nil if none is set
98
+ # Note that if a timeset is set then all pins will always return a wave as they will pick
99
+ # up a default waveform if none is explicitly assigned to it.
100
+ def compare_wave(code = nil)
101
+ if t = dut.current_timeset
102
+ # Cache this for performance since potentially this is something that could be called on
103
+ # every cycle in some applications
104
+ @compare_waves ||= {}
105
+ @compare_waves[t.id] ||= {}
106
+ @compare_waves[t.id][code] ||= dut.current_timeset.send(:wave_for, self, type: :compare, code: code)
107
+ end
108
+ end
109
+
82
110
  # Causes the pin to continuously drive 1 for 2 seconds and then drive 0 for 2 seconds.
83
111
  #
84
112
  # This is not an API that is intended to be used within a pattern. Rather it is a debug aid when
@@ -321,6 +349,20 @@ module Origen
321
349
  end
322
350
  end
323
351
 
352
+ # If the pin was defined initially as part of a group then this will return that group,
353
+ # otherwise it will return nil
354
+ def group
355
+ @primary_group
356
+ end
357
+ alias_method :primary_group, :group
358
+
359
+ # If the pin is a member of a primary group, this returns its index number within that
360
+ # group, otherwise returns nil
361
+ def group_index
362
+ @primary_group_index
363
+ end
364
+ alias_method :primary_group_index, :group_index
365
+
324
366
  # Returns a hash containing the pin groups that the given pin is a member of
325
367
  def groups
326
368
  # Origen.pin_bank.all_pin_groups.select do |name, group|
@@ -556,18 +598,22 @@ module Origen
556
598
 
557
599
  def set_value(val)
558
600
  invalidate_vector_cache
559
- # If val is a data bit extract the value of it
560
- val = val.respond_to?(:data) ? val.data : val
561
- # Assume driving/asserting a nil value means 0
562
- val = 0 unless val
563
- if val > 1
564
- fail "Attempt to set a value of #{val} on pin #{name}"
565
- end
566
- @repeat_previous = false
567
- if inverted?
568
- @value = val == 0 ? 1 : 0
601
+ if val.is_a?(String) || val.is_a?(Symbol)
602
+ @vector_formatted_value = val.to_s
569
603
  else
570
- @value = val
604
+ # If val is a data bit extract the value of it
605
+ val = val.respond_to?(:data) ? val.data : val
606
+ # Assume driving/asserting a nil value means 0
607
+ val = 0 unless val
608
+ if val > 1
609
+ fail "Attempt to set a value of #{val} on pin #{name}"
610
+ end
611
+ @repeat_previous = false
612
+ if inverted?
613
+ @value = val == 0 ? 1 : 0
614
+ else
615
+ @value = val
616
+ end
571
617
  end
572
618
  end
573
619
  alias_method :data=, :set_value
@@ -931,6 +977,16 @@ module Origen
931
977
  fail "Pin ext_pulldown attribute '#{value}' must be either true or false"
932
978
  end
933
979
  end
980
+
981
+ private
982
+
983
+ def primary_group=(group)
984
+ @primary_group = group
985
+ end
986
+
987
+ def primary_group_index=(number)
988
+ @primary_group_index = number
989
+ end
934
990
  end
935
991
  end
936
992
  end
@@ -0,0 +1,169 @@
1
+ module Origen
2
+ module Pins
3
+ module Timing
4
+ class Timeset
5
+ attr_reader :id
6
+
7
+ # Returns an array containing the defined waves for drive cycles.
8
+ # The wave at position 0 will be applied be default to any pin which
9
+ # does not otherwise have a specific wave assignment.
10
+ attr_reader :drive_waves
11
+
12
+ # Returns an array containing the defined waves for compare cycles
13
+ # The wave at position 0 will be applied be default to any pin which
14
+ # does not otherwise have a specific wave assignment.
15
+ attr_reader :compare_waves
16
+
17
+ def initialize(id)
18
+ @id = id
19
+ @drive_waves = []
20
+ @compare_waves = []
21
+ # Look up tables that map pins to waves
22
+ @compare_pin_map = {}
23
+ @drive_pin_map = {}
24
+ # Temporary storage of pin assignments
25
+ @pin_ids = { drive: [], compare: [] }
26
+
27
+ # Create the default waves, these can be overridden later
28
+ wave do |w|
29
+ w.compare :data, at: 'period / 2'
30
+ end
31
+
32
+ wave do |w|
33
+ w.drive :data, at: 0
34
+ end
35
+ end
36
+
37
+ # Add a new drive or compare wave to the timeset
38
+ #
39
+ # timeset.wave :tck do |w|
40
+ # w.drive :data, at: 0
41
+ # w.drive 0, at: 25
42
+ # w.dont_care at: "period - 10"
43
+ # end
44
+ def wave(*pin_ids)
45
+ options = pin_ids.last.is_a?(Hash) ? pin_ids.pop : {}
46
+ w = Wave.new(self, options)
47
+ yield w
48
+ if w.drive?
49
+ if pin_ids.empty?
50
+ w.send(:index=, 0)
51
+ drive_waves[0] = w
52
+ @pin_ids[:drive][0] = pin_ids
53
+ else
54
+ w.send(:index=, drive_waves.size)
55
+ drive_waves << w
56
+ @pin_ids[:drive] << pin_ids
57
+ end
58
+ else
59
+ if pin_ids.empty?
60
+ w.send(:index=, 0)
61
+ compare_waves[0] = w
62
+ @pin_ids[:compare][0] = pin_ids
63
+ else
64
+ w.send(:index=, compare_waves.size)
65
+ compare_waves << w
66
+ @pin_ids[:compare] << pin_ids
67
+ end
68
+ end
69
+ end
70
+ alias_method :compare_wave, :wave
71
+ alias_method :drive_wave, :wave
72
+
73
+ # The timeset will cache a view of the dut's pins for performance,
74
+ # calling this method will clear that cache and regenerate the internal
75
+ # view. This should generally not be required, but available for corner cases
76
+ # where a pin is added to the dut after the cache has been generated.
77
+ def clear_cache
78
+ @all_pin_ids = nil
79
+ @groups = nil
80
+ compare_waves.each { |w| w.send(:clear_cache) }
81
+ drive_waves.each { |w| w.send(:clear_cache) }
82
+ end
83
+
84
+ private
85
+
86
+ # The pin assignments are done lazily to cater for the guy who will want
87
+ # to define waves ahead of pins or some such
88
+ def assign_pins
89
+ @pin_ids[:drive].each_with_index do |ids, i|
90
+ expand_groups(ids) do |id|
91
+ @drive_pin_map[id] ||= []
92
+ @drive_pin_map[id] << i
93
+ end
94
+ end
95
+ @pin_ids[:compare].each_with_index do |ids, i|
96
+ expand_groups(ids) do |id|
97
+ @compare_pin_map[id] ||= []
98
+ @compare_pin_map[id] << i
99
+ end
100
+ end
101
+ @pin_ids = :done
102
+ end
103
+
104
+ def expand_groups(ids)
105
+ ids.each do |id|
106
+ if g = dut.pin_groups[id]
107
+ g.each do |pin|
108
+ yield pin.id
109
+ end
110
+ else
111
+ yield id
112
+ end
113
+ end
114
+ end
115
+
116
+ def wave_for(pin, options)
117
+ assign_pins unless @pin_ids == :done
118
+ if options[:type] == :drive
119
+ i = @drive_pin_map[pin.id]
120
+ if i
121
+ if i.size > 1
122
+ code = options[:code]
123
+ code = nil if code == 1
124
+ code = nil if code == 0
125
+ i.each do |ix|
126
+ return drive_waves[ix] if drive_waves[ix].code == code
127
+ end
128
+ else
129
+ drive_waves[i[0]]
130
+ end
131
+ else
132
+ drive_waves[0]
133
+ end
134
+ else
135
+ i = @compare_pin_map[pin.id]
136
+ if i
137
+ if i.size > 1
138
+ code = options[:code]
139
+ code = nil if code == 'L' || code == :L
140
+ code = nil if code == 'H' || code == :H
141
+ i.each do |ix|
142
+ return drive_waves[ix] if drive_waves[ix].code == code
143
+ end
144
+ else
145
+ compare_waves[i[0]]
146
+ end
147
+ else
148
+ compare_waves[0]
149
+ end
150
+ end
151
+ end
152
+
153
+ def pin_ids_for(wave)
154
+ assign_pins unless @pin_ids == :done
155
+ map = wave.drive? ? @drive_pin_map : @compare_pin_map
156
+ if wave.index == 0
157
+ all_pin_ids.select { |id| !map[id] || map[id].include?(0) }
158
+ else
159
+ all_pin_ids.select { |id| map[id] && map[id].include?(wave.index) }
160
+ end
161
+ end
162
+
163
+ def all_pin_ids
164
+ @all_pin_ids ||= dut.pins.values.map(&:id)
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,147 @@
1
+ module Origen
2
+ module Pins
3
+ module Timing
4
+ class Wave
5
+ attr_reader :events, :timeset, :index
6
+ # Returns the pattern code value associated with the wave. By default this will return nil
7
+ # if no code was given at the time the wave was defined, which means it is the wave that will
8
+ # be applied for the conventional code values of 0, 1, H, L.
9
+ attr_reader :code
10
+
11
+ VALID_DRIVE_DATA = [0, 1, :data]
12
+ VALID_COMPARE_DATA = [0, 1, :data]
13
+
14
+ def initialize(timeset, options = {})
15
+ @code = options[:code]
16
+ @code = nil if [0, 1, 'H', 'L', :H, :L].include?(@code)
17
+ @timeset = timeset
18
+ @events = []
19
+ end
20
+
21
+ # Returns the events array but with any formula based times
22
+ # evaluated.
23
+ # Note that this does not raise an error if the period is not currently
24
+ # set, in that case any events that reference it will have nil for
25
+ # their time.
26
+ def evaluated_events
27
+ if dut.current_timeset_period
28
+ events.map { |e| [calc.evaluate(e[0], period: dut.current_timeset_period).ceil, e[1]] }
29
+ else
30
+ fail 'The current timeset period has not been set'
31
+ end
32
+ end
33
+
34
+ # Returns an array containing all dut pin_ids that
35
+ # are assigned to this wave by the parent timeset
36
+ def pin_ids
37
+ @pins_ids ||= timeset.send(:pin_ids_for, self)
38
+ end
39
+
40
+ # Returns an array containing all dut pin objects that
41
+ # are assigned to this wave by the parent timeset
42
+ def pins
43
+ @pins ||= pin_ids.map { |id| dut.pin(id) }
44
+ end
45
+
46
+ def drive(data, options)
47
+ self.type = :drive
48
+ validate_data(data) do |d|
49
+ validate_time(options) do |t|
50
+ events << [t, d]
51
+ end
52
+ end
53
+ end
54
+
55
+ def compare(data, options)
56
+ self.type = :compare
57
+ validate_data(data) do |d|
58
+ validate_time(options) do |t|
59
+ events << [t, d]
60
+ end
61
+ end
62
+ end
63
+ alias_method :compare_edge, :compare
64
+
65
+ def dont_care(options)
66
+ self.type = :drive
67
+ validate_time(options) do |t|
68
+ events << [t, :x]
69
+ end
70
+ end
71
+ alias_method :highz, :dont_care
72
+
73
+ def type
74
+ @type ||= :drive
75
+ end
76
+
77
+ def drive?
78
+ @type == :drive
79
+ end
80
+
81
+ def compare?
82
+ @type == :compare
83
+ end
84
+
85
+ private
86
+
87
+ def clear_cache
88
+ @pin_ids = nil
89
+ @pins = nil
90
+ end
91
+
92
+ def index=(val)
93
+ @index = val
94
+ end
95
+
96
+ def validate_data(data)
97
+ valid = drive? ? VALID_DRIVE_DATA : VALID_COMPARE_DATA
98
+ data = :data if :data == :pattern
99
+ unless valid.include?(data)
100
+ fail "Uknown data value #{data}, only these are valid: #{valid.join(', ')}"
101
+ end
102
+ yield data
103
+ end
104
+
105
+ def calc
106
+ return @calc if @calc
107
+ require 'dentaku'
108
+ @calc = Dentaku::Calculator.new
109
+ end
110
+
111
+ def type=(t)
112
+ if @type
113
+ if @type != t
114
+ fail 'Timing waves cannot both drive and compare within a cycle period!'
115
+ end
116
+ else
117
+ @type = t
118
+ end
119
+ end
120
+
121
+ def validate_time(options)
122
+ unless options[:at]
123
+ fail 'When defining a wave event you must supply the time via the option :at'
124
+ end
125
+ t = options[:at]
126
+
127
+ if t.is_a?(String)
128
+ d = calc.dependencies(t) - %w(period period_in_ns)
129
+ unless d.empty?
130
+ fail "Wave time formulas can only include the variable 'period' (or 'period_in_ns'), this variable is not allowed: #{d}"
131
+ end
132
+ t = t.gsub('period_in_ns', 'period')
133
+ unless calc.evaluate(t, period: 100)
134
+ fail "There appears to be an error in the formula: #{t}"
135
+ end
136
+ yield t
137
+ return
138
+ elsif t.is_a?(Numeric)
139
+ yield t
140
+ return
141
+ end
142
+ fail 'The :at option in a wave event definition must be either a number or a string'
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,76 @@
1
+ module Origen
2
+ module Pins
3
+ # Top-level manager for the devices pin timing setups,
4
+ # an instance of this class is automatically instantiated
5
+ # and available as dut.timing
6
+ module Timing
7
+ autoload :Timeset, 'origen/pins/timing/timeset'
8
+ autoload :Wave, 'origen/pins/timing/wave'
9
+
10
+ # Add a very basic timeset where all pins will have default waves,
11
+ # which will drive for the whole cycle and compare at 50% of the
12
+ # current period
13
+ #
14
+ # add_timeset :func
15
+ def add_timeset(*args, &block)
16
+ if block_given?
17
+ timesets(*args, &block)
18
+ else
19
+ timesets(args.first) {}
20
+ end
21
+ end
22
+
23
+ def timesets(name = nil, options = {})
24
+ name, options = nil, name if name.is_a?(Hash)
25
+ @timesets ||= {}.with_indifferent_access
26
+ # If defining a new timeset
27
+ if block_given?
28
+ @timesets[name] ||= Timeset.new(name)
29
+ yield @timesets[name]
30
+ else
31
+ if name
32
+ @timesets[name]
33
+ else
34
+ @timesets
35
+ end
36
+ end
37
+ end
38
+
39
+ # Returns the currently selected timeset, or nil
40
+ def current_timeset(*args, &block)
41
+ if block_given?
42
+ timesets(*args, &block)
43
+ else
44
+ if args.first
45
+ timesets(args.first)
46
+ else
47
+ timesets[@current_timeset]
48
+ end
49
+ end
50
+ end
51
+ alias_method :timeset, :current_timeset
52
+
53
+ # Set the current timeset, this will be called automatically
54
+ # if the timeset is changed via tester.set_timeset
55
+ def current_timeset=(id)
56
+ if timesets[id]
57
+ @current_timeset = id
58
+ else
59
+ fail "Timeset #{id} has not been defined!"
60
+ end
61
+ end
62
+ alias_method :timeset=, :current_timeset=
63
+
64
+ # Set the current timeset period, this will be called automatically
65
+ # if the timeset is changed via tester.set_timeset
66
+ def current_timeset_period=(val)
67
+ @current_timeset_period = val
68
+ end
69
+
70
+ # Returns the current timeset period or nil
71
+ def current_timeset_period
72
+ @current_timeset_period
73
+ end
74
+ end
75
+ end
76
+ end
data/lib/origen/pins.rb CHANGED
@@ -179,6 +179,9 @@ module Origen
179
179
  autoload :OtherPin, 'origen/pins/other_pin'
180
180
  autoload :VirtualPin, 'origen/pins/virtual_pin'
181
181
  autoload :FunctionProxy, 'origen/pins/function_proxy'
182
+ require 'origen/pins/timing'
183
+
184
+ include Timing
182
185
 
183
186
  # @api private
184
187
  # API v2, deprecated
@@ -242,7 +245,9 @@ module Origen
242
245
  end
243
246
  yield group if block_given?
244
247
  group.each do |pin|
248
+ pin.send(:primary_group_index=, pin.id)
245
249
  pin.id = "#{group.id}#{pin.id}".to_sym
250
+ pin.send(:primary_group=, group)
246
251
  pin.finalize
247
252
  end
248
253
  if group.size == 1
@@ -104,7 +104,7 @@ module Origen
104
104
  # not intended to be inserted into production pattern logic.
105
105
  def sync(size = nil, options = {})
106
106
  size, options = nil, size if size.is_a?(Hash)
107
- if tester.try(:link?)
107
+ if tester.respond_to?(:capture)
108
108
  preserve_flags do
109
109
  v = tester.capture do
110
110
  store!(sync: true)
@@ -24,6 +24,11 @@ module Origen
24
24
  include Origen::Model
25
25
  end
26
26
 
27
+ # Top-level timing manager/API, returns an instance of Origen::Pins::Timing
28
+ def timing
29
+ @timing ||= Pins::Timing.new
30
+ end
31
+
27
32
  def reset!
28
33
  reset bang: true
29
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: origen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen McGinty
@@ -338,6 +338,20 @@ dependencies:
338
338
  - - "~>"
339
339
  - !ruby/object:Gem::Version
340
340
  version: '1'
341
+ - !ruby/object:Gem::Dependency
342
+ name: dentaku
343
+ requirement: !ruby/object:Gem::Requirement
344
+ requirements:
345
+ - - "~>"
346
+ - !ruby/object:Gem::Version
347
+ version: '2'
348
+ type: :runtime
349
+ prerelease: false
350
+ version_requirements: !ruby/object:Gem::Requirement
351
+ requirements:
352
+ - - "~>"
353
+ - !ruby/object:Gem::Version
354
+ version: '2'
341
355
  description:
342
356
  email:
343
357
  - stephen.f.mcginty@gmail.com
@@ -486,6 +500,9 @@ files:
486
500
  - lib/origen/pins/pin_collection.rb
487
501
  - lib/origen/pins/pin_common.rb
488
502
  - lib/origen/pins/power_pin.rb
503
+ - lib/origen/pins/timing.rb
504
+ - lib/origen/pins/timing/timeset.rb
505
+ - lib/origen/pins/timing/wave.rb
489
506
  - lib/origen/pins/virtual_pin.rb
490
507
  - lib/origen/ports.rb
491
508
  - lib/origen/ports/bit_collection.rb