Electronics, PCB Design and PCB Layout Daycounter, Inc.
Engineering Services

Custom Firmware, Electronics Design, and PCB Layout

                 
 
Electronics Design
Firmware Development
Software Development
Embedded Design
PCB Layout
Digital Signal Processing (DSP)
Reverse Engineering
Prototyping
Device Driver Development
VHDL
Motor Controllers
Microcontrollers
Data Acquisition Systems
Palm OS Software
Windows CE Software
Pocket PC Software
Design for Manufacturing
Through Hole to Surface Mount (SMT) Conversion 
Microchip PIC Consultant
MSP430 Development
DSP56 Development
RoHs Redesign
Design for USB



Silver Board Contract Assembly


 

DSP563CC Compiler Tricks

Freescale's DSP563xx 24 bit processor is a great digital signal processor, and best yet their development tools are free.  Their C compiler is gnu based, and isn't supported by them anymore, and so it can be a challenge to use.  This web page describes some of the pit falls we encountered when using the compiler, and hard learned lessons, that we believe will be useful to other developers who are targeting this processor.

COMPILER 

            For our project we used the DSP563CC compiler from Motorola.  Initially we ran into a few problems with this compiler.  First, the DSP56303 has three memory spaces: P, X, and Y.  The P memory is used for program memory.  The X and Y are used for data.  The problem is that the compiler only allows you to use one memory space or the other (X or Y).  It does not allow you to use both X and Y at the same time unless you combine them to use one big 48 bit memory space (Referred to in the documentation as L memory).  This presented a problem since we were designing our board to access both X and Y memory for data storage.  To get around this problem we created a function using inline assembly to manually write to the memory space.  This can be tricky since whichever memory space you specify to the compiler it uses for the stack and variable initialization.  Make sure that you specify in the crt0.asm file the maximum memory address that the compiler can use (the crt0.asm will be explained in more depth later).  That way you won’t be writing to any address that the compiler is using.  Here is an example read and write function to the Y memory space:

void YMemWrite(unsigned int Address, unsigned int Value)

{

            __asm("move %0,y:(%1)"::"R"(Value),"A"(Address));

}

 

unsigned int YMemRead(unsigned int Address)

{

            unsigned int ReturnValue;

            __asm("move y:(%1),%0":"=D"(ReturnValue):"A"(Address));

            return ReturnValue;

}

 

The __asm function takes in a string parameter representing the assembly code and then a list of variables.  The %#s in the string represent the variables in the list.  The list is separated into 2 parts by colons.  The first part contains the variables to be written to, and the second contains the variables to be read.  That is why there are no values in the first part of the Write function.  There is a third part but we won’t discuss it here.  The variable modifiers “R”, “A”, and “=D” tell the compiler what type of variable it is.  See the compiler manual for more information.  Be careful using modifiers!  The compiler manual has a whole list of various modifiers for both the variable lists and for use inside the assembly.  Using some of them can make the register usage of your inline assembly incompatible with the compiled C code.  Also, using the wrong modifier or using the one in the wrong place can be very tedious to debug.  We found that the simpler… the better.

 

So now you have written your code and modified the crt0.asm file and now you are ready to compile.  Here is a few problems that we had when it was time to compile.  First lets explain how the compiler works.

Using the g563c command line compiler can implement both the compiler and linker in one step.  It also adds in some library files that are needed by the compiler for certain function calls, debugging, etc.  (Note: If you try and compile and link separate using the command line linker it does not automatically link the library file in.). 

You will almost always have to modify the crt0.asm file for your project.  This files sets up the stack, interrupt vector, and other variables.  It also specifies the top memory location of the stack.  The compiler manual specifies that this file can then be linked in by using the “–crt” command after the asm file is compiled.  Example:

:> g563c –c crt0.asm

:> g563c –crt crt0.cln foo.c

The problem is this doesn’t work.  The reason is is that crt0.cln is already in the library that the linker tries to add so it will give you duplication errors.  The way to fix this is find the library file that you are using.  They are in the “\LIB” folder in the DSP directory.  It will have three library files.  One for X, Y, and L memory space options (if you did not specify a memory space it defaults to Y).  Run dsplib.exe from the command prompt and it will give you the dsplib prompt (make sure your in the LIB folder).  Type in “list <library filename>”.  This will list all the modules compiled into the library.  Find the name of the crt module (usually crt0.cln).  Then type “delete <library name> <crt module name>” to delete the module from the library (you may want to back it up first using extract).  Now the –crt option should work fine.

 

BOOTING FROM EXTERNAL DEVICE (EEPROM, SCI, ETC)

Thanks to Motorola this is not as easy as it looks.  There are three problems that occur when booting from an external device.  All of them have to do with memory initialization.  When the code is compiled and loaded into the DSP from the ADS debugger than the data memory automatically gets initialized.  When you load the memory from an eeprom or SCI only the program memory gets restored.  Fortunately there is a solution to all three of the problems.

 

Problem 1: System Variable Initialization
Crt0.asm, sig.asm, and exit.c files all contain variables that are initialized by the ADS debugger not manually in code (whoever programmed them used “dc” instead of copying values into memory).  That means that those variables will not be initialized when the program memory is restored from an eeprom or SCI booting.  The variables are contained in the data memory space.  The solution to this is to manually initialize the variables in our code so that when the program memory is restored it will then restore the data memory that is needed.  The only place we can do this is in the crt0.asm file since anything past that needs these variables initialized to run properly.  The following variables require initialization (grouped together by the file that they are located in):

 

crt0.asm                                   sig.asm                                    exit.c

F__stack_safety                        F__sig_drop_count                    F__functions_registered

F__mem_limit                            F__c_sig_handlers                    F__exit_function_registry

F__break

F__y_size

Ferrno

F__max_signal

F__time

 

We initialized the variables with the following code (Remember this is for Y memory space only) placed in the crt0.asm file at the start of the F__start section:

 

; Added by Daycounter Engineering to enable booting from an external source

; (Eeprom, SCI, etc.)

;               Initialize Y - variables on startup

; F__stack_safety

                move                #1024,x0

                move                x0,y:F__stack_safety

; F__mem_limit

                move                #TOP_OF_MEMORY,x0

                move                x0,y:F__mem_limit

; F__break

                move                #TOP_OF_MEMORY,x0

                move                x0,y:F__break

; F__y_size

                move                #DSIZE,x0

                move                x0,y:F__y_size

; Ferrno

                move                #0,x0

                move                x0,y:Ferrno

; F__max_signal

                move                #$fe,x0

                move                x0,y:F__max_signal

; F__time

                move                #0,x0

                move                x0,y:F__time

 

; sig.asm

; F__sig_drop_count

                move                #0,x0

                move                x0,y:F__sig_drop_count

; F__c_sig_handlers

                move                #F__c_sig_handlers,r0

                do                #$3e,F__c_sig_handlers_end

                move                #F__sig_dfl,x0

                move                x0,y:(r0)+

                move                #0,x0

                move                x0,y:(r0)+

F__c_sig_handlers_end

 

; exit.c

; F__functions_registered

                move                #0,x0

                move                x0,y:F__functions_registered

; F__exit_function_registry

                move                #F__exit_function_registry,r0

                move                #0,x0

                do                #33,F__exit_function_registry_end

                move                x0,y:(r0)+

F__exit_function_registry_end

 

Problem 2: Switch Statement Addresses

There is another problem with variable initialization.  The compiler also places memory addresses for switch statements in the data memory section.  The memory addresses are used in the assembly jump statements.  In other words it does a compare and then jumps to the appropriate address.  Fortunately the compiler has a command line parameter to move these addresses into the program memory (you would think it would do this by default).  The parameter is “-mp-mem-switchable”.  Add this to your compile command and your newly compiled program should have no problems booting from an external device.

 

Problem 3: Program Variable Initialization

 This problem occurs when you initialize a variable inside your code.  Here are some examples contained in a function or globally:

static unsigned int foo1 = 0;

unsigned int foo2 = 0;

The previous code is initialized through the debugger so when you boot from a eeprom or SCI the variables initially have garbage values.  The solution to this is easy always manually assign the variables in your code.  For example:

            static unsigned int foo1;

            unsigned int foo2;

            if first time running (psuedo code)

foo1 = 0;

            foo2 = 0;

You have to be careful with this that you global or static variables only get initialized when they are supposed to.  It is a good idea to always initialize your variables when working with the DSP even if it may matter or not.

 

Double Check

Ok, now we have done the previous solutions but we want to ensure that we didn’t forget anything.  We can check to see what variables are being initialized in the data memory space by having the compiler output a memory map file.  This is done by including the “-M <filename>” parameter in the linker command; however, this gets tricky since we are compiling and linking in the same step.  Including this in your g563c command line won’t work.  We need to only send it to the linker.  This is done by using the –j parameter.  The following line of code is an example:

            g563c -j "-M Mapfile.txt" foo.c

Whatever is in the string following the –j gets sent directly to the linker.  This then creates a file called Mapfile.txt that contains all the memory mapping information generated by the linker.  You can open this file and see all the variables that are mapped into the data memory space.  Double check that all the bytes are accounted for and you should have no problems booting.

BOOTING FROM SCI USING HYPERTERMINAL

            We wanted a quick and easy way to download the code to our DSP device.  So we decided to use Hyperterminal.  Which works great you just have to have the data in the right format before sending the file.  You need a binary file with the words arranged least significant byte first, with the first 3 bytes containing the number of program words, and the next 3 bytes containing the start address.  To do this we wrote a command line program to convert an srec file to a binary file in this format.  Now after compiling your program into a cld file you convert it to an srec file by using the srec.exe program included with the compiler.  This creates separate files for each memory space.  You then take the .p file and convert it using our PackDSP56.exe program.  The following is an example:

            g563c –g –o foo.cld foo.c

            srec foo.cld

            PackDSP56 foo.p foo.bin

This packages our program memory into a file that is ready for Hyperterminal.  Now open up Hyperterminal (Note directions are given using Windows XP, may be different on other operating systems).  Type in a name for the new connection.  Hit ok and select the com port that your device is connected to.  Then set the bits per second to the value to match your device (The frequency going into the SCI’s SCLK pin divided by 16).  Select 8 Data bits, None Parity, 1 Stop bit, and no flow control.  Click ok and the terminal should be up and connected.  Reset your DSP with the proper mod inputs set to boot off the SCI and then click on Transfer->Send Text File… from the top menu.  Select your binary file and click open.  The hyperterminal should dump numerous characters and make weird beeps as the file is transferred.  The reason for this is the DSP repeats back to hyperterminal the information it receives.  Since the information the hyperterminal is receiving is not really text it displays weird characters.  Don’t worry it does work it just isn’t very pretty.  When your finished your code should be downloaded and running (It jumps to the start of your code and begins execution after downloading it from the SCI).

 

Note: programming your device from the SCI only copies the information received into the program memory at the address specified.  If you want to program an external eeprom you will have to write or use a bootloader program to download to the program memory.  When the bootloader is run you can then download your program through the bootloader program into the eeprom or external memory.  Since each bootloader is different we enabled our PackDSP56.exe program with various options to aid in this process.  See the PackDSP56 documentation for more information.  For our project our program memory was small enough to fit entirely on the onboard program memory space so when we want to program our eeprom we download the code to the device using hyperterminal and then when our code runs it checks to see what boot mode we are in if it is in “boot from SCI” mode we copy the entire program memory to the eeprom.  If it isn’t it runs the program like normal.  That way everytime it is programmed from the SCI it flashes the program to the eeprom and if it boots in any other mode it will not.

 

PACKDSP56 DOCUMENTATION

PackDSP56 is a program we developed to create a binary file from and srec file.  An srec file is the file created by the srec.exe utility included with the Motorola’s DSP563ccc compiler (pretty much the same as an s19 file).  

Click here to download: PackDsp56.exe 

PackDSP56 is a command line program with the following usage:

            PackDSP56 [options] <inputfile> <outputfile>

The <inputfile> is the srec file and the output file is the binary file you want to output to (Caution: it will overwrite the specified file).  The following are the optional command line parameters (options):

 

            -NoHeader  This option leaves out the first 6 bytes from the output file that contain the 3 bytes of the number of program words and the 3 bytes of the starting address.

 

            -MemSize:<value>  This option changes the default target memory size.  The default value is 4k.  The value is the max size that the binary file can be programmed too.  If the program memory that you are programming on your DSP is larger than 4k you will have to specify it here for the program to work properly.

 

            -StartAdd:<value>  This option changes the default target starting address on the DSP.  The default value is 0x000000.  The input value can be up to address 0xffffff.  Does not work if the –NoHeader option is used.

 

            -BytesPerAdd:<value>  This option changes the default number of bytes in each program word.  The default value is 3 for the DSP563xx family since they have 24bit memory.  We put this option in just incase the user would like to use the utility to program other devices using different memory sizes and would like to specify how it is packed.

 

            You can bring up the option’s descriptions anytime by typing PackDSP56 without any options or files specified.

USING % OR /

            For our project timing was essential so I was disappointed to find out that the % and / routines that the compiler uses are very slow and tedious.  I ended up removing all of the % and / from my code except for a few on initialization when the timing for the ISRs didn’t matter.  Here are some examples of how to get the same computations without using % or /.

 

%

This gets used a lot especially if you are using circular buffers or have numbers that increment and loop back to zero.  If the number you are moding by is a power of 2 this becomes really easy.  You can simply mask over the carry bit.  For example say I want to do the following: x = x%8;

This can be written by simple masking off all the bits 8 or greater or in other words: x &= 0x000007;

This only allows the x variable to increment from 0 to 7 then it repeats again.  This is quick an easy if you what you are moding by is a power of 2.  Else the quickest way I have come up with is to check if the value is greater than the max and if it is then subtract the max.  For example: x = x%35;

Would be:

if (x>=35) x -= 35;

 

/

Once again our divide is very simple if we are dividing by a power of 2.  Now we simply have to shift the bits to divide.  For example: x = x/8; is more easily computed as x <<= 3;  Lets see if this works.  If x = 16 (binary: 00010000) then x <<= 3 = 00000010 or 2.  This even works if x is not a power of 8 it just removes the remainder.

If we divide by a number that is not a power of 2 things get a lot more complicated.  We wont go into optimization routines for this type of computations.  See a Digital Signal Processing book for help.  My advice is to not do this computation if you have to.  Another way is to use the compiler to do the computation but do it when the timing isn’t critical like at the first of the program during Initialization or during the main loop (not in an ISR).

 

Daycounter is a contract engineering company specializing in a variety of electronics and software design. Please contact us if you would like to outsource your next product design.






Soil Moisture Sensor Probe



Water Level Sensor