From: John Chambers <JohnC@ihr.mrc.ac.uk> Subject: Single-Stepping Trick Date: 24 Mar 2000 00:00:00 GMT Newsgroups: comp.dsp THIS WORK IS PLACED IN THE PUBLIC DOMAIN
Name: Single Stepping a SHARC
Category: DSP instruction trick
Application: Useful for making a debugger or watching your code blow up in slow motion
Advantages: Allows single stepping with interrupts enabled
Introduction: Emulators are expensive and not always too useful. This trick allows execution of a single instruction under control of a PC
The Trick:
The basis of the trick is that an delayed branch requires 3 cycles to execute and an interrupt requires 3 cycles to be recognised. So if we set an interrupt flag in software (say the USER3 interrupt on the SHARC) just after an RTI(DB) instruction, one cycle of the program is executed and then control returns to the USER3 ISR. This ISR copies all the register values to an shared area of memory where the PC reads it. The return value of the PC stack is the program counter (next address to be executed).
The following program illustrates the point. Toggling control_count from 1 to zero and back will single step through user_code.
I've used this to make a debugger for the sharc but it's designed to interface through the printer port to my own hardware. However it should be easy to port. If there is any interest I'll put the code on a web site somewhere (but be warned it's pre alpha!)
John
/*---------------------------------------------------------------------------*/
/*
Monitor Program to allow single stepping of code
TARGET: ADDS-21061-EZ-LAB
To assemble and link:
asm21k init
asm21k monitor
ld21k -a ezkit init.o monitor.o
*/
/*---------------------------------------------------------------------------*/
/* ADSP-21060 System Register bit definitions */
#include "def21060.h"
/* Preprocessor definitions */
#define BUFLEN 1024
#define SAMPLES 1000
#define USER3 SFT3I
//init.asm calls monitor_step from SFT3I
/*---------------------------------------------------------------------------*/
.segment /dm seg_knld;
.var control_count;
.var step_count;
.var register_store[64];
.endseg;
/*---------------------------------------------------------------------------*/
.segment /pm seg_knlc;
.global monitor_main;
.global monitor_step;
/*---------------------------------------------------------------------------*/
monitor_main:
// A delayed branch takes 3 cycles to execute, and an iterrupt takes 3
// cycles to be recognised. So if we set the interrupt flag in software then
// just after an rti(DB) intstruction, one cycle of the program is executed
// before the code returns. The return address from the PC stack is the
// value of the program counter. The rest of the registers are stored
// in memory locations so the PC can read them. Thats the theory anyway.
r3 = 0;
dm(control_count) = r3;
dm(step_count)= r3;
bit set mode1 IRPTEN; //enable global ints and nesting
bit set imask USER3; //enable USER3 int
jump user_code (db);
bit set irptl USER3; //cause a software USER3 INT
nop;
nop;
nop;
nop;
nop;
monitor_step:
dm(register_store) = PCSTK; //Store value of the program counter
dm(register_store+1) = r0; //And the int values of the regs
dm(register_store+2) = r1;
dm(register_store+3) = r2;
dm(register_store+4) = r3;
dm(register_store+5) = r4;
dm(register_store+6) = r5;
dm(register_store+7) = r6;
r1 = dm(step_count); //Incr the count of steps completed
r1 = r1 + 1;
dm (step_count) = r1;
wait_for_zero:
r0 = dm(control_count); //Wait for the control number to go
to 0
btst r0 by 0;
if sz jump wait_for_zero;
wait_for_one:
r0 = dm(control_count); //Then wait for it to go to 1
btst r0 by 0;
if not sz jump wait_for_one;
r0 = dm(register_store+1);
r1 = dm(register_store+2);
rti(db); //Return to the user code
bit set irptl USER3; //cause a software USER3 INT
nop;
/*---------------------------------------------------------------------------*/
.endseg;
.segment /pm seg_pmco;
user_code:
r3=21;
r3=22;
jump full_stop;
r3=24;
r3=25;
r3=26;
r3=27;
r3=28;
nop;
nop;
nop;
nop;
full_stop:
nop;
jump full_stop;
.endseg;