origen 0.11.0 → 0.12.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.
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