Bare Metal Raspberry Pi 3B+: JTAG
Mar 23, 2019
19 minute read

Introduction

This is the second article of my Bare Metal Raspberry Pi series, the first was about Network Boot which I will use in this post.

There are many and much better tutorials about JTAG than I can write. It is a mature technology, used by many working in embedded development. It is normally very straightforward to use, since the embedded boards usually have a specific JTAG port, or a JTAG capability integrated into the USB port. However, Raspberry Pi has none of these, no JTAG port and no JTAG capability on USB. On the other hand, ARM, naturally, supports JTAG and JTAG capability is exposed through GPIO pins on Raspberry Pi. That is what we are going to use.

As in the previous article, I am specifically talking about Raspberry Pi 3B+ in this article.

If you look at the BCM2835 ARM Peripherals Document, on section 6.2 Alternative Function Assignments, you can see some GPIO pins have functions starting with ARM_, namely ARM_TDI, ARM_TDO, ARM_TMS, ARM_TCK, ARM_RTCK, ARM_TRST. These are the pins exposing the ARM’s JTAG functionality.

I think because of the previous generation Raspberry Pi boards, some (or maybe most) tutorials in the internet refers to JTAG GPIO pins with ALT5 functions, or a combination of ALT4 and ALT5 functions. In this post and series, I will use only ALT4 pins, that is GPIO 22-27.

Prerequisites

There are many ways to use JTAG. I think the simplest and the cheapest for our purpose is to use a cable with FTDI’s MPSSE chips. The one I specifically use is FTDI C232HM-DDHSL-0. This cable contains an FTDI FT232H chip, and it enables JTAG communication using USB, so it can easily be connected to your computer.

When you have the physical connection in place, you need a software to interact with the target board according to JTAG spec. The software I am going to use is openocd. I cloned its git repo and build it (just ./configure and make). Pay attention when you configure openocd, you should see this line at the end of ./configure output:

MPSSE mode of FTDI based devices        yes (auto)

it probably needs a few development packages (e.g. libusb).

I also have the serial console cable between RPi 3B+ and my computer.

When you connect the FTDI MPSSE cable, you will see the following in dmesg output:

[ 7536.148947] usb 1-2: new high-speed USB device number 6 using xhci_hcd
[ 7536.301521] usb 1-2: New USB device found, idVendor=0403, idProduct=6014
[ 7536.301523] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 7536.301524] usb 1-2: Product: C232HM-DDHSL-0
[ 7536.301525] usb 1-2: Manufacturer: FTDI
[ 7536.301525] usb 1-2: SerialNumber: FT1UG2NO
[ 7536.304373] ftdi_sio 1-2:1.0: FTDI USB Serial Device converter detected
[ 7536.304412] usb 1-2: Detected FT232H
[ 7536.304711] usb 1-2: FTDI USB Serial Device converter now attached to ttyUSB1

JTAG Signals

Please read at least the wikipedia article about JTAG, as I am not going to explain it in detail.

JTAG is a serial synchronous protocol, hence, it uses a clock, TCK, and two data lines TDI and TDO that can be connected to multiple devices in a daisy chain. There is also a shared control signal, called TMS. In this tutorial, and for bare-metal programming of RPi, we will use only the RPi board, no daisy chaining.

So we have the following signals in JTAG:

  • TMS (Test Mode Select)
  • TCK (Test Clock)
  • TDI (Test Data In)
  • TDO (Test Data Out)

This is the absolute minimum. In addition to these signals, there are TRST (Test Reset), SRST (System Reset) and ARM specific RTCK (Return Test Clock) signals. These are optional, you might need these or not, depending on the board, software and how you debug using JTAG.

As I said above, Rasberry Pi 3B+ expose 4 must signals (TMS, TCK, TDI, TDO) and TRST and RTCK. SRST is the normal system reset signal, it is not exposed on RPi 3B+.

Enabling JTAG on Raspberry Pi

As I mentioned, JTAG functionality is exposed in GPIO pins as alternative functions. Therefore, in order to use it, these pins has to be configured accordingly in order to be able to access ARM JTAG Debugging capability. So the options we have:

  • configure JTAG at firmware boot time
  • configure JTAG in a bare-metal application
  • configure JTAG in Linux (maybe in kernel or by configuring a driver) or as a user application in Linux

Which option to use depends on what you want to do. Because our aim is bare metal applications, either the first or second works for us, and we will use the first one.

There are a few different ways to do it, the easiest one I think is this. If you add the following directive to config.txt and reboot you will have JTAG on ALT4 pins.

enable_jtag_gpio=1

More info about this directive is here and here.

The other ways can be (I did not test these): using gpio directive in config.txt or creating an appropriate dt-blob.bin.

TFTP Contents

As explained in the previous post, I have the following files in TFTP:

  • bootcode.bin
  • f399a191/config.txt
  • f399a191/fixup.dat
  • f399a191/start.elf
  • f399a191/infloop.bin (see below to understand what it is)
  • f399a191/kernel8.img is a symlink to infloop.bin (or simply rename infloop.bin as kernel8.img)

config.txt contains:

enable_jtag_gpio=1

Keep in mind, I have enabled BOOT_UART option in bootcode.bin as I described in the previous post. The enable_uart directive in config.txt has nothing to do with logging to UART at this early stage.

kernel8.img is not a linux kernel. It is the minimum possible program you can write, since we need to boot something, otherwise the board does not come to a state that we can connect with JTAG (I am not sure yet, since also we do not know the details of firmware, what state the board is if you do now provide anything to run). The infloop.bin program is (in AArch64 assembly):

l: b l

it is just a branch (b instruction) to itself (label l), so an infinite loop.

$ ls -l infloop.bin
-rw-rw-r-- 1 ubuntu ubuntu 4 Mar 27 13:44 infloop.bin
$ hexdump infloop.bin
0000000 0000 1400
0000004

it is just 4 bytes. You do not know at this point how to compile it, so you can simply grab it from my RPi bare metal programming repo.

openocd configuration

When using openocd, we need to tell or configure the test system. When I say configure, I mean there are specific things that has to be done depending on the test hardware you are using. This part is a bit difficult, especially if you do not have expertise in the field. There are a few repos and blog posts about this, but not all are up-to-date, and the configuration is different for previous openocd versions and previous generation of Raspberry Pi boards.

Below is a config I am currently using, based on Petr Tesařík’s post Debugging Raspberry Pi 3 with JTAG. He also shows how to enable JTAG just by using Linux sysfs, I recommend you check it out.

transport select jtag

reset_config trst_and_srst

adapter_khz 1000
jtag_ntrst_delay 500

if { [info exists CHIPNAME] } {
  set _CHIPNAME $CHIPNAME
} else {
  set _CHIPNAME rpi3
}

if { [info exists DAP_TAPID] } {
   set _DAP_TAPID $DAP_TAPID
} else {
   set _DAP_TAPID 0x4ba00477
}

jtag newtap $_CHIPNAME tap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -enable
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.tap

set _TARGETNAME $_CHIPNAME.a53
set _CTINAME $_CHIPNAME.cti

set DBGBASE {0x80010000 0x80012000 0x80014000 0x80016000}
set CTIBASE {0x80018000 0x80019000 0x8001a000 0x8001b000}
set _cores 4

for { set _core 0 } { $_core < $_cores } { incr _core } {

    cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 0 \
        -ctibase [lindex $CTIBASE $_core]

    target create $_TARGETNAME.$_core aarch64 \
        -dap $_CHIPNAME.dap -coreid $_core \
        -dbgbase [lindex $DBGBASE $_core] -cti $_CTINAME.$_core

    $_TARGETNAME.$_core configure -event reset-assert-post "aarch64 dbginit"
}

It is a bit difficult to find the source for all addresses mentioned in the configuration, at the moment accept them as they are.

set _DAP_TAPID 0x4ba00477 jtag newtap $_CHIPNAME tap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -enable

TAP means Test Access Port. DAP means Debug Access Port and it is an ARM term.

ir means Instruction Register, irlen, IR length in bits, is a property of the system. ircapture specifies the data that is loaded into IR to put the JTAG system in ARM into IRCAPTURE state, think of this as the initial state. irmask is used to verify the instruction scan. expected-id is the IDCODE to be found when the JTAG chain is scanned. Here the IDCODE is defined as $_DAP_TAPID, which is 0x4ba00477.

set DBGBASE {0x80010000 0x80012000 0x80014000 0x80016000}
set CTIBASE {0x80018000 0x80019000 0x8001a000 0x8001b000}
set _cores 4

and

cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 0 \
    -ctibase [lindex $CTIBASE $_core]

target create $_TARGETNAME.$_core aarch64 \
    -dap $_CHIPNAME.dap -coreid $_core \
    -dbgbase [lindex $DBGBASE $_core] -cti $_CTINAME.$_core

CTI means Cross-Trigger Interface and it is also specific to ARM architecture. For ARMv8, CTI is mandatory and each core has one instance of it. RPi 3B+ have four cores, so the above block is repeated four times.

It creates a CTI, at the defined base at CTIBASE[core_num], and then it creates a target to be debugged (with JTAG), using the just created CTI instance, and using a debug base at DBGBASE[core_num]. Unfortunately I know this sounds not clear, but it is difficult to explain without going into details which may even be more confusing at this moment.

You also need a configuration for the cable, the one below is my modifications on https://github.com/0x6a77/KinetisOpenOcdConfigs/blob/master/scripts/interface/c232hm-edhsl-0.cfg .

interface ftdi
ftdi_vid_pid 0x0403 0x6014
ftdi_device_desc C232HM-DDHSL-0

# ftdi_layout_init <values> <directions>
# initial value:
# 0078 = 0000 0000 0001 1000
# TRST, TMS=1, all others zero
# initial direction:
# 0111 = GPIOL3=RTCK=input, GPIOL2=dontcare=output, GPOL1=SRST=output, GPIOL0=TRST=output
# 1011 = [1=TMS=output, 0=TDO=input, 1=TDI=output, 1=TCK=output]
ftdi_layout_init 0x0018 0x007b

# GPIOL0 is TRST
ftdi_layout_signal nTRST -data 0x0010

It basically tells:

  • to use ftdi driver
  • with the mentioned vid and pid of USB device
  • initialize the FTDI registers as such (to setup the JTAG pin directions, initial values correctly)
  • setup TRST pin

The setup here is very important. Nothing will work with a wrong board or cable setup. I should say I have spent a good amount of time to figure out what is the best settings (that works most of the time), the posts in the internet is also not always consistent or helpful. You can get these files in my bare-metal RPi repo.

Tutorial

Step 1 - Setup

Connect the USB serial console cable between RPi 3B+ and the computer, and with a software of your choice connect to the console (e.g. with screen /dev/ttyUSB0 115200).

Connect the JTAG cable to RPi 3B+. Please check the datasheet but for the cable I mentioned above, I have the following connections:

CableRPi Header PIN NoRPi BCM PIN/Name
Brown (TMS)1327 (ARM_TMS)
Orange (TCK)2225 (ARM_TCK)
Yellow (TDI)3726 (ARM_TDI)
Green (TDO)1824 (ARM_TDO)
Grey (TRST)1522 (ARM_TRST)

Do not connect the JTAG cable to the computer yet.

Step 2 - Reboot

Reboot RPi 3B+ to boot from network.

The following will be displayed on the console (I omit the HDMI logs):

Raspberry Pi Bootcode

USB ethernet boot
Done ARP for 192.168.97.2 got a0:8c:fd:c3:a3:bb
Read File: config.txt, 19 (bytes)

Raspberry Pi Bootcode
Read File: config.txt, 19
Read File: start.elf, 2857060 (bytes)
Read File: fixup.dat, 6666 (bytes)
MESS:00:00:31.361698:0: brfs: File read: /mfs/sd/config.txt
MESS:00:00:31.365696:0: brfs: File read: 19 bytes
MESS:00:00:41.459945:0: brfs: File read: /mfs/sd/config.txt
MESS:00:00:41.708077:0: gpioman: gpioman_get_pin_num: pin DISPLAY_DSI_POR
T not defined
MESS:00:00:41.926086:0: *** Restart logging
MESS:00:00:41.928574:0: brfs: File read: 19 bytes
MESS:00:00:51.933575:0: Failed to open command line file 'cmdline.txt'
MESS:00:01:33.374988:0: brfs: File read: /mfs/sd/kernel8.img
MESS:00:01:33.378946:0: Loading 'kernel8.img' to 0x80000 size 0x4
MESS:00:01:33.389090:0: No kernel trailer - assuming DT-capable
MESS:00:01:33.393335:0: brfs: File read: 4 bytes
MESS:00:01:43.397943:0: Failed to load Device Tree file 'bcm2710-rpi-3-b.dtb'
MESS:00:01:43.403975:0: gpioman: gpioman_get_pin_num: pin EMMC_ENABLE not defined
MESS:00:01:43.412233:0: uart: Set PL011 baud rate to 103448.300000 Hz
MESS:00:01:43.418524:0: uart: Baud rate change done...
MESS:00:01:43.421952:0: uart: Baud rate change done...

This is the last log you will see.

Step 2 - JTAG

Connect the JTAG cable to the desktop computer. On Linux, either you should use openocd with sudo, or adjust the udev rules to access the device without sudo. I am using it without sudo, because I setup the rule.

$ openocd -f c232hm-ddhsl-0.cfg -f rpi3.cfg

Open On-Chip Debugger 0.10.0+dev-00740-g380502d8 (2019-03-14-11:29)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
trst_only separate trst_push_pull
adapter speed: 1000 kHz
jtag_ntrst_delay: 500
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1000 kHz
Info : JTAG tap: rpi3.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd.), part: 0xba00, ver: 0x4)
Info : rpi3.a53.0: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.a53.1: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.a53.2: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.a53.3: hardware has 6 breakpoints, 4 watchpoints
Info : Listening on port 3333 for gdb connections
Info : Listening on port 3334 for gdb connections
Info : Listening on port 3335 for gdb connections
Info : Listening on port 3336 for gdb connections

If you see the log above, all are good. If not, there is something wrong. Check the connections again, and then again. Also, unplug and replug the JTAG cable to the computer, try restarting RPi etc. I sometimes spent plenty of time to debug the problem, and I believe the above setup is the most stable that I have almost always a successful connection. If you have the same setup, and if it does not work, please check every config and connection.

I set the adapter speed to 1Mhz, it can actually be higher but since we only use wires and not a proper connector, it is a good idea to be conservative.

As we configure in rpi.cfg that we expect it, we read the IDCODE of 0x4ba00477 through JTAG.

openocd opens the connection to the board with the above command, and then you can use telnet (or gdb) to send commands and see the output, basically you do the JTAG debugging like this with openocd.

$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
>

We can look the Debug Access Port (DAP) information first:

> rpi3.dap info
AP ID register 0x24770002
	Type is MEM-AP APB
MEM-AP BASE 0x80000000
	ROM table in legacy format
		Component base address 0x80000000
		Peripheral ID 0x01000bf000
		Designer is 0x1bf, Broadcom
		Part is 0x0, Unrecognized
		Component class is 0x1, ROM table
		MEMTYPE system memory not present: dedicated debug bus
	ROMTABLE[0x0] = 0x10003
		Component base address 0x80010000
		Peripheral ID 0x04004bbd03
		Designer is 0x4bb, ARM Ltd.
		Part is 0xd03, Cortex-A53 Debug (Debug Unit)
		Component class is 0x9, CoreSight component
		Type is 0x15, Debug Logic, Processor
	ROMTABLE[0x4] = 0x11003
		Component base address 0x80011000
		Peripheral ID 0x04004bb9d3
		Designer is 0x4bb, ARM Ltd.
		Part is 0x9d3, Cortex-A53 PMU (Performance Monitor Unit)
		Component class is 0x9, CoreSight component
		Type is 0x16, Perfomance Monitor, Processor
	ROMTABLE[0x8] = 0x12003
		Component base address 0x80012000
		Peripheral ID 0x04004bbd03
		Designer is 0x4bb, ARM Ltd.
		Part is 0xd03, Cortex-A53 Debug (Debug Unit)
		Component class is 0x9, CoreSight component
		Type is 0x15, Debug Logic, Processor
	ROMTABLE[0xc] = 0x13003
		Component base address 0x80013000
		Peripheral ID 0x04004bb9d3
		Designer is 0x4bb, ARM Ltd.
		Part is 0x9d3, Cortex-A53 PMU (Performance Monitor Unit)
		Component class is 0x9, CoreSight component
		Type is 0x16, Perfomance Monitor, Processor
	ROMTABLE[0x10] = 0x14003
		Component base address 0x80014000
		Peripheral ID 0x04004bbd03
		Designer is 0x4bb, ARM Ltd.
		Part is 0xd03, Cortex-A53 Debug (Debug Unit)
		Component class is 0x9, CoreSight component
		Type is 0x15, Debug Logic, Processor
	ROMTABLE[0x14] = 0x15003
		Component base address 0x80015000
		Peripheral ID 0x04004bb9d3
		Designer is 0x4bb, ARM Ltd.
		Part is 0x9d3, Cortex-A53 PMU (Performance Monitor Unit)
		Component class is 0x9, CoreSight component
		Type is 0x16, Perfomance Monitor, Processor
	ROMTABLE[0x18] = 0x16003
		Component base address 0x80016000
		Peripheral ID 0x04004bbd03
		Designer is 0x4bb, ARM Ltd.
		Part is 0xd03, Cortex-A53 Debug (Debug Unit)
		Component class is 0x9, CoreSight component
		Type is 0x15, Debug Logic, Processor
	ROMTABLE[0x1c] = 0x17003
		Component base address 0x80017000
		Peripheral ID 0x04004bb9d3
		Designer is 0x4bb, ARM Ltd.
		Part is 0x9d3, Cortex-A53 PMU (Performance Monitor Unit)
		Component class is 0x9, CoreSight component
		Type is 0x16, Perfomance Monitor, Processor
	ROMTABLE[0x20] = 0x0
		End of ROM table

CoreSight is the ARM technology name for debugging. As we can see above, there are two CoreSight components for each core, Cortex-A53 Debug Logic and Cortex-A53 PMU Performance Monitor (slight typo there).

Lets see the targets (that we can debug with JTAG):

> targets
    TargetName         Type       Endian TapName            State
--  ------------------ ---------- ------ ------------------ ------------
 0  rpi3.a53.0         aarch64    little rpi3.tap           running
 1  rpi3.a53.1         aarch64    little rpi3.tap           running
 2  rpi3.a53.2         aarch64    little rpi3.tap           running
 3* rpi3.a53.3         aarch64    little rpi3.tap           running

You remember these are the names we give in the rpi.cfg configuration.

> target current
rpi3.a53.3

This is the core we are currently targeting to debug. Some commands implicitly targets the current target, such as:

> reg
===== Aarch64 registers
(0) x0 (/64)
(1) x1 (/64)
(2) x2 (/64)
...
very long output showing all registers

In order to put a core into (halting) debug state (this is an ARM term, more on this later), we can issue halt.

> halt
rpi3.a53.3 cluster 0 core 3 multi core
target halted in AArch64 state due to debug-request, current mode: EL2H
cpsr: 0x000003c9 pc: 0x64
MMU: disabled, D-Cache: disabled, I-Cache: disabled

Much can be said about this output:

  • This is the core number 3 (starting from 0, so it is actually the 4th core), in cluster 0. ARM architecture groups cores into one or more clusters, and I believe there is a single cluster in BCM2837. So always cluster 0.
  • It is halted since we requested it. It is in AArch64 state. Current mode is EL2H, Exception Level 2 using SP_EL2. I will explain these in the next post.
  • cpsr is current program status register. Actually it is called PSTATE in AArch64, but openocd writes this as cpsr (as in AArch32). Actually the last 9 encodes the state and mode, 9=1001, 10=is EL2, 0=64-bit, 1=SP_ELn so SP_EL2 is in use. 3c = 0011 1100, the 1111 here is interrupt masks for Debug, System Error, IRQ and FIQ, so they are all disabled.
  • MMU is disabled, since we did not set it up, and it seems firmware does not set it up, there is no MMU right now. So we directly access the physical memory. Also, data and instruction caches are disabled.

In this state, the core is really stopped, it is not executing anything and it is not responding to any synchronous (Debug, System Error) or asynchronous (IRQ, FIQ) exceptions.

More information about caches can also be seen:

> aarch64 cache_info
L1 I-Cache: linelen 64, associativity 2, nsets 256, cachesize 32 KBytes
L1 D-Cache: linelen 64, associativity 4, nsets 128, cachesize 32 KBytes
L2 D-Cache: linelen 64, associativity 16, nsets 512, cachesize 512 KBytes

If you issue reg now, you can actually access the registers (usually core has to be halted to access the registers) (I am only showing below the ones with the values):

> reg
===== Aarch64 registers
(0) x0 (/64): 0x0000000000000050 (dirty)
(1) x1 (/64): 0x0000000080000000
...
(32) pc (/64): 0x0000000000000064 (dirty)
(33) cpsr (/32): 0x000003C9 (dirty)
...
(71) ELR_EL2 (/64): 0x4934010024AB7D60
(72) ESR_EL2 (/32): 0x28311EBB
(73) SPSR_EL2 (/32): 0x4E140FCA
...
===== Aarch32 registers
...

Dirty means it is kept in the internal cache of openocd, and you can force an actual read (or write) with the force option:

> reg x0 force
x0 (/64): 0x0000000080000003

It is not dirty anymore:

> reg x0
x0 (/64): 0x0000000080000003

Cross Trigger Interface (ARM term) registers can also be dumped:

> rpi3.cti.0 dump
     CTR (0x0000) 0x00000001
    GATE (0x0140) 0x00000000
   INEN0 (0x0020) 0x00000000
   INEN1 (0x0024) 0x00000000
   INEN2 (0x0028) 0x00000000
   INEN3 (0x002c) 0x00000000
   INEN4 (0x0030) 0x00000000
   INEN5 (0x0034) 0x00000000
   INEN6 (0x0038) 0x00000000
   INEN7 (0x003c) 0x00000000
   INEN8 (0x0040) 0x00000000
  OUTEN0 (0x00a0) 0x00000001
  OUTEN1 (0x00a4) 0x00000002
  OUTEN2 (0x00a8) 0x00000000
  OUTEN3 (0x00ac) 0x00000000
  OUTEN4 (0x00b0) 0x00000000
  OUTEN5 (0x00b4) 0x00000000
  OUTEN6 (0x00b8) 0x00000000
  OUTEN7 (0x00bc) 0x00000000
  OUTEN8 (0x00c0) 0x00000000
    TRIN (0x0130) 0x00000000
   TROUT (0x0134) 0x00000000
    CHIN (0x0138) 0x00000000
   CHOUT (0x013c) 0x00000000
  APPSET (0x0014) 0x00000000
  APPCLR (0x0018) 0x00000000
APPPULSE (0x001c) 0x00000000
   INACK (0x0010) 0x00000000

With JTAG, we can resume the execution from current program counter or from somewhere else:

> help resume
resume [address]
      resume target execution from current PC or address

or step a single instruction:

> help step
step [address]
      step one instruction from current PC or address

read from and write to memory:

> help mdw
mdw ['phys'] address [count]
      display memory words
> help mdh
mdh ['phys'] address [count]
      display memory half-words
> help mdb
mdb ['phys'] address [count]
      display memory bytes
> help mww
mww ['phys'] address value [count]
      write memory word
> help mwh
mwh ['phys'] address value [count]
      write memory half-word
> help mwb
mwb ['phys'] address value [count]
      write memory byte

dump image from or load image to memory:

> help dump_image
dump_image filename address size
> help load_image
load_image filename address ['bin'|'ihex'|'elf'|'s19'] [min_address] [max_length]

list/set/remove breakpoints or watchpoints:

> help bp
bp <address> [<asid>] <length> ['hw'|'hw_ctx']
      list or set hardware or software breakpoint
> help rbp
rbp address
      remove breakpoint
> help wp 
wp [address length [('r'|'w'|'a') value [mask]]]
      list (no params) or create watchpoints
> help rwp
rwp address
      remove watchpoint

openocd can also be used with gdb.

As a last example, lets see the code (infloop.bin) we are running, with JTAG.

First thing to know is RPi firmware loads the 64-bit kernel to 0x80000. If we dump this address:

> mdw 0x80000
0x00080000: 14000000

This is same as infloop.bin, so that is correct. Lets look at PC (program counter). Since we halted the core, and it runs an infinite loop there, its value should be 0x80000.

> reg pc            
pc (/64): 0x0000000000000064

Strange, the value is 0x64. Confusing at first, but you should remember we have 4 independent cores. So one core is running our infloop.bin, and I actually dont know what others are doing at this point. We can find which core is running infloop.bin, lets try 0 first:

> targets rpi3.a53.0
> halt
rpi3.a53.0 cluster 0 core 0 multi core
target halted in AArch64 state due to debug-request, current mode: EL2H
cpsr: 0x000003c9 pc: 0x80000
MMU: disabled, D-Cache: disabled, I-Cache: disabled
> reg pc
pc (/64): 0x0000000000080000

Here it is. The core 0 (first core) is running our code. Just for curiosity, lets see the other cores (1 and 2):

> targets rpi3.a53.1
> halt
rpi3.a53.1 cluster 0 core 1 multi core
target halted in AArch64 state due to debug-request, current mode: EL2H
cpsr: 0x000003c9 pc: 0x64
MMU: disabled, D-Cache: disabled, I-Cache: disabled
> reg pc
pc (/64): 0x0000000000000064
> targets rpi3.a53.2
> halt 
rpi3.a53.2 cluster 0 core 2 multi core
target halted in AArch64 state due to debug-request, current mode: EL2H
cpsr: 0x000003c9 pc: 0x64
MMU: disabled, D-Cache: disabled, I-Cache: disabled
> reg pc            
pc (/64): 0x0000000000000064

So, the first core (core 0) is running infloop.bin, other three cores (1,2,3) have PC at 0x64. At the moment, I am not sure what they are or were doing.

I think this is enough for an introductory tutorial. On the next post, after seeing a bit more of bare metal programming, we will use the other commands like step, resume, bp.


The best way to receive updates is