Cornell University
Electrical Engineering 476
Older versions of a
CodeVision 1.24.6 Debugger
for Atmel Mega32 microcontrollers
Debugger based on aOS. Codevision 1.24.6
A simplified version of aOS was constructed to act as a debugger. The intent was to make a system in which a student could debug any C program with very minimal modification. How successful the attempt is remains to be seen (stay tuned until Spring 2006). The program memory used for the debugger is about 3k words of flash. Debugger time overhead when running the target application is zero (At RESET time the debugger runs briefly to set up its structures). SRAM overhead is about 800 bytes, so the debugger is not useful for data-hungry programs like video.
Note that the debugger code has compiler-version dependencies! The dependencies occur because local variables are stored in specific ways in different compiler versions and the variables have to be manipulated directly in assembler. A version which compiles in 1.23.8 is linked in a section below.
There are only a few changes that have to be made to the target C program to run it with the debugger:
mega32.h
include and stdio.h
includes
if they were in your code.main
with interrupts turned on. This is generally
no problem but you may want to add a line #asm("cli")
to turn them off until you are ready.Project...Configure
dialog set the Data Stack size to
200 and the clock speed to the correct crystal value.debug(IDnumber).
There is a demo code which blinks
some lights and enters the debugger at reset and when button 1 is pushed. Button 2 tests serial input to the target code. Register zero
is set to 0x55
just before entering the debugger when button 1 is pushed. The demo code
assumes that PORTB will be hooked to the LEDs on the STK500, PORTC to the
switches, and the UART (PORTD.0 and D.1) attached to a PC running hyperterm.
The demo code is a slightly modified version of sched1.c
used as an example in Lab 1 in 2005.printf
and scanf
. Note that in Codevision,
scanf
does not echo characters.When you enter the debugger, the three MCU timers are frozen, but interrupts
are left on. If you don't like this particular behavior, you can hack on the
following macro debug
(in debugger.c) to change the defaults. The saveitall
and loaddatareg
functions are asm routines to make it easier for the debugger to get the stack pointers and register values.
#define debug(id) \ do{ \ aos_t0temp = TCCR0; \ aos_t1temp = TCCR1B; \ aos_t2temp = TCCR2; \ TCCR0 = 0; \ TCCR1B = 0; \ TCCR2 = 0; \ saveitall(); \ aos_break_id = id; \ aos_resume_task(cmd_tcb); \ loaddatareg(); \ TCCR0 = aos_t0temp; \ TCCR1B = aos_t1temp;\ TCCR2 = aos_t2temp; \ }while(0)
If you want to be able to enter the debugger with a <control-c>
from hyperterm, uncomment the following line near the beginning of the debugger.c
file.
//#define use_rxc_isr
You might have to increase the stack sizes defined near the beginning of the
debugger.c file if you do any recursion, or deeply nested function calls, or
define lots of local variables in functions. In Codevision C, the hardware stack
contains return addresses and the results of any assembler-level push
opcodes, inlcuding register saves when entering an ISR. The data stack contains
parameters passed to functions and local variables. Change these two lines near
the beginning of the file debugger.c.
#define AOS_TASK_HSTK_SIZE 100
#define AOS_TASK_DSTK_SIZE 200
The commands available at the db>
prompt (appearing on the
PC hyperterm window) are:
h |
Prints a short version of this table |
g |
Exit command shell and run target program |
<control-c> |
Stop target program and enters command shell with DebugID=255. NOTE: you must uncomment a line near the beginning of debugger.c for this command to work. |
debug(char debugID) |
When used in the target program being debugged, this macro
enters the command shell with the specified debugID. The debugID can be
used as an error code. Example: debug(4) |
x |
Forces a RESET of the MCU and trashes the state of your program. This is implemented using the watchdog timer reset function. |
i ioregAddress |
Read an i/o register. The register address is entered in hexadecimal.
The result displayed in in hex. Example: i 13 Reads the state of the pushbuttons attached to PINC. |
I ioregAddress iodata |
Write to an i/o register. The register address and data are entered in
hexadecimal |
r datareg |
Read data register. The register number is in decimal. The
result displayed is in hex. The .map file generated by the
compiler will show you where variables are stored. Note that Codevision can store varibles in SRAM or in registers. You have to look at the map file to verify where a variable is stored. Registers 1 to 15 are available for global variables. Registers 16 to 21 can store local variables. Registers 22 to 31 store state information for Codevision. Normally, no user information
should be in these registers.Example: r 16 Displays the value 55 if the demo program is running and button
one is pressed to enter the command shell. This is because the running
program writes a 0x55 to data register sixteen just before it calls the debug(3)
macro. |
R datareg data |
Write task data register. The register number is in decimal.
The data is in hex.
Writes 0xaa to register 0. The debugger does not allow you to modify registers 22 to 31 because of the dynamic contents of these internal C registers make it hard to predict the results of a modification. If the
demo program is running, it stops with debugID=1 just after reset time. There are six local variables in registers when it stops. |
m address |
Read memory. Address is given in hex. The |
M address data |
Write data to memory. Address and data are in hex. Writes 0xaa to address 7d0 . Remember that
there is NO memory protection, so you can easily trash the system with this
command. |
s |
Read the memory location of the top of the hardware stack. This is mostly useful to make sure that you have not overrun the space allocated to the hardware stack. The value reported by this command is the stack location before the context switch to the debugger stores 32 bytes on the stack. |
d |
Read the memory location of the top of the data stack. This is mostly useful to make sure that you have not overrun the space allocated to the data stack. If you are looking for local variables, remember that Codevision puts the first six bytes of local variables defined in a function in data registers R16-R21. After that they are placed on the data stack. The example program stops with debugID=1 at reset time. The data stack has 6 stored bytes, then six local variables in registers. |
The Backspace
key works at the debugger command line, but does
not echo the erased characters. After backspacing, new characters type over
old characters. No error checking is done at the command line. Unrecognized
commands are ignored. Poorly formed parameters (e.g. letters in a number) default
to whatever C does to them, which is usually to return a zero.
Note that there is compiler-version dependent code in several places in debugger.c. This code compiled on Codevision 1.24.6.
Debugger for Codevision 1.23.8
Please note that this version of the debugger does not work in Codevision versions 1.24 and above. See above for the current version.
There are only a few changes that have to be made to a standalone C program to run it with the debugger:
main
to mymain
. This is because main
is used to set up the debugger itself.mega32.h
include and stdio.h
includes if they were in your code.mymain
with interrupts turned on. This is generally no problem but you may want to add a line #asm("cli")
to turn them off until you are ready.Project...Configure
dialog set the Data Stack size to 200 and the clock speed to the correct crystal value.debug(IDnumber).
There is a demo code which blinks some lights and enters the debugger when a button is pushed. Register zero is set to 0x55
just before entering the debugger. The demo code assumes that PORTB will be hooked to the LEDs on the STK500, PORTC to the switches, and the UART (PORTD.0 and D.1) attached to a PC running hyperterm. The demo code is a slightly modified version of sched1.c used as an example in Lab 1 in 2005.printf
and scanf
. Note that in Codevision, scanf
does not echo characters.When you enter the debugger, the three MCU timers are frozen, but interrupts are left on. If you don't like this particular behavior, you can hack on the following macro debug
(in debugger.c) to change the defaults.
#define debug(id) \\ do{ \\ aos_t0temp = TCCR0; \\ aos_t1temp = TCCR1B; \\ aos_t2temp = TCCR2; \\ TCCR0 = 0; \\ TCCR1B = 0; \\ TCCR2 = 0; \\ aos_break_id = id; \\ aos_resume_task(cmd_tcb); \\ }while(0)
If you want to be able to enter the debugger with a <control-c>
from hyperterm, uncomment the following line near the beginning of the debugger.c file.
//#define use_rxc_isr
You might have to increase the stack sizes defined near the beginning of the debugger.c file if you do any recursion, or deeply nested function calls, or define lots of local variables in functions. In Codevision C, the hardware stack contains return addresses and the results of any assembler-level push
opcodes, inlcuding register saves when entering an ISR. The data stack contains parameters passed to functions and local variables. Change these two lines near the beginning of the file debugger.c.
#define AOS_TASK_HSTK_SIZE 100
#define AOS_TASK_DSTK_SIZE 200
The commands available at the db>
prompt (appearing on the PC hyperterm window) are:
h |
Prints a short version of this table |
g |
Exit command shell and run other tasks |
<control-c> |
Stop other tasks and enters command shell with BreakID=255. NOTE: you must uncomment a line near the beginning of debugger.c for this command to work. |
debug(char debugID) |
When used in the target program being debugged, this macro enters the command shell with the specified debugID. The debugID can be used as an error code. Example: debug(4) |
x |
Forces a RESET of the MCU and trashes the state of your program. |
i ioregAddress |
Read an i/o register. The register address is entered in hexadecimal. The result displayed in in hex. Example: i 13 Reads the state of the pushbuttons attached to PINC. |
I ioregAddress iodata |
Write to an i/o register. The register address and data are entered in hexadecimal |
r datareg |
Read data register. The register number is in decimal. The result displayed is in hex. The .map file generated by the compiler will show you where variables are stored. A few of the registers are modified by the scheduler before the debugger takes control. r22,r23, and r26-r31 are used by the context switch. Normally, no user information should be in these registers.Example: r 0 Displays the value 55 if the demo program is running and button zero is pressed to enter the command shell. This is because the running program writes a 0x55 to data register zero just before it calls the debug macro. |
R datareg data |
Write task data register. The register number is in decimal. The data is in hex. Writes 0xaa register 0 |
m address |
Read memory. Address is given in hex. The .map file generated by the compiler will show you where variables are stored. Remember that by default, Codevision stores the first few variables you define into registers.Example: m 7d0 |
M address data |
Write data to memory. Address and data are in hex. Writes 0xaa to address 7d0 . Remember that there is NO memory protection, so you can easily trash the system with this command. |
s |
Read the memory location of the top of the hardware stack. This is mostly useful to make sure that you have not overrun the space allocated to the hardware stack. The top 31 bytes on the target program hardware stack are the registers saved during the context switch to the debugger, plus the SREG. SREG is stored at top of stack, but is probably modifed (relative to the target program) by the scheduler before the context switch to the debugger. To access the registers, use the r command. |
d |
Read the memory location of the top of the data stack. This is mostly useful to make sure that you have not overrun the space allocated to the data stack. If you are looking for local variables, remember that Codevision puts the first six bytes of local variables defined in a function in data registers R16-R21. After that they are placed on the data stack along with lots of other stuff which makes local variables hard to find. |
The Backspace
key works at the debugger command line, but does not echo the erased characters. After backspacing, new characters type over old characters. No error checking is done at the command line. Unrecognized commands are ignored. Poorly formed parameters (e.g. letters in a number) default to whatever C does to them, which is usually to return a zero.
Copyright Cornell University August 2005