NAME(S)  ____________________________________________________________________

 

CS 341 – Lab 2

Computer Architecture and Organization

Introduction to the Harvard Architecture

 

Equipment:  Arduino UNO microcomputer, PC with Arduino IDE installed, and a USB cable.

 

The ulab system CPU and the PC processors in our SAPC systems are built along the lines of the Von Neumann architecture.  In this architecture, the processor stores both program code and program data (including the stack) in a single memory space.  Code and data must be located at different addresses within that single memory space.  The UNIX and SAPC memory maps shown in the lecture notes indicate the non-overlapping ranges of memory addresses used for code, data, and stack in each of those systems. 

 

The processor used in the Arduino UNO (the ATMEGA328) is built along the lines of the Harvard architecture.  In this architecture, the processor stores program code and program data/stack in separate memory spaces.  The memory addresses used for code may appear to overlap the memory addresses used for data or the stack, but there is no conflict since they are located in separate memory spaces.

 

There are three memory spaces in the Arduino UNO processor architecture.  See Figure 1.  Note that addresses for each of the three memory spaces start with 0x0000 and go to a symbolic constant as the end address - FLASHEND, RAMEND, or E2END.  These constants are defined in the IDE compiler and you will determine their hex values below.

 

The Flash (program code) and EEPROM (configuration data) are non-volatile meaning that their contents are preserved across power cycles.  The RAM is volatile so its contents are lost when the power is turned off.  The contents of RAM are initialized during the power on sequence in the program code.  Note that the 32 general purpose registers and the I/O device registers are “memory mapped” into the data memory address space from 0x0000 to 0x00ff.  The program memory is organized as 16 bit words while the data memory and EEPROM are organized as bytes.

Figure 1

 

In this lab, you will explore the program and data memory spaces.  In Lab 3, you will explore the EEPROM memory space.  In Lab 4, you will explore the memory mapped I/O.

 

In C programming, pointers can only access a single address space.  In the Von Neumann architecture, a pointer’s value can be the address of either data (an array) or of program code (a function pointer) and dereferencing a function pointer can be used to access the code itself, i.e. the binary value of the instruction at that address.  In the Harvard architecture, the C pointer space is mapped to the data memory space since that is the most common use for pointers.  Although a function can be executed via dereferencing a function pointer, dereferencing the function pointer cannot be used to read the contents of program memory space.

 

// correct usage of a function pointer in either architecture

void (*pointer)() = &functionName;       // defines and initializes the value of a function pointer

(*pointer) ();                           // calls functionName via the function pointer

 

// incorrect usage of a function pointer in a Harvard architecture

Serial.println((int) *((int *) pointer), HEX);   // prints contents from RAM data memory

                                                 // does NOT print contents of program memory

 

The ATMEGA processor includes some special features that allow access to program memory as data (e.g. for downloading code).  The Arduino IDE compiler and library have some extensions to allow use of these features.  In the compiler, there is a special attribute called PROGMEM.  This attribute can be used in global data definitions (outside the scope of all functions in the sketch) to tell the compiler to place the data in program memory instead of data memory.  (If PROGMEM is used in an automatic variable definition (inside the scope of a function), the attribute is ignored and the memory will be allocated on the stack in RAM.)  The library provides the macros pgm_read_byte and pgm_read_word which are defined in the pgmspace.h header file that allow your code to access program memory.

 

Write the setup function in a new sketch to perform the following experiments:

 

1. Print the hex values of the three constants for the end of each of the memory spaces.  Fill in the values you get below:

 

FLASHEND   0x____________

RAMEND      0x____________

E2END          0x____________

 

2. Define a function in your sketch that simply prints something to verify that the function was called.  Define a function pointer and call the function via the pointer as described above.  Then add code to print in hex various addresses and contents of addresses in program memory and RAM as follows:

 

(int) &functionName                                        0x_____________

(int) &pointerName                                          0x_____________

(int) value of pointer variable                            0x_____________

int value at pointer in RAM                  0x_____________

(Cast your pointer variable to an int *, dereference it, and cast result to an int for printing.)

int value at pointer in program memory 0x_____________

(Use one of the macros described above and cast return value to an int for printing.)

 

3. Define a global const char array1[] with the PROGMEM attribute and initialize it with the string literal “Array 1”.  Print the array and the address of the array in Hex.  (You will need to write a loop using one of the avr/pgmspace.h macros to read the char value of each entry in the array before printing it.)

Record the value: ________________ (Note: This address should be in program memory.)

 

Define a global char array2[] without the PROGMEM attribute and initialize it with the string literal “Array 2”.  Print the array and the address of the array in HEX.

Record the value: ________________ (Note:  This address should be in the initialized data area in RAM.)

 

Define a local automatic variable char array3[] inside the { } of the setup function without the PROGMEM attribute and initialize it with the string literal “Array 3”. Print the array and the address of the array in Hex.

Record the value: ________________ (Note:  This address should be in the stack area in RAM.)

 

Define a local automatic variable char *array4 inside the { } of the setup function without the PROGMEM attribute and initialize it by casting the return value from a call to malloc(strlen(“Array 4”) + 1)to char *.  Use strcpy to copy “Array 4” into array4.  Print the array and the address of the array (not the address of the pointer to the array) in Hex. 

Record the value: ________________ (Note:  This address should be in the heap area in RAM.)

 

4.  Print some other locations or ranges of locations in RAM if that would be useful to answer the question below for your report.

 

Study the map of typical C-complier strategies for allocating memory shown at:  http://www.geeksforgeeks.org/memory-layout-of-c-program/.  (Note that the Arduino processor has a Harvard architecture so the text portion is located in the separate program memory address space – not the RAM address space.) From that information and the data that you gathered above, try to determine the compiler’s rules for allocating addresses in the Arduino RAM.  What locations in RAM are used for the initialized data, the uninitialized data, the heap, and the stack?  Include your conclusions and rationale supporting them in your report.  Submit your report to the TA in your next lab session with a copy of the code for your final sketch.

___ / 10