origen_link 0.2.0 → 0.3.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 +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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14d0facfcb04447ab5238b62f38ee31b4332e212
|
4
|
+
data.tar.gz: f68f21592acc3eb1dd48301cac3e220a742b789a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e162fafd6a68753e03afb20a20cd6f779b7a25a5d50473711f83a9769955453c3e1259adfedd02a9b3c36f8077fc4d9823c68ed5f802b9a8ae8c049265070010
|
7
|
+
data.tar.gz: 8fd17ea6f4eb541a880e9c526d36d265ba3fdfc075e276bfd65b6f535f785a854b615b576df5f2467df791a23d57c27535eb912758bfec2c4e48ecf3930071ee
|
data/bin/start_link_server
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
$LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'config'))
|
3
4
|
require 'socket'
|
4
5
|
require 'origen_link/server/sequencer'
|
6
|
+
require 'version'
|
5
7
|
|
6
8
|
server = TCPServer.open('', 12_777)
|
7
|
-
puts 'server started'
|
8
9
|
pinsequencer = OrigenLink::Server::Sequencer.new
|
10
|
+
pinsequencer.version = OrigenLink::VERSION
|
9
11
|
remoteuser = ''
|
10
12
|
remotehost = ''
|
11
13
|
sessionactivity = Time.now
|
12
14
|
sessionactive = false
|
15
|
+
puts "server version #{pinsequencer.version} started"
|
13
16
|
|
14
17
|
loop do
|
15
18
|
client = server.accept
|
data/config/version.rb
CHANGED
@@ -11,6 +11,10 @@ module OrigenLink
|
|
11
11
|
@pinmap = pinmap.gsub(/\s+/, '')
|
12
12
|
response = send_cmd('pin_assign', @pinmap)
|
13
13
|
setup_cmd_response_logger('pin_assign', response)
|
14
|
+
# store pins to the pinmap hash, this is used to remove non-link pins
|
15
|
+
# from the pattern in cases where the app includes too many pins
|
16
|
+
pinmap_arr = @pinmap.split(',')
|
17
|
+
0.step(pinmap_arr.size - 1, 2) { |p| @pinmap_hash[pinmap_arr[p]] = true }
|
14
18
|
end
|
15
19
|
|
16
20
|
# pinorder=
|
@@ -24,9 +28,40 @@ module OrigenLink
|
|
24
28
|
# example:
|
25
29
|
# tester.pinorder = 'tclk,tms,tdi,tdo'
|
26
30
|
def pinorder=(pinorder)
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
cmd = []
|
32
|
+
cmd << 'pin_patternorder'
|
33
|
+
cmd << pinorder.gsub(/\s+/, '')
|
34
|
+
@pinorder = cmd[1]
|
35
|
+
if @pinmap.nil?
|
36
|
+
@batched_setup_cmds << cmd
|
37
|
+
else
|
38
|
+
response = send_cmd(cmd[0], @pinorder)
|
39
|
+
setup_cmd_response_logger(cmd[0], response)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# set_pinorder
|
44
|
+
# This method is called if the app has not explicitly set the pin order
|
45
|
+
# for the Link server using pinorder= (above)
|
46
|
+
#
|
47
|
+
# Origen.app.pin_pattern_order is read to get the pin pattern order
|
48
|
+
# and pass it to pinorder=
|
49
|
+
def set_pinorder
|
50
|
+
pinlist = ''
|
51
|
+
ordered_pins_cache.each do |pin|
|
52
|
+
unless pin.is_a?(Hash)
|
53
|
+
if pin.size == 1
|
54
|
+
pinlist = pinlist + ',' unless pinlist.length == 0
|
55
|
+
pinlist = pinlist + pin.name.to_s
|
56
|
+
else
|
57
|
+
dut.pins(pin.id).map.each do |sub_pin|
|
58
|
+
pinlist = pinlist + ',' unless pinlist.length == 0
|
59
|
+
pinlist = pinlist + sub_pin.name.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
self.pinorder = pinlist
|
30
65
|
end
|
31
66
|
|
32
67
|
# pinformat=
|
@@ -36,9 +71,16 @@ module OrigenLink
|
|
36
71
|
# example:
|
37
72
|
# tester.pinformat = 'func_25mhz,tclk,rl'
|
38
73
|
def pinformat=(pinformat)
|
39
|
-
|
40
|
-
|
41
|
-
|
74
|
+
cmd = []
|
75
|
+
cmd << 'pin_format'
|
76
|
+
cmd << replace_tset_name_w_number(pinformat.gsub(/\s+/, ''))
|
77
|
+
@pinformat = cmd[1]
|
78
|
+
if @pinmap.nil?
|
79
|
+
@batched_setup_cmds << cmd
|
80
|
+
else
|
81
|
+
response = send_cmd(cmd[0], @pinformat)
|
82
|
+
setup_cmd_response_logger(cmd[0], response)
|
83
|
+
end
|
42
84
|
end
|
43
85
|
|
44
86
|
# pintiming=
|
@@ -51,9 +93,119 @@ module OrigenLink
|
|
51
93
|
# example:
|
52
94
|
# tester.pintiming = 'func_25mhz,tms,0,tdi,0,tdo,1'
|
53
95
|
def pintiming=(pintiming)
|
54
|
-
|
55
|
-
|
56
|
-
|
96
|
+
cmd = []
|
97
|
+
cmd << 'pin_timing'
|
98
|
+
cmd << replace_tset_name_w_number(pintiming.gsub(/\s+/, ''))
|
99
|
+
@pintiming = cmd[1]
|
100
|
+
if @pinmap.nil?
|
101
|
+
@batched_setup_cmds << cmd
|
102
|
+
else
|
103
|
+
response = send_cmd(cmd[0], @pintiming)
|
104
|
+
setup_cmd_response_logger(cmd[0], response)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# process_timeset(tset)
|
109
|
+
# This method will check the pin timing api for the current timeset.
|
110
|
+
# If the timset is programmed, it will be processed into the Link
|
111
|
+
# timing format, registered, and sent to the server.
|
112
|
+
# Else, a warning message will be displayed
|
113
|
+
#
|
114
|
+
# example generated command:
|
115
|
+
# send_cmd('pin_timingv2', '2,drive,5,data,tck;10,data,tdi|compare,35,data,tck,tdo;5,data,tdi')
|
116
|
+
def process_timeset(tset)
|
117
|
+
# Check to see if this timeset has been programmed
|
118
|
+
if dut.timesets(tset.to_sym).nil?
|
119
|
+
# Timset has not been programmed through the pin timing api
|
120
|
+
tset_warning(tset)
|
121
|
+
'' # return empty string on failure
|
122
|
+
else
|
123
|
+
# Timeset has been programmed
|
124
|
+
# accumulate and sort drive events
|
125
|
+
drive_str = tset_argstr_from_events(dut.timesets(tset.to_sym).drive_waves)
|
126
|
+
|
127
|
+
# accumulate and sort compare events
|
128
|
+
compare_str = tset_argstr_from_events(dut.timesets(tset.to_sym).compare_waves)
|
129
|
+
|
130
|
+
# construct the command argument string
|
131
|
+
argstr = get_tset_number(tset).to_s
|
132
|
+
if drive_str.length > 0
|
133
|
+
argstr = argstr + ',drive,' + drive_str
|
134
|
+
argstr = argstr + '|compare,' + compare_str if compare_str.length > 0
|
135
|
+
elsif compare_str.length > 0
|
136
|
+
argstr = argstr + ',compare,' + compare_str
|
137
|
+
else
|
138
|
+
fail "#{tset} timeset is programmed with no drive or compare edges"
|
139
|
+
end
|
140
|
+
|
141
|
+
# send the timeset information to the server
|
142
|
+
cmd = []
|
143
|
+
cmd << 'pin_timingv2'
|
144
|
+
cmd << argstr
|
145
|
+
if @pinmap.nil?
|
146
|
+
@batched_setup_cmds << cmd
|
147
|
+
else
|
148
|
+
response = send_cmd('pin_timingv2', argstr)
|
149
|
+
setup_cmd_response_logger('pin_timingv2', response)
|
150
|
+
end
|
151
|
+
|
152
|
+
# return the tset number from the tset hash
|
153
|
+
"tset#{@tsets_programmed[tset]},"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# tset_argstr_from_events(timing_waves)
|
158
|
+
# consume timing wave info and return command argstr format
|
159
|
+
# output: 10,data,tdi,tms;15,data,tck;25,0,tck;35,data,tms
|
160
|
+
def tset_argstr_from_events(waves)
|
161
|
+
event_time = []
|
162
|
+
event_setting = {}
|
163
|
+
event_pins = {}
|
164
|
+
waves.each do |w|
|
165
|
+
# skip any events wave that does not have any associated pins
|
166
|
+
unless w.pins.size == 0
|
167
|
+
w.evaluated_events.each do |e|
|
168
|
+
event_key = e[0].to_f # time stamp for the event
|
169
|
+
event_time << event_key
|
170
|
+
event_setting[event_key] = [] unless event_setting.key?(event_key)
|
171
|
+
event_setting[event_key] << e[1].to_s
|
172
|
+
|
173
|
+
event_pins[event_key] = [] unless event_pins.key?(event_key)
|
174
|
+
event_pins[event_key] << []
|
175
|
+
w.pins.each do |p|
|
176
|
+
event_pins[event_key].last << p.id.to_s if @pinmap_hash[p.id.to_s]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
# now sort the events into time order and build the tset argstr
|
182
|
+
event_time.uniq!
|
183
|
+
event_time.sort!
|
184
|
+
rtnstr = ''
|
185
|
+
event_time.each do |event_key|
|
186
|
+
rtnstr = rtnstr + ';' unless rtnstr.length == 0
|
187
|
+
event_setting[event_key].each_index do |i|
|
188
|
+
pins = event_pins[event_key][i].uniq.join(',')
|
189
|
+
rtnstr = rtnstr + ';' if i > 0
|
190
|
+
rtnstr = rtnstr + event_key.to_s + ',' + event_setting[event_key][i] + ',' + pins if pins.length > 0
|
191
|
+
end
|
192
|
+
end
|
193
|
+
rtnstr.gsub(/;+/, ';')
|
194
|
+
end
|
195
|
+
|
196
|
+
# tset_warning(tset)
|
197
|
+
# This method is used to display a no timing info warning.
|
198
|
+
# The warning is displayed only once per tset that is
|
199
|
+
# encountered
|
200
|
+
def tset_warning(tset)
|
201
|
+
unless @tsets_warned.key?(tset)
|
202
|
+
Origen.log.warn("No timing information provided for timeset :#{tset}")
|
203
|
+
Origen.log.warn('Default timing will be used (pin operations are in pattern order)')
|
204
|
+
Origen.log.warn('Specify timing through the timing api or by using:')
|
205
|
+
Origen.log.warn(" tester.pinformat = 'func_25mhz,tclk,rl'")
|
206
|
+
Origen.log.warn(" tester.pintiming = 'func_25mhz,tms,0,tdi,0,tdo,1'")
|
207
|
+
@tsets_warned[tset] = true
|
208
|
+
end
|
57
209
|
end
|
58
210
|
|
59
211
|
# replace_tset_name_w_number(csl)
|
@@ -2,11 +2,17 @@ require 'origen_link/server/pin'
|
|
2
2
|
|
3
3
|
module OrigenLink
|
4
4
|
module Server
|
5
|
+
# The Jtag class is not part of the OrigenLink plug-in/server ecosystem.
|
6
|
+
# It implements standard jtag protocol. It can be used to implement a
|
7
|
+
# Jtag protocol server (not included in this repository presently).
|
5
8
|
class Jtag
|
9
|
+
# The value read from TDO during the shift
|
6
10
|
attr_reader :tdoval
|
11
|
+
# Enable extra output
|
7
12
|
attr_accessor :verbose_enable
|
8
13
|
attr_accessor :anytdofail
|
9
14
|
|
15
|
+
# Create the jtag pin objects
|
10
16
|
def initialize(tdiio = 16, tdoio = 23, tmsio = 19, tckio = 26, tck_period = 0.000001)
|
11
17
|
@tdipin = Pin.new(tdiio, :out)
|
12
18
|
@tdopin = Pin.new(tdoio, :in)
|
@@ -19,10 +25,12 @@ module OrigenLink
|
|
19
25
|
@anytdofail = false
|
20
26
|
end
|
21
27
|
|
28
|
+
# not needed, no need for wait states between operations
|
22
29
|
def tck_period=(value)
|
23
30
|
@tck_half_period = value / 2
|
24
31
|
end
|
25
32
|
|
33
|
+
# close out file IO objects for the pins
|
26
34
|
def destroy
|
27
35
|
@tdipin.destroy
|
28
36
|
@tdopin.destroy
|
@@ -34,6 +42,7 @@ module OrigenLink
|
|
34
42
|
@tckpin = nil
|
35
43
|
end
|
36
44
|
|
45
|
+
# perform 1 jtag cycle
|
37
46
|
def do_cycle(tdival, tmsval, capturetdo = false)
|
38
47
|
@tdipin.out(tdival)
|
39
48
|
@tmspin.out(tmsval)
|
@@ -47,11 +56,13 @@ module OrigenLink
|
|
47
56
|
@tckpin.out(0)
|
48
57
|
end
|
49
58
|
|
59
|
+
# advance state machine to test logic reset, then return to run/test idle
|
50
60
|
def do_tlr
|
51
61
|
8.times { do_cycle(0, 1) }
|
52
62
|
do_cycle(0, 0)
|
53
63
|
end
|
54
64
|
|
65
|
+
# shift a value in on tdi, optionall capture tdo
|
55
66
|
def do_shift(numbits, value, capturetdo = false, suppresscomments = false, tdocompare = '')
|
56
67
|
@tdoval = 0
|
57
68
|
@tdostr = ''
|
@@ -98,6 +109,7 @@ module OrigenLink
|
|
98
109
|
end
|
99
110
|
end
|
100
111
|
|
112
|
+
# perform an ir-update
|
101
113
|
def do_ir(numbits, value, options = {})
|
102
114
|
defaults = {
|
103
115
|
capturetdo: false,
|
@@ -130,6 +142,7 @@ module OrigenLink
|
|
130
142
|
do_cycle(0, 0)
|
131
143
|
end
|
132
144
|
|
145
|
+
# perform a dr update
|
133
146
|
def do_dr(numbits, value, options = {})
|
134
147
|
defaults = {
|
135
148
|
capturetdo: true,
|
@@ -160,6 +173,7 @@ module OrigenLink
|
|
160
173
|
do_cycle(0, 0)
|
161
174
|
end
|
162
175
|
|
176
|
+
# traverse the pause dr state
|
163
177
|
def pause_dr
|
164
178
|
do_cycle(0, 1)
|
165
179
|
do_cycle(0, 0)
|
@@ -171,6 +185,7 @@ module OrigenLink
|
|
171
185
|
do_cycle(0, 0)
|
172
186
|
end
|
173
187
|
|
188
|
+
# traverse the pause ir state
|
174
189
|
def pause_ir
|
175
190
|
do_cycle(0, 1)
|
176
191
|
pause_dr
|
@@ -5,20 +5,31 @@
|
|
5
5
|
|
6
6
|
module OrigenLink
|
7
7
|
module Server
|
8
|
+
# The server pin class is used to perform IO using the UDOO pins
|
8
9
|
class Pin
|
9
10
|
@@pin_setup = {
|
10
11
|
in: 'in',
|
11
12
|
out: 'out'
|
12
13
|
}
|
13
14
|
|
15
|
+
# True if this pin exists in /sys/class/gpio after export. False otherwise.
|
14
16
|
attr_reader :gpio_valid
|
17
|
+
# This pin's vector data for the pattern cycle being executed
|
18
|
+
attr_accessor :pattern_data
|
19
|
+
# This pin's index in the vector data received from the plug-in app
|
20
|
+
attr_accessor :pattern_index
|
21
|
+
# This pin's response for the executing vector
|
22
|
+
attr_accessor :response
|
15
23
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
24
|
+
# Export the pin io files through the system and take control of the IO
|
25
|
+
#
|
26
|
+
# This method will execute system command
|
27
|
+
# "echo #{ionumber} > /sys/class/gpio/export"
|
28
|
+
# to create the IO file interface. It will
|
29
|
+
# set the direction, initial pin state and initialize
|
30
|
+
# instance variables
|
31
|
+
#
|
32
|
+
# Receives
|
22
33
|
# ionumber - required, value indicating the pin number (BCM IO number,
|
23
34
|
# not the header pin number)
|
24
35
|
# direction - optional, specifies the pin direction. A pin is
|
@@ -49,8 +60,94 @@ module OrigenLink
|
|
49
60
|
end
|
50
61
|
@pin_val_obj = File.open(@pin_val_name, 'r+') if @gpio_valid
|
51
62
|
end
|
63
|
+
@pattern_data = ''
|
64
|
+
@pattern_index = -1
|
65
|
+
@response = 'W'
|
66
|
+
@cycle_failure = false
|
67
|
+
@data_is_drive = false
|
68
|
+
end
|
69
|
+
|
70
|
+
# data_is_drive?
|
71
|
+
# returns whether the current pattern data is drive
|
72
|
+
def data_is_drive?
|
73
|
+
@data_is_drive
|
74
|
+
end
|
75
|
+
|
76
|
+
# data_is_compare?
|
77
|
+
# returns whether the current pattern data is compare
|
78
|
+
def data_is_compare?
|
79
|
+
!@data_is_drive
|
52
80
|
end
|
53
81
|
|
82
|
+
# cycle_failure
|
83
|
+
# returns a boolean indicating pass/fail status of this pin
|
84
|
+
def cycle_failure
|
85
|
+
if @response == 'W'
|
86
|
+
true # force failure if no operation performed
|
87
|
+
else
|
88
|
+
@cycle_failure
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# load_pattern_data
|
93
|
+
# Grab this pin's data character from the pattern data
|
94
|
+
def load_pattern_data(cycle)
|
95
|
+
if @pattern_index > -1
|
96
|
+
@pattern_data = cycle[@pattern_index]
|
97
|
+
@response = 'W'
|
98
|
+
@cycle_failure = false
|
99
|
+
if @pattern_data == '1' || @pattern_data == '0'
|
100
|
+
@data_is_drive = true
|
101
|
+
else
|
102
|
+
@data_is_drive = false
|
103
|
+
end
|
104
|
+
else
|
105
|
+
@gpio_valid = false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# process_event(event)
|
110
|
+
# perform the requested pin operation and update the response (if required)
|
111
|
+
def process_event(operation, requested_action)
|
112
|
+
if operation == :drive
|
113
|
+
if data_is_drive?
|
114
|
+
if requested_action == 'data'
|
115
|
+
out(@pattern_data)
|
116
|
+
@response = @pattern_data
|
117
|
+
else
|
118
|
+
out(requested_action)
|
119
|
+
end # requested_action == 'data'
|
120
|
+
end # data_is_drive?
|
121
|
+
end # operation == :drive
|
122
|
+
if operation == :compare
|
123
|
+
if data_is_compare?
|
124
|
+
if requested_action == 'data'
|
125
|
+
case self.in
|
126
|
+
when '0'
|
127
|
+
@cycle_failure = true if @pattern_data == 'H'
|
128
|
+
if @pattern_data == 'X'
|
129
|
+
@response = '.'
|
130
|
+
else
|
131
|
+
@response = 'L'
|
132
|
+
end
|
133
|
+
# end of when '0'
|
134
|
+
when '1'
|
135
|
+
@cycle_failure = true if @pattern_data == 'L'
|
136
|
+
if @pattern_data == 'X'
|
137
|
+
@response = '`'
|
138
|
+
else
|
139
|
+
@response = 'H'
|
140
|
+
end
|
141
|
+
# end of when '1'
|
142
|
+
else
|
143
|
+
@response = 'W'
|
144
|
+
end # case
|
145
|
+
end # requested_action == 'data'
|
146
|
+
end # data_is_compare?
|
147
|
+
end # operation == :compare
|
148
|
+
end
|
149
|
+
|
150
|
+
# Close the file IO objects associated with this pin
|
54
151
|
def destroy
|
55
152
|
if @gpio_valid
|
56
153
|
@pin_dir_obj.close
|
@@ -60,10 +157,10 @@ module OrigenLink
|
|
60
157
|
end
|
61
158
|
end
|
62
159
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
160
|
+
# Sets the output state of the pin.
|
161
|
+
#
|
162
|
+
# If the pin is setup as an input,
|
163
|
+
# the direction will first be changed to output.
|
67
164
|
#
|
68
165
|
def out(value)
|
69
166
|
if @gpio_valid
|
@@ -75,10 +172,10 @@ module OrigenLink
|
|
75
172
|
end
|
76
173
|
end
|
77
174
|
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
175
|
+
# Reads and returns state of the pin.
|
176
|
+
#
|
177
|
+
# If the pin is setup as an output, the direction will first
|
178
|
+
# be changed to input.
|
82
179
|
#
|
83
180
|
def in
|
84
181
|
if @gpio_valid
|
@@ -95,11 +192,10 @@ module OrigenLink
|
|
95
192
|
end
|
96
193
|
end
|
97
194
|
|
98
|
-
#
|
99
|
-
# description - Sets the pin direction
|
195
|
+
# Sets the pin direction
|
100
196
|
#
|
101
|
-
#
|
102
|
-
#
|
197
|
+
# Receives:
|
198
|
+
# direction - specifies the pin direction. Input is default.
|
103
199
|
#
|
104
200
|
# Valid direction values:
|
105
201
|
# :in - input
|
@@ -113,6 +209,7 @@ module OrigenLink
|
|
113
209
|
end
|
114
210
|
end
|
115
211
|
|
212
|
+
# Returns 'OrigenLinPin' + io number
|
116
213
|
def to_s
|
117
214
|
'OrigenLinkPin' + @ionumber.to_s
|
118
215
|
end
|