origen_arm_debug 0.4.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.
File without changes
@@ -0,0 +1,437 @@
1
+ module OrigenARMDebug
2
+ class SWJ_DP
3
+ include Origen::Registers
4
+
5
+ # Returns the parent object that instantiated the driver, could be
6
+ # either a DUT object or a protocol abstraction
7
+ attr_reader :owner
8
+ attr_reader :imp
9
+ attr_accessor :write_ap_dly
10
+ attr_accessor :acc_access_dly
11
+
12
+ # Initialize class variables
13
+ #
14
+ # @param [Object] owner Parent object
15
+ # @param [Hash] options Options to customize the operation
16
+ #
17
+ # @example
18
+ # # Create new SWD::Driver object
19
+ # DUT.new.arm_debug.swj_dp
20
+ #
21
+ def initialize(owner, implementation, options = {})
22
+ @owner = owner
23
+
24
+ if implementation == :jtag || implementation == :swd
25
+ @imp = implementation
26
+ else
27
+ msg = "SWJ-DP: '#{implementation}' implementation not supported. JTAG and SWD only"
28
+ Origen.log.error msg
29
+
30
+ # Just default to jtag for now
31
+ @imp = :jtag
32
+ end
33
+
34
+ @write_ap_dly = 8
35
+ @acc_access_dly = 7
36
+
37
+ @current_apaddr = 0
38
+ @orundetect = 0
39
+
40
+ add_reg :dpacc, 0x00, 35, rnw: { pos: 0 },
41
+ a: { pos: 1, bits: 2 },
42
+ data: { pos: 3, bits: 32 }
43
+
44
+ add_reg :apacc, 0x00, 35, rnw: { pos: 0 },
45
+ a: { pos: 1, bits: 2 },
46
+ data: { pos: 0, bits: 35 }
47
+
48
+ add_reg :reserved, 0x00, 32, data: { pos: 0, bits: 32 }
49
+ add_reg :ctrl_stat, 0x04, 32, data: { pos: 0, bits: 32 }
50
+ add_reg :select, 0x08, 32, data: { pos: 0, bits: 32 }
51
+ add_reg :rebuff, 0x0C, 32, data: { pos: 0, bits: 32 }
52
+
53
+ # jtag-dp only
54
+ add_reg :idcode, 0x00, 32, data: { pos: 0, bits: 32 }
55
+ add_reg :abort, 0x00, 35, rnw: { pos: 0 },
56
+ a: { pos: 1, bits: 2 },
57
+ data: { pos: 0, bits: 32 }
58
+ end
59
+
60
+ # Method to add additional Memory Access Ports (MEM-AP) with specified base address
61
+ #
62
+ # @param [Integer] name Short name for mem_ap that is being created
63
+ # @param [Integer] base_address Base address
64
+ #
65
+ # @examples
66
+ # arm_debug.add_mem_ap('alt_ahbapi', 0x02000000)
67
+ #
68
+ def add_mem_ap(name, base_address)
69
+ instance_variable_set("@#{name}", MemAP.new(self, base_address: base_address))
70
+ self.class.send(:attr_accessor, name)
71
+ end
72
+
73
+ #-------------------------------------
74
+ # DPACC Access API
75
+ #-------------------------------------
76
+
77
+ # Method to read from a Debug Port register
78
+ #
79
+ # @param [String] name Name of register to be read from
80
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
81
+ # @param [Hash] options Options to customize the operation
82
+ # @option options [Integer] edata Value to compare read data against
83
+ def read_dp(name, options = {})
84
+ options = { r_attempts: 1, mask: 0xffffffff }.merge(options)
85
+ if @imp == :swd
86
+ read_dp_swd(name, options)
87
+ else
88
+ read_dp_jtag(name, options)
89
+ end
90
+ end
91
+
92
+ # Method to read from a Debug Port register and compare for an expected value
93
+ #
94
+ # @param [String] name Name of register to be read from
95
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
96
+ # @param [Integer] edata Value to compare read data against
97
+ # @param [Hash] options Options to customize the operation
98
+ def read_expect_dp(name, edata, options = {})
99
+ options[:edata] = edata
100
+ read_dp(name, options)
101
+ end
102
+
103
+ # Method to write to a Debug Port register
104
+ #
105
+ # @param [String] name Name of register to be written to
106
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
107
+ # @param [Integer] wdata Value to written
108
+ # @param [Hash] options Options to customize the operation
109
+ def write_dp(name, wdata, options = {})
110
+ options = { w_attempts: 1 }.merge(options)
111
+ if @imp == :swd
112
+ write_dp_swd(name, wdata, options)
113
+ else
114
+ write_dp_jtag(name, wdata, options)
115
+ end
116
+ end
117
+
118
+ # Method to write to and then read from a Debug Port register
119
+ #
120
+ # @param [String] name Name of register to be written to and read from
121
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
122
+ # @param [Integer] wdata Value to written
123
+ # @param [Hash] options Options to customize the operation
124
+ def write_read_dp(name, wdata, options = {})
125
+ write_dp(name, wdata, options)
126
+ read_dp(name, options)
127
+ if @imp == :swd
128
+ cc "SW-DP: WR-32: name='#{name}', "\
129
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
130
+ else
131
+ cc "JTAG-DP: WR-32: name='#{name}', "\
132
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
133
+ end
134
+ end
135
+
136
+ #-------------------------------------
137
+ # APACC Access API
138
+ #-------------------------------------
139
+
140
+ # Method to read from a Access Port register
141
+ #
142
+ # @param [Integer] addr Address of register to be read from
143
+ # @param [Hash] options Options to customize the operation
144
+ # @option options [Integer] edata Value to compare read data against
145
+ def read_ap(addr, options = {})
146
+ rwb = 1
147
+ options = { r_attempts: 1 }.merge(options)
148
+
149
+ # Create another copy of options with select keys removed.
150
+ # This first read is junk so we do not want to store it or compare it.
151
+ junk_options = options.clone.delete_if do |key, val|
152
+ (key.eql?(:r_mask) && val.eql?('store')) || key.eql?(:compare_data)
153
+ end
154
+
155
+ apacc_access(addr, rwb, random, 0, junk_options)
156
+ read_dp('RDBUFF', options) # This is the real data
157
+
158
+ if @imp == :swd
159
+ cc "SW-AP: R-32: addr=0x#{addr.to_s(16).rjust(8, '0')}"
160
+ else
161
+ cc "JTAG-AP: R-32: addr=0x#{addr.to_s(16).rjust(8, '0')}"
162
+ end
163
+ end
164
+
165
+ # Method to read from a Access Port register and compare against specific value
166
+ #
167
+ # @param [Integer] addr Address of register to be read from
168
+ # @param [Integer] edata Value to compare read data against
169
+ # @param [Hash] options Options to customize the operation
170
+ def read_expect_ap(addr, edata, options = {})
171
+ options[:edata] = edata
172
+ read_ap(name, options)
173
+ end
174
+ alias_method :wait_read_expect_ap, :read_expect_ap
175
+
176
+ # Method to write to a Access Port register
177
+ #
178
+ # @param [Integer] addr Address of register to be read from
179
+ # @param [Integer] wdata Value to written
180
+ # @param [Hash] options Options to customize the operation
181
+ def write_ap(addr, wdata, options = {})
182
+ rwb = 0
183
+ options = { w_attempts: 1 }.merge(options)
184
+ apacc_access(addr, rwb, wdata, 0, options)
185
+ $tester.cycle(repeat: @write_ap_dly) if @imp == :jtag
186
+ if @imp == :swd
187
+ cc 'SW-AP: W-32: '\
188
+ "addr=0x#{addr.to_s(16).rjust(8, '0')}, "\
189
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
190
+ else
191
+ cc 'JTAG-AP: W-32: '\
192
+ "addr=0x#{addr.to_s(16).rjust(8, '0')}, "\
193
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
194
+ end
195
+ end
196
+
197
+ # Method to write to and then read from a Debug Port register
198
+ #
199
+ # @param [Integer] addr Address of register to be read from
200
+ # @param [Integer] wdata Value to written
201
+ # @param [Hash] options Options to customize the operation
202
+ def write_read_ap(addr, wdata, options = {})
203
+ write_ap(addr, wdata, options)
204
+ read_ap(addr, options)
205
+ if @imp == :swd
206
+ cc 'SW-AP: WR-32: '\
207
+ "addr=0x#{addr.to_s(16).rjust(8, '0')}, "\
208
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
209
+ else
210
+ cc 'JTAG-AP: WR-32: '\
211
+ "addr=0x#{addr.to_s(16).rjust(8, '0')}, "\
212
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
213
+ end
214
+ end
215
+
216
+ private
217
+
218
+ #-----------------------------------------------
219
+ # DPACC Access Implementation-Specific methods
220
+ #-----------------------------------------------
221
+
222
+ # Method to read from a Debug Port register with SWD protocol
223
+ #
224
+ # @param [String] name Name of register to be read from
225
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
226
+ # @param [Hash] options Options to customize the operation
227
+ def read_dp_swd(name, options = {})
228
+ rwb = 1
229
+ case name
230
+ when 'IDCODE' then dpacc_access(name, rwb, random, options)
231
+ when 'ABORT' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is write-only!"
232
+ when 'CTRL/STAT' then dpacc_access(name, rwb, random, options)
233
+ when 'SELECT' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is write-only!"
234
+ when 'RDBUFF' then dpacc_access(name, rwb, random, options)
235
+ when 'WCR' then dpacc_access(name, rwb, random, options)
236
+ when 'RESEND' then dpacc_access(name, rwb, random, options)
237
+ else Origen.log.error "Unknown #{@imp.to_s.upcase}-DP register name #{name}"
238
+ end
239
+ cc "SW-DP: R-32: name='#{name}'"
240
+ end
241
+
242
+ # Method to read from a Debug Port register with JTAG protocol
243
+ #
244
+ # @param [String] name Name of register to be read from
245
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
246
+ # @param [Hash] options Options to customize the operation
247
+ def read_dp_jtag(name, options = {})
248
+ rwb = 1
249
+ set_ir(name) if name == 'IDCODE'
250
+ case name
251
+ when 'IDCODE' then jtag.write_dr(random, size: 32)
252
+ when 'ABORT' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is write-only!"
253
+ when 'CTRL/STAT' then dpacc_access(name, rwb, random, options)
254
+ when 'SELECT' then dpacc_access(name, rwb, random, options)
255
+ when 'RDBUFF' then dpacc_access(name, rwb, random, options)
256
+ else Origen.log.error "Unknown #{@imp.to_s.upcase}-DP register name #{name}"
257
+ end
258
+ read_dp_jtag('RDBUFF', options) if name != 'IDCODE' && name != 'RDBUFF'
259
+ cc "JTAG-DP: R-32: name='#{name}'"
260
+ end
261
+
262
+ # Method to write to a Debug Port register with SWD protocol
263
+ #
264
+ # @param [String] name Name of register to be read from
265
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
266
+ # @param [Integer] wdata Data to be written
267
+ # @param [Hash] options Options to customize the operation
268
+ def write_dp_swd(name, wdata, options = {})
269
+ rwb = 0
270
+ case name
271
+ when 'IDCODE' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is read-only!"
272
+ when 'ABORT' then dpacc_access(name, rwb, wdata, options)
273
+ when 'CTRL/STAT' then dpacc_access(name, rwb, wdata, options)
274
+ when 'SELECT' then dpacc_access(name, rwb, wdata, options)
275
+ when 'RDBUFF' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is read-only!"
276
+ when 'WCR' then dpacc_access(name, rwb, wdata, options)
277
+ when 'RESEND' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is read-only!"
278
+ else Origen.log.error "Unknown #{@imp.to_s.upcase}-DP register name #{name}"
279
+ end
280
+ cc "SW-DP: W-32: name='#{name}', "\
281
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
282
+ end
283
+
284
+ # Method to write to a Debug Port register with JTAG protocol
285
+ #
286
+ # @param [String] name Name of register to be read from
287
+ # Supports: 'IDCODE','ABORT','CTRL/STAT','SELECT','RDBUFF','WCR','RESEND'
288
+ # @param [Integer] wdata Data to be written
289
+ # @param [Hash] options Options to customize the operation
290
+ def write_dp_jtag(name, wdata, options = {})
291
+ rwb = 0
292
+ case name
293
+ when 'IDCODE' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is read-only!"
294
+ when 'ABORT' then dpacc_access(name, rwb, wdata, options)
295
+ when 'CTRL/STAT' then dpacc_access(name, rwb, wdata, options)
296
+ when 'SELECT' then dpacc_access(name, rwb, wdata, options)
297
+ when 'RDBUFF' then Origen.log.error "#{name} #{@imp.to_s.upcase}-DP register is read-only!"
298
+ else Origen.log.error "Unknown #{@imp.to_s.upcase}-DP register name #{name}"
299
+ end
300
+ cc "JTAG-DP: W-32: name='#{name}', "\
301
+ "data=0x#{wdata.to_s(16).rjust(8, '0')}"
302
+ end
303
+
304
+ def dpacc_access(name, rwb, wdata, options = {})
305
+ addr = get_dp_addr(name)
306
+
307
+ if name == 'CTRL/STAT' && @imp == :swd
308
+ set_apselect(@current_apaddr & 0xFFFFFFFE, options)
309
+ end
310
+ set_ir(name) if @imp == :jtag
311
+ acc_access(addr, rwb, 0, wdata, options)
312
+ end
313
+
314
+ def apacc_access(addr, rwb, wdata, rdata, options = {})
315
+ set_apselect((addr & 0xFFFFFFFE) | (@current_apaddr & 1), options)
316
+ if @imp == :swd
317
+ options.delete(:w_delay) if options.key?(:w_delay)
318
+ else
319
+ set_ir('APACC')
320
+ end
321
+ acc_access((addr & 0xC), rwb, 1, wdata, options)
322
+ end
323
+
324
+ def acc_access(addr, rwb, ap_dp, wdata, options = {})
325
+ if @imp == :swd
326
+ acc_access_swd(addr, rwb, ap_dp, wdata, options = {})
327
+ else
328
+ acc_access_jtag(addr, rwb, ap_dp, wdata, options = {})
329
+ end
330
+ end
331
+
332
+ def acc_access_swd(addr, rwb, ap_dp, wdata, options = {})
333
+ if (rwb == 1)
334
+ swd.read(ap_dp, addr, options)
335
+ else
336
+ swd.write(ap_dp, addr, wdata, options)
337
+ end
338
+ options = { w_delay: 10 }.merge(options)
339
+ swd.swd_dio_to_0(options[:w_delay])
340
+ end
341
+
342
+ def acc_access_jtag(addr, rwb, ap_dp, wdata, options = {})
343
+ if !options[:r_attempts].nil?
344
+ attempts = options[:r_attempts]
345
+ elsif !options[:r_attempts].nil?
346
+ attempts = options[:w_attempts]
347
+ else
348
+ attempts = 1
349
+ end
350
+
351
+ attempts.times do
352
+ if name == 'RDBUFF'
353
+ r = $dut.reg(:dap)
354
+ if options[:r_mask] == 'store'
355
+ r.bits(3..34).store
356
+ elsif options.key?(:compare_data)
357
+ r.bits(3..34).data = options[:compare_data]
358
+ end
359
+
360
+ options = options.merge(size: r.size)
361
+ jtag.read_dr(r, options)
362
+ else
363
+ options = options.merge(size: 35)
364
+ addr_3_2 = (addr & 0x0000000C) >> 2
365
+ wr_data = (wdata << 3) | (addr_3_2 << 1) | rwb
366
+ jtag.write_dr(wr_data, options)
367
+ end
368
+ end
369
+ $tester.cycle(repeat: @acc_access_dly)
370
+ end
371
+
372
+ def get_dp_addr(name)
373
+ case name
374
+ when 'IDCODE' then return 0x0
375
+ when 'ABORT' then return 0x0
376
+ when 'CTRL/STAT' then return 0x4
377
+ when 'SELECT' then return 0x8
378
+ when 'RDBUFF' then return 0xC
379
+ when 'WCR' then return 0x4
380
+ when 'RESEND' then return 0x8
381
+ else Origen.log.error "Unknown #{@imp.to_s.upcase}-DP register name #{name}"
382
+ end
383
+ end
384
+
385
+ def set_ir(name)
386
+ new_ir = get_ir_code(name)
387
+ jtag.write_ir(new_ir, size: 4)
388
+ end
389
+
390
+ def get_ir_code(name)
391
+ case name
392
+ when 'IDCODE' then return 0b1110 # JTAGC_ARM_IDCODE
393
+ when 'ABORT' then return 0b1000 # JTAGC_ARM_ABORT
394
+ when 'CTRL/STAT' then return 0b1010 # JTAGC_ARM_DPACC
395
+ when 'SELECT' then return 0b1010 # JTAGC_ARM_DPACC
396
+ when 'RDBUFF' then return 0b1010 # JTAGC_ARM_DPACC
397
+ when 'RESEND' then Origen.log.error "#{name} is a SW-DP only register"
398
+ when 'WCR' then Origen.log.error "#{name} is a SW-DP only register"
399
+ when 'APACC' then return 0b1011 # JTAGC_ARM_APACC
400
+ else Origen.log.error "Unknown JTAG-DP register name: #{name}"
401
+ end
402
+ 0
403
+ end
404
+
405
+ def set_apselect(addr, options = {})
406
+ if @imp == :swd
407
+ addr &= 0xff0000f1
408
+ else
409
+ addr &= 0xff0000f0
410
+ end
411
+
412
+ if (addr != @current_apaddr)
413
+ write_dp('SELECT', addr & 0xff0000ff, options)
414
+ end
415
+ @current_apaddr = addr
416
+ end
417
+
418
+ # Generates 32-bit random number. Although, for pattern comparison
419
+ # it is better to used the same value so that is what is used here.
420
+ # To turn on random-ness, un-comment rand() line.
421
+ def random
422
+ # rand(4294967295) # random 32-bit integer
423
+ # 0x55555555 # completely unroll jtag data shift
424
+ 0x00000000 # compress read out jtag shifts
425
+ end
426
+
427
+ # Provides shortname access to top-level jtag driver
428
+ def jtag
429
+ owner.owner.jtag
430
+ end
431
+
432
+ # Provides shortname access to top-level swd driver
433
+ def swd
434
+ owner.owner.swd
435
+ end
436
+ end
437
+ end
@@ -0,0 +1,20 @@
1
+ Pattern.create do
2
+
3
+ ss "Test write register, should write value 0xFF01"
4
+ $dut.reg(:test).write!(0xFF01)
5
+ ss "Test read register, should read value 0xFF01"
6
+ $dut.reg(:test).read!
7
+ ss "Test bit level read, should read value 0xXXXxxx1"
8
+ $dut.reg(:test).bit(:bit).read!
9
+
10
+
11
+ $dut.read_register($dut.reg(:test))
12
+ $dut.write_register($dut.reg(:test), 0xFF02)
13
+
14
+
15
+ $dut_swd.arm_debug.swj_dp.read_dp("IDCODE");
16
+ $dut_swd.arm_debug.swj_dp.write_read_dp("CTRL/STAT", 0x50000000, edata: 0xf0000000);
17
+ $dut_swd.arm_debug.swj_dp.read_ap(0x010000FC);
18
+ $dut_swd.arm_debug.swj_dp.write_read_ap(0x01000004, 0x10101010);
19
+
20
+ end