16-Bit Language Tools Getting Started 70094E
16-Bit Language Tools Getting Started 70094E
16-Bit Language Tools Getting Started 70094E
LANGUAGE TOOLS
GETTING STARTED
• Microchip believes that its family of products is one of the most secure families of its kind on the market today, when used in the
intended manner and under normal conditions.
• There are dishonest and possibly illegal methods used to breach the code protection feature. All of these methods, to our
knowledge, require using the Microchip products in a manner outside the operating specifications contained in Microchip’s Data
Sheets. Most likely, the person doing so is engaged in theft of intellectual property.
• Microchip is willing to work with the customer who is concerned about the integrity of their code.
• Neither Microchip nor any other semiconductor manufacturer can guarantee the security of their code. Code protection does not
mean that we are guaranteeing the product as “unbreakable.”
Code protection is constantly evolving. We at Microchip are committed to continuously improving the code protection features of our
products. Attempts to break Microchip’s code protection feature may be a violation of the Digital Millennium Copyright Act. If such acts
allow unauthorized access to your software or other copyrighted work, you may have a right to sue for relief under that Act.
Preface
NOTICE TO CUSTOMERS
All documentation becomes dated, and this manual is no exception. Microchip tools and
documentation are constantly evolving to meet customer needs, so some actual dialogs
and/or tool descriptions may differ from those in this document. Please refer to our web site
(www.microchip.com) to obtain the latest documentation available.
Documents are identified with a “DS” number. This number is located on the bottom of each
page, in front of the page number. The numbering convention for the DS number is
“DSXXXXXA”, where “XXXXX” is the document number and “A” is the revision level of the
document.
For the most up-to-date information on development tools, see the MPLAB® IDE on-line help.
Select the Help menu, and then Topics to open a list of available on-line help files.
INTRODUCTION
This chapter contains general information that will be useful to know before using 16-Bit
Language Tools. Items discussed include:
• Document Layout
• Conventions Used in this Guide
• Recommended Reading
• The Microchip Web Site
• Development Systems Customer Change Notification Service
• Customer Support
DOCUMENT LAYOUT
This document describes how to use 16-Bit Language Tools as development tools to
emulate and debug firmware on a target board. The manual layout is as follows:
• Chapter 1: Installation and Overview – How to install the 16-Bit Language Tools
on your PC and how they work.
• Chapter 2: Tutorial 1 – Creating a Project – How to set up a project using 16-Bit
Language Tools.
• Chapter 3: Tutorial 2 – Real-Time Interrupt – How to create a 16-bit application
using a real-time interrupt.
• Chapter 4: Tutorial 3 – Mixed C and Assembly Files – How to create a 16-bit
application using a combination of C and assembly code files.
DOCUMENTATION CONVENTIONS
Description Represents Examples
Arial font:
Italic characters Referenced books MPLAB® IDE User’s Guide
Emphasized text ...is the only compiler...
Initial caps A window the Output window
A dialog the Settings dialog
A menu selection select Enable Programmer
Quotes A field name in a window or “Save project before build”
dialog
Underlined, italic text with A menu path File>Save
right angle bracket
Bold characters A dialog button Click OK
A tab Click the Power tab
Text in angle brackets < > A key on the keyboard Press <Enter>, <F1>
Courier New font:
Plain Courier New Sample source code #define START
Filenames autoexec.bat
File paths c:\mcc18\h
Keywords _asm, _endasm, static
Command-line options -Opa+, -Opa-
Bit values 0, 1
Constants 0xFF, ’A’
Italic Courier New A variable argument file.o, where file can be
any valid filename
Square brackets [ ] Optional arguments mpasmwin [options]
file [options]
Curly brackets and pipe Choice of mutually exclusive errorlevel {0|1}
character: { | } arguments; an OR selection
Ellipses... Replaces repeated text var_name [,
var_name...]
Represents code supplied by void main (void)
user { ...
}
RECOMMENDED READING
This documentation describes how to use 16-Bit Language Tools. Other useful
documents are listed below. The following Microchip documents are available and
recommended as supplemental reference resources.
Readme Files
For the latest information on Microchip tools, read the associated Readme files (HTML
files) included with the software.
MPLAB® Assembler, Linker and Utilities for PIC24 MCUs and dsPIC® DSCs
User’s Guide (DS51317)
A guide to using the 16-bit assembler, object linker, object archiver/librarian and various
utilities.
MPLAB C Compiler for PIC24 MCUs and dsPIC® DSCs User’s Guide (DS51284)
A guide to using the 16-bit C compiler. The 16-bit linker is used with this tool.
16-Bit Language Tools Libraries (DS51456)
A descriptive listing of libraries available for Microchip 16-bit devices. This includes
standard (including math) libraries and C compiler built-in functions. DSP and 16-bit
peripheral libraries are described in Readme files provided with each peripheral library
type.
Device-Specific Documentation
The Microchip website contains many documents that describe 16-bit device functions
and features. Among these are:
• Individual and family data sheets
• Family reference manuals
• Programmer’s reference manuals
CUSTOMER SUPPORT
Users of Microchip products can receive assistance through several channels:
• Distributor or Representative
• Local Sales Office
• Field Application Engineer (FAE)
• Technical Support
Customers should contact their distributor, representative or field application engineer
(FAE) for support. Local sales offices are also available to help customers. A listing of
sales offices and locations is included in the back of this document.
Technical support is available through the web site at: http://support.microchip.com
Note: When uninstalling an upgraded version of the 16-bit compiler, the entire
installation will be removed. If files have been added to directories after the
previous installation, these will not be removed.
TRISB and PORTB are Special Function Registers (SFRs) on the dsPIC30F6014
device. PORTB is a set of general purpose input/output pins. TRISB bits configure the
PORTB pins as inputs (1) or outputs (0).
Use File>Save As to save this file with the file name MyFile.c in the \examples
folder under the installation folder.
4. At “Step Four: Add existing files to your project”, a C file will be added to the
project. Select the source file created earlier, MyFile.c, in the \examples
folder. Press ADD>> to add it to the list of files to be used for this project (on the
right). For information on the letter to the left of the added file, see MPLAB IDE
Help, “Projects and Workspaces”. Leave the “A” for this project.
A linker script file no longer needs be added to a project using the 16-bit C
compiler. The linker will automatically find and use the correct linker script for the
selected device.
Select Next> to continue.
5. At the Summary screen, review the “Project Parameters” to verify that the device,
toolsuite and project file location are correct. If you wish to change anything, use
Back to return to a previous wizard dialog. Click Finish to create the new project
and workspace.
Note: If an error was made, highlight a file name and press the Delete key or use
the right mouse menu to delete a file. Place the cursor over “Source Files”
or other folder and use the right mouse menu to add the proper files to the
project.
To view the functions and variables in this short example, click the Symbols tab.
If you do not see any symbols on this tab, right-click in the window to pop up a menu
and make sure that “Enable Tag Locators” is checked.
The offending typo “nt” is in black text rather than blue – a good indication that
something is wrong, since key words are shown in blue color fonts. Typing an “i” to
make the “nt” the proper key word “int,” results in the text turning blue. Selecting Proj-
ect>Project Build All again produces a successful build.
The red stop sign symbol in the margin along the left side of the Source window
indicates that the breakpoint has been set and is enabled.
Note: There are three ways to enter Watch variables: (1) in the method described
above, a variable can be picked from a list, (2) the symbol’s name can be
typed directly in the Symbol Name column in the Watch window or (3) the
variable’s name can be highlighted in the source text and dragged to the
Watch window.
The program should halt just before the statement at the breakpoint is executed.
The green arrow in the left margin of the Source window points to the next state-
ment to be executed. The Watch window should show counter with a value of
‘1’. The value of ‘1’ will be shown in red, indicating that this variable has changed.
As each statement executes, the green arrow in the margin of the Source window
moves to the next statement to be executed.
7. Place the cursor on the line with the breakpoint, and use the right mouse button
menu to select “Remove Breakpoint”. Now press the Run button. The
“Running...” message should appear on the lower left of the Status bar, and next
to it, a moving bar will indicate that the program is running. The Step icon to the
right of the Run icon will be grayed out. If the Debugger menu is pulled down, the
Step options will also be grayed out. While in the Run mode, these operations
are disabled.
To interrupt a running program, use Halt on the toolbar.
Once the program has stopped (halted), the step icons are no longer grayed out.
Note: There are two basic modes while debugging: Halt or Run. Most debugging
operations are done in Halt mode. In Run mode, most debug functions are
not operational. Registers cannot be inspected or changed and a project
cannot be rebuilt. Functions that try to access the memory or internal
registers of the running target will not be available in Run mode.
The map file (MyProject.map) is present in the project directory and may be opened
by selecting File>Open, and then browsing to the project directory. Select Files of Type
“All files(*.)” in order to see the map file. The excerpt in Example 2-2 from the
MyProject.map file shows the program and data memory area usage after
MyProject.C was compiled.
This information can also be made visible in the Output window by checking the
“Display Memory Usage” checkbox on the MPLAB LINK30 tab, Diagnostics category.
Note: The Disassembly Listing does not display dsPIC30F instructions correctly.
Working register operands, W0 through W15, are displayed as hexa-
decimal numbers, 0x0000 through 0x001E. For example, register W1 is
displayed as 0x0002, and register W15 is displayed as 0x001E.
2. Select View>Program Memory window to see only the machine and assembly
code in program memory.
By selecting the various tabs at the bottom of the Program Memory window, the
code can be viewed with or without symbolic labels, as a raw hex dump, as mixed
PSV code and data, or just as PSV data.
Note: See the device data sheet for more information about PSV data.
Breakpoints can be set, single stepped and all debug functions performed in any
of the Source code, Disassembly and Program Memory windows.
3. Make sure the program is halted by pressing the Halt button. In the Program
Memory window, click on the Symbolic tab at the bottom to view the code tagged
with symbols. Scroll down and click on the line with the label main, which corre-
sponds to the main() function in the C file. Use the right mouse button to set a
breakpoint on main. Press the Reset icon (or select to Debugger>Reset and
select Processor Reset).
5. Go back and look at the Source File window (File>Open) and the Disassembly
window (View>Disassembly Listing). The breakpoint should be seen in all three
windows. The step function can now be used in any window to single step
through C source lines or to single step through the machine code.
#include <p30fxxxx.h>
#define CONSTANT1 10
#define CONSTANT2 20
/* ------------------------------------------------ */
int variable3;
#include <p30fxxxx.h>
For this tutorial, one constant, two variables and an array need to be defined. Just
after the template header, the constants defined are named CONSTANT1 and
CONSTANT2. Comment those out, and below the CONSTANT2 line, add a comment
and the definition for TMR1_PERIOD 0x1388.
Note: The period 0x1388 = 5000 decimal. The timer will count at a rate one fourth
the oscillator frequency. 5000 cycles at 5 MHz (the 20 MHz oscillator is
divided by four) yields a time-out for the counter at every 1 ms.
Define some variables to track the code operation in this example. Position these in the
GLOBAL DEFINITIONS area, after the definition of variable3. Add two new integer
variables, main_counter and irq_counter. Then, for the interrupt timer routine,
create a structure of three unsigned integer variable elements, timer, ticks and
seconds, named RTclock:
int variable3;
int main_counter;
int irq_counter;
The other template code in this tutorial can be left in or commented out. It is probably
better to comment it out at this time since these definitions will get compiled and take
up memory space. Make sure to comment out all the sample arrays, since they use the
macros which can be commented out. Also, as the code grows, it may be difficult to
remember which code is used by the application and which was part of the original
template.
Note: When using the template, remember that when beginning to code the
application, only a few elements of the template may be needed. It may be
helpful to comment out those portions of code that are not being used so
that later, when similar elements are needed, they can be referred back to
as models.
After the section labelled, END OF GLOBAL DEFINITIONS, type in the following
routine to initialize Timer1 as an interrupt timer using the internal clock:
void reset_clock(void)
{
RTclock.timer = 0; /* clear software registers */
RTclock.ticks = 0;
RTclock.seconds = 0;
TMR1 = 0; /* clear timer1 register */
PR1 = TMR1_PERIOD; /* set period1 register */
T1CONbits.TCS = 0; /* set internal clock source */
IPC0bits.T1IP = 4; /* set priority level */
IFS0bits.T1IF = 0; /* clear interrupt flag */
IEC0bits.T1IE = 1; /* enable interrupts */
SRbits.IPL = 3; /* enable CPU priority levels 4-7 */
T1CONbits.TON = 1; /* start the timer */
}
This routine uses Special Function Register names, such as TMR1 and
T1CONbits.TCS that are defined in the header (.h) file. Refer to the data sheet for
more information on these control bits and registers for Timer1.
A main routine and an Interrupt Service Routine may need to be written. The most
complex routine is the Interrupt Service Routine. It is executed when Timer1 counts
down 0x1388 cycles. It increments a counter sticks at each of these 1 ms interrupts
until it exceeds one thousand. Then it increments the seconds variable in the
RTclock structure and resets sticks. This routine should count time in seconds.
irq_counter++;
if (sticks++ == 1000)
{ /* if time to rollover */
sticks = 0; /* clear seconds ticks */
RTclock.seconds++; /* and increment seconds */
}
There are three sample interrupt functions in the template file. Comment out
_INT0Interrupt() because it uses two of the template file sample variables and, as
a result, will not compile. _ADCInterrupt() can be commented out too, since it will
not be used in this tutorial.
By comparison to the Timer1 interrupt code, the main() code is simple. Type the
following in for the body, replacing the line “/* code goes here */”:
for (;;)
main_counter++;
}
#include <p30fxxxx.h>
/*#define CONSTANT1 10
#define CONSTANT2 20 */
/* ------------------------------------------------ */
struct clockType
{
unsigned int timer; /* countdown timer, milliseconds */
unsigned int ticks; /* absolute time, milliseconds */
unsigned int seconds; /* absolute time, seconds */
} RTclock;
oid reset_clock(void)
{
RTclock.timer = 0; /* clear software registers */
RTclock.ticks = 0;
RTclock.seconds = 0;
TMR1 = 0; /* clear timer1 register */
PR1 = TMR1_PERIOD; /* set period1 register */
T1CONbits.TCS = 0; /* set internal clock source */
IPC0bits.T1IP = 4; /* set priority level */
IFS0bits.T1IF = 0; /* clear interrupt flag */
IEC0bits.T1IE = 1; /* enable interrupts */
SRbits.IPL = 3; /* enable CPU priority levels 4-7 */
T1CONbits.TON = 1; /* start the timer */
}
for (;;)
main_counter++;
}
/
/* Interrupt Service Routine 2 */
/* Fast context save (using push.s and pop.s) */
irq_counter++;
if (sticks++ == 1000)
{ /* if time to rollover */
sticks = 0; /* clear seconds ticks */
RTclock.seconds++; /* and increment seconds */
}
*/
/********* END OF INTERRUPT SERVICE ROUTINES ********/
Note: The simulator runs at a speed determined by the PC, so it will not run at the
actual dsPIC30F DSC speed as set by the clock in this dialog. However, all
timing calculations are based on this clock setting, so when timing
measurements are made using the simulator, times will correspond to those
of an actual device running at this frequency.
One way to measure time with the simulator is to use the Stopwatch. Select
Debugger>Stopwatch to view the Stopwatch dialog. The Stopwatch will always clear
on simulator Reset.
If the run was successful, then a Watch window can be set to inspect the program’s
variables. Select View>Watch to bring up a Watch window. Add the variable RTclock
(from the drop-down box next to Add Symbol.)
RTclock is a structure, as indicated by the small plus symbol in the box to the left of
its name. Click on the box to expand the structure so it looks like this:
Note: The Address column for sticks does not have a value. This is another
indication that sticks is a local variable.
When inspecting the variables in the Watch window at this first breakpoint, all of them
should be equal to zero. This is to be expected, since Timer1 just has been initialized
and counter has not yet been incremented for the first time.
Press the Step Into icon to step once around the main() loop. The value of
main_counter should now show ‘0001’. The interrupt routine has not yet fired.
Looking at the Stopwatch window, the elapsed time only increments by a microsecond
each time through the main() loop. To reach the first interrupt, you would have to step
a thousand times (1000 x 1 μs = 1 ms).
In order to test the interrupt functionality, remove the breakpoint at main_counter++
by clicking on the highlighted line with the right mouse button and select “Remove
Breakpoint”. Now select “Set Breakpoint” in the right mouse menu to put a breakpoint
in the Interrupt Service Routine at the irq_counter++ statement. Then, press Run.
The Stopwatch should look like this:
The value shown in the Time window is 1.0228 ms. This is about what was expected,
since the interrupt should happen every millisecond. There was some time since Reset
that was counted by the Stopwatch, including the C start-up code and the Timer1
initialization.
The main_counter value should now show 1000. Press the Step Into icon a few more
times to see the changing variables, especially sticks and irq_counter, which are
incrementing each time the interrupt happens.
The Stopwatch Time window shows 1.001226 seconds, which is close to a one-second
interrupt. A good time measurement would be to measure the time to the next interrupt.
That value could then be subtracted from the current time. Or, since it doesn’t matter
how much time it took to get here – the main interest is the time between interrupts –
press Zero on the Stopwatch and then press Run.
Note: The Stopwatch always tracks total time in the windows on the right side of
the dialog. The left windows can be used to time individual measurements.
Pressing Zero will not cause the total time to change.
#include "p30f6014.h"
#include <stdio.h>
.text
.global _modulo
_modulo:
; The 3 pointers were passed in W0, W1 and W2 when function was called
; Transfer pointers to appropriate registers for MPY
LSR W3, W3
RCALL array_loop ; do multiply set
INC2 W8, W8 ; Change alignment of X pointer
RCALL array_loop ; second multiply set
POP W10
POP W8
RETURN
; Return to main C program
array_loop:
; Set up DO loop with count 'PRODLEN - 1' (passed in W3)
DO W3, here
; Do a point-by-point multiply
MPY W4*W7, A, [W8]+=2, W4, [W10]+=2, W7
MOV ACCAH, W5
here: MOV W5, [W2++]
RETURN
.end
This tutorial will use the standard I/O function, printf(), to display messages to the
Output window. In order to use printf(), the build options for the linker need to have
the heap enabled. Make sure that the linker build option is set as shown in Figure 2-8
with 512 bytes allocated for the heap.
When building the project (Project>Build All), it should compile with no error messages.
If an error is received, make sure the project is set up with the same options as for the
previous two tutorials.
This tutorial sets up three arrays. It fills two of them with a test numerical sequence,
then calls an assembly language routine that multiplies the values in the two 16-bit
arrays and puts the result into the third 32-bit array. Using modulo arithmetic for
addressing, the two source arrays are traversed twice to generate two sets of products
in the output array, with the pointer to one array adjusted at the second pass through
the multiply loop to change the alignment between the multipliers. Using an assembly
language routine ensures that the arithmetic will be done using the DSP features of the
dsPIC30F6014.
The assembly language routine takes four parameters: the addresses of each of the
three arrays and the array length. It returns its result in the product array. This routine
runs in a continual loop, with the source arrays getting increasingly larger numbers as
the program repeatedly executes the main endless loop.
Set up a Watch window to look at the variables involved in this calculation. Add the
three arrays array1, array2 and array3. Also add the SFRs ACCA, WREG8 and
WREG10. The Watch window should look like this:
Click on the plus symbol to the left of the symbol name to expand the arrays. At this
point in the program, both array1 and array2 should have been set up with initial
values, but array3 should be all zeros, since the modulo() routine has not yet been
called.
Click on any element in the array to select the element, and then right click on the
element to change the radix of the display. Change the radix for all three arrays to
decimal.
Note: Changing the radix for any element of an array changes the radix for all
elements in that array.
Set a breakpoint in the modulo.s file at the start of the DO loop. Run to the breakpoint
and scroll the Watch window to look at array3. It should still be all zeroes.
Run again (run to example3 breakpoint, run to modulo breakpoint once, run to modulo
breakpoint again) to see the results for the second pass through the DO loop:
Remove the breakpoint from modulo.s and press Run to see the next time through
the loop. Press Run a few more times to see the values change with subsequent
executions of this multiplication process. Finally, remove the breakpoint from
example3.c.
printf("Product Array\n");
Now recompile your project (Project>Build All). Press Run, let it run for a few seconds,
then press Halt. If the Output window is not present, enable it on View>Output. Click
the SIM UART1 tab. A log of the contents of array3 should have been generated in
the Output window.
01/02/08