1) Missing custom libraries. The libraries need to be added to LIB.DIR.
2) Modified Z-World libraries. The original programmer may have modified
libraries provided by Z-World, and any changes are in the newly installed libraries.
1) This may occur if a compiler interleave occured in the middle of a string table. To fix this
problem, just disable interleaving by adding '#nointerleave' to the top of your program.
2) You may have used a string constant within an inline C statement contained in a block of inline assembly. An obvious way around this is to not use inline C at all.
You may find the location of the error by using Disassemble At Cursor option (Ctrl-F10, or INSPECT | DISASSEMBLE AT CURSOR). Place the cursor somewhere in you program, press Ctrl-F10, then move up or
down depending on whether the address of disassembly is higher or lower than the address you're seeking. If the logical address is less than 2000h, the address is in the BIOS and probably nonsense. If the logical address is E000h or greater, the address is in virtual memory and incomplete.
Whenever Dynamic C initializes communications with a controller, it downloads a symbol table residing in the target BIOS (this occurs during the display on the "Loading Information from Target" message).
This table tells Dynamic C where to find I/O device and entry points for library routines in EPROM.
If you move the serial cable without downloading the symbol table of the new BIOS, Dynamic C will mistakenly think it's still connected to the first target and compile the program using the symbol table of the first BIOS. Unless the two BIOSes are identical, the program will compile, but fail when library routines in EPROM are called.
Whenever you move the Dynamic C serial cable, perform the Reset Target function (either from the menu or by pressing Ctrl-Y). This will dump the old symbol table and load the symbol table from the BIOS in the current target.
Burning a program into EPROM which worked in RAM is a common source of errors. It is not a particularly difficult process, but it does involve multiple steps and several unknowns. The sequence is rather simple:
1. When program development is complete, compile the program using Ctrl-F3, the "Compile to File" option. The "Options/Compiler/File" type for the "Compile to File" option should be set to "Code with BIOS (*.BIN)".
2. If the compilation produces errors, attempt to correct the errors and retry compilation to file.
3. After this is accomplished, a .BIN file will have been generated containing the binary image of your compiled program. If the "Create .HEX File Also" box (OPTIONS | COMPILER) is checked, an extended Intel HEX file is generated.
4. If the compilation is successful, exit Dynamic C. You now have the data necessary to program an EPROM. Please note that Z-World controllers cannot program EPROMs. Programming must be done by third party devices.
5. Use an EPROM which is large enough for the program and fast enough for your controller. To determine the size of your program, look at the size of the .BIN file (don't use the .HEX file which is artificially large). Use the .BIN or .HEX file to program the selected EPROM.
6. Your newly programmed EPROM should replace the normal Dynamic C EPROM. Normally, configuration jumpers should not require any changes. However, based on the size of the new EPROM, it may be necessary to move one or two jumpers. See the hardware manual for your controller for more details.
The following trouble-shooting tips are listed by frequency of occurrence and the ease of remedy. They should uncover most problems.
1. You should be using an EPROM with a part number of some variation of 27C256, 27C512, 27C010, 27C020, or 27C040, which are different sizes of EPROM.
2. The EPROM may not be large enough for the program. To determine the program size, look at the size of the .BIN file. Don't use the size of the .HEX file, because it contains overhead and it's over twice as large as the actual program! EPROMs are measure in KiloBits, so a 27C256 is 32KByte, 27C512 is 64KByte, etc. Make sure the KByte size of the EPROM is at least as large as the size of the .BIN file.
3. The EPROM may not be fast enough. Controllers with 9Mhz clocks require 100 nSec or faster memory. All controllers faster than 9Mhz should use 70nSec memory.
4. The board may not be jumpered correctly for the selected EPROM. Most Z-World controllers that are not FLASH based, are shipped with 27C256s installed. If this is the size EPROM the selected, no jumpers are needed to be moved. If a 27C512 or larger is selected, one or two address jumpers will need to be changed. Consult Z-World documentation and/or schematics for the particulars for any given board. The SmartCore Z1 is configured to accept a 27C010, 27C020 or 27C040 without jumper changing. Smaller EPROMS (the 28-pin 27C256 and 27C512) will not work without moving J3, a surface-mounted zero-ohm resistor.
5. It is possible the EPROM was not burned correctly. Since EPROM programmers tend to verify in the manner they have programmed, verification is of little help if the programmer is setup incorrectly. The best way to verify the correctness of the programming procedure is to burn a new Dynamic C EPROM. Once this EPROM is burned, place the EPROM in a target board and try to have Dynamic C respond as normal. Note that it is not sufficient to copy the EPROM by reading the original EPROM into a buffer. The point of this exercise is to test whether your programmer can convert a file to an EPROM, not whether it can duplicate EPROMs. Most EPROM programmer problems occur in reading files.
6. Your program may contain STDIO (printf, putchar, etc.) function calls. These functions calls are intended for debugging purposes only. They communicate with the STDIO window in Dynamic C. Since the debugging kernel is not running when a program is in EPROM, executing one these commands will cause the program execution to stop.
7. Are variables being modified which are initialized when declared? Consider the declaration "int x = 6;". This defines an integer x. Since x is assigned a value when it is declared, the variable x is placed in code space, which at development time is RAM. During development, x may be freely modified. When the program is burned into EPROM, however, the value of x is also placed in EPROM and now it's value is fixed. If the proper execution of your program relies on modifying such variables, it will fail in EPROM.
A certain amount of overhead is incurred during development code for the purposes of debugging. When the program is compiled to EPROM, debugging code is removed, making the program slightly smaller and faster. While this may not seem like a problem, it can create some difficulties:
1. Busy delay loops tuned empirically by hand will run faster in ROM. The proportion of execution overhead devoted to debugging is inversely proportional to code complexity. Since busy loops are trivial code, debugging overhead is high and its removal can be dramatic. Loops in ROM can execute 2 to 3 times faster! The solution is to make such routines stand-alone functions and declare them as nodebug. This will remove the overhead at development time and should make the delay uniform.
2. If timing sensitive code (such as the initialization of an LCD) is written incorrectly, debugging overhead may introduce enough delay to allow the procedure to work. When this overhead is removed, the routine then fails. In this situation, the incorrect code must be fixed.
The KIO used with the Little Giant Series (models BL11xx) works during development, but not in EPROM. Some Dynamic C kernels initialize the wait state generator differently depending on whether the system is in RAM or EPROM. Since LCDs and the KIO require maximum I/O wait states, adding the statement
"outport ( DCNTL,0x30 );" as the first executable line of the main may solve some timing problems.
Two binary files compiled at different times will have the following differences which result in different check sums:
0x2240:one bytes
0x2288-0x228f: time information, 8 bytes
0x2305-0x2306: checksum two bytes
end of root code: 8 bytes, time information
A common problem in embedded systems is having variables retain their value across power up. This is needed in order to prevent operators from having to reprogram the system each time the power is cycled. Fortunately, Z-World boards and Dynamic C make this very easy. Variables are assigned fixed locations at compile time. Most Z-World boards have battery-backed RAM. And Dynamic C doesn't implicitly initialize any variables (unlike most
C compilers which zero static variables). Consequently, all global and local static variables in Dynamic C programs are non-volatile. Once a value is assigned to a variable, this value is retained until it is assigned another.
There are three ways variables get their values:
1. Explicit Initialization
In this case, the user must take some action (install a jumper, press a button, etc.) that will be recognized by the program as a signal to set all globals to their default values. This system has the benefit of being the only 100% fool-proof way to know whether or not a system needs initialization. The downside is an operator may forget to explicitly initialize the unit.
2. "Magic Number"
A magic number is a pattern stored in memory which indicates whether or not the system
has been initialized. In C, a magic number is normally implemented as:
long lMagic
void main ()
{ if (lMagic != 0x55AA5AA5L) {
// Do Initializations
lMagic = 0x55AA5AA5;
}
// Program Goes on from Here
}
Note that the magic number (0x55AA5AA5L) is chosen because it is unlikely to occur
randomly in RAM. Not accounting for the physical nature of randomized RAM, this
method will fail only once in 4.3 billion units.
3. Bounds Checking
Bounds checking is nothing more than an extension of the magic number principle.
Mission critical values (such as set points and safety ranges) are bounds checked
to see whether or not their values are reasonable, both with respect to other system
parameters and to absolute norms. If one (or any one of a collection) of such
parameters is out of bounds, the parameter (or entire group of parameters) is reset.
Bounds checking has the benefit of allowing the programmer to partition the variables to
be initialized into two or more group, while magic numbers allow a pass or fail type
initialization. Bounds checks are also more reliable because they can normalize the
system state in case a transient upset or software error wipes out one or more vital
operating parameters. The only draw back is some additional coding, but the result is
well worth the effort.