File: adprog.c

package info (click to toggle)
lpc21isp 1.97-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster, sid, trixie
  • size: 612 kB
  • sloc: ansic: 6,283; makefile: 36
file content (325 lines) | stat: -rw-r--r-- 12,037 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/******************************************************************************

Project:           Portable command line ISP for NXP LPC1000 / LPC2000 family
                   and Analog Devices ADUC70xx

Filename:          adprog.c

Compiler:          Microsoft VC 6/7, Microsoft VS2008, Microsoft VS2010,
                   GCC Cygwin, GCC Linux, GCC ARM ELF

Author:            Martin Maurer (Martin.Maurer@clibb.de)

Copyright:         (c) Martin Maurer 2003-2014, All rights reserved
Portions Copyright (c) by Aeolus Development 2004 http://www.aeolusdevelopment.com

    This file is part of lpc21isp.

    lpc21isp is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    lpc21isp is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    and GNU General Public License along with lpc21isp.
    If not, see <http://www.gnu.org/licenses/>.
*/

#if defined(_WIN32)
#if !defined __BORLANDC__
#include "StdAfx.h"
#endif
#endif // defined(_WIN32)
#include "lpc21isp.h"

#ifdef AD_SUPPORT
#include "adprog.h"

/***************************** AnalogDevicesSync ************************/
/**  Attempt to synchronize with an Analog Device ARM micro.  Sends a
backspace and reads back the microcontrollers response.  Performs
multiple retries. Exits the program on error, returns to caller in the
case of success.
*/
static void AnalogDevicesSync(ISP_ENVIRONMENT *IspEnvironment)
{
    BINARY sync;                        /* Holds sync command.          */
    AD_SYNC_RESPONSE response;          /* Response from micro.         */
    int sync_attempts;                  /* Number of retries.           */

    /*  Make sure we don't read garbage later instead of the        */
    /* response we expect from the micro.                           */
    ClearSerialPortBuffers(IspEnvironment);

    DebugPrintf(2, "Synchronizing\n"); /* Progress report.             */

    sync = ANALOG_DEVICES_SYNC_CHAR;    /* Build up sync command.       */

    /*  Perform the actual sync attempt.  First send the sync       */
    /* character, the attempt to read back the response.  For the   */
    /* AD ARM micro this is a fixed length block.  If response is   */
    /* received attempt to validate it by comparing the first       */
    /* characters to those expected.  If the received block does    */
    /* not validate or is incomplete empty the serial buffer and    */
    /* retry.                                                       */
    for (sync_attempts = 0; sync_attempts < 5; sync_attempts++)
    {
        SendComPortBlock(IspEnvironment, &sync, 1);

        if (ReceiveComPortBlockComplete(IspEnvironment, &response, sizeof(response),
            500) == 0)
        {

            if (memcmp(response.product_id, ANALOG_DEVICES_SYNC_RESPONSE,
                ANALOG_DEVICES_SYNC_SIZE) == 0)
            {
                return;
            }
            else
            {
                DumpString(3, &response, sizeof(response),
                    "Unexpected response to sync attempt ");
            }
        }
        else
        {
            DebugPrintf(3, "No (or incomplete) answer on sync attempt\n");
        }

        ClearSerialPortBuffers(IspEnvironment);
    }

    DebugPrintf(1, "No (or unacceptable) answer on sync attempt\n");
    exit(4);
}

typedef struct {
    char start1;
    char start2;
    BINARY bytes;
    char cmd;
    BINARY address_h;
    BINARY address_u;
    BINARY address_m;
    BINARY address_l;
    BINARY data[251];
} AD_PACKET;

/***************************** AnalogDevicesFormPacket ******************/
/**  Create an Analog Devices communication packet from the constituent
elements.
\param [in] cmd The command being sent, one of 'E' for erase, 'W' for
write, 'V' for verify or 'R' for run..
\param [in] no_bytes the number of data bytes to send with the command in
the packet.
\param [in] address the address to apply the command to.
\param [in] data the data to send with the packet, may be null if no_bytes
is zero.
\param[out] packet that will be filled.
*/
static void AnalogDevicesFormPacket(ISP_ENVIRONMENT *IspEnvironment,
                                                char cmd, int no_bytes, unsigned int address,
                                                const void *data, AD_PACKET *packet)
{
    BINARY checksum;
    const BINARY *data_in;
    int i;

    (void)IspEnvironment; /* never used in this function */

    /*  Some sanity checking on the arguments.  These should only   */
    /* fail if there is a bug in the caller.                        */
    /*  Check 1) that the number of data bytes is in an acceptable  */
    /* range, 2) that we have a non-null pointer if data is being   */
    /* put in the packet and 3) that we have a non-null pointer to  */
    /* the packet to be filled. We just exit with an error message  */
    /* if any of these tests fail.                                  */
    if ((no_bytes < 0) || (no_bytes > 250))
    {
        DebugPrintf(1,
            "The number of bytes (%d) passed to FormPacket is invalid.\n",
            no_bytes);
        exit(-1);
    }
    if ((data == 0) && (no_bytes != 0))
    {
        DebugPrintf(1,
            "A null pointer to data paased to FormPacket when data was expected.\n");
        exit(-1);
    }
    if (packet == 0)
    {
        DebugPrintf(1,
            "A null packet pointer was passed to FormPacket.\n");
        exit(-1);
    }

    checksum = 0;               /*  Checksum starts at zero.            */

    data_in = (BINARY*) data;             /*  Pointer pun so we can walk through  */
    /* the data.                            */

    packet->start1 = 0x7;       /*  The start of the packet is constant.*/
    packet->start2 = 0xE;

    /*  Fill in the rest of the packet and calculate the checksum   */
    /* as we go.                                                    */

    /* The number of bytes is the number of data bytes + the        */
    /* address bytes + the command byte.                            */
    packet->bytes = (BINARY)(no_bytes + 5);

    checksum += packet->bytes;

    /*  The command for the packet being sent.  No error checking   */
    /* done on this.                                                */
    packet->cmd = cmd;

    checksum += cmd;

    /*  Now break up the address and place in the proper packet     */
    /* locations.                                                   */
    packet->address_l = (BINARY)(address & 0xFF);
    packet->address_m = (BINARY)((address >> 8) & 0xFF);
    packet->address_u = (BINARY)((address >> 16) & 0xFF);
    packet->address_h = (BINARY)((address >> 24) & 0xFF);

    checksum += packet->address_l;
    checksum += packet->address_m;
    checksum += packet->address_u;
    checksum += packet->address_h;

    /*  Copy the data bytes into the packet.  We could use memcpy   */
    /* but we have to calculate the checksum anyway.                */
    for (i = 0; i < no_bytes; i++)
    {
        packet->data[i] = data_in[i];
        checksum += data_in[i];
    }

    /*  Finally, add the checksum to the end of the packet.         */
    packet->data[i] = (BINARY)-checksum;
}

/***************************** AnalogDevicesSendPacket ******************/
/**  Send a previously form Analog Devices communication.  Retry a
couple of times if needed but fail by exiting the program if no ACK is
forthcoming.
\param [in] packet the packet to send.
*/
static void AnalogDevicesSendPacket(ISP_ENVIRONMENT *IspEnvironment,
                                                const AD_PACKET * packet)
{
    BINARY response;
    int retry = 0;

    do {
        retry++;

        /*  Make sure we don't read garbage later instead of    */
        /* the response we expect from the micro.               */
        ClearSerialPortBuffers(IspEnvironment);

        /*  Send the packet, the size is the number of data     */
        /* bytes in the packet plus 3 bytes worth of header     */
        /* plus checksum.                                       */
        SendComPortBlock(IspEnvironment, packet, packet->bytes + 4);

        /*  Receive the response and check, return to caller    */
        /* if successful.                                       */
        if (ReceiveComPortBlockComplete(IspEnvironment, &response, 1, 5000) == 0)
        {
            if (response == ANALOG_DEVICES_ACK)
            {
                DebugPrintf(3, "Packet Sent\n");
                return;
            }
            if (response != ANALOG_DEVICES_NAK)
            {
                DebugPrintf(3, "Unexpected response to packet (%x)\n", (int)response);
            }
            DebugPrintf(2, "*");
        }
    } while (retry < 3);

    DebugPrintf(1, "Send packet failed\n");
    exit(-1);
}

/***************************** AnalogDevicesErase ***********************/
/**  Erase the Analog Devices micro.  We take the simple way out and
just erase the whole thing.
*/
static void AnalogDevicesErase(ISP_ENVIRONMENT *IspEnvironment)
{
    BINARY pages;
    AD_PACKET packet;

    pages = 0;
    DebugPrintf(2, "Erasing .. ");
    AnalogDevicesFormPacket(IspEnvironment, 'E', 1, 0, &pages, &packet);
    AnalogDevicesSendPacket(IspEnvironment, &packet);
    DebugPrintf(2, "Erased\n");
}

#define AD_PACKET_SIZE (250)

/***************************** AnalogDevicesWrite ***********************/
/**  Write the program.
\param [in] data the program to download to the micro.
\param [in] address where to start placing the program.
\param [in] bytes the size of the progrm to download.
*/
static void AnalogDevicesWrite(ISP_ENVIRONMENT *IspEnvironment,
                                         const void *data, long address, size_t bytes)
{
    AD_PACKET packet;
    const BINARY *prog_data;

    DebugPrintf(2, "Writing %d bytes ", bytes);
    prog_data = (const BINARY*) data;
    while (bytes > AD_PACKET_SIZE)
    {
        AnalogDevicesFormPacket(IspEnvironment, 'W', AD_PACKET_SIZE, address, prog_data, &packet);
        AnalogDevicesSendPacket(IspEnvironment, &packet);
        address += AD_PACKET_SIZE;
        prog_data += AD_PACKET_SIZE;
        bytes -= AD_PACKET_SIZE;
        DebugPrintf(2, ".");
    }
    if (bytes > 0)
    {
        AnalogDevicesFormPacket(IspEnvironment, 'W', bytes, address, prog_data, &packet);
        AnalogDevicesSendPacket(IspEnvironment, &packet);
        DebugPrintf(2, ".");
    }
}

/***************************** AnalogDevicesDownload ********************/
/**  Perform the download into an Analog Devices micro.  As a quick and
* dirty hack against flash relocations at 0x80000
* \return 0 if ok, error code else
* \ToDo: possible to implement the return value instead of calling
* exit() in sub-functions
*/
int AnalogDevicesDownload(ISP_ENVIRONMENT *IspEnvironment)
{
    AnalogDevicesSync(IspEnvironment);
    AnalogDevicesErase(IspEnvironment);
    if (IspEnvironment->BinaryLength > 0x80000)
    {
        DebugPrintf(2, "Note:  Flash remapped 0x80000 to 0.\n");
        AnalogDevicesWrite(IspEnvironment, IspEnvironment->BinaryContent + 0x80000, 0, IspEnvironment->BinaryLength-0x80000);
    }
    else
    {
        AnalogDevicesWrite(IspEnvironment, IspEnvironment->BinaryContent, 0, IspEnvironment->BinaryLength);
    }
    return (0);
}
#endif // AD_SUPPORT