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.
- checksums.yaml +4 -4
- data/bin/start_link_server +4 -1
- data/config/version.rb +1 -1
- data/lib/origen_link/configuration_commands.rb +161 -9
- data/lib/origen_link/server/jtag.rb +15 -0
- data/lib/origen_link/server/pin.rb +115 -18
- data/lib/origen_link/server/sequencer.rb +214 -106
- data/lib/origen_link/server_com.rb +5 -1
- data/lib/origen_link/test/top_level.rb +5 -5
- data/lib/origen_link/test/top_level_controller.rb +2 -0
- data/lib/origen_link/vector_based.rb +153 -45
- data/pattern/jtag_comm_timing_api.rb +26 -0
- data/templates/web/index.md.erb +396 -9
- metadata +6 -5
@@ -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
|
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
|
-
#
|
40
|
-
attr_reader :fail_count
|
41
|
-
|
42
|
-
attr_reader :
|
43
|
-
|
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 '
|
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
|
-
|
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
|
267
|
-
#
|
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
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
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 '
|
441
|
+
# returns 'OrigenLink::VectorBased'
|
331
442
|
#
|
332
|
-
#
|
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
|
-
#
|
345
|
-
#
|
346
|
-
#
|
347
|
-
#
|
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
|
data/templates/web/index.md.erb
CHANGED
@@ -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
|
-
|
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
|
-
|
11
|
+
# Plug-in
|
12
12
|
|
13
|
-
|
13
|
+
Instructions for integrating and using the plug-in with your app.
|
14
14
|
|
15
|
-
|
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
|
-
|
19
|
+
~~~ruby
|
20
|
+
gem 'origen_link'
|
21
|
+
~~~
|
20
22
|
|
21
|
-
### How To Setup the Application Environment
|
22
23
|
|
23
|
-
|
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
|