Discussion
Forum
Home
Company Information
Information Request
Linux How-to Guides
ADSP 21xx Digital Signal
Processing Tutorials
SW Utilities
On-line Order Form
Aerospace Projects
Commercial Projects
Circuit Boards
Server Support
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 ADSP21XX
Flash Memory X-Modem Software Loader for the 218X
General
In this chapter, we introduce a software loader, which can be used to
download code to the target Flash memory, to fascilitate field upgrades.
This software loader enables one to use a block eraseable flash memory
chip and keep multiple copies of software or firmware in this single chip.
With the use of a loader program, one can selectively update any or all
of these blocks, without the use of an EPROM programmer.
I am never going to do field upgrades! Fine, but if you have
ever been in the situation where you wanted to do something at home while
you don't have an EPROM programmer, then this chapter is for you...
Home Upgradeable Software
If you have ever been in the situation where you wanted
to do something at home while you don't have an EPROM programmer, then
this chapter is for you too!
|
This leaves the question of how to get the loader into the EPROM. For
that you need an EPROM programmer, but once you have programmed a handful
of chips with your loader, you won't need a programmer anymore. It would
be even better, if you would enable the security fuse for the bottom sector
where the loader resides, to prevent accidental erasure of the laoder itself.
These fuses, available on the 29F010 chip, are reversable with the use
of an EPROM programmer, but prevents the loader from being erased under
ordinary circumstances. (The chip can be erased accidentally when a program
crashes and the processor executes random instructions, unless the security
fuses are enabled.).
A Robust Loader
In order to create a robust loader, we had to improve many of the code
modules discussed in other chapters. The serial UART, EEPROM routines and
even the debugger were improved and are all part of the loader in directory
\work\xmodem. We also had to create a new utility, to enable
us to debug the loader itself. This utility, called Xbug,
is included in the directory \aerosoft.
While there are many commercial programs that provide an X-modem capability,
including the buggy HyperTerminal which is given away for free with Windoze,
they all suffer from the same problem. These programs will not show the
data transferred between the PC and the target while the download is in
progress. Furthermore, they get confused when the target sends extraneous
characters to the PC.
Xbug is a limited, but very tolerant program and will
display everything passing on the channel. It also allows one to send debug
strings back to the PC. Provided that these strings don't contain the characters
ACK or NAK, Xbug won't care a hoot. This
provides a handy debug capability.
We assume that you are using a block eraseable Flash memory chip, eg.:
AMD29F010. This chip provides 1 megabit of storage in 8 pages of 16 kilobytes
each. A handy feature of this chip is that the page sizes corresponds to
the DSP page sizes, which makes it easy to use. All of PM can be stored
in 3 pages. If you save the software loader in page zero and a code image
in pages 1 to 3, you still have half the memory left for other things,
eg. non-volatile variables or PLD images.
In this example, page 1 is reserved for non-volatile variables and the
code is saved in pages 2 to 4. You can change the examples to position
the code wherever you prefer.
Whyfore X-Modem?
X-modem is pretty much the grand daddy of computer file exchange protocols.
It is popular, because of its simplicity. There are a few variations on
the theme called X-modem CRC, Y-modem, Y-modem batch and Z-modem. These
protocols are all the same - except where they are different...
X-Modem Variants
The X-Modem, X-modem CRC, Y-modem, Y-modem batch and Z-modem
protocols are all the same - except where they are different...
|
The differences concern the use of record 00 for the file name and file
size, as well as the type of checksum and the length of the records. The
basic idea remains the same however, with the result that all the protocols
suffer from the same basic problem: If the PC and the target would lose
synchronization, there is no way to recover automatically. This is not
a problem on a reliable channel though.
The disadvantages of X-modem for general pupose file transfers are legion,
but those same disadvantages are to our benefit for software loading. For
instance, Y-modem transfers the file name in record zero. This is nice
for a computer file exchange, but our DSP will choke on the file name if
we program it into EPROM!
The X-modem Protocol
The basic handshake is very simple. The file is packetized into small
packets. Each packet is framed with a header and a footer. The header contains
a synchronization character and sequence number, while the footer contains
a checksum. At startup, the receiving party (the target) sends a not-acknowledge
(NAK=15H) to the transmitting party (the PC). The PC then sends the first
packet, starting with sequence number 01H. When the target finished processing
the packet, it sends an acknowledge (ACK=06H), which signals the PC to
send the next packet.
This process carries on until either the PC runs out of data and sends
an end-of-text character (EOT=04H), or the target cancels the operation
with a cancel character (CAN=18H). All packets are 128 bytes in size. This
means that the last packet usually needs some padding to fill it up. This
padding character is the CP/M end-of-file character (EOF=1AH).
Packet Format:
SOH SEQ ~SEQ Data... SUM
where
SOH = 01H
SEQ = 01H..FFH, 00H..FFH, 00H..FFH, etc
~SEQ = 1's complement of SEQ
Data = 128 bytes of Free form data, 00H..FFH
SUM = simple 8 bit sum of all data bytes (no carry)
|
Flow control is maintained with special characters: acknowledge (send
next packet), not-acknowledge (repeat packet), cancel (give up) and end-of-text
(done).
Flow Control Characters
ACK = 06H, send next packet
NAK = 15H, repeat packet
CAN = 18H, cancell operation
EOT = 04H, done
Padding Character
EOF = 1AH, padding
|
Loading the Loader
The loader is very simple indeed and fits comfortably in one segment
of the EPROM. All source code is located in directory \work\xmodem
on the CD-ROM. Program the file \work\xmdm001.bin
at address 00000H of the EPROM. It would be a good idea to activate the
protection fuse for that sector while programming.
As presented here, the loader serial UART uses the External Flag pins
of the DSP for serial communication. If you fit RS232 drivers to those
two pins, then you would have a simple interface to a PC, operating at
9600 baud, 8 data bits, no parity and 1 stop bit. Your target card would
also require a crystal speed of 16MHz to operate properly. If your hardware
does not conform to these requirements, the serial UART I/O and timer ISR
may require some tweaking. This is described in some detail in the serial
UART chapter. I assume that people who use DSPs know what they are doing...
With the loader programmed at address 00000H of the boot EPROM, it will
load and run when the target is switched on. The loader will pause for
5 seconds, waiting for some keypresses from the user. If not received,
it will continue and boot the application code from page 2 in the EPROM.
If you press the Enter key, followed by the Y key for confirmation, the
loader will run and will immediately start to erase the EPROM from pages
2 through 7. The erase process is very thorough and verifies that every
byte is programmed to FFH, retrying as required.
Once the erase phase is complete, the loader will send a series of NAK
characters to the PC, to prompt it into action for an X-modem download.
Loading Code
The easiest method is to use a terminal emulator utility such as Procomm
or Crosstalk for the X-modem transfer. The HyperTerminal utility supplied
with Windoze is best avoided. It sometimes works, but frequently tends
to quit, proclaiming that the transaction was cancelled by the target.
This wasted a lot of my time, until I put a protocol analyzer on the line
and verified that it is indeed a HyperTerminal bug and not a bug in the
target code...
(HyperTerminal also starts with sequence number 00 instead of 01, which
is yet another bug.)
Start the terminal emulator and select VT100 emulation, at 9600 baud,
N81. Power up the target and follow the on-screen prompts. Remember that
you have to press Enter within 5 seconds from power up!
In order to verify that the loader works as expected, it is a good idea
to load a text file with known data, then after the load process is done,
read the EPROM with an EPROM programmer and verify that the data is indeed
located in page 2 at address 8000H as expected.
If you load proper code, with a 32 word BDMA loader located at addess
8000H (use the bdmahi.dsp example), the loader will transfer control to
the loaded code upon completion. If any errors are encountered during the
load process, the loader will run again.
In order to fascilitate testing, we have supplied a working program
flash.dsp which will flash a LED on the PF4 pin of the
DSP. The LED flasher is combined with the BDMA loader bdmahi.dsp
into a binary file flashhi.bin, which you can load with
the X-modem software loader.
Debugging your X-modem Loader
The only drawback of a terminal emulation program is that they do not
show the actual traffic on the serial port during the X-modem transfer.
This makes it very hard to debug the target code, unless one connects two
receive channels from another PC to the serial lines as a protocol analyzer.
We have included two utilities on the CD-ROM to enable you to debug
these serial communication programs. The better program is called Hexcom,
in the \aerosoft directory. This is a powerful two channel
protocol analyzer program, which we have been using for more than 10 years
and which served us well.
Another utility is called Xbug and resides in the same
directory. Xbug is a self contained X-modem debugger and
is useful when you have only one PC, or only one serial port on the PC.
Xbug accepts a file name on the DOS command line and will
transfer this file to the target, using the X-modem protocol, while showing
all the communication between the PC and the target on screen. It will
also log the communication to a log file xbug.log, for
easier fault finding.
Both programs are easy to use, have a minor bug or two, but will help
you see clearly what is going on, on the serial ports of your PC. Furthermore,
we included the full source code too (in directory \aerosoft\zip
on the CD), so you can change these programs to suit your environment.
Have a look at the Xbug source code first, it is a lot
simpler than Hexcom and uses the same serial communications
engine, fastcom.c.
The X-modem Source code
The X-modem loader consists of four distinct modules:
- xmdmmn.dsp, the main file and timer ISR
- xmdmee.dsp, the Flash EPROM routines
- xmdmut.dsp, the serial UART state machines
- xmdmld.dsp, the X-modem loader state machine
The following support modules are also included:
- xmdmbg.dsp, the runtime debugger, to help you get the code going
- flash.dsp, the LED flasher test program
- bdmalo.dsp, the BDMA page 0 till 2 loader
- bdmahi.dsp, the BDMA page 2 till 4 loader
The full source code is located in directory \work\xmodem
on the CD-ROM.
Here is the full text of the loader, note that we have already presented
simple versions of the UART and EPROM read/write routines in other chapters.
The loader will work with those examples, but it is recommended that you
rather use the improved versions on the CD-ROM, which have better error
checking.
/*********************************************************************
*
* Name: xmdmld.dsp
*
* Description: X-Modem SW Loader
*
* Copyright (c) 1996, Aerospace Software Ltd., Calgary, Canada
*
* Revisions:
* -------------------------------------------------------------------
* 1.0 July 96 Herman Oosthuysen Simple X-Modem loader
*
* Operation:
* -------------------------------------------------------------------
*
*********************************************************************/
.MODULE/RAM LOAD;
#define LD
#include "incl\xmdmmc.h"
#include "incl\xmdmrg.h"
#include "incl\xmdmmn.h"
#include "incl\xmdmee.h"
#include "incl\xmdmut.h"
#include "incl\xmdmld.h"
#undef LD
#define LD_SEQUENCE 0 /* 0 = normal, 1 = write numeric sequence */
#define LD_ECHO 0 /* 0 = normal, 1 = echo received characters */
/*********************************************************************
* Name: ld_loader
* Description: Start the SW Loader
* Constraints: ar = type of load: LD_CODE/LD_PLD
*********************************************************************/
ld_loader:
MAC_ENTER
ld_loader_enter:
call ut_clr_screen; /* opening screen */
ar = ^XMDM_MSG;
call ut_string_out;
call ut_crlf;
ar = ^LOADER_MSG;
call ut_string_out;
call ut_crlf;
call ut_crlf;
ax0 = 10; /* set 10s confirmation timeout */
ar = LD_10SEC; /* wait 10s for a key press to be received */
dm(load_timer) = ar;
ld_loader_wait_key:
call ut_char_in;
af = ar + 1;
if ne jump ld_loader_keypressed;
ar = dm(load_timer);
af = pass ar;
if ne jump ld_loader_wait_key;
call ld_reboot;
ld_loader_keypressed: /* test for any key */
ay0 = LD_CR;
af = ar - ay0;
if eq jump ld_loader_confirm_txt; /* if ENTER , get confirmation */
call ld_reboot; /* not enter - Reboot with Run Code Image */
jump ld_loader_exit;
ld_loader_confirm_txt:
ar = ^AREYOUSURE_MSG;
call ut_string_out;
call ut_crlf;
ld_loader_key_confirm: /* test for confirm key */
call ut_char_in;
af = ar + 1;
if eq jump ld_loader_key_confirm;
ax0 = ar;
call ut_char_out;
ar = ax0;
ay0 = 0x59; /* something - check it */
af = ar - ay0;
if eq jump ld_loader_go; /* Y run loader */
ay0 = 0x79;
af = ar - ay0;
if eq jump ld_loader_go; /* y run loader */
ay0 = 0x6E;
af = ar - ay0;
if eq jump ld_reboot; /* n reboot */
ay0 = 0x4E;
af = ar - ay0;
if eq jump ld_reboot; /* N reboot */
jump ld_loader_confirm_txt; /* wozzat? - loop */
ld_loader_go:
ar = 0; /* init the start page and address */
dm(ld_address) = ar;
ar = EE_PAGE2;
dm(ld_page) = ar;
ar = LD_ERASE; /* start the load machine - erase EPROM */
dm(ld_state) = ar;
ar = LD_NAK;
dm(ld_hello) = ar;
ar = LD_1SEC;
dm(load_timer) = ar;
ld_loader_wait_conf:
ar = dm(load_timer);
af = pass ar;
if ne jump ld_loader_wait_conf;
ld_loader_exit:
MAC_EXIT
rts;
/*********************************************************************
* Name: ld_machine
* Description: SW Loader Finite State Machine
* Constraints: none
*********************************************************************/
ld_machine:
MAC_ENTER
ld_machine_enter:
ar = dm(ld_state); /* current state */
MAC_MACHINE(^LD_TBL, ar) /* lookup and jump */
ld_machine_return:
dm(ld_state) = ar; /* next state */
ld_machine_exit:
MAC_EXIT
rts;
/*********************************************************************
* Name: ld_idle
* Description: do nothing
* Constraints: none
*********************************************************************/
ld_idle:
ar = LD_IDLE;
jump ld_machine_return;
/*********************************************************************
* Name: ld_erase
* Description: erase EPROM pages 2 through 7
* Constraints: none
*********************************************************************/
ld_erase:
MAC_ENTER
ld_erase_enter: /* erase from pages 2 till 7 */
call ut_crlf;
ay1 = EE_PAGE1;
ax1 = EE_PAGE2;
cntr = LD_MAX_PAGES;
do ld_erase_code until ce;
ar = ax1;
call ee_erase;
af = pass ar; /* save the error count */
ar = dm(ld_errors);
ar = ar + af;
dm(ld_errors) = ar;
ar = ax1 + ay1;
ax1 = ar;
ar = 0x2E;
call ut_char_out;
ar = 2;
dm(load_timer) = ar;
ld_erase_wait:
ar = dm(load_timer);
af = pass ar;
if ne jump ld_erase_wait;
ld_erase_code: nop;
ar = dm(ld_errors); /* test the error count */
ar = pass ar;
if eq jump ld_erase_pass;
ld_erase_fail: /* FAILED! */
call ut_crlf;
ar = ^ERASE_FAIL_MSG;
call ut_string_out;
call ut_crlf;
ar = LD_1SEC; /* wait for text to be displayed */
dm(load_timer) = ar;
ld_erase_wait2:
ar = dm(load_timer);
af = pass ar;
if ne jump ld_erase_wait2;
jump ld_erase_enter; /* retry erase */
ld_erase_pass: /* PASS! */
call ut_crlf;
ar = ^ERASE_PASS_MSG;
call ut_string_out;
call ut_crlf;
ar = ^XMODEM_MSG;
call ut_string_out;
call ut_crlf;
ar = LD_1SEC; /* wait for text to be displayed */
dm(load_timer) = ar;
ld_erase_wait3:
ar = dm(load_timer);
af = pass ar;
if ne jump ld_erase_wait3;
ar = LD_START;
ld_erase_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_start
* Description: send C/Ack every 10 seconds until SOH/STX
* Constraints: none
*********************************************************************/
ld_start:
MAC_ENTER
ld_start_enter:
call ut_char_in; /* look for SOH or STX */
af = ar + 1;
if eq jump ld_start_timer; /* nothing received - send C again */
#if LD_ECHO
call ut_char_out;
#endif
ld_start_syn: /* something received */
ay0 = LD_SOH;
af = ar - ay0;
if eq jump ld_start_soh; /* short msg */
ay0 = LD_STX;
af = ar - ay0;
if eq jump ld_start_stx; /* long msg */
jump ld_start_timer; /* garbage received - send C again */
ld_start_soh:
ar = LD_SHORT; /* SOH = short packets */
dm(ld_length) = ar;
jump ld_start_clear;
ld_start_stx:
ar = LD_LONG; /* STX = long packets */
dm(ld_length) = ar;
ld_start_clear:
call ld_buffer_init;
ar = LD_1SEC; /* reset timer */
dm(load_timer) = ar;
ar = LD_RX_BLOCK; /* go receive block number */
jump ld_start_exit;
ld_start_timer:
ar = dm(load_timer); /* test C timer */
af = pass ar;
if eq jump ld_start_c;
ar = LD_START; /* not expired */
jump ld_start_exit;
ld_start_c: /* expired */
ar = LD_10SEC; /* reset timer */
dm(load_timer) = ar;
ar = dm(ld_hello); /* send C = CRC16, or NAK = Sum */
call ut_char_out;
ar = dm(ld_eot_cnt); /* count the syn errors */
ar = ar + 1; /* if more than 10, assume EOT */
dm(ld_eot_cnt) = ar;
ay0 = LD_EOT_MAX;
af = ar - ay0;
if eq jump ld_start_err;
ar = LD_START;
jump ld_start_exit;
ld_start_err:
ar = LD_END;
ld_start_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_rx_syn
* Description: receive syn = stx/soh
* Constraints: none
*********************************************************************/
ld_rx_syn:
MAC_ENTER
ld_rx_syn_enter:
call ut_char_in; /* look for SOH or STX */
af = ar + 1;
if eq jump ld_rx_syn_timer;/* nothing received */
ay1 = ar;
#if LD_ECHO
call ut_char_out;
#endif
ay0 = LD_SOH;
af = ar - ay0;
if eq jump ld_rx_syn_soh; /* short msg */
ay0 = LD_STX;
af = ar - ay0;
if eq jump ld_rx_syn_stx; /* long msg */
ay0 = LD_EOT;
af = ar - ay0;
if eq jump ld_rx_syn_eot; /* end of transmission */
jump ld_rx_syn_timer; /* garbage received */
ld_rx_syn_eot:
ar = LD_END; /* end of transmission - quit */
jump ld_rx_syn_exit;
ld_rx_syn_soh:
ar = LD_SHORT; /* SOH = short packets */
dm(ld_length) = ar;
jump ld_rx_syn_clear;
ld_rx_syn_stx:
ar = LD_LONG; /* STX = long packets */
dm(ld_length) = ar;
ld_rx_syn_clear:
call ld_buffer_init;
ar = LD_RX_BLOCK; /* go receive block number */
jump ld_rx_syn_exit;
ld_rx_syn_timer:
ar = dm(load_timer); /* test timer */
af = pass ar;
if eq jump ld_rx_syn_none;
ar = LD_RX_SYN; /* not expired */
jump ld_rx_syn_exit;
ld_rx_syn_none:
ar = LD_1SEC; /* reset timer */
dm(load_timer) = ar;
ar = dm(ld_eot_cnt); /* count the syn errors */
ar = ar + 1; /* if more than 10, assume EOT */
dm(ld_eot_cnt) = ar;
ay0 = LD_EOT_MAX;
af = ar - ay0;
if eq jump ld_rx_syn_err;
ar = LD_RX_SYN;
jump ld_rx_syn_exit;
ld_rx_syn_err:
ar = LD_END;
ld_rx_syn_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_rx_block
* Description: receive a block number
* Constraints: none
*********************************************************************/
ld_rx_block:
MAC_ENTER
ld_rx_block_enter:
call ut_char_in;
af = ar + 1;
if eq jump ld_rx_block_none;
#if LD_ECHO
call ut_char_out;
#endif
ay0 = ar; /* save the block in ay0 */
ar = LD_1SEC; /* reset timer */
dm(load_timer) = ar;
ar = dm(ld_bpos); /* second block number? */
af = pass ar;
if ne jump ld_rx_block_done;
ar = 1;
dm(ld_bpos) = ar;
ar = dm(ld_block); /* first block number */
af = ar - ay0;
if eq jump ld_rx_block_repeat; /* same message as before? */
dm(ld_block) = ay0; /* save block number */
ar = LD_RX_BLOCK;
jump ld_rx_block_exit;
ld_rx_block_repeat: /* repeated message */
ar = 0;
dm(ld_valid) = ar; /* invalid */
ar = LD_RX_BLOCK;
jump ld_rx_block_exit;
ld_rx_block_done:
ar = dm(ld_block); /* 2nd block no is 1's compliment */
ar = not ar;
ay1 = 0x00FF; /* byte mask the compliment */
ar = ar AND ay1;
af = ar - ay0;
if ne jump ld_rx_block_err;
ar = LD_RX_DATA; /* go get the message data */
jump ld_rx_block_exit;
ld_rx_block_err:
ar = 0;
dm(ld_valid) = ar; /* block error --> invalid message */
ar = LD_RX_DATA; /* go get the message data */
jump ld_rx_block_exit;
ld_rx_block_none:
ar = dm(load_timer);
af = pass ar;
if eq jump ld_rx_block_err2; /* block timeout */
ar = LD_RX_BLOCK;
jump ld_rx_block_exit;
ld_rx_block_err2:
{call mn_flasher;}
ar = LD_TX_NAK;
ld_rx_block_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_rx_data
* Description: receive a packet and test CRC
* Constraints: none
*********************************************************************/
ld_rx_data:
MAC_ENTER
ld_rx_data_enter:
call ut_char_in;
af = ar + 1;
if eq jump ld_rx_data_none;
ay0 = ar; /* save the rx data in ay0 */
#if LD_ECHO
call ut_char_out;
#endif
call ld_calc_crc; /* calculate progressive CRC */
ar = LD_1SEC; /* reset timer */
dm(load_timer) = ar;
ar = dm(ld_pos); /* save the data in the rx buffer */
ar = ar + 1; /* inc the data position */
dm(ld_pos) = ar;
MAC_WR_DM(ay0, ^ld_buf, ar)
ay1 = dm(ld_length); /* compare with long/short length */
af = ar - ay1;
if eq jump ld_rx_data_done; /* end? */
ar = LD_RX_DATA;
jump ld_rx_data_exit; /* next */
ld_rx_data_done:
ar = LD_RX_CRC;
jump ld_rx_data_exit;
ld_rx_data_none:
ar = dm(load_timer);
af = pass ar;
if eq jump ld_rx_data_err; /* timeout */
ar = LD_RX_DATA;
jump ld_rx_data_exit;
ld_rx_data_err:
ar = LD_TX_NAK;
ld_rx_data_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_get_crc
* Description: verify CRC16/Sum
* Constraints: none
*********************************************************************/
ld_get_crc:
MAC_ENTER
ld_get_crc_enter:
call ut_char_in;
af = ar + 1;
if eq jump ld_get_crc_none;
ay0 = ar;
#if LD_ECHO
call ut_char_out;
#endif
ar = LD_1SEC; /* reset timer */
dm(load_timer) = ar;
ld_get_crc_done:
ar = dm(ld_crc); /* compare CRCs */
af = ar - ay0;
if ne jump ld_get_crc_fail;
ar = dm(ld_valid);
af = pass ar;
if eq jump ld_get_crc_fail;
ar = LD_WRITE; /* CRC OK and valid */
jump ld_get_crc_exit;
ld_get_crc_fail: /* CRC bad or invalid */
ar = LD_TX_NAK;
jump ld_get_crc_exit;
ld_get_crc_none:
ar = dm(load_timer);
af = pass ar;
if eq jump ld_get_crc_err; /* timeout */
ar = LD_RX_CRC;
jump ld_get_crc_exit;
ld_get_crc_err:
ar = LD_TX_NAK;
ld_get_crc_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_write
* Description: write to EPROM
* Constraints: none
*********************************************************************/
ld_write:
MAC_ENTER
ld_write_enter:
ay0 = dm(ld_page); /* page */
ax0 = dm(ld_address); /* address */
ax1 = 1; /* buffer start position */
cntr = dm(ld_length);
do ld_write_loop until ce;
MAC_DISABLE(mr0)
call ee_program; /* select program mode */
MAC_RD_DM(ar, ^ld_buf, ax1)
#if LD_SEQUENCE
ar = ax1; /* sequential write test */
#endif
call ee_write_byte; /* write to EPROM */
MAC_ENABLE(mr0)
af = pass ar;
ar = dm(ld_errors);
ar = ar + af;
dm(ld_errors) = ar; /* save error count */
ar = ax1 + 1; /* inc buf pos */
ax1 = ar;
ay1 = LD_ADDR_MSK; /* inc address */
ar = ax0 + 1;
ar = ar AND ay1;
ax0 = ar;
dm(ld_address) = ar;
if ne jump ld_write_next; /* inc page */
ar = EE_PAGE1;
ar = ar + ay0;
ay0 = ar;
dm(ld_page) = ar;
ld_write_next: nop;
ld_write_loop: nop;
ar = LD_TX_ACK; /* send ACK - next record */
ld_write_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_tx_ack
* Description: send an ACK
* Constraints: none
*********************************************************************/
ld_tx_ack:
MAC_ENTER
ld_tx_ack_enter:
ar = LD_ACK; /* send ACK */
call ut_char_out;
ar = LD_1SEC;
dm(load_timer) = ar;
ar = LD_RX_SYN;
ld_tx_ack_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_tx_nak
* Description: send a NAK
* Constraints: none
*********************************************************************/
ld_tx_nak:
MAC_ENTER
ld_tx_nak_enter:
ar = LD_NAK; /* send NAK */
call ut_char_out;
ar = LD_1SEC;
dm(load_timer) = ar;
ar = LD_RX_SYN;
ld_tx_nak_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_end
* Description: SW load end - reboot
* Constraints: none
*********************************************************************/
ld_end:
MAC_ENTER
ld_end_enter:
ar = LD_ACK; /* send ACK */
call ut_char_out;
ar = LD_CAN; /* send CAN - to ensure PC terminates X-Modem now */
call ut_char_out;
ar = LD_10SEC;
dm(load_timer) = ar;
ld_end_wait1:
ar = dm(load_timer);
ar = pass ar;
if ne jump ld_end_wait1;
call ut_crlf; /* Finito */
ar = ^DONE_MSG;
call ut_string_out;
ar = dm(ld_errors); /* display write error count */
call ut_out_hex4;
ar = LD_10SEC;
dm(load_timer) = ar;
ld_end_wait2:
ar = dm(load_timer);
ar = pass ar;
if ne jump ld_end_wait2;
ar = dm(ld_errors);
ar = pass ar; /* test the error count */
if ne jump mn_reset; /* Fail: run loader again */
call ld_reboot; /* Pass: reboot from page 2 */
ld_end_exit:
MAC_EXIT
jump ld_machine_return;
/*********************************************************************
* Name: ld_reboot
* Description: Reboot the DSP with 32 words of code starting at page 2
* Constraints: none
*********************************************************************/
ld_reboot:
MAC_ENTER
ld_reboot_enter:
{call mn_flasher;} /* DEBUG!!! */
dis ints; /* Disable all interrupts */
ar = 0; /* Reboot the DSP from Flash memory Page 2 */
dm(BEAD) = ar; /* External address = 0 */
dm(BIAD) = ar; /* Internal address = 0 */
ar = LD_REBOOT_P2; /* EPROM page = 2 */
dm(BCONTROL) = ar; /* Halt while copying, then reset */
ar = 32;
dm(BWCOUNT) = ar;
jump mn_reset; /* Warm boot: should not get here... */
ld_reboot_exit:
MAC_EXIT
rts;
/*********************************************************************
* Name: ld_calc_crc
* Description: calculate progressive CRC
* Constraints: ar = data byte
*********************************************************************/
ld_calc_crc:
MAC_ENTER
ld_calc_crc_enter:
ay1 = ar;
ar = dm(ld_crc);
ar = ar + ay1;
ay1 = 0x00FF;
ar = ar AND ay1;
dm(ld_crc) = ar;
ld_calc_crc_exit:
MAC_EXIT
rts;
/*********************************************************************
* Name: ld_eprom_crc
* Description: calculate CRC on EPROM pages 2 through 7
* returns CRC in ar
* Constraints: CRC saved in global variable ld_crc
*********************************************************************/
ld_eprom_crc:
MAC_ENTER
ld_eprom_crc_enter:
ar = 0;
dm(ld_crc) = ar; /* zero the crc */
ay0 = EE_PAGE2; /* EPROM page */
ax0 = 0; /* EPROM page address */
cntr = LD_MAX_PAGES;
do ld_eprom_crc_pages until ce;
cntr = LD_ADDR_MSK; /* cntr can only hold 3FFF max */
do ld_eprom_crc_calc until ce;
ar = ax0;
call ee_read_byte;
call ld_calc_crc;
ar = ax0 + 1; /* next address */
ax0 = ar;
ld_eprom_crc_calc: nop;
ar = ax0; /* do the 4000th byte */
call ee_read_byte;
call ld_calc_crc;
ar = ax0 + 1; /* next address */
ax0 = ar;
ar = EE_PAGE1; /* next page */
ar = ar + ay0;
ay0 = ar;
ld_eprom_crc_pages: nop;
ar = dm(ld_crc); /* return crc in ar */
ld_eprom_crc_exit:
MAC_EXIT
rts;
/********************************************************************
* Name: ld_buffer_init()
* Description: Erase the packet buffer
* Constraints: none
********************************************************************/
ld_buffer_init:
MAC_ENTER
ay0 = 0; /* zero the load buffer */
ax0 = 0;
cntr = LD_LONG;
do ld_buffer_zero until ce;
MAC_WR_DM(ay0, ^ld_buf, ax0)
ar = ax0 + 1;
ax0 = ar;
ld_buffer_zero: nop;
ar = 0; /* SYN received: init the receiver */
dm(ld_eot_cnt) = ar;
dm(ld_bpos) = ar;
dm(ld_pos) = ar;
dm(ld_crc) = ar;
dm(ld_errors) = ar;
ar = 1; /* assume valid until proven bad */
dm(ld_valid) = ar;
MAC_EXIT
rts;
.ENDMOD;
/************************* EOF - xmdmld.dsp **************************/
|
Finally, here we have the definitions required to make the source work:
/*********************************************************************
*
* Name: xmdmld.h
*
* Description: X-Modem SW Loader
*
* Copyright (c) 1996, Aerospace Software Ltd., Calgary, Canada
*
* Revisions:
* -------------------------------------------------------------------
* 1.0 July 96 Herman Oosthuysen Simple X-Modem loader
*
* Operation:
* -------------------------------------------------------------------
* This loader uses the basic Xmodem protocol
*
* The handshake starts when the target sends a NAK to the PC
* The PC sends the first packet, starting at packet number 01H
* Packets have 128 data bytes and a simple 8 bit checksum
* Once the target saved the packet to EPROM, it sends an ACK
* The PC responds with another packet, etc
* The end of the file is signified by the PC sending CTRL-Z
* If the target detects a Checksum error, it sends a NAK to the PC
* The PC sends the previous packet again
*
* Packet format:
* SOH BLK ~BLK DATA... CHK
*
* where
* SOH = 01H
* BLK = packet number, starting at 01H till FFH, then 00H, etc
* ~BLK = 1s compliment of BLK, as a simple check
* DATA = free form data, up to 128 bytes
* CHK = simple 8 bit addition of all data bytes
*
* ACK = 06H
* NAK = 15H
* CAN = 18H
* CTRL-Z = 1AH
*
* Y-Modem:
* Y-modem is an adaptation of X-modem. The differences are:
* 1024 byte packets
* start with character C = 43H instead of NAK
* the first packet 00H may contain the file name (xmdm starts at 01H)
* instead of a checksum, Y-modem uses a 16 bit CRC16
* otherwise, the idea is the same.
*
*
*********************************************************************/
/* Resolve public definitions */
#undef PUBLIC
#undef PROTOTYPE
#ifdef LD
#define PUBLIC GLOBAL
#define PROTOTYPE ENTRY
#else
#define PUBLIC EXTERNAL
#define PROTOTYPE EXTERNAL
#endif
/***************************** Literals *****************************/
/* control characters */
#define LD_SOH 0x01
#define LD_STX 0x02
#define LD_EOT 0x04
#define LD_ACK 0x06
#define LD_NAK 0x15
#define LD_CAN 0x18
#define LD_C 0x43
#define LD_EOF 0x1A
#define LD_CR 0x0D
/* timeout */
#define LD_10SEC 500
#define LD_1SEC 100
#define LD_EOT_MAX 10
/* data block sizes */
#define LD_SHORT 128 /* SOH */
#define LD_LONG 1024 /* STX */
/* loader states */
#define LD_IDLE 0x0000 /* do nothing */
#define LD_ERASE 0x0001 /* erase EPROM (PLD/CODE) */
#define LD_START 0x0002 /* send C every 10 seconds */
#define LD_RX_SYN 0x0003 /* get soh/stx */
#define LD_RX_BLOCK 0x0004 /* get block no */
#define LD_RX_DATA 0x0005 /* get a packet */
#define LD_RX_CRC 0x0006 /* test CRC */
#define LD_WRITE 0x0007 /* program packet in EPROM */
#define LD_TX_ACK 0x0008 /* send ACK */
#define LD_TX_NAK 0x0009 /* send NAK */
#define LD_END 0x000A /* send ACK and reboot */
#define LD_TBL_SIZE 0x000B
/* Type of Load */
#define LD_CODE 0x0000
#define LD_PLD 0x0001
#define LD_ADDR_MSK 0x3FFF /* address wrap mask */
#define LD_MAX_PAGES 0x0006 /* six pages to erase/load */
#define LD_REBOOT_P2 0x0208 /* load from EPROM page 2 and reboot */
#define LD_RETRY_MAX 0x0010 /* maximum write retry counter */
/***************************** Constants ****************************/
#ifdef LD
.VAR/PM/SEG=INT_PM
LD_TBL[LD_TBL_SIZE];
.INIT LD_TBL:
^ld_idle,
^ld_erase,
^ld_start,
^ld_rx_syn,
^ld_rx_block,
^ld_rx_data,
^ld_get_crc,
^ld_write,
^ld_tx_ack,
^ld_tx_nak,
^ld_end;
/***************************** Variables ****************************/
.VAR/DM/SEG=INT_DM
ld_buf[LD_LONG],
ld_state,
ld_rx_crc,
ld_crc,
ld_block,
ld_bpos,
ld_cpos,
ld_pos,
ld_length,
ld_rxd,
ld_txd,
ld_valid,
ld_type,
ld_page,
ld_address,
ld_hello,
ld_eot_cnt,
ld_errors;
#endif
/***************************** Publics ******************************/
/***************************** Prototypes ***************************/
.PROTOTYPE
ld_machine, /* Loader Finite State Machine */
ld_loader, /* Start the load process */
ld_calc_crc,
ld_end,
ld_buffer_init;
/************************* EOF - xmdmld.h ****************************/
|
How to build the X-modem Loader Code
Drag and drop copy the whole directory \work\xmodem
from the CD-ROM onto your hard disk.
Build the code using the batch file m.bat
This will create a bunch of binary files:
- flashhi.bin, the LED flasher binary code file, for page 2 (address
08000H) in the EPROM.
- xmdm001.bin, the X-modem loader binary code file for page 0 (address
00000H) in the EPROM.
Have fun!
|
|