CST 337 Lab 1 -- Introduction to C on the PIC32MZ

During this lab we will will look at some introductory concepts related to using C for programming embedded systems. We are only going to do some basic programming in terms of simply flashing LEDs, but we can use this to introduce using C on the PIC32MZ and some basic ideas relating to how C statements relate to the generated machine code and how this can affect performance. Note that we will also use Timer2 in order to time the LED flashes, so you may want to review the section 14 in the reference material in order to brush up on PIC32 timers.

We will begin by writing a simple program to turn one (or several) of the LEDs on and off.
The HMZ144 processor board has an LED connected to RH2 (port H bit 2). The OT CSE uP1 board, on which the HMZ144 is mounted, also has 8 more LEDs -- connected to RH8 through RH15.

The Lab

1) See the MPLab setup notes section below for a refresher on setting up the MPLab software and bringing up the source editor.  Instructions on setting up MPLab on your own computer are also given at the end of the document.

MPLab setup notes:

2) The "#include <xc.h>" statement  will select the appropriate include file to provide names and addresses for the various processor resources. This is based on the device we selected in the project wizard. In our case this is C:\Program Files\Microchip\xc32\v4.30\pic32mx\include\proc\PIC32MZ-EC\p32mz2048ecg144.h .
Note, substitute PC32MZ-EF and efg144 or efm144 in the above instructions if your kit uses an EFG or EFM processor. Also do this in the following instructions.

3) Open p32mz2048ecg144.h with an appropriate editor (i.e. notepad++) but be careful not to make or save any changes! Search for LATH.  Remember that LATH is the special function register (sfr) that controls what appears on port H pins. Note how it is defined as an "extern volatile uint32_t". The "extern" means its address is defined elsewhere (in the "sfrs" section). The "volatile" descriptor means that the contents of the SFR may be changed in ways the compiler cannot be aware of (which is appropriate for sfrs since they are attached to I/O devices). Assuming the include file is accurate, the resource names defined here should be the same as those used in the Family Reference Manual and Datasheets. Note how a typedef of a union of two bit field structs is defined for LATHbits. Thus, LATHbits.LATH0 can be used to reference bit 0 of the port H latch and LATHbits.w can refer to all 32 bits -- as can LATH alone. Also note how similar definitions are provided for PORTH and TRISH and how definitions are provided for LATHSET, LATHCLR and LATHINV and the set clear and invert addresses for TRISH and PORTH etc.
Note that many of the peripherals, including port H, are actually 16-bit devices (to be backward/hardware compatible with earlier 16-bit PICS) but they are still defined as 32-bit unsigned ints. There is no issue with this on this processor since all device addresses are aligned to 32-bit addresses and with a 32-bit bus, there is no loss of performance in writing or reading an aligned 32-bit value as opposed to a 16-bit value. (We will talk more about alignment later on in lecture.)
(By the way, while bitfields are appropriately defined in p32mz2048ecg144.h, such definitions should generally be avoided in your own code. The order of bitfields (LSB first or MSB first) is hardware dependent, so code with bitfields in it is not portable. However, p32mz2048ecg144.h is obviously used for specific hardware and so, by definition it is not intended to be portable!)

4) Search further down p32mz2048ecg144.h for .extern LATH. This statement is further needed by the linker to resolve the external address for LATH. Note how its address is shown using a comment. Note that the comment does not define the address, it is just there for our convenience. Make a note of the LATH* addresses for your future reference in this lab. The address assignment is actually done in an assembly file (...\xc32\v4.30\pic32mx\lib\proc\32mz2048ecg144\p32MZ2048ECG144.S). The ultimate authority for SFR addresses is the Datasheet.

5) Though I'm fairly sure all SFRs have already been given symbolic names, assume that one wasn't defined, or that it was defined wrongly. How could we access it? (Also note that the  development environments for other processors may not provide these resources.)   Consider the following two approaches to defining and referencing mylatH --
a)
volatile unsigned int * mylatH;

mylatH = (volatile unsigned int *) 0xBF860730; /* setting up the address */
*mylatH |= 1;        /* setting bit 0 of LATH */

b)
#define mylatH (* (volatile unsigned int *) 0xBF860730)
mylatH |= 1;        /* setting bit 0 of LATH */

Note how the second approach should result in less code and storage space than the first approach (it doesn't need space for a pointer and doesn't need to initialize it), though both may ultimately end up generating the same amount of code, if compiler optimization is available and turned on.  However, we won't need either of these since the LATH SFR is already defined for us in p32mz2048ecg144.h and p32MZ2048ECG144.S.

6) Search down further in p32mz2048ecg144.h for _LATH_LATH0_MASK and note how mask positions and lengths are defined for all the LATH bits.

7) Finish writing the program to toggle an LED bit. First, use code like:
        LATH  |= _LATH_LATH0_MASK;  // Set the LED
        LATH &= ~_LATH_LATH0_MASK; // Clear the LED
to toggle the LED (assuming we wanted to toggle a hypothetical LED on bit 0 -- you will want to toggle a different bit because there isn't actually an LED tied to port H bit 0!  (The port H bits that do have LEDs wired to them are noted at the top of the Lab). Structure your program to toggle the LED in an infinite while loop. We will first test it by using the simulator and a watch on the port containing the LED.
Note that you will have to configure the port pins (and only the port pins) connected to the LEDs for output by writing the appropriate value to the TRISH sfr.  You don't want to inadvertently configure a port pin for output that is already connected to the output of another device, creating a conflict and potentially damaging either the other device or the PIC32. You might also want to turn off the analog input functionality of the pins used for LEDs via the ANSELH sfr. (Note that only PortH bits 0,1, 4, 5 and 6 have analog input capability).  Doing this is not strictly necessary in actual hardware, but in an actual system it would lower the power used by the LED pins. In past years, for some students, the version of the Simulator being used at that time did not show PORTH bits toggling unless they also turned off analog input functionality. Let me know if you see this happen with the version of MPLab X we are currently using.

8) Select the Simulator tool from the Hardware tools pane in the Project Properties window (File > Project Properties or Right click on the project name in the Project window and select Properties or the wrench button in the Dashboard). Next, compile and debug your code (Debug Project icon). Note that first setting the "Debug Startup" option to "Main" as described in the MPLab setup section above will cause the Debug Project Icon to stop execution at the first instruction in main rather than proceeding directly to running (simulating in this case) your code. If you didn't do this, then your code is now running (simulating). If this is the case, clicking the pause button will stop your code and highlight the line in your code that will be executed next (assuming the program hasn't already exited). The reset button will (by default) reset the device to the beginning of main.

9) Note (as seen in the output > (Build, Load, ...) window) that during the build, all .c files added to the project are compiled to object (.o) files using xc32-gcc. Then xc32-gcc is used to link all the object files just created and any that were added to the project directly (these would appear in the object files list of the Project window). It also searches libraries for any library routines that were used in the project. Both libraries explicitly added to the project (Library Files in the Project window) and standard libraries (see the gcc command line in the output window) are linked. The linker is guided by the default linker script "...\xc32\v4.30\pic32mx\lib\proc\32mz2048ecg144\p32mz2048ecg144.ld". This file contains the reference to processor.o fie (compiled from the p32MZ2048ECG144.S file) that contains the sfr address definitions as well as memory region (i.e. Flash and SRAM) sizes and interrupt and exception vector addresses. Should we need to provide additional instructions to the linker this can be done through a .ld file added to the project (in the Linker Files section of the Project window). Finally a binary format file (.elf format) is produced and loaded.

10) Open the Watch window (Window > Debugging > Watches) and right click in the window to add a new watch or do Debug > New Watch) and add the PORTH SFR. Single step through the LED set and clear statements to verify that the bit(s) are being properly set and cleared. Note that you can also right click on a variable or SFR reference in your code and select "New Watch".

11) Open the disassembly window (Window > Debugging > Disassembly) and observe the code that was generated for the LED "set" and "clear" statements. When you first open the disassembly window, it does not usually properly sync the display to the current value of the PC (shown towards the upper right side of the icon bar). The quickest way to sync the listing to the PC is to do a "Step Into", but this will also execute the first assembly instruction at the current PC.
Note how "Step Into" stepping when the source window is highlighted steps C instruction lines, while "Step Into" stepping while the disassembly window is highlighted steps assembly level instructions. "Step Over" stepping, while the disassembly window is highlighted, steps C instruction lines.
Observe how the LED "set" statement was implemented in the disassembly and copy this section of the disassembly listing for your report

Be careful to include only the instructions that implement the C LED "set" statement in your copy.  Note that the Disassembly window sometimes shows duplicate instructions and instructions out of sequence. You can catch this by paying attention to the instruction addresses which would normally go up by 4 for each instruction.
(You can also see a disassembly listing using Window > Debugging> Output > Disassembly Listing File, but this listing is not correlated to C instructions.)

Go through the assembly instructions that were generated to implement the C LED "set" statement so that you understand how it works, noting what SFR addresses are read from or written to as well as what value is written. SFRs are mapped in the memory space of the processor so they would be accessed using load (l[whb][u]) and store (s[whb]) instructions. (Also note that lui is NOT a load instruction!!  It does not read from an address in memory!) 

Since load and store instructions use offset addressing, to find what addresses are read and written, you will need to add the sign-extended offset in the instruction to the contents of the address register used by the instruction.  You can observe the contents of the address register by setting a watch on that register (i.e. often v0) and stopping the code just before the load or store instruction is executed.
(Note that Window > Target Memory Views > Execution Memory also shows the binary encoding of the instructions which can sometimes be helpful in this process if you remember what part of the I-type instruction format used for loads and stores holds the offset.)

12) Change the C LED "set" statement to one that looks like LATHbits.LATH0 = 1. Then rebuild and debug your code (to make sure it works). Observe how the set statement was implemented in the disassembly and copy this portion of the disassembly listing for your report. Is the implementation substantially different than what you saw in step 11? How many instructions are used and how many times is an SFR is  referenced (i.e. read or written with load or store instructions) in each case (step 11 and step 12)? 
Record the answers to these questions for your report.
Note: make sure that you always stop the debugger before making code changes and running a new debug session, or you may end up getting bad results! You should almost never have more than one "(Build, Load, ...)" output window active in MPLab X.
Also note that you will have to re-open the disassembly window since the debugger stop will close it. This is to make sure the disassembly stays in sync with the source.

13) Change the C LED set statement to one that looks like LATHSET = _LATH_LATH0_MASK. Debug your code (to make sure it works). Observe how the set statement was implemented in the disassembly and copy this portion of the disassembly listing for your report. Is the implementation different than what you saw in steps 11 and 12? What are the differences and why? How many instructions are used and how many times is an SFR referenced in this case?  Record the answers to these questions for your report.

14) Note that LATHINV =_LATH_LATH0_MASK can be used to perform both the LED "set" and "clear" functions. Modify your code to use a single LATHINV write per pass of the LED toggle loop and use this approach from this point forward.

15) Now add a delay based on polling Timer2 to your infinite loop to allow the LED to be on for 1/2 second and off for 1/2 second. Assume an 84MHZ system clock (SYSCLK) and a 21MHZ peripheral bus 3 clock (PBCLK3 -- which clocks the MZ timers). That is, we are assuming that there is a divide by 4 between the SYSCLK and PBCLK3.  Note that in the PIC32MZ, SYSCLK clocks the processor core and PBCLK3 is the clock source for the timers and that the PBCLK3 is derived from the SYSCLK via a divider that can be set to divide by 1 through 128. Refer to the PIC32MZ Reference manual section 14 as needed to set up Timer2. Note that Timer2 is a type B timer. Configure it for 16-bit un-gated operation with an appropriate prescale value. You will have to pick a prescaler just big enough so that the resulting clock frequency doesn't cause the timer to roll over (reach 2^16 counts before 1/2 second has elapsed. Then determine, using the prescaler value you have chosen, what count TMR2 will reach at 1/2 second of elapsed time.

Note that microchip recommends first turning off the timer by setting the TON bit in T2CON to 0, and setting the initial values of TMR2, PR2 and other bits in T2CON as needed, then separately setting TON to start the timer.

As an example, to initialize T2CON, use an approach like the following:

// Set T2CON for a prescale value of 5 (1:32) along with setting the T32 bit
// configuring it for 32 b
it mode all other bits including TON are 0 (so it will also
// turn T2 off)
T2CON = ((5 << _T2CON_TCKPS_POSITION) & _T2CON_TCKPS_MASK) | _T2CON_T32_MASK;

(Note that the above approach is much more self documenting than T2CON = 0x0058  and it is my preferred style!)

Note that your settings will need to be different than the ones shown here!  You won't need 32-bit mode, and the prescaler will need to be different.

Many students think that they should use a T2CONSET assignment to turn bits on and T2CONCLR assignment to turn bits off, because they have learned that this approach only sets or clears the bits that they want without changing any other bits. And, while this would be true in the body of the code, it is not needed at initialization!  At initialization we want to set the state of all the control bits. A simple assignment to T2CON is usually much more readable and reliable for this purpose.

Be careful with the relative precedence of bitwise logical and shift operators and any comparison operators!

Note that polling for a set timer 2 interrupt flag (T2IF) is generally a safer approach than polling for a specific TMR2 value.  When timer 2 reaches the value in PR2, it sets T2IF and automatically resets TMR2 to 0 and begins counting again.  In the PIC32MZ, T2IF is held in the IFS0 register (in bit 9) and can be tested by ANDing IFS0 with the _IFS0_T2IF_MASK. Also note that just because we are using an interrupt flag does not require us to enable or use interrupts!

16) If you attempt to simulate the adjusted code with a 1/2 second delay in it, single stepping the delay loop is out of the question. Even setting a breakpoint after the delay loop and running to that point will take a very long time since the simulator does not run in real time! Temporarily reduce the delay (lower the value in PR2 a lot), and set a breakpoint on the LED toggle statement to make sure your modified code is getting through the delay loop.
(Breakpoints can be set by single clicking on the line number to the right of the line where you want to set the breakpoint.)

17) Before going on, set the delay back to what it needs to be for 1/2 sec. between LED toggles. Then, consider how we might check, using the simulator, that the delay is correct. Open the StopWatch tab (Window > Debugging > StopWatch). Note that the stopwatch records the number of SYSCLK cycles between breakpoints. Run to your breakpoint several times. (Note that with the delay set for 1/2 sec., it may well take a long time for the simulator to reach the breakpoint.)
a) Run one more pass of the loop and record the number of SYSCLK cycles it takes to execute one pass of the loop (don't concern yourself with the reported time values, only the cycle count). 
b) Record the PR2 value and the prescaler value that your code is currently using for your report.
c) Assuming  a 21MHz PBCLK3, about how many PBCLK3 cycles do you expect to elapse over one pass of the timing loop (which should be 1/2 sec.)? Alternately you can determine exactly how many PBCLK3 cycles there should be using your actual PR2 and prescaler values.
d) Assuming an 84MHz SYSCLK, about how many SYSCLK cycles do you expect to elapse over one pass of the timing loop (1/2 sec)?
e) Using this information, and the number of SYSCLK cycles you actually measured using the stopwatch in part a) above, determine what the actual clock division is between the measured SYSCLK cycles and the expected PBCLK3 cycles for the current code configuration (according to the simulator).
Record  your data, work and your answers to these questions for your report. Make sure you show your work in your report. (Note that the time values shown in the Stopwatch window are based on a simulator configuration setting in project properties that defaults to a 1MHz clock cycle. This configuration value can be changed, but it is often simpler just to work from the cycle counts and ignore the reported time values.)

18) In the PIC32MZ, the PBCLK3 divider is controlled by the PB3DIV register.  By default, PB3DIV is set to divide the system clock by 2. To change it so it divides by 4, add the following code to your program above the code that sets up timer 2.

    SYSKEY = 0; // Ensure lock
    SYSKEY = 0xAA996655; // Write Key 1
    SYSKEY = 0x556699AA; // Write Key 2
    PB3DIV = _PB3DIV_ON_MASK | 3 & _PB3DIV_PBDIV_MASK; // 0 = div by 1, 1 = div by 2, 2 = div by 3 etc up to 128
    SYSKEY = 0; // Re lock

Note that the PIC32 architecture wants to make sure that we don't accidentally modify peripheral clocks. This is the reason behind the SYSKEY statements above. In order to modify PBxDIV registers (along with several other registers), they must first be unlocked. Unlocking is done by writing Key1 followed by Key2 to the SYSKEY register.  Exactly how soon Key2 must be written following Key1 is not precisely defined (anywhere that I can find) in the documentation, but the manuals indicate that doing this with back to back C statements is sufficiently fast, but that if we were to put another C statement between the Key 1 and Key2 writes, it may no longer work. Writing any other value to SYSKEY will lock the registers again. Writing 0 to SYSKEY before writing Key 1 helps to make sure that an unlock sequence wasn't already partially started in previous code. Also note that the unlock sequence should not be run when the sequence could be interrupted. In our case, interrupts have not been enabled, so this is not an issue.

19) Once the above code has been added, rebuild and re-run the code. Ensure that the PB3DIV register has its ON bit set and its PBDIV field set to 3. A quick way to do this when the debugger is paused at a point the the code after the PB3DIV assignment statement is to hover the mouse over the PB3DIV text in the code (this is referred to as "bitfield mouseover" in MPLab X).  Now, repeat the stopwatch measurement.  Did the measurement change from what you found in part 16? 
Now, comment out the write of Key1 and Key 2 to SYSKEY, and rebuild and re-run the code. Was PB3DIV modified according to the simulator?   Should it have been?  What two things does this tell you about the simulator?
Record your stopwatch measurement and your answers to all these questions for your report.
 
20) Before we run the code in actual hardware, we need to take care to also configure the system bus clock. The PIC32 processor is capable of running with many different clock configurations, but the initial clock configuration must be determined before the processor boots up since the processor needs a clock in order to do anything.  In the PIC32 the initial clock configuration along with other initial settings are controlled by the bits in DEVCFGn registers. These values in these bits are determined when the device is programmed (specifically when the boot flash memory is programmed).  MPLab X has a default set of values that it will use to configure the PIC32MZ.  The Configurations Bits window (Window > Target Memory Views > Configuration bits) shows the current configuration bits settings, and could be used to see what the default settings are at this point. Unfortunately, the default settings are not compatible with our system.

21) The values to program in the configuration registers can be set by #pragma config statements in the code itself. This is a good idea since clock (and other initial) settings are often integral to a project and not having these settings be recorded in the source code can lead to problems down the road. We only need to use #pragma configs for configuration bit values that are different than the MPLab X defaults.

// PIC32MZ2048ECG144, EFM144 or or EFG144 based HMZ144 board Configuration Bit Settings
// DEVCFG2
#if defined(__32MZ2048EFG144__) || defined(__32MZ2048EFM144__)
#pragma config FPLLIDIV = DIV_4         // System PLL Input Divider (4x Divider) for 24MHz clock (Rev C1 board w EFG) 24MHz/4 = 6MHz
                                        // also 24MHz clock rev C board w EFM (weird - went back to C. rev D also is EFM but with Osc)
#pragma config UPLLFSEL = FREQ_24MHZ    // USB PLL Input Frequency Selection (USB PLL input is 24 MHz)
#else
#pragma config FPLLIDIV = DIV_2         // System PLL Input Divider (2x Divider) for 12 MHz crystal (Rev B and C boards w ECG) 12MHz/2 = 6MHz
#pragma config UPLLEN = OFF             // USB PLL Enable (USB PLL is disabled)
#endif
#pragma config FPLLRNG = RANGE_5_10_MHZ // System PLL Input Range (5-10 MHz Input)
#pragma config FPLLICLK = PLL_POSC      // System PLL Input Clock Selection (POSC is input to the System PLL)
#pragma config FPLLMULT = MUL_112       // System PLL Multiplier (PLL Multiply by 112) 6MHz * 112 = 672MHz
#pragma config FPLLODIV = DIV_8         // System PLL Output Clock Divider (8x Divider) 672MHz / 8 = 84MHz

// DEVCFG1
#pragma config FNOSC = SPLL             // Oscillator Selection Bits (Primary Osc (HS,EC))
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disable SOSC)
#if defined(__32MZ2048EFG144__)
#pragma config POSCMOD = EC             // Primary Oscillator Configuration EC - External clock osc
                                        // Rev C1 board w EFG uses an Oscillator (Rev D boards too))
#else
#pragma config POSCMOD = HS             // Primary Oscillator Configuration HS - Crystal osc
                                        // Rev B and C (w ECG or EFM) use Crystals
#endif
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disabled, FSCM Disabled)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled)
#pragma config FDMTEN = OFF             // Deadman Timer Enable (Deadman Timer is disabled)
#pragma config DMTINTV = WIN_127_128    // Default DMT Count Window Interval (Window/Interval value is 127/128 counter value)
#pragma config DMTCNT = DMT31           // Max Deadman Timer count = 2^31

// DEVCFG0
#pragma config JTAGEN = OFF             // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx2        // ICD/ICE is on PGEC2/PGED2 pins (not default!)


Add the above pragmas before the first #include in your code and then re-make it. Open the Configurations Bits window (Window > Target Memory Views > Configuration bits) and verify several of the above settings.
(Note that changes can be made in the configurations bits window (in the Options column), but that doing so does not automatically make changes to the pragmas in your code. However, the "Generate Source Code to Output" button in this window can create a set of pragmas that you can cut and paste into your source code.)
In the above, we only included pragmas for configuration bits that were different from the MPLab X defaults, and this is the approach that we will use in lab. However, it should be noted that it would be better in an industrial setting to include pragmas for all the configuration bits, since using our approach would rely on new versions of MPLab X to always have the same defaults.

For the ECG144 processor, The POSCMD, FNOSC and FPLLICLK pragma bits put the Primary oscillator in HS mode (means the Primary oscillator is driven by an external crystal) and connects the primary oscillator to the system PLL.  The external crystal on the ECG based HMZ144 board has a frequency of 12MHz.  The external crystal is then divided by 2 (FPLLIDIV).  The FPLLRNG bits configure the PLL to operate with an input clock between 5 and 10 MHz (6 MHz in our case). The FPLLMULT bits set the PLL to multiply its input clock by 112, which produces a 672MHz clock at the PLL output. This value is then divided (FPLLODIV) by 8 to produce an 84MHz system clock.  The other bits set the correct ICD channel, disable the secondary oscillator, USB PLL and the watchdog and deadman timers etc.
For the EFG144 and EFM144 processors the settings are similar, but differ mainly where needed to account for the fact that the EFM based HMZ144 board uses a 24MHz external crystal and the EFG based HMZ144 board uses a 24MHz external clock chip rather than a 12MHz external crystal.


22) Before going to actual hardware we will experiment with the Simulator's Logic Analyzer.
Show the result to the professor. Note: MPLab X version 5.35 broke the trace and logic analyzer features.  This appears to be fixed in our version (5.50 and later). Let me know if you have problems.

23) Stop the debugger, and close the Logic Analyzer window. Make sure the configuration bits are set as indicated in part 20. Connect up the Real ICE (or ICD 4) emulator and the target hardware following instructions given by the professor.  Remember to connect the ICE to the target board (if it isn't already), then power up the ICE before powering up the target hardware (and when shutting down, remember to power down the target hardware before powering down the ICE). Also, make sure to power the target board from the standalone USB power supply.  Don't use a PC USB port to power the target board.  Select the Real ICE (or ICD4 if that is the debugger in your kit) as the debugger hardware.  Reset your timer delay (PR2) to the right value for a 1/2 sec delay.  Re-build your project and program the target hardware.  Reset and run.  Observe that the LED(s) toggle as required.   Make sure the timing is correct -- that is, make sure that the LED flashes about once each second. Debug as needed. Use the debugger to check to make sure that PB3DIV has the right value. Demonstrate your running program.

24) Verify that the unlock sequence must be executed in order to change the PB3DIV value. Record how you did this and your conclusion for your report.

25) Use the Stopwatch to time one iteration of the timing loop. When the Real ICE or ICD 4 is the debugger, there must be two breakpoints for the stopwatch to function. One will start the stopwatch and the other will stop it. Set the first breakpoint on the instruction just after the LED toggle and the second breakpoint on the LED toggle instruction itself, with the intent to time from one LED toggle to just before the next one, which would be close to the time between toggles. (If there isn't an instruction after the LED toggle instruction, you may need to rearrange your code or add a dummy instruction).  Using the properties button in the Stopwatch window, select the breakpoint on the instruction after the toggle for the start condition and the other for the stop condition. Also, uncheck the "Start condition will cause the target device to halt" option. 
Note that the Real ICE and ICD 4 stopwatch counts SYSCLK cycles, like the simulator stopwatch, but unlike the simulator stopwatch, it is a 24-bit timer so it may roll over when timing this loop. (I'm not sure how many bits are available in the simulator's stopwatch, but it appears to be more than 32.)
Record your work, results and answers, for your report.

26) In the breakpoint tab (Window > Debugging > Breakpoints) right click on a breakpoint and select Customize. Then experiment with the "pass count" options. What does this do?  Record your answer for your report.

27) Debug > New breakpoint dialog (or right click in the Breakpoints Tab) allows you to set breakpoints on Data (data memory accesses), Address (instruction or data reference to an address) or various other system events. Right clicking on a variable name in the code window also lets you set data breakpoints. Experiment with data breakpoints to see if they can be set and work for both global and local variables and for SFRs. Record the results for your report.
Observe the disassembly window while running to these breakpoints along with choosing "break on read", "break on write" and "break on read or write" options for them.
Note that with local variables you can't use the right click option to set them. You will need to use the breakpoint dialog to do this. You will have to manually supply the local variable's address which you can get by hovering over the variable in the code window, or through the watch window when debugging.  To get the correct address requires that the variable be "in scope" meaning that you have to be inside the routine that declares it (and in this version of MPLab X, possibly even at or below the first point in the code where it is used).  Also, do not type anything in the Symbol box in the set breakpoint dialog for local variables!   Be careful whenever you re-compile since the variable's address can change if the code is modified, but the data breakpoint would still have the old address!
With SFRs, not all can be selected by name via the Data breakpoint dialog (i.e. T2CONSET or LATHINV) but breakpoints for these can be set (as with local variables) via an explicit address.
Also note that some global variables may be written to by startup code that runs before main begins.

You can also set Sequential breakpoints (run till the first breakpoint in a sequence is triggered, then look for the next breakpoint) and Anded breakpoints (break only when a code breakpoint and a data breakpoint are triggered) through the Breakpoints dialog. This is accomplished through right clicking on a breakpoint in the breakpoints tab and selecting "Complex Breakpoint".

28) What is the difference between how software and hardware breakpoints are implemented? You can use the Real ICE user's guide on the class webpage for information about this. Also, how many software breakpoints, how many hardware data breakpoints, and how many hardware instruction breakpoints are available in the PIC32MZ under Real ICE? (Give a separate number for each category.)  You can find some of this information on the Dashboard window under Debug Resources.  Software breakpoints can be enabled under Project Properties > Real ICE > Debug Options or with the resources button in the Dashboard. Why, as reported in the Real ICE User's guide, would using software breakpoints impact "device endurance"? Include your answers in your report.
Note that, at least in versions 5.xx of MPLab and forward, the Real ICE and ICD 4 provide no support for software breakpoints in our PIC32MZ processors. I am not sure if this is because that support has not been developed yet for the MZ processors, or maybe Microchip has decided that the MZ processors provide for enough hardware breakpoints so that software breakpoints are not needed.  Answer these questions assuming that software breakpoints were supported.

29) Power down the target board, then the Real ICE (or ICD4) and return to the simulator. The Simulator's Trace function (that was used to do the logic analyzer earlier) is used primarily to allow code execution to be traced. Turn tracing on as described in the first bullet in item 21 above. You might also want to shorten the delay loop. Run one pass of the loop and look at the results in the Trace window (Window > Debugging > Trace). (Note that right clicking on the trace window provides an option to clear the trace buffer). By examining the trace window, determine what each of the SA, SD, S2A, S2D, DA and DD headings in the Trace window likely mean.  Record your answer for the report.  Note that the Real ICE can also trace, but extra hardware setup is required to support this.


Report

A formal report is not required, simply turn in the following items.
Setting up MPLab on your own computer
Versions 6.00 of MPLAB X and version 4.30 of the XC32 compiler are used in the lab.  It would be best to use these same versions on your own machine.  Previous and subsequent versions have bugs and issues that may not be covered in lab instructions.