origen_link 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ require 'origen_jtag'
2
+
1
3
  module OrigenLink
2
4
  module Test
3
5
  class TopLevelController
@@ -6,41 +6,48 @@ require 'origen_link/configuration_commands'
6
6
  require 'origen_link/callback_handlers'
7
7
  module OrigenLink
8
8
  # OrigenLink::VectorBased
9
- # This class is meant to be used for live silicon debug. Vector data that Origen
9
+ # This class describes the OrigenLink app plug-in. Vector data that Origen
10
10
  # generates is intercepted and sent to a debug device (typically will be a Udoo
11
11
  # Neo - www.udoo.org). The debug device can be any device that is able to serve
12
12
  # a TCP socket, recieve and interpret the command set used by this class and send
13
13
  # the expected responses.
14
14
  #
15
- # Integration instructions
16
- # Set the pin map (must be done first) and pin order
17
- # if tester.link?
18
- # tester.pinmap = 'tclk,26,tms,19,tdi,16,tdo,23'
19
- # tester.pinorder = 'tclk,tms,tdi,tdo'
20
- # end
21
- #
22
- # Set Origen to only generate vectors for pins in the pinmap (order should match)
23
- # pin_pattern_order :tclk, :tms, :tdi, :tdo, only: true if tester.link?
24
- #
25
- # At the beginning of the Startup method add this line
26
- # tester.initialize_pattern if tester.link?
27
- #
28
- # At the end of the Shutdown method add this line
29
- # tester.finalize_pattern if tester.link?
30
- #
31
- # Create a link environment with the IP address and socket number of a link server
32
- # $tester = OrigenLink::VectorBased.new('192.168.0.2', 12777)
33
15
  class VectorBased
34
16
  include OrigenTesters::VectorBasedTester
35
17
  include ServerCom
36
18
  include CaptureSupport
37
19
  include ConfigurationCommands
38
20
 
39
- # these attributes are exposed for testing purposes, a user would not need to read them
40
- attr_reader :fail_count, :vector_count, :total_comm_time, :total_connect_time, :total_xmit_time
41
- attr_reader :total_recv_time, :total_packets, :vector_repeatcount, :tsets_programmed, :captured_data
42
- attr_reader :vector_batch, :store_pins_batch, :comment_batch
43
- attr_reader :user_name, :initial_comm_sent
21
+ # The number of cycles that fail
22
+ attr_reader :fail_count
23
+ # The number of vector cycles generated
24
+ attr_reader :vector_count
25
+ # The accumulated total time spent communicating with the server
26
+ attr_reader :total_comm_time
27
+ # The accumulated total time spent establishing the server connection
28
+ attr_reader :total_connect_time
29
+ # The accumulated total time spent transmitting to the server app
30
+ attr_reader :total_xmit_time
31
+ # The accumulated total time spent receiving from the server app
32
+ attr_reader :total_recv_time
33
+ # The accumulated total number of packets sent to the server
34
+ attr_reader :total_packets
35
+ # The accumulated number of times push_vector was called with the present tset and pin info
36
+ attr_reader :vector_repeatcount
37
+ # The look up of programmed tsets. Names are converted to a unique number identifier
38
+ attr_reader :tsets_programmed
39
+ # Data captured using tester.capture
40
+ attr_reader :captured_data
41
+ # Array of vectors waiting to be sent to the sever
42
+ attr_reader :vector_batch
43
+ # Used with capture
44
+ attr_reader :store_pins_batch
45
+ # Array of comments received through push_comment
46
+ attr_reader :comment_batch
47
+ # The name of the user running OrigenLink
48
+ attr_reader :user_name
49
+ # Indicates that communication has been initiated with the server
50
+ attr_reader :initial_comm_sent
44
51
 
45
52
  def initialize(address, port, options = {})
46
53
  @address = address
@@ -58,6 +65,7 @@ module OrigenLink
58
65
  @max_packet_time = 0
59
66
  @max_receive_time = 0
60
67
  @tsets_programmed = {}
68
+ @tsets_warned = {}
61
69
  @tset_count = 1
62
70
  @store_pins = []
63
71
  @captured_data = []
@@ -72,6 +80,22 @@ module OrigenLink
72
80
  @pattern_comments = {}
73
81
  @user_name = Etc.getlogin
74
82
  @initial_comm_sent = false
83
+ @initial_vector_pushed = false
84
+ @pinorder = ''
85
+ @pinmap_hash = {}
86
+ @batched_setup_cmds = []
87
+
88
+ # check the server version against the plug-in version
89
+ response = send_cmd('version', '')
90
+ response = 'Error' if response.nil? # prevent run time error in regression tests
91
+ response.chomp!
92
+ server_version = response.split(':')[1]
93
+ server_version = '?.?.? - 0.2.0 or earlier' if response =~ /Error/
94
+ app_version = Origen.app(:origen_link).version
95
+ Origen.log.info("Plug-in link version: #{app_version}, Server link version: #{server_version}")
96
+ unless app_version == server_version
97
+ Origen.log.warn('Server version and plug-in link versions do not match')
98
+ end
75
99
  end
76
100
 
77
101
  # push_comment
@@ -92,14 +116,89 @@ module OrigenLink
92
116
  end
93
117
  end
94
118
 
119
+ # ordered_pins(options = {})
120
+ # expand pin groups to their component pins after the pin ordering is completed
121
+ # OrigenLink always operates on individual pins. This saves other methods
122
+ # from each needing to handle pins and/or groups of pins.
123
+ def ordered_pins(options = {})
124
+ result = super
125
+ groups = []
126
+ result.each { |p| groups << p if p.size > 1 }
127
+ groups.each do |group|
128
+ # locate this group in the result array
129
+ i = result.index(group)
130
+ result.delete_at(i)
131
+ dut.pins(group.id).map.each do |sub_pin|
132
+ result.insert(i, sub_pin)
133
+ i += 1
134
+ end
135
+ end
136
+
137
+ if @pinmap.nil?
138
+ # create the pinmap if pin metadata was provided
139
+ pinarr = []
140
+ result.each do |pin|
141
+ if pin.meta.key?(:link_io)
142
+ pinarr << pin.name.to_s
143
+ pinarr << pin.meta[:link_io].to_s
144
+ end
145
+ end
146
+ self.pinmap = pinarr.join(',') unless pinarr.size == 0
147
+ end
148
+
149
+ result
150
+ end
151
+
152
+ # fix_ordered_pins(options)
153
+ # This method is called the first time push_vector is called.
154
+ #
155
+ # This method will create the pinmap from pin meta data if needed.
156
+ #
157
+ # This method will remove any pin data that doesn't correspond
158
+ # to a pin in the link pinmap and remove those pins from the
159
+ # @ordered_pins_cache to prevent them from being rendered
160
+ # on the next cycle.
161
+ # This will prevent unwanted behavior. The link server
162
+ # expects only pin data for pins in the pinmap.
163
+ def fix_ordered_pins(options)
164
+ # remove non-mapped pins from the ordered pins cache - prevents them appearing in future push_vector calls
165
+ orig_size = @ordered_pins_cache.size
166
+ @ordered_pins_cache.delete_if { |p| !@pinmap_hash[p.name.to_s] }
167
+ Origen.log.debug('OrigenLink removed non-mapped pins from the cached pin order array') unless orig_size == @ordered_pins_cache.size
168
+ # update pin values for the current push_vector call
169
+ vals = []
170
+ @ordered_pins_cache.each { |p| vals << p.to_vector }
171
+ options[:pin_vals] = vals.join('')
172
+ options
173
+ end
174
+
95
175
  # push_vector
96
176
  # This method intercepts vector data from Origen, removes white spaces and compresses repeats
97
177
  def push_vector(options)
178
+ unless @initial_vector_pushed
179
+ if @pinmap.nil?
180
+ Origen.log.error('OrigenLink: pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
181
+ else
182
+ Origen.log.debug('OrigenLink: executing pattern with pinmap:' + @pinmap.to_s)
183
+ end
184
+
185
+ # remove pins not in the link pinmap
186
+ options = fix_ordered_pins(options)
187
+
188
+ # now send any configuration commands that were saved prior to pinmap setup (clears all server configs)
189
+ @batched_setup_cmds.each do |cmd|
190
+ response = send_cmd(cmd[0], cmd[1])
191
+ setup_cmd_response_logger(cmd[0], response)
192
+ end
193
+
194
+ @initial_vector_pushed = true
195
+ end
196
+ set_pinorder if @pinorder == ''
98
197
  programmed_data = options[:pin_vals].gsub(/\s+/, '')
99
198
  unless options[:timeset]
100
199
  puts 'No timeset defined!'
101
200
  puts 'Add one to your top level startup method or target like this:'
102
- puts '$tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns'
201
+ puts 'tester.set_timeset("nvmbist", 40) # Where 40 is the period in ns'
103
202
  exit 1
104
203
  end
105
204
  tset = options[:timeset].name
@@ -126,9 +225,6 @@ module OrigenLink
126
225
  # flush_vector
127
226
  # Just as the name suggests, this method "flushes" a vector. This is necessary because
128
227
  # of repeat compression (a vector isn't sent until different vector data is encountered)
129
- #
130
- # Don't forget to flush when you're in debug mode. Otherwise, the last vector of a
131
- # write command won't be sent to the server.
132
228
  def flush_vector(programmed_data = '', tset = '')
133
229
  # prevent server crash when vector_flush is used during debug
134
230
  unless @previous_vectordata == ''
@@ -140,7 +236,15 @@ module OrigenLink
140
236
  if @tsets_programmed[@previous_tset]
141
237
  tset_prefix = "tset#{@tsets_programmed[@previous_tset]},"
142
238
  else
143
- tset_prefix = ''
239
+ # The hash of programmed tsets does not contain this tset
240
+ # Check the timing api to see if there is timing info there
241
+ # and send the timing info to the link server
242
+ if dut.respond_to?(:timeset)
243
+ tset_prefix = process_timeset(tset)
244
+ else
245
+ tset_warning(tset)
246
+ tset_prefix = ''
247
+ end
144
248
  end
145
249
 
146
250
  if @batch_vectors
@@ -233,9 +337,11 @@ module OrigenLink
233
337
 
234
338
  vector_cycles.each do |cycle|
235
339
  thiscyclefail = false
340
+ bad_pin_data = false
236
341
  if pfstatus == 'F'
237
342
  # check to see if this cycle failed
238
343
  0.upto(cycle.length - 1) do |index|
344
+ bad_pin_data = true if (cycle[index] == 'W')
239
345
  thiscyclefail = true if (cycle[index] == 'H') && (expected_msg[expected_msg.length - cycle.length + index] == 'L')
240
346
  thiscyclefail = true if (cycle[index] == 'L') && (expected_msg[expected_msg.length - cycle.length + index] == 'H')
241
347
  end
@@ -247,6 +353,10 @@ module OrigenLink
247
353
  expected_msg_prnt = ''
248
354
  prepend = 'P:'
249
355
  end
356
+ if bad_pin_data
357
+ expected_msg_prnt = ' ' + 'W indicates no operation, check timeset definition'
358
+ prepend = 'F:'
359
+ end
250
360
 
251
361
  if output_obj.nil?
252
362
  microcode prepend + cycle + expected_msg_prnt + msg
@@ -263,8 +373,8 @@ module OrigenLink
263
373
  end
264
374
 
265
375
  # initialize_pattern
266
- # This method sets initializes variables at the start of a pattern.
267
- # it is called automatically when pattern generation starts.
376
+ # This method initializes variables at the start of a pattern.
377
+ # It is called automatically when pattern generation starts.
268
378
  def initialize_pattern
269
379
  @fail_count = 0
270
380
  @vector_count = 0
@@ -280,11 +390,12 @@ module OrigenLink
280
390
  @total_xmit_time = 0
281
391
  @total_recv_time = 0
282
392
 
283
- if @pinmap.nil?
284
- Origen.log.error('pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
285
- else
286
- Origen.log.debug('executing pattern with pinmap:' + @pinmap.to_s)
287
- end
393
+ # moved to push_vector to allow auto-pinmap
394
+ # if @pinmap.nil?
395
+ # Origen.log.error('OrigenLink: pinmap has not been setup, use tester.pinmap= to initialize a pinmap')
396
+ # else
397
+ # Origen.log.debug('OrigenLink: executing pattern with pinmap:' + @pinmap.to_s)
398
+ # end
288
399
  end
289
400
 
290
401
  # finalize_pattern
@@ -327,11 +438,9 @@ module OrigenLink
327
438
  end
328
439
 
329
440
  # to_s
330
- # returns 'Origen::VectorBased'
441
+ # returns 'OrigenLink::VectorBased'
331
442
  #
332
- # This method at the moment is used for implementing code that runs only if the
333
- # environment is set to link vector based. tester.link? will be used once the testers
334
- # plug in supports the method link?.
443
+ # No longer a use for this. Use tester.link?
335
444
  def to_s
336
445
  'OrigenLink::VectorBased'
337
446
  end
@@ -341,11 +450,10 @@ module OrigenLink
341
450
  # true = pass
342
451
  # false = fail
343
452
  #
344
- # TODO: capture transaction vector data and response for use in debug
345
- #
346
- # if !tester.transaction {dut.reg blah blah}
347
- # puts 'transaction failed'
348
- # end
453
+ # @example
454
+ # if !tester.transaction {dut.reg blah blah}
455
+ # puts 'transaction failed'
456
+ # end
349
457
  def transaction
350
458
  if block_given?
351
459
  synchronize
@@ -0,0 +1,26 @@
1
+ Pattern.create(options={:name => "Timing_API_Test"})do
2
+ tester.set_timeset('api_tst', 40)
3
+ $dut.timeset :api_tst do |t|
4
+ t.wave :tclk do |w|
5
+ w.drive :data, at: 10
6
+ w.drive 0, at: 30
7
+ end
8
+ t.wave :tdi, :tms, :tdo do |w|
9
+ w.drive :data, at: 5
10
+ end
11
+ t.wave do |w|
12
+ w.compare :data, at: 25
13
+ end
14
+ end
15
+ $dut.current_timeset_period = 40
16
+
17
+ $dut.jtag.reset
18
+ $dut.jtag.idle
19
+ ss "reading Halo debugger ID"
20
+ $dut.reg(:testreg).read(0x5ba00477)
21
+ $dut.jtag.read_dr dut.reg(:testreg), size: 32
22
+ $dut.jtag.write_ir 0, size: 4
23
+ ss "reading Halo JTAG ID"
24
+ $dut.reg(:testreg).read(0x1984101d)
25
+ $dut.jtag.read_dr dut.reg(:testreg), size: 32
26
+ end
@@ -4,22 +4,409 @@
4
4
  %# formatting like this, but in most cases that is not required.
5
5
  <h1><%= Origen.app.namespace %> <span style="font-size: 14px">(<%= Origen.app.version %>)</span></h1>
6
6
 
7
- ### Purpose
7
+ # Purpose
8
8
 
9
- This is a plug-in for Origen that enables live silicon debug directly from origen source.
9
+ This is a plug-in for Origen that enables live silicon debug directly from origen source. There are 2 parts to OrigenLink, the plug-in and the server. Setup for each is documented separately. The OrigenLink server is capable of being shared by multiple people working on the same project. It will lock out other users during your debug or pattern execution session.
10
10
 
11
- ### How To Use
11
+ # Plug-in
12
12
 
13
- Add the following line to your application's GEMFILE:
13
+ Instructions for integrating and using the plug-in with your app.
14
14
 
15
- * gem 'origen_link'
15
+ ## How To Import
16
16
 
17
- Add the following line to your application:
17
+ * Add the following line to your application's GEMFILE:
18
18
 
19
- * require 'origen_link'
19
+ ~~~ruby
20
+ gem 'origen_link'
21
+ ~~~
20
22
 
21
- ### How To Setup the Application Environment
22
23
 
23
- Describe how a user would setup a new workspace for this application...
24
+ * Add a link environment (./environment/link.rb). See server setup section for more information:
25
+
26
+ ~~~ruby
27
+ # Note that the field for providing the computer name or IP address is a string
28
+ OrigenLink::VectorBased.new('<ServerComputerName -- or IP_Address>', 12777)
29
+ ~~~
30
+
31
+
32
+ * Select link as your environment to run a pattern over the link connection when the origen generate command is run:
33
+
34
+ ~~~
35
+ origen e environment/link.rb
36
+ origen g pattern/my_pattern.rb
37
+ ~~~
38
+
39
+
40
+ ## How To Configure
41
+
42
+ Before OrigenLink can be used the physical pin map and timing must be setup. There are 2 supported ways of doing this. The high-level setup method allows OrigenLink to determine how to configure the server side pin sequencer. The high-level setup method is highly recommended. For cases where the high-level setup is not functioning correctly, legacy apps, or if you just enjoy doing things the hard way, a legacy low-level api is available. Both will be described here.
43
+
44
+ ### How To Configure - High-Level Method (Preferred)
45
+
46
+ This is the recomended way to setup OrigenLink in your application.
47
+
48
+ #### Physical Pin Map
49
+
50
+ OrigenLink needs to tell the server application which physical IO pin on the UDOO device (see UDOO-Neo GPIO documentation) corresponds to which DUT pin in your app. The number after 'gpio_' is what OrigenLink needs. This IO number is passed to OrigenLink through pin meta data.
51
+
52
+ ~~~ruby
53
+ add_pin :tclk, meta: {link_io: 8}
54
+ add_pin :tdi, meta: {link_io: 146}
55
+ ~~~
56
+
57
+ #### Timing
58
+
59
+ OrigenLink emulates a tester sequencer through software. The timing it implemtents will not be precise. Setting the drive edge of tdi to 5ns and tclk to 20ns will not result in tclk being driven 15ns after tdi. What will happen, though, is the sequencer will drive tdi first, followed by tclk. It is recommended that you use the built in Origen API for setting timing.
60
+
61
+ ~~~ruby
62
+ # Configure timing for jtag communication with RL tclk
63
+ tester.set_timeset('api_tst', 40)
64
+ dut.timeset :api_tst do |t|
65
+ t.drive_wave :tclk do |w|
66
+ w.drive :data, at: 10
67
+ w.drive 0, at: 30
68
+ end
69
+ t.drive_wave :tdi, :tms, :tdo do |w|
70
+ w.drive :data, at: 5
71
+ end
72
+ t.compare_wave do |w|
73
+ w.compare :data, at: 25
74
+ end
75
+ end
76
+ ~~~
77
+
78
+ #### Finished
79
+
80
+ Start your OrigenLink server (see server setup section), connect your DUT to the server device and off you go.
81
+
82
+ ### How To Configure - Low-Level Legacy API (Skip Reading This Section)
83
+
84
+ The prefered configuration method is above. These methods for configuring the server application are supported for legacy applications. This method is more prone to producing hard to debug errors during setup of a new app.
85
+
86
+ #### Low-Level Physical Pin Map Setup
87
+
88
+ Create a comma separated list of pin_name and IO# and pass it to the tester. The .pinmap method will clear all server settings. So, this should be done first. The pin name string provided to the pinmap must exactly match the pin name string provided to the pin timing and pin order methods. Any typo may result strange behavior (pin operation not occuring, timing error messages, and/or server crash). No cause for alarm, just be sure to check for consistent names if you get those problems.
89
+
90
+ ~~~ruby
91
+ tester.pinmap = 'tclk,119,tms,6,tdi,116,tdo,124'
92
+ ~~~
93
+
94
+ #### Low-Level Pattern Pin Order Setup
95
+
96
+ Pins that don't have an assiciated physical IO will not have their pin data transmitted to the link server. Create a comma separated list of the pins that are linked (only include pins that are in the pinmap) in the order that they appear in the pattern. Using this command is not needed unless you used the low-level pin map setup command AND your dut.pins(:pin_name) doesn't match the pin name that you setup using 'tester.pinmap ='.
97
+
98
+ ~~~ruby
99
+ pin_pattern_order :tclk, :tms, :tdi, :tdo # This sets the order of the pins in the pattern
100
+
101
+ # order given below must match order in pin_pattern_order
102
+ # names used below must match names used in tester.pinmap=
103
+ # the below example will cause issues because of a typo: 'tck' versus 'tclk' (tester.pinmap = 'tclk,119,)
104
+ tester.pinorder = 'tck,tms,tdi,tdo' # This tells the server what order the pins are in the pattern
105
+ ~~~
106
+
107
+ #### Low-Level Timing Setup
108
+
109
+ ~~~ruby
110
+ # pinformat=
111
+ # This method is used to setup the pin clock format on the debugger device.
112
+ # The supported formats are rl and rh
113
+ #
114
+ # example:
115
+ # tester.pinformat = 'func_25mhz,tclk,rl'
116
+
117
+ # pintiming=
118
+ # This method is used to setup the pin timing on the debugger device.
119
+ # Timing is relative to the rise and fall of a clock
120
+ #
121
+ # timing value: 0 1 2
122
+ # clock waveform: ___/***\___
123
+ #
124
+ # example:
125
+ # tester.pintiming = 'func_25mhz,tms,0,tdi,0,tdo,1'
126
+
127
+ ~~~
128
+
129
+ ### Debugging Your App
130
+
131
+ What follows are some pointers for using OrigenLink to debug your app (or pattern). Run the origen pattern generation command with debug enabled and set a break point in your code to interactively debug.
132
+
133
+ ~~~
134
+ origen g pattern/my_pattern.rb -d
135
+ ~~~
136
+
137
+ ~~~ruby
138
+ # inside pattern/my_pattern.rb
139
+
140
+ dut.reg(:MyReg).write!(my_value)
141
+
142
+ # stop after updating this register to observe device state
143
+ debugger # generation of the pattern will pause here
144
+ ~~~
145
+
146
+ #### Debugging Pin Map
147
+
148
+ There are pin methods available to aid debug of an OrigenLink setup.
149
+
150
+ ~~~
151
+ # Cause a pin to continuously toggle every 2 seconds
152
+ (debugger prompt): dut.pin(:tdi).hello
153
+
154
+ # Stop the pin from toggling
155
+ (debugger prompt): dut.pin(:tdi).goodbye
156
+ ~~~
157
+
158
+ #### Staying Synchronized
159
+
160
+ For efficiency vectors that are generated by your app are compressed and stored until the pattern generation is completed. This means that when execution reaches a 'debugger' statement the previously generated vectors may not have been sent to the server yet. There are a handful of ways to make this happen.
161
+
162
+ * Use the tester.synchronize command from the debugger interface:
163
+
164
+ ~~~
165
+ # one time synchronize
166
+ (debugger prompt): tester.synchronize
167
+
168
+ # tell debugger to evaluate the synchronize command every time it gets control
169
+ # will cause continuous synchronization
170
+ (debugger prompt): disp tester.synchronize
171
+ ~~~
172
+
173
+ * Use tester.transaction (makes the most sense to have this in your app's reg read/write methods). Before the transaction method executes the code block provided it will perform a synchronization. Then, the code in the block is executed (which generates new vectors) and a second synchronization is performed. The transaction method returns a boolean indicating whether the vectors generated by the code block passed or failed.
174
+
175
+ ~~~ruby
176
+ result = tester.transaction { dut.jtag.shift_xx (yy) }
177
+ if tester.link?
178
+ # result = true if the code in the provided block passed
179
+ end
180
+ ~~~
181
+
182
+ #### Capturing DUT Information
183
+
184
+ There are a few methods for observing the actual state of the DUT.
185
+
186
+ * Capture using tester.capture
187
+
188
+ ~~~ruby
189
+ # example of capturing and programatically using information read from the DUT
190
+ ss "reading default ID"
191
+ dut.reg(:testreg).bits(31..0).store
192
+ default_id = tester.capture {dut.jtag.read_dr dut.reg(:testreg), size: 32 }
193
+ default_id_str = default_id[0].to_s(2)
194
+ default_id_str.reverse!
195
+ default_id = default_id_str.to_i(2)
196
+ puts '**************************************************'
197
+ puts 'Captured default ID through JTAG: 0x' + default_id.to_s(16)
198
+ puts '**************************************************'
199
+ ~~~
200
+
201
+ * Capture using mem api
202
+
203
+ This command will read the contents of memory from the DUT and display it.
204
+
205
+ ~~~
206
+ # example of mem api use
207
+ (debugger prompt): dut.mem(0x2000_0000).sync(3)
208
+ 20000000: DEADBEEF
209
+ 20000004: 200000D4
210
+ 20000008: 1C000898
211
+
212
+ (debugger prompt): dut.mem(0x2000_0000).write!(0x1234567)
213
+ (debugger prompt): dut.mem(0x2000_0000).sync(3)
214
+ 20000000: 01234567
215
+ 20000004: 200000D4
216
+ 20000008: 1C000898
217
+ ~~~
218
+
219
+ * reg.sync!
220
+
221
+ This command will read the contents of the register from the DUT and display it by bit field.
222
+
223
+ ~~~
224
+ (debugger prompt): dut.reg(:MyReg).sync!
225
+ ~~~
226
+
227
+ #### When A Shared Server Session Isn't Properly Ended
228
+
229
+ The server is able to handle multiple users. Once a user connects to the server, it will only allow access from that same IP address and user name until the session has ended. The session end command is transmitted when pattern generation is completed. If a user fails to properly end their session (happens when you exit debug mode by typing 'q' instead of 'c'), the server application will continue to lock out access until the time out has expired (20 minutes). Before terminating a running session, check with the user who started the session to make sure they aren't in the middle of debug (the user id and IP address will be displayed when you try to connect). If the previous session needs to be manually terminated, this is how you do it (this should be a rare exception to the rule - it's bad manners to forcefully kill another user's session):
230
+
231
+ ~~~ruby
232
+ # run this code to forcefully terminate a session
233
+ require 'socket'
234
+
235
+ TCPSocket.open('udooneo-computer-name', 12777) do |link|
236
+ link.puts("session_end\n\n")
237
+ while received = link.gets
238
+ puts received
239
+ end
240
+ link.puts("session_kill\n\n")
241
+ while received = link.gets
242
+ puts received
243
+ end
244
+ end
245
+ puts "unlocked the server"
246
+ ~~~
247
+
248
+ ------------------------------------------------------------------------------------------------------
249
+
250
+ # Server
251
+
252
+ This section describes how to setup a new IOT device to serve the OrigenLink pin sequencer.
253
+
254
+ ## Setting Up a New UDOO device
255
+
256
+ Here are the steps to take to setup a new UDOO-Neo to run the OrigenLink server application. These instructions are not intended to be exhaustive, but should be good enough to get you up and running.
257
+
258
+ * Change the name of the computer to something unique (like udooneo-myname). This name is the string that you will enter in your environment ruby file for your app. Your computer running origen g pattern/my_pattern.rb and the UDOO should be attached to the same network (either through USB or ethernet)
259
+
260
+ ~~~
261
+ prompt$> sudo nano /etc/hostname
262
+ ~~~
263
+
264
+ ~~~ruby
265
+ # Note that the field for providing the computer name or IP address is a string
266
+ OrigenLink::VectorBased.new('<ServerComputerName -- or IP_Address>', 12777)
267
+ ~~~
268
+
269
+ * Disable the M4 core (allows access to all IO's from unix)
270
+
271
+ ~~~
272
+ menu -> preferences -> Udoo web configuration -> Advanced settings
273
+ ~~~
274
+
275
+ * Install ruby. This is a decision point. If you want to install the OrigenLink gem, the steps are a bit more complicated.
276
+
277
+ ** Option 1 (I want the gem): You need to install ruby 2.2.
278
+
279
+ ~~~
280
+ prompt$> sudo apt-get ruby22
281
+ ~~~
282
+
283
+ ** Option 2 (I'm fine using git to pull down the server code)
284
+
285
+ ~~~
286
+ # of course you're still welcomed to install a newer ruby version
287
+ # the server will run just fine
288
+ prompt$> sudo apt-get ruby
289
+ ~~~
290
+
291
+ * Install the OrigenLink server.
292
+
293
+ ** Option 1 (if you installed ruby22 you can do this). Install origen and origen_link gems.
294
+
295
+ ~~~
296
+ # follow the instructions for installing origen at origen-sdk.org
297
+ prompt$> sudo gem install origen
298
+ prompt$> sudo gem install origen_link
299
+ ~~~
300
+
301
+ ** Option 2 (This is what I do)
302
+
303
+ ~~~
304
+ prompt$> git clone https://github.com/Origen-SDK/OrigenLink.git
305
+
306
+ # if you get SSL certificate errors do this and retry the clone command
307
+ prompt$> git config --global http.sslverify false
308
+ ~~~
309
+
310
+ ## Starting the Server
311
+
312
+ If you installed Ruby 2.x and Origen, run this command to start the server:
313
+
314
+ ~~~
315
+ <command_prompt>$ start_link_server
316
+ ~~~
317
+
318
+ If you chose to clone the OrigenLink project from github, navigate to the OrigenLink directory and type this command:
319
+
320
+ ~~~
321
+ <command_prompt>$ bin/start_link_server
322
+ ~~~
323
+
324
+ The server is now running
325
+
326
+ ## Physical Interconnect
327
+
328
+ Keep the following things in mind when connecting the IO's of your UDOO Neo to a DUT
329
+
330
+ * Ground is important. As a rule of thumb, connect at least 2 ground wires between the UDOO and your DUT
331
+
332
+ * Pay attention to pin levels. The UDOO IO's are 3.3v. If your device is not also 3.3v, the most reliable and convenient way to connect the IO's is by using a bi-directional (auto-direction sensing) level shifter. This one works well and is easy to find (available on Amazon Prime at the writing of this document): TXB0108
333
+
334
+ * An alternative method for shifting a voltage down is to use a diode and pullup resistor. The anode gets connected to the higher voltage device. The cathode is connected to the lower voltage device. A 10K pullup resistor is connected from the cathode to the supply voltage for the lower voltage device. --- A word of advice: This method is quick and easy. But, you get a slow rise time which makes it a terrible way of shifting the level of a clock signal (like TCLK for JTAG or SPICLK for SPI).
335
+
336
+ ---
337
+
338
+ # How It Works
339
+
340
+ This section will explain the in's and out's of how the plug-in and server applications work and how they communicate. This section is intended to aid developers who want to add or modify features. More in depth information can be found in the api documentation.
341
+
342
+ ## Server Side App
343
+
344
+ What follows is the structure of the server side app.
345
+
346
+ ### Pin IO
347
+
348
+ Pin IO is accomplished by using the file objects exported by linux at \sys\class\gpio
349
+
350
+ ~~~
351
+ <command_prompt>$ cd /sys/class/gpio
352
+
353
+ # export the file objects for a pin
354
+ <command_prompt>$ echo 20 > export
355
+
356
+ # read the state of gpio20
357
+ <command_prompt>$ cd gpio20
358
+ <command_prompt>$ cat value
359
+
360
+ # drive gpio20 to logic 1
361
+ <command_prompt>$ echo out > direction
362
+ <command_prompt>$ echo 1 > value
363
+
364
+ # change gpio20 from output to input
365
+ <command_prompt>$ echo in > direction
366
+ ~~~
367
+
368
+ ### Pin Class
369
+
370
+ The server pin class implements IO interactions for a pin. When the pin assign command creates a pin object, it exports the associated IO number and opens IO objects for the direction and value of that pin. The IO objects are kept open until the pin is destroyed. For more information see the api documentation.
371
+
372
+ ### Pin Sequencer
373
+
374
+ The pin sequencer is the class that does all of the heavy lifting for the server side app. It implements all of the command messages that begin with "pin_". See the api documentation (Server::Sequencer) for more information. Timing is perhaps the most complicated construct to understand:
375
+
376
+ ~~~ruby
377
+ # tset is a number that corresponds to a timeset name from origen ex: 1 corresponds to 'tp0'
378
+ # @cycletiming[tset] is a hash
379
+ # each timeset contains these keys:
380
+ # 'events' - [array of timestamps for timing events]
381
+ # 'drive_event_data' - hash, the keys of the hash correspond to elements of 'events'
382
+ # - each value in the hash is an array
383
+ # - each element in the array is one of 3 values: 'data', '0', or '1'
384
+ # 'drive_event_pins' - hash, the keys of the hash correspond to elements of 'events'
385
+ # - each value in the hash is an array
386
+ # - each element in the array is an array of pin objects
387
+ # - the drive event data will be performed for each pin object
388
+ # 'compare_event_data' - similar to drive_event_data, the only valid event data is 'data'
389
+ # 'compare_event_pins' - similar to drive_event_pins
390
+
391
+ @cycletiming[tset] = {
392
+ ['events'] = [0, 5, 10, 35]
393
+ ['drive_event_data'] = {
394
+ 0: ['data']
395
+ 10: ['data','0']
396
+ 35: ['0']
397
+ }
398
+ ['drive_event_pins'] = {
399
+ 0: [[pin_obj1, pin_obj2]]
400
+ 10: [[pin1,pin2], [pin3]]
401
+ 35: [[pin4]]
402
+ }
403
+ }
404
+ ~~~
405
+
406
+ The main message supported by the sequencer is 'pin_cycle'. As the name implies, this message implements 1 or more cycles of vector data. It will return the response of the dut to the plug-in side app along with pass/fail information.
407
+
408
+ ### Server
409
+
410
+ The server serves a TCP socket at 12777. No fancy gems are used for several reasons, the main one being simplicity. Ruby has a built in socket library that is extremely simple to use. Multiple messages from the plug-in side app can be received with a single connection. "\n" indicates the end of a message. "\n\n" indicates that the packet of messages has ended. Why TCP and not UDP? I know the web says that UDP socket communication is faster. But, my testing indicated otherwise. Plus, TCP is more reliable.
24
411
 
25
412
  % end