Link here

Debugging Programs
Unit testing C-language functions using the interactive debugger

Using the interactive debugger

In Your First C Program Using Mosaic IDE Plus, you gained experience using the debugging tools in the GNU C environment that lets you interactively execute any function declared with the _Q attribute. This technique lets you exercise the functions using input arguments of your own choosing by simply typing commands at that terminal. Now we’ll look more closely at the operation of the interactive debugging environment, and explain how to use it to examine and manipulate the values of static variables, Forth Array elements, and memory locations.

The interactive debugging environment conveys several advantages. First, you can test each function of a program individually without changing the main() function and recompiling. This saves compilation and download time. Second, the environment makes it easy to unit test each function with a wide range of input parameters, allowing you to isolate bugs that might otherwise be missed until later in the program development cycle. Such thorough function-by-function unit testing of a program facilitates more rapid development of reliable programs.

We’ll start by learning how to use the interactive environment to examine the values of static variables. The explanation of how this works involves taking a brief high-level look at the interactive QED-Forth language that is built into the PDQ Board. Understanding how QED-Forth operates will empower you to take full advantage of the debugging capabilities of the PDQ Board.

 

Overview of the Forth language and programming environment

The QED-Forth interactive environment makes it easy to examine the contents of static variables. A brief overview of how the Forth language works will help clarify the procedure.

 

The Forth data stack

Forth is a stack-oriented high level language that combines the interactive benefits of an interpreter with the speed of a compiler. Unlike C, FORTH is implemented as a two-stack language. In addition to the return stack that most languages use to keep track of function calls and returns, FORTH has a data stack that is used to pass parameters. All arithmetic, logical, I/O, and decision operations remove any required arguments from the data stack and leave the results on the data stack. This leads to postfix notation: the operation is stated after the data or operands are placed on the stack. This is the same notation used by Hewlitt Packard’s RPN (reverse polish notation) calculators.

Unlike C, Forth uses spaces as delimiters to distinguish different keywords and tokens. For example, a C compiler can easily parse the addition expression:

5+4

as three distinct tokens: 5, +, and 4. But because the above expression was typed without any spaces, Forth would interpret the expression as a single token, assume it’s the name of a function, and would try to find it in its dictionary. In Forth the expression must be entered as:

5  4  + 

which includes the required spaces and uses postfix notation to add the numbers and leave the result on the data stack.

To see how this works, we’ll talk to the interactive QED-Forth interpreter on the PDQ Board. To start, enter the terminal now: if the terminal program is already active, click on its window or hold down the “Alt” key and press “Tab” until the terminal announcement appears on your screen. If you haven’t started the terminal program yet during this session, click tools→ Mosaic Terminal from the Mosaic IDE Plus to start it up. Connect and power up your PDQ Board as described in the “Installing and Connecting to the PDQ Board” Chapter; pressing the Return key should cause QED-Forth’s ok prompt to appear in the terminal window.

To start, we’ll ensure that the current number base is decimal by typing the command,

DECIMAL

from the terminal. (The other valid base is HEX; QED-Forth accepts the 0x prefix in decimal mode for entering a hexadecimal number). With each character you type QED-Forth echoes the character in you terminal window. The ↓ arrow in the line above indicates that you pressed the Enter key which sends a carriage return character; but you won’t see it as an echoed character on your screen. QED-Forth executes this command when the terminating carriage return is received. Also recall that QED-Forth case-insensitive, so you can freely mix upper and lower case letters. Now we can put some numbers on the QED-Forth data stack by typing:

 5  7

followed by a carriage return. QED-Forth responds:

OK   ( 2 ) \ 5 \ 7

QED-Forth is showing a picture of its data stack. The ( 2 ) means that there are two items on the stack. Each of the items is listed, and items are separated by a \ character, which can be read as under. So we could describe the stack right now as 5 under 7; the 7 is on top of the stack, and the 5 is under it. If there are more than 5 items on the stack, the stack print displays the number of stack items and the values of the top 5 items.

The stack print that shows what’s on the data stack is a feature of the debugging environment. To disable the stack print, you could execute (that is, type at your terminal) the DEBUG OFF command. It is not recommended that you do this, though; it’s very helpful to keep track of the items on the data stack while developing your program.

To multiply the numbers that are now on the stack, type the multiply operator which is a * character:

*↓

and QED-Forth responds: ok ( 1 ) \ 35 The QED-Forth * operator removes the two operands 5 and 7 from the stack, multiplies them, and puts the result of the multiplication on the stack. To subtract 5 from the number on the stack, type:

5  - ↓

which produces the response:

OK  ( 1 ) \ 30

The QED-Forth - (minus) operator takes the 35 and the 5 from the stack, subtracts, and puts the result on the data stack.

To print the result to the terminal, we could simply type the printing word:

. ↓

(that’s right, the command is simply a dot, the period on your keyboard) which prints the response:

30   OK

The printing word . removes the 30 from the stack and prints it in the current number base. The stack is now empty, so QED-Forth does not print a stack picture after the ok.

Notice that throughout this exercise QED-Forth has been interpreting and executing commands immediately. This is because the Forth language is interactive. The results of executing commands can be immediately determined. If they are incorrect, the command can be changed to correct the problem. This leads to a rapid iterative debugging process that speeds program development. This interactive function execution has been harnessed to speed development of C programs for the PDQ Board.

 

Interactive numeric printing and number entry

There are a variety of QED-Forth printing functions, and some related functions that set the current number base and clean up the data stack. Here is a short list of useful functions that can be executed interactively:

Function Description
. Prints a 16 bit signed integer in the current number base
U. Prints a 16 bit unsigned integer in the current number base
HEX. Prints a 16 bit unsigned integer in hexadecimal base
D. Prints a 32-bit signed long in the current number base
HEXD. Prints a 32-bit unsigned long in hexadecimal base
PrintFP Prints an ANSI/IEEE floating point number, synonym for F.
F. Prints an ANSI/IEEE floating point number, synonym for PrintFP
HEX Sets the number base to hexadecimal
DECIMAL Sets the number base to decimal
SP! Clears all items off the stack without printing anything

Each of the printing routines removes a number from the data stack and prints it to the terminal. Because characters are promoted to unsigned int in Forth, the . (dot) function is also used to print 8-bit character data. Both C and QED-Forth use the same 32 IEEE format for floating point numbers, so a floating point number typed at the terminal can be passed without translation as an argument to an interactively invoked C function.

The default QED-Forth number base after a COLD restart is DECIMAL. The number base can be changed to hexadecimal by executing HEX. If you need to ensure that a number is entered as hexadecimal, precede it with 0x (with no intervening spaces). For example, the following command line leaves hexadecimal A (equivalent to decimal ten) on the stack even though the current base is decimal:

DECIMAL 0xA↓

The QED-Forth operating system recognizes all numbers with an embedded decimal point as as floating point quantities. All non-floating-point numbers typed at the terminal or printed by QED-Forth are converted and printed using the current number base (corresponding to the most recent execution of DECIMAL or HEX) unless printed using a function like HEX. or HEXD. that specifies the number base to be used during printing. Floating point numbers are always converted using the decimal number base.

Integers (non-floating point numbers) are always converted to a 16-bit value unless preceded by the space-delimited DIN keyword. For example, to enter the 32-bit hexadecimal value 0x13579BDF, type at the terminal:

DIN  0x13579BDF↓
 

Displaying the values of static variables

Now that we understand how the Forth data stack works, the procedure for examining variables will make sense. The examples presented here use code from the getstart.c program that we’ve already discussed in detail in the Your First C Program Using Mosaic IDE Plus Chapter. If you have already downloaded the program, you are ready to go. If your board is presently running a multitasking application and you want to download a new file, type

COLD

to stop the program so that a new download file can be accepted.

If you have not yet compiled the getstart.c program and you want to do the exercises, first compile by following the steps here. After the compilation, enter the Mosaic Terminal and use the “File→Send File” menu item to send getstart.dlf to the PDQ Board. To run the program, type

MAIN

at your terminal – this initializes all the pointers and variables. After typing main, let’s type

Nap( )

to put the calculation task asleep; remember to type at least one space after "Nap(" As explained here, this stops the variables from being updated in the background.

Let’s start by initializing the contents of the radius variable to 5 by interactively executing (typing) from the terminal:

SetRadiusAndArea( int 5)

Remember to type at least one space after the ( character and after int. This function is defined in getstart.c as:

void _Q SetRadiusAndArea( uint r)
{  radius = r;
area = CalcArea( r);
}

It assigns the specified input parameter to the unsigned integer radius variable, and assigns the corresponding circular area to the floating point area variable.

Now we can check the value of radius. The following interactive command places the contents of the integer variable named radius on the Forth data stack:

INT  radius↓

QED-Forth responds with:

OK  ( 1 ) \ 5

Because radius is defined as an unsigned integer, we use the unsigned integer printing routine named U. (U-dot) to remove the value from the Forth data stack and print it. Type

U.↓

to print the radius an unsigned integer. QED-Forth responds with:

5 OK 

To speed things up, we can type the entire command sequence on one line so that QED-Forth immediately prints the result. Type:

 INT  radius  U.↓

and QED-Forth responds with:

5 OK 

To interactively examine the contents of the floating point area variable, type the command sequence:

FLOAT  area  PRINTFP

and QED-Forth responds:

78.54 OK 

which is indeed the area of a circle whose radius equals 5.

 

Extracting the value referenced by a pointer

Sometimes C programs add an additional layer of indirection, referencing a value by means of a pointer. An example of this technique appears in the getstart.c program in the form of the static variables radius_ptr and area_ptr; they are defined as:

_qv static uint*   radius_ptr;
_qv static float*  area_ptr;

Remember, the _qv keyword allows variables to be interactively accessed from forth.

In the InitVars() function near the end of the program, these pointers are initialized as follows:

radius_ptr = &radius;
area_ptr = &area;

In other words, radius_ptr holds the address of a variable that represents the radius, and area_ptr holds the address of a variable that represents the area. Given the radius_ptr and area_ptr, we want to be able to extract the value of radius and area. The following keywords can be executed interactively to accomplish this:

char*      int*      long*      float*

Note that there cannot be any spaces before the * in each keyword, and there must be at least one space after the * and before any subsequent number or variable name.

For example, to print the radius you can type:

int*  radius_ptr  U.↓

The int* keyword fetches the 16-bit address from radius_ptr and from that location fetches the integer contents. U. then prints the answer to the terminal. Similarly, to print the area you can type:

float*  area_ptr  PrintFP↓

The float* keyword fetches the 16-bit address from area_ptr and from the resulting location fetches the floating point contents. PrintFP then prints the result.

 

Signed versus unsigned numbers

Note that the type specifier used above does not specify signed versus unsigned numbers; rather, the printing function determines whether the number is interpreted as signed or unsigned. For example, type the following two command lines from the terminal and see how QED-Forth responds:

65535   U.↓
65535   . ↓

In the first instance, QED-Forth prints 65535, while in the second instance, QED-Forth prints -1 (we’re assuming that you have not changed the number base to HEX). The same binary pattern (in this case, all 16 bits of the number are set) can represent either 65535 or -1 depending on how the number is interpreted and printed. Thus by choosing the printing function, you can control whether a number is displayed as a signed or unsigned quantity.

 

Summary

In summary, to display the contents of a simple static variable, type a command of the form:

type  variable_name  print_function_name

where type is one of the following keywords:

char      int      long      float

To display the contents of a static variable that is pointed to by a pointer, type a command of the form:

type      pointer_name      print_function_name

where type is one of the following keywords:

char*    int*     long*     float* 
 

Use type keywords to interactively call C functions

The same family of familiar C type-declaration keywords that we used to fetch the contents of variables is also used to facilitate interactive calling of C functions. Recall that these keywords are:

char    int     long    float
char*   int*    long*   float*

We have seen that these keywords are used in two different contexts while debugging. In the prior Chapter we used them to declare the type of an input parameter while interactively calling a function. For example, we can interactively type from the terminal:

CalcArea( int 5)↓

where the int keyword is used with the same syntax as an ANSI-C function prototype to declare the input arguments to the called function.

Second, we used the type keywords in this chapter to extract the value from a variable, as in the interactive QED-Forth command

int  radius  U.↓

which prints the contents of the radius variable as an unsigned integer.

These two contexts for the use of the int keyword are related. For example, to calculate the area corresponding to the current value of the radius variable, we can interactively execute:

CalcArea( int radius)↓

and QED-Forth prints the resulting floating point area in its summary of the return value. The int keyword serves two complementary purposes here: it tells QED-Forth that the input parameter is a 16-bit integer, and it extracts the value of the radius variable so the variable is passed by value.

When interactively calling a function, all parameters that are passed by value should be preceded by the appropriate type keyword. However, when passing the address of a variable or a structure, simply state the variable or structure name without any type specifiers or & (address-of) operators.

For example, the function prototype for the DimAndInitFPArray() function in getstart.c is:

Q void DimAndInitFPArray(float val,int rows,int cols,FORTH_ARRAY* array_ptr)

and the program includes the array declaration:

FORTH_ARRAY circle_parameters;

which declares circle_parameters as a FORTH_ARRAY structure in memory. As we shall see, executing (typing) the name circle_parameters in QED-Forth leaves the address of the array structure on the stack, so there is no need for additional type declarators or & operators. Thus to interactively dimension the array to have 10 rows, 2 columns and a initialization value of 12.34, we type from the terminal:

DimAndInitFPArray( float 12.34,int 10,int 2,circle_parameters)↓

To verify that this worked, you can execute:

PrintFPArray( circle_parameters)↓

which displays the contents of the newly initialized circle_parameters matrix.

 

Under the hood of the QED-Forth interactive debugger

This section is for the curious among you; you need not read or understand this section to use the QED-Forth interactive debugger. However, it will give you additional insight into the debugging environment.

 

Variable declarations

In the example above, radius is defined in the GETSTART.dlf download file as a QED-Forth constant whose value is the address of the radius variable. To see for yourself, use your editor to open the GETSTART.dlf file. Select “Open” from the editor’s “file” menu, set the “Files of Type” option to either “Programming Files” or “All Files”, and double click on GETSTART.dlf in the C:\MosaicPlus\my_projects\getstart directory. The top portion of the file is the hexadecimal dump of the compiled C code in the Motorola S2 record format. Near the bottom of the file you’ll see some XCONSTANT declarations. Among them is the declaration:

din 000025a0 xconstant radius

which defines radius as a QED-Forth constant that places the hexadecimal value 25a0 on the stack. The DIN word tells QED-Forth that the next word is a 32-bit double number. (If DIN is not used, 32-bit numbers in the input stream are truncated to 16 bits.) You can verify this by clicking on the Terminal window and typing:

radius  HEX.↓

from the terminal. This command sequence instructs QED-Forth to print the hexadecimal address of the radius variable. Note that if you want to pass the address of the radius variable as a parameter to a function (also known as passing a pointer or passing by reference), you leave out the int keyword before radius in the parameter list.

The keyword int is actually a QED-Forth function that examines the next token in the input stream; if it is already a number such as 5 or 3.2, int simply converts it to the nearest integer. If the next token is a variable address (such as radius), int extracts the 16-bit contents stored at the address. To see this behavior for yourself, try the following commands at your terminal:

int  5  U.↓
int  5.45  U.↓
int  radius  U.↓

These three statements all yield identical results if the value of radius is still 5.

 

Function declarations

Returning to the GETSTART.dlf file that you opened in the editor, scroll to the area just above the list of XCONSTANT definitions and you will see a set of lines starting with the : (colon) character. In Forth, the : character marks the start of a new definition (function or subroutine), and the ; (semi-colon) marks the end of the definition. These are the function definitions that tell QED-Forth the names and execution addresses of each function in GETSTART.c that was preceded by the _Q declarator. Among these functions you will find some familiar ones including:

: CalcArea(
: SetRadiusAndArea(
: DimAndInitFPArray(
: PrintFPArray(

The body of each of these Forth definitions defines the compilation address and invokes the routine CALL.CFN (meaning call-C-function). CALL.CFN accepts an optional list of comma-delimited parameters terminated by a closing ) and then sets up the stack frame and calls the function.

So when you type the interactive command

CalcArea( int radius)↓

with a terminal enter key, here’s what happens:

  1. When QED-Forth accepts the carriage return, it starts interpreting the command line that has been entered. It looks for the first space-delimited token, and it finds the token:
    CalcArea( 

  2. It looks in its dictionary, and sure enough, it finds that this token has been defined; the definition was compiled when the GETSTART.dlf download file was sent to the PDQ Board.
  3. When QED-Forth executes the CalcArea( token, it executes the CALL.CFN routine which starts looking for a terminating ) character, and processes any tokens that are present.
  4. The next space-delimited token found is int, which looks for the next token (in this case, radius). Because radius is not a number, int assumes that it is a variable and extracts the 16-bit contents from the address that is left on the Forth data stack by radius. The contents are left on the Forth data stack.
  5. The terminating ) is found, so the CALL.CFN routine pushes the items on the Forth data stack onto the C stack in the proper order to make a legal C stack frame, and then executes the CalcArea() function as defined in the C program at the specified execution address.
  6. When the CalcArea() function returns, QED-Forth traps its return value from the Freescale 9S12 (HCS12) processor registers and prints the value using integer and floating point formats.

 

Summary

The Mosaic IDE Plus calls a collection of Makefile scripts to create the QED-Forth debugging declarations that appear at the bottom of the .dlf download file. The Makefile has to decide whether each compiler symbol in the .syms file is a callable function, a variable, or a FORTH_ARRAY. The Make Tool identifies callable functions by detecting the functions that were placed in a special section dedicated to functions tagged with the _Q designator, and in response prints the functionname( definition into the .dlf file. The Make Tool identifies variables that were tagged with the _QV designator, and prints a QED-Forth XCONSTANT declaration into the .dlf file. When the Makefile is done, a .dlf is produced that contains all the compiled application code in hexadecimal form as well as the QED-Forth headers which facilitate interactive debugging.

 

Other useful QED-Forth functions

QED-Forth is a complete language that includes hundreds of pre-defined functions, all of which reside in Flash on the PDQ Board. Many of these functions are declared in the header files in the C:\MosaicPlus\c\libraries\include\mosaic directory, and so are callable from C. The names and descriptions of these functions are detailed in the Control C Glossary in the documentation package. But there are also additional routines described there that are useful while debugging; these allow you to:

  • Modify the contents of EEPROM on the HCS12 processor.
  • Dump the contents of a specified region of memory in hex and ascii format using the DUMP command.
  • Specify a new baud rate for the serial ports to speed downloads using the BAUD command.
  • Configure the PDQ Board to execute a specified program each time a reset or restart occurs using the AUTOSTART: or PRIORITY.AUTOSTART: command.
  • Dump out a replica of the board’s program memory space in Intel Hex or Motorola S2 record format to archive your production code.

In sum, the versatile QED-Forth language enhances the power of GNU C by providing many operating system functions as well as an interactive debugging environment that speeds program development and testing.

 

The PDQ Board kernel versus prior kernels

The "Forth V6 New Features" page details the differences between the V6.xx firmware on the PDQ Board, and the V4.xx firmware found on earlier products based on the 68HC11 processor. If you have experience with the earlier firmware versions and want to know what’s involved in upgrading your software, please consult this page for detailed information.



See also → Using Paged Memory

 
This page is about: GNU C Interactive Debugging, Unit Testing, Function Testing – Using the interactive debugger for effective testing of your C application program Forth, postfix, stack
 
 
Navigation