origen_arm_debug 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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