![]() |
|
ADSP 21xx
Have you found this site useful? Did we save you time? Did we cure your head-ache? Is your hair growing back now? Please make a donation to help with maintenance. |
Objective Real-Time Software on the ADSP21XXObject SpecificationsIntroductionIdentifying a software object is usually straight forward. The trick is to keep things functional and not to get too carried away breaking things up into impractically small modules. An object needs some size and weight to it. However, one should not exceed 16 to 32 kilobyte of text, or one may get mighty confused from scrolling up and down. For those few souls who were half asleep in the past few years, a software object is a structure of data, together with its associated procedures, or functions. This is none other than our old friend, a software module, or program unit, in a different guise. Also, a procedure, function, subroutine and method are all the same thing. Furthermore, inheritance and polymorphism is just a severe case of bad type definitions. The terminology tends to change whenever somebody writes a new doctor's thesis, but it is just the same old stuff warmed over, with little improvement, if any (Ooh, I can hear the OOD Lynch Mobs chanting in the distance...). In a typical DSP project, many objects can be identified based upon the processor functionality. Examples being:
Some objects will be singular, such as the flash memory. Others will be diverse, with no comonality, such as the interrupt handlers, while multiple CODECs will almost always be candidates for careful consideration, where you can exploit the symmetry in the hardware design, to save you from a large amount of coding and debugging. The symmetrical constructs are the cases where object oriented design really comes into its own. Source File OrganizationProper file layout can save you from a large amount of hammering at the keyboard, while if you aproach it wrongly, it may become a permanent drag for the life of the project. Essentially, objects need to be defined before they can be accessed. This dictates a certain form to the source files. Another important consideration is to avoid typing the same thing twice. The C pre-processor comes in very handy in this regard and helps us to avoid bugs, due to erroneous definitions.
How does one get the preprocessor to do one's bidding? If a preprocessor variable is defined in a source file, immediately before the list of include files, then we can use this variable to drive a set of conditional compilation switches in the include files. Some preprocessors are very picky and will remember a definition forever, while others limit the scope of a definition to only the file in which it was declared. Since it is better safe than sorry, we also added #undef commands to explicitly limit the scope of the directives. Example 1 is a short extract from a source file, which shows the definition of the pre-processor variable AERO.
In order to clarify the method behind the madness, I added a few details on a fictitious DSP project with some lamps and a small display. Example 2 shows the use of the preprocessor variable AERO, to resolve variable definitions. In essence, the variables and prototypes will be interpreted as GLOBAL or ENTRY in the source file in which AERO is defined, while they will be interpreted as EXTERNAL in all other source files that may include the header files. Therefore, one does not have to type the definitions twice, which reduces bugs and wear on the keyboard, as explained in more detail below.
The File HeaderWe start off with the file header block, to show what this is and who is the culprit responsible for it. After the header block, we encounter a strange C pre-processor directive: #ifdef AERO. In this conditional block, two new dot commands are created from four existing ones, to save your strained wrists and fingers and avoid a nasty bug or two. The identifier AERO should be defined in the file aero.dsp.
When the header file is included as part of aero.dsp, the top part of the conditional statement will be true and a variable defined as PUBLIC will be interpreted as a GLOBAL variable definition. When this header file is included by any other .dsp file, AERO will be undefined and the PUBLIC variable will be defined as EXTERNAL. This allows us to type the variable definition once only and get it over with! The same trick is used for procedure prototypes, which will be alternately defined as ENTRY or EXTERNAL. LiteralsNext comes the LITERAL block, where we define immediate constants and macros. These are things that are typically needed lower down in the file, so we have to declare them up at the top. Constants and VariablesAfter Literals, we get to Constants, Initialized Variables and Uninitialized Variables. Here, we need another pre-processor trick, to ensure that the definitions are processed once only, together with the aero.dsp file and not with all the other .dsp files that may include the file, to prevent multiple definitions of the same thing. In this block, we define three different memory segments. The INT_CONST segment in Program Memory (PM) space, the INT_INIT segment and INT_DM segments in Data Memory (DM) space. Note the use of commas in the variable list. This avoids having to type .VAR/DM/RAM/SEG=INT_DM, over and over, for every single variable.
Constants are initialized in an INIT statement. Group all the INIT statements together, after the .VAR declaration block. This will cause the values to be saved in the constant segment. At runtime, we have to copy this value to the initialized variable data segment. Note that we are using the same name, except that the constant is uppercase only and the corresponding initialized variable lower case only. As the assembler is case sensitive, we can do that. Since we have defined four different memory segments, the initialization of variables can be performed with a simple copy loop, to copy the entire INT_CONST segment to the INT_INIT segment in one go, at start up. Another thing we have to do, is to zero all of the ordinary variables, to ensure an ordered startup. This can be performed with a zero copy loop over all of the INT_DM segment. (The fourth memory segment, for those who are wondering, is the program code segment INT_PM).
The reason behind initializing variables from the program space, is to make the memory map simpler and enable us to use a utility called EXE2PROM.EXE instead of the quirky SPL21.EXE. By doing so, we need only save the program memory in EPROM and can ignore all of data memory in the linker output. This creates a very compact EPROM image, although it does waste some program memory space, but not much, since a typical system has very few initialized variables. As usual, it is a tradeoff, but a minor one in this case
Speaking of initialised variables. These things behave funny in program space. The initialisation value of a variable, will be loaded into the lower 16 bits of the 24 bit word, while a pointer (table or procedure address) will automatically be loaded into the upper 16 bits! This is just nuts. Therefore, to avoid having to write a complicated initialisation routine, all variable initialisation data should be multiplied by 0x0100. That explains the extra 00 tacked onto the end of AERO_LAMP_DEFAULT in example 2. ProceduresThe aero_pub.h file shows a procedure definition. Always start by listing procedure prototypes and variable definitions in the _loc.h file. Only move the definitions to the _pub.h file, once it becomes clear that another object needs to access it. Definitions in the _loc file are hidden from other objects and you should strive towards maximum information hiding, to reduce the danger of bugs spreading in your program.
In order to achieve minimal coupling, you should never have any variables defined in a _pub.h file. The only exception maybe, being a global system state variable, which should only be read, but never modified, outside of the module where it is defined. This exception is made in the interest of efficiency, since all objects always need to know whether the system is still in initialization state, or maybe in built in test state etc. For all other variables, if an object has to access a variable in another object, write a procedure to retrieve or modify the information. This minimizes coupling and maximizes information hiding, leading to fewer bugs and easier maintenance. |
|
Copyright © 1996-2008, Aerospace Software Ltd., GPL. |