CST 337 Lab 4 -- Memory Allocation
and Initialization
In this lab, we will examine some of the issues related to memory
allocation and initialization in the PIC32 and C. We will only need to use the simulator for this
project.
The Lab
1) Start a new project, and enter the following code (note that you
should be able to "cut and paste"):
// Memory Allocation and Initialization Test
#include <xc.h>
#include <string.h>
const char a[] = "CST 337 Lab
4";
char b[]
= "Initialized Global Var";
const char c[] = "Initialized
Constant String";
char d[100]
= "Initialized String Array";
unsigned int gc = 0; //global
initialized to 0
unsigned int gd; //global
uninitialized
unsigned int ge = 0x45;
//global initialized to 0x45
int main(void)
{
unsigned int
ic = 0x0F1E2D3C;
unsigned int id = 1;
unsigned int ie;
const unsigned int icf = 0x98765432;
static unsigned int isf = 0x67452301;
static const unsigned int iscf = 0xABCDEFFE;
unsigned long long Lc = 0x4B5A69788796A5B4LL;
static const unsigned long long Lcc = 0xFEDCBA9876543210LL;
unsigned long long Ld = 1;
unsigned long long Le;
char la[] = "Local String Test";
ie = 0x19283746;
ic = icf;
ic = isf;
ic = iscf;
id = gc;
Le = 0x0123456789ABCDEFLL;
Le = Lcc;
Le = Lc;
strcpy(d,b); // Copy the b string into the d string
strcpy(d,a);
strcpy(d,c);
strcpy(b,c);
}
2) Build and (if necessary) debug the code. Open the Watch window
and add the sp, fp and gp registers. (Note that
the fp register
is also known as s8). (Note that I have seen some debug sessions
where the watch window wasn't updating registers properly. In this
case, the "CPU Registers" target memory view will provide the values
of fp, sp and gp). Then add all of the global and local variables
defined in the code. To add the locals, the focus (i.e. the PC) will
have to be inside the main routine, and past the point in the code
where they are initialized. (Set a breakpoint on the first
strcpy call and run to that point then add the locals.) Adding
the globals and locals in the order they were declared will probably
help with later steps, as will ensuring that the properties of the
string symbols in the watch window is ASCII. Make sure the debugger
is set to reset and debug startup at the reset vector (Tools >
Options > Embedded > Reset @ > Reset Vector). Next,
open the Window > Target Memory Views > Data Memory window,
right click on the data table and select "Fill Memory ..." to fill
all of RAM (0x0000.0000 -- 0x0007.FFFC) with 0x55555555. (Also
selecting "Keep Address Range" may save you some time later if you
have to repeat this step.) Note that 0x55 is the ASCII code for
"U". Then, reset the device.
3)
- Did resetting the device change any of RAM? (Check this in the
Data Memory Window)
- Record and report the virtual addresses of the global
variables, and their initial values from the watch window. (You may have to do a "Step
Into" once do get the watch window to update and then Reset again).
- In what memory segment (Kuseg, Kseg0 or Kseg1 and Program
Flash, Boot Flash or Data RAM do the addresses of the
global constants fall? Repeat this for the global variables.
(see Figure 4-4 for 2048KB devices in the Data Sheet)
4)
- What does the simulator say the initial values of the sp,
fp and
gp are?
(I do not believe these are actually initialized in the
processor itself -- after the lab is completed, you can check
using actual hardware if you wish.)
- Check that the address of the pc just after reset is 0xBFC0000
(look at the top line in MPLAB window for pc). If the PC is not
showing this value, do a "Step Into" once. It should now likely
show 0xBFC0004 (or close to that).
- What segment of memory is this address in (KUseg, Kseg0 or
Kseg1 and Program Flash, Boot Flash or Data RAM)?
- What does table 6.8 in the MIPS Volume III manual say this
is the address of? (This table is also in lecture 6 slides.)
5) Use the "Step Into" key to step (about 13 times) while watching
the the values of the sp and gp registers. The sp will
change twice then the gp register will change twice.
- Record the final values of each of the sp and
gp registers.
- What segment of memory is each of these registers pointing to
(KUseg, Kseg0 or Kseg1 and Program Flash, Boot Flash or Data
RAM)?
6) Set a breakpoint on the opening brace at the beginning of main()
(before any code is executed in main). Run to that point. (Open the
disassembly window and verify that the breakpoint is on the first
assembly instruction in main.)
- Did sp
or gp change on the run to main? Record the new
value of any changed registers.
- Which global values changed? (Note that while the debugger
will highlight some values that changed, it doesn't always catch
them all, so be observant.)
- In particular, what value was gd initialized to, even
though the code did not request this?
- What caused all these variables
to change? (You might want to refer to section 15.3 of the MPLAB
XC32 Compiler Users guide for PIC32 M processors found in
C:\Program
Files\Microchip\xc32\v2.41\docs\MPLAB-XC32-Compiler-UG-PIC32M-DS-50002799
which describes the runtime startup process.)
7) Some of the local variables in main will no longer be "Invalid
Address" or "Out of scope" in the watch window and some of them will
already have their initial values.
- What is common about the type declarations of these local
variables that already have their initial values?
- Of these variables, some fall in Kseg0 Program Flash, and some
in Kseg0 Data RAM. What is different about the type declarations
of the variables that fall in Program Flash, and those that fall
in Data RAM?
- What do you think initialized the items in Data RAM?
- When do you think the items in Program Flash were initialized?
Note that previous versions of
the debugger did not report a local variable
as being in scope until is was initialized, assigned or used the first
time. In MPLAB 6, all locals properly go in scope
(no longer have an invalid address) the moment main is
entered.
8) Single step instructions in the main code window until some of
the local variables addresses change to different addresses. What
register being assigned a value caused these addresses to be
changed? What is another name for this register? Why did changing
its value change the addresses the debugger determines for these
variables? (Look at the disassembly to see how ic (and
others of these variables) are being referenced).
Skip this question. This actually works in MPLAB
version 6.00, but in previous years the local variables did not
get accurate addresses until after the frame pointer (fp or s8)
was assigned since they are all referenced as offsets from the fp.
9) Check what addresses all of the non-static (also known as
"automatic") local variables have. (Depending upon the version of
the debugger, you may have to step past the instructions that use or
initialize them in main to get their addresses to resolve (not
"Invalid", blank or not show a register).
- What registers have values close to these addresses?
- How would we commonly refer to the section of RAM that
contains these addresses?
- It may surprise you that the non-static const local variables
reside in RAM. So, how can const local variables be constant?
10)
- Reset and run to main again. Then single step to the
instruction that initializes ic (the first
instruction in main).
- Looking at the disassembly, determine and record where
the data used to initialize ic is
stored. (Note that you have to be careful when decoding things
from the disassembly listing, as sometimes part of the address
or data is given in hex and part in decimal. Also, the
disassembly listing sometimes gives instructions out of order,
so pay attention to the instruction addresses, Also note that
the initial value of ic may be separated into
two 16 bit chunks.)
- In what memory segment is this initializer data? (KUseg, Kseg0
or Kseg1 and Program Flash, Boot Flash or Data RAM)?
11)
- At what address is the first byte of the string data used to initialize la stored?
(This will be the address used for the first lw instruction in
the disassembly for the "char la[] = ... ;" line. Note
that you have to be careful when decoding the address from the
disassembly listing, as sometimes part of the address is given
in hex in an lui instruction and part in decimal in an offset in
a lw instruction.)
- In what memory segment (KUseg, Kseg0 or Kseg1 and Program
Flash, Boot Flash or Data RAM) is this?
- In the target execution memory window, right click and select
Kseg0 addresses. Then,right click and select "Go to.." to go to
this address. You will also have to set the format (bottom of
the window) to Data. Verify that the value stored at this
address is correct. Reset the debugger, and repeat the
operation used to initialize all of Data RAM to 0x55555555 as
described in step 2. Then set a breakpoint on the "char la[] =
... ;" line (approximately line 26) and run to that point.
Then, single step through the disassembly code that initializes
la while
observing its value in the watch window. Pay particular
attention to what happens after each store instruction (i.e. sw,
sh, sb). Is local string initialization this way a good idea?
Would it be better for us to have used strcpy() to initialize la?
12)
- Give the KSeg0 addresses for the initializers
(i.e. the addresses of the data used to initialize) for the
non-constant global variables b[], d[]
and ge.
You may have to search around a bit in the Execution memory
window formatted for Data. If you are observant you may already
have noticed them (i.e in the last step). You can also search
for "Glob" or "00000045" in the execution memory window. But, be
careful, your search might find the variables in RAM that were
initialized. If the address you find matches any of the
the global variable addresses determined in step 3, you haven't
found the right spot.
- In what type of memory do these initializers fall (Program
Flash, Boot Flash or Data RAM)?
- Which approach is more memory efficient for strings like a[]
and b[];
to use the const declaration in as in a[] or not as in b[]?
- Why is it more efficient? (Hint: How much total memory (RAM
and Flash) is needed for b[] and the data used to initialize
it?)
13) Set a breakpoint on the last strcpy() in the main program. Run
to, then step over it while observing the global strings in the
watch window.
- What is the problem with this strcpy?
- What string did the extra data go into?
- What determines the end of a string as far as strcpy() is
concerned?
- How does the debugger determine how long strings are for
purposes of displaying them in the watch window?
- What do you think would happen if we added a strcpy(d, b)
following the last strcpy(b,c)? Try it to see what happens.
Also, to make sure you know what really happened, put a
breakpoint on a line of code (you may have to add one) that
follows this strcpy.
- Explain what happened and why it happened. (Hint: use
the Window > Target Memory View > Data Memory to examine
what ended up in RAM starting at d's address after running
strcpy(d, b). Then remove the
added strcpy(d,b) (and the extra line of code for the
breakpoint if you had to add one).
14) Record the starting and ending addresses of main from
the disassembly window. (For the ending address, give the
address of the nop following the jr ra at the end of main.)
15) Examine the project's map file that is found in the
project directory. Using the file explorer, navigate to the
project's directory, then open the dist\default\debug directory. The
project map file will have the .map extension. Open it with a
suitable text editor. Scroll down to the "Microchip PIC32
Memory-Usage Report" section of the map file.
- From the information in this section (don't go beyond the
"Discarded input sections" heading), create two tables
-- one table for RAM and one for Flash (including boot flash) --
showing the starting addresses and relative positions of each of
the following sections from the memory usage report. See below
for templates.
- The RAM table should include .sdata, .sbss, .data, heap and
stack sections.
- The Flash table should include .text.main_entry, the .text
that corresponds to the start of main, .app_excpt, .vectors,
.dinit, .rodata, .reset, and .bev_excpt sections.
- For consistency, put low numbered addresses on the top of
the tables. Place any Kseg1 addresses in the table based on
their corresponding Kseg0 addresses (i.e. Kseg1 address
0xBFC00000 maps to the same physical address as Kseg0 address
0x9FC00000) but indicate that the section is accessed through
Kseg1.
- In or next to each of these sections show the address of
each of the
- global constants and variables,
- static constants and variables,
- and the address of any data used to initialize another
variable for all variables (except those whose initializer
is in the main program itself).
- For the data used to initialize, also list the name of the
variable it is used to initialize (i.e. b init).
- Also indicate in the table the starting and ending address
of main,
- and the address of the top
of the stack ("stack" from the map file will be the
bottom of the stack).
As a check, here is a list of the variables that need to appear in the tables: a, b, c, d, gc, gd, ge, isf, iscf,
and Lcc.
Also, show the addresses of the initializers for b, d, ge, and
la.
It might be a good idea to show your tables to the professor before
turning then in to make sure they are in the right format and have
all the needed elements.
Data Ram
section |
variables
|
address |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Program Flash
section |
variables and
initializers
|
address |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16) From their addresses, and from information found in tables 6.8
(Exception Vector Base Addresses) and 6.9 (Exception Vector Offsets)
of MIPS Volume III (or see Lec 6 slides),
- what the purpose of each of the .reset, .bev_excpt .and
.app_excpt sections?.
The Report
Include a print out of the final version of your code and the
data and answers to all the questions posed in steps 3-16 (including
the tables in step 15) above. Submit this as a single pdf file
in Canvas.