Forum: Compiler & IDEs Suche Hilfe bei Optimierung Bootloader


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich habe mir einen Bootloader geschrieben, der soweit prima 
Funktioniert.
Nur verbraucht er die vollen 2k und ich könnte etwas mehr Flash fürs 
Programm brauchen. Daher mal eine Bitte, ob ihr drüber sehen könntet und 
mir sagen, wo man optimieren könnte.

Plattform ist ein ATmega32 mit avr gcc.

Das Protokoll ist vorgegeben und nicht veränderbar!
Alle Daten werden als ASCII übertragen, eine Zahl 0x01 also als "01" und 
somit 2 char.

Das ganze läuft ohne Interrupts (Deaktiviert)

Der Bootloader kann direkt vom Programm oder beim Start des Controllers 
aufgerufen werden. Die Unterscheidung mache ich über die Reset source.

Ist es ein boot nach einem Normalen Reset, prüfe ich die Programm 
Checksumme (steht im EEPROM) und führe das Programm aus, sofern diese ok 
ist, sonst bleibe ich im Bootloader.

Protokoll ist erstes Zeichen "@", dann Empfänger Adresse, sender Adresse 
und dann Kommando mit ggf. Nutzdaten

"@017FT?\r" Frägt zum Beispiel die Modul Version ab.

Achja, fehlende Defines kommen aus dem Makefile, wie "MODULE_ID", was 
einfach "1001" ist.

Kommandos, des Bootloaders, jeweils ohne "@" und Adressen. Diese kann 
man man hier als Beispiel immer mit "@017f" annehmen.

T? => liefert Modul Typ, zur Erkennung der Firmware
RT => Löst einen Reboot per Watchdog aus
BT => schickt page size (0x80 beim ATMega32)
BP => springt direkt ins Programm
BLnnMMMMMMMOOOO => Daten zum schreiben, nn = Anzahl Bytes (nicht 
Zeichen), MM = Daten, OOOO = CRC Summe der Daten
BSnnnnMMMM => Daten Transfer Start, nnnn = ID der Firmware, muss mit 
modul ID übereinstimmen, MMMM = Anzahl Bytes die kommen

Wäre über Tipps echt Dankbar.

Gruss
Juergen

Linker Zeile
1
avr-gcc -mmcu=atmega32 -I.  -MD -MP -MF .dep/bootloader.elf.d -D_TYPE_1001_="1001" -D_HAS_I2C_="_HAS_I2C_" -DMODULE_TYPE="1001" -DMODULE_ID=0x1001 -DF_CPU=16000000UL -DBOOTLOADER_ADR=0x7000 -DMODULE_VERSION=\"0.2.1\" -DADDR_PC_DEF=127 -DADDR_TX_LCD_DEF=102 -DADDR_TX_MAIN_DEF=101  -DADDR_RX_MAIN_DEF=1 -DADDR_BROADCAST_DEF=0 -DMY_DEF_ADDR=1 -DADDR_DEBUG_RS232_DEF=99 -DMAX_FUNC_CNT=32 -std=gnu99 obj/bootloader.o obj/addr_list.o --output bootloader.elf -Wl,-Map=bootloader.map,--cref -Ttext=0x7000

Und der Quellcode des Bootloaders
1
/**
2
 * The common bootloader for RX and TX
3
 */
4
5
#include <avr/io.h>
6
#include <avr/interrupt.h>
7
#include <avr/pgmspace.h>
8
#include <avr/eeprom.h>
9
#include <avr/boot.h>
10
#include <stdlib.h>
11
#include <string.h>
12
#include <util/crc16.h>
13
#include <avr/wdt.h>
14
15
#include "../global_files/avr_helper.h"
16
17
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
18
#warning "compiling mega32"
19
#define RESET_SOURCE_MASK (_BV(JTRF)|_BV(WDRF)|_BV(BORF)|_BV(EXTRF)|_BV(PORF))
20
#elif _TYPE_1003_
21
#warning "compiling mega328(p)"
22
#define RESET_SOURCE_MASK (_BV(WDRF)|_BV(BORF)|_BV(EXTRF)|_BV(PORF))
23
#elif _TYPE_1006_
24
#warning "compiling mega1284p"
25
#define RESET_SOURCE_MASK (_BV(WDRF)|_BV(BORF)|_BV(EXTRF)|_BV(PORF))
26
#else
27
#error "This code is not yet done for this module type"
28
#endif
29
30
char rxbuffer_boot[SPM_PAGESIZE*2+20]; // This is a hughe array, but we are in the bootloader and have all for us
31
char txbuffer[40];
32
uint16_t rxpos_boot = 0;    // anzahl empfangener Zeichen
33
uint8_t txpos = 0;    // anzahl zu sendender Zeichen
34
uint8_t txspos =  0;  // nächstes zu senden
35
uint8_t myAddress = MY_DEF_ADDR;  // unsere Adresse
36
uint32_t page = 0;
37
uint16_t crc = 0;
38
uint16_t shouldCrc = 0;
39
40
/**
41
 * The different states of the bootloader
42
 */
43
enum
44
{
45
    BOOT_CMD = 1,
46
    BOOT_DATA,
47
    BOOT_CHK
48
};
49
50
#define PROGMEM_CRC_ADDR 0x0000
51
52
void (*startMainApp)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */
53
54
/*
55
FUSES =
56
{
57
    .low = 0xFF, // 0xff
58
    .high = ( FUSE_EESAVE & FUSE_SPIEN & FUSE_CKOPT & FUSE_BOOTSZ1 & FUSE_BOOTSZ0 ) // 0xc1
59
};
60
*/
61
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
62
FUSES =
63
{
64
    0xFF,
65
    ( FUSE_EESAVE & FUSE_SPIEN & FUSE_CKOPT & FUSE_BOOTSZ1 & FUSE_BOOTSZ0 ) // 0xc1
66
    //EFUSE_DEFAULT
67
};
68
#elif defined (_TYPE_1006_) || defined (_TYPE_1003_)
69
FUSES =
70
{
71
    0xFD,
72
    ( FUSE_EESAVE & FUSE_SPIEN & FUSE_BOOTSZ1 & FUSE_BOOTSZ0 ), // 0xc1
73
    0xFF
74
    //EFUSE_DEFAULT
75
};
76
#endif
77
void ( *bootloader ) ( void ) = (void(*)(void))0x3800;
78
79
80
/**
81
 * converts AH to uint8_t value
82
 */
83
uint8_t ahtoUint8 ( uint8_t *data )
84
{
85
    uint8_t val = 0;
86
87
    for ( uint8_t i = 0 ; i < 2 ; i++ )
88
    {
89
        if ( ( data[i] >= '0' ) && ( data[i] <= '9' ) )
90
            val = ( val * 16 ) + ( data[i] - '0' );
91
        else if ( ( data[i] >= 'a' ) && ( data[i] <= 'f' ) )
92
            val = ( val * 16 ) + ( data[i] - 'a' + 0x0a );
93
        else
94
            break;
95
    }
96
97
    return val;
98
}
99
100
/**
101
 * converts AH to uint16_t value
102
 */
103
uint16_t ahtoUint16 ( uint8_t *data )
104
{
105
    uint16_t val = 0;
106
107
    for ( uint8_t i = 0 ; i < 4 ; i++ )
108
    {
109
        if ( ( data[i] >= '0' ) && ( data[i] <= '9' ) )
110
            val = ( val * 16 ) + ( data[i] - '0' );
111
        else if ( ( data[i] >= 'a' ) && ( data[i] <= 'f' ) )
112
            val = ( val * 16 ) + ( data[i] - 'a' + 0x0a );
113
        else
114
            break;
115
    }
116
117
    return val;
118
}
119
120
/**
121
 * convert uint16_t to AH. The result is stored in buffer
122
 */
123
char* uint16ToAh ( char *buffer, uint16_t val )
124
{
125
    uint16_t t = val;
126
    uint8_t z;
127
128
    for ( uint8_t i = 0 ; i < 4 ; i++ )
129
    {
130
        z = ( uint8_t ) ( ( t & 0xF000 ) >> 12 );
131
        t = t << 4;
132
133
        if ( ( z >= 0 ) && ( z <= 9 ) )
134
            buffer[i] = z + '0';
135
        else if ( ( z >= 0x0a ) && ( z <= 0x0f ) )
136
            buffer[i] = z + 'a' - 10;
137
    }
138
139
    buffer[4] = 0;
140
141
    return buffer;
142
}
143
144
/**
145
 * convert uint8_t to AH. The result is stored in buffer
146
 */
147
char* uint8ToAh ( char *buffer, uint8_t val )
148
{
149
    uint8_t t = val;
150
    uint8_t z;
151
152
    for ( uint8_t i = 0 ; i < 2 ; i++ )
153
    {
154
        z = ( uint8_t ) ( ( t & 0xF0 ) >> 4 );
155
        t = t << 4;
156
157
        if ( ( z >= 0 ) && ( z <= 9 ) )
158
            buffer[i] = z + '0';
159
        else if ( ( z >= 0x0a ) && ( z <= 0x0f ) )
160
            buffer[i] = z + 'a' - 10;
161
    }
162
163
    buffer[2] = 0;
164
165
    return buffer;
166
}
167
168
/**
169
 * Append Zero terminated string to TX buffer
170
 */
171
void uartPuts(const char *sb)
172
{
173
    int len = strlen(sb);
174
    if ((txpos+len) < sizeof(txbuffer))
175
    {
176
        strcat(txbuffer, sb);
177
        txpos+=strlen(sb);
178
    }
179
}
180
181
#define MSG "BOOT" /*SPM_PAGESIZE "\r\n"*/
182
183
/**
184
 * setup UART comm port
185
 */
186
void initUart(uint16_t baud)
187
{
188
    uint16_t ubrr = ( uint16_t ) ( ( ( uint32_t ) F_CPU / ( 16UL * baud ) ) - 1 );
189
190
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
191
    UBRRH = ( uint8_t ) ( ubrr << 8 ) & 0x0F;
192
    UBRRL = ( uint8_t ) ( ubrr & 0xFF );
193
194
    UCSRA = _BVC(U2X) | _BVC(MPCM);
195
196
    UCSRB = _BV ( RXEN ) | _BV ( TXEN ) | _BVC( RXCIE ) | _BVC(UCSZ2);
197
198
    UCSRC = _BV ( URSEL ) | _BVC(UMSEL) | _BV ( UCSZ1 ) | _BV ( UCSZ0 ) | _BVC(USBS) | _BVC(UPM1) | _BVC(UPM0) | _BVC(UCPOL);
199
#elif defined (_TYPE_1006_) || defined (_TYPE_1003_)
200
    UBRR0H = ( uint8_t ) ( ubrr << 8 ) & 0x0F;
201
    UBRR0L = ( uint8_t ) ( ubrr & 0xFF );
202
203
    UCSR0A = _BVC(U2X0) | _BVC(MPCM0);
204
205
    UCSR0B = _BV ( RXEN0 ) | _BV ( TXEN0 ) | _BVC( RXCIE0 ) | _BVC(UCSZ02);
206
207
    UCSR0C = _BVC ( UMSEL01 ) | _BVC(UMSEL00) | _BV ( UCSZ01 ) | _BV ( UCSZ00 ) | _BVC(USBS0) | _BVC(UPM01) | _BVC(UPM00) | _BVC(UCPOL0);
208
#endif
209
}
210
211
212
213
/**
214
 * write a buffer to flash page.
215
 * This is 1:1 from the avrlibc documentation
216
 */
217
void boot_program_page (uint32_t page, uint8_t *buf)
218
{
219
    uint16_t i;
220
    uint8_t sreg;
221
222
    uint8_t z='0'+page;
223
224
    // Disable interrupts.
225
226
    sreg = SREG;
227
    cli();
228
229
    eeprom_busy_wait ();
230
231
    boot_page_erase (page);
232
    boot_spm_busy_wait ();      // Wait until the memory is erased.
233
234
    for (i=0; i<SPM_PAGESIZE; i+=2)
235
    {
236
        // Set up little-endian word.
237
238
        uint16_t w = *buf++;
239
        w += (*buf++) << 8;
240
241
        boot_page_fill (page + i, w);
242
    }
243
244
    boot_page_write (page);     // Store buffer in flash page.
245
    boot_spm_busy_wait();       // Wait until the memory is written.
246
247
    // Reenable RWW-section again. We need this if we want to jump back
248
    // to the application after bootloading.
249
250
    boot_rww_enable ();
251
252
    // Re-enable interrupts (if they were ever enabled).
253
254
    SREG = sreg;
255
}
256
257
/**
258
 * Send the header of a command
259
 */
260
void sendHeader(uint8_t toAddr)
261
{
262
    char buffer[6];
263
    uartPuts("@");
264
    uartPuts(uint8ToAh(buffer, toAddr));
265
    uartPuts(uint8ToAh(buffer, myAddress));
266
}
267
268
/**
269
 * send module ID
270
 */
271
void sendModuleID(uint8_t toAddr)
272
{
273
    char buffer[6];
274
    sendHeader(toAddr);
275
    uartPuts("T");
276
    uartPuts(uint16ToAh(buffer, MODULE_ID));
277
    uartPuts("\r");
278
}
279
280
/**
281
 * Send flash page size
282
 * This is used by the app to adjust data send
283
 */
284
void sendPageSize(uint8_t toAddr)
285
{
286
    char buffer[6];
287
    sendHeader(toAddr);
288
    uartPuts("BT");
289
    uartPuts(uint16ToAh(buffer, SPM_PAGESIZE));
290
    uartPuts("\r");
291
}
292
293
/**
294
 * Send ACK for reboot command
295
 */
296
void sendRebootACK(uint8_t toAddr)
297
{
298
    char buffer[6];
299
    sendHeader(toAddr);
300
    uartPuts("RTA");
301
    uartPuts("\r");
302
}
303
304
/**
305
 * Send Bot loader ACK
306
 */
307
void sendBootloaderACK(uint8_t toAddr)
308
{
309
    char buffer[6];
310
    sendHeader(toAddr);
311
    uartPuts("BLA");
312
    uartPuts(uint16ToAh(buffer, page));
313
    uartPuts("\r");
314
}
315
316
/**
317
 * Send bootloader NACK
318
 */
319
void sendBootloaderNACKcrc(uint8_t toAddr)
320
{
321
    char buffer[6];
322
    sendHeader(toAddr);
323
    uartPuts("BLNcrc");
324
    uartPuts(uint16ToAh(buffer, crc));
325
    uartPuts(",sh ");
326
    uartPuts(uint16ToAh(buffer, shouldCrc));
327
    uartPuts("\r");
328
}
329
330
/**
331
 * Send Prog CRC
332
 */
333
void sendProgCRC(uint8_t toAddr, uint16_t crc)
334
{
335
    char buffer[6];
336
    sendHeader(toAddr);
337
    uartPuts("CRC");
338
    uartPuts(uint16ToAh(buffer, crc));
339
    uartPuts(uint16ToAh(buffer, eeprom_read_word(PROGMEM_CRC_ADDR)));
340
    uartPuts("\r");
341
}
342
343
void sendBootloaderNACK(uint8_t toAddr)
344
{
345
    char buffer[6];
346
    sendHeader(toAddr);
347
    uartPuts("BLN");
348
    uartPuts("\r");
349
}
350
351
/**
352
 * Send Bootloader start downbload ACK or NACK
353
 */
354
void sendBootloaderStartACK(uint8_t toAddr, uint8_t ack)
355
{
356
    char buffer[6];
357
    sendHeader(toAddr);
358
    if (ack)
359
        uartPuts("BSA\r");
360
    else
361
        uartPuts("BSN\r");
362
}
363
364
365
uint8_t doTransmitUart(void)
366
{
367
    // Handle transmit data
368
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
369
    if ((txpos > 0) && (UCSRA & _BV(UDRE)))
370
#elif defined (_TYPE_1006_) || defined (_TYPE_1003_)
371
    if ((txpos > 0) && (UCSR0A & _BV(UDRE0)))
372
#endif
373
    {
374
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
375
        UDR = txbuffer[txspos];
376
#elif defined (_TYPE_1006_) || defined (_TYPE_1003_)
377
        UDR0 = txbuffer[txspos];
378
#endif
379
        txspos++;
380
381
        if (txspos >= txpos)
382
        {
383
            txspos = 0;
384
            txpos = 0;
385
            txbuffer[0] = 0;
386
        }
387
    }
388
    return txpos;
389
}
390
391
/**
392
 * Caclulate program CRC
393
 * This function calculate the CRC over all program space
394
 */
395
uint16_t calcProgCRC(void)
396
{
397
    crc = 0;
398
    for (uint16_t i=0; i<(BOOTLOADER_ADR); i++)
399
    {
400
        crc = _crc_xmodem_update(crc, pgm_read_byte ( i ));
401
    }
402
    return crc;
403
}
404
405
406
407
/**
408
 * Our main loop
409
 */
410
int main ( void )
411
{
412
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
413
    uint8_t resetSource = MCUCSR & RESET_SOURCE_MASK;
414
    MCUCSR &= ~RESET_SOURCE_MASK;
415
#elif defined (_TYPE_1006_) || defined (_TYPE_1003_)
416
    uint8_t resetSource = MCUSR & RESET_SOURCE_MASK;
417
    MCUSR &= ~RESET_SOURCE_MASK;
418
#endif
419
    wdt_disable(); // Important or we risk a mess
420
    cli();
421
422
423
    initUart ( 38400 );  
424
425
426
    uint8_t status = BOOT_CMD;
427
    uint8_t toAddr = 0x00;
428
    uint8_t fromAddr = myAddress;
429
    uint16_t fwsize = 0;  // size of fw to load
430
    uint16_t rxfwsize = 0;  // size of received size
431
    char buffer[10];
432
    uint8_t cmd_rx = 0;
433
434
    // we just forced into boot loader
435
    // aCK this and report page size !
436
    //sendModuleID(toAddr);
437
    //while (doTransmitUart());
438
    uint16_t iscrc = calcProgCRC();
439
    sendProgCRC(ADDR_BROADCAST_DEF, iscrc);
440
    while(doTransmitUart());
441
 
442
    sendPageSize(ADDR_BROADCAST_DEF);
443
    while (doTransmitUart());  // Empty TX buffer
444
445
    while ( 1 )
446
    {
447
        /*
448
         * IF get this far without any reset source set,
449
         * we are directly called by the main app
450
         * So we have to skip the CRC check and directly continue with the bootloader
451
         */
452
        if (resetSource != 0)  // not jumped in on purpose (from main app)
453
        {
454
            /*
455
             * We have come here after a reset. We now check the CRC of tzhe program
456
             * If it is ok, we will start the main app. If not, we will continue
457
             * with the boot loader
458
             */
459
            if (iscrc == eeprom_read_word(PROGMEM_CRC_ADDR))
460
            {
461
                static uint16_t delay=0xffff;
462
        delay--;
463
                if (delay == 0)
464
                {
465
                    sendHeader(ADDR_BROADCAST_DEF);
466
                    uartPuts("MPCRCOk\r");
467
                    startMainApp();  // reboot into main prog
468
                }
469
            }
470
            else
471
            {
472
                sendHeader(ADDR_BROADCAST_DEF);
473
                //uartPuts("MPCRCErr\r");
474
            }
475
        }
476
        while (doTransmitUart());  // Empty TX buffer
477
478
        // Handle receive data
479
        char c = 0;
480
#if defined(_TYPE_1001_) || defined(_TYPE_1002_) || defined(_TYPE_1004_)
481
        if (UCSRA & _BV(RXC))
482
        {
483
            c = UDR;
484
#elif defined (_TYPE_1006_) || defined (_TYPE_1003_)
485
        if (UCSR0A & _BV(RXC0))
486
        {
487
            c = UDR0;
488
#endif
489
            rxbuffer_boot[rxpos_boot] = c;
490
            rxpos_boot++;
491
            if (c == '\r')
492
      {
493
        resetSource = 0;  // Stay here
494
                cmd_rx = 1;
495
      }
496
            if (rxpos_boot > sizeof(rxbuffer_boot))
497
            {
498
                rxpos_boot=0;
499
            }
500
501
        }
502
        doTransmitUart();
503
        if (cmd_rx == 1)
504
        {
505
            uint8_t error = 0;
506
            // If the syntax is not correct, we will simply ignore the rest and fall through.
507
            if ((rxbuffer_boot[0] == '@') || (rxpos_boot >= 7)) // "@" + <adr> + B + <cmd> + <optional data>
508
            {
509
                toAddr = ahtoUint8((uint8_t*)&rxbuffer_boot[1]);
510
                fromAddr = ahtoUint8((uint8_t*)&rxbuffer_boot[3]);
511
                if ((toAddr == myAddress) || (toAddr == ADDR_BROADCAST_DEF))
512
                {
513
                    // Ok, this is a message for us !
514
                    if ((rxbuffer_boot[5] == 'T') && (rxbuffer_boot[6] == '?'))
515
                    {
516
                        sendModuleID(fromAddr);
517
                    }
518
                    else if ((rxbuffer_boot[5] == 'R') && (rxbuffer_boot[6] == 'T'))
519
                    {
520
                        sendRebootACK(fromAddr);
521
                        wdt_enable(WDTO_250MS);
522
                    }
523
                    else if (0) //(rxbuffer_boot[5] == 'D') && (rxbuffer_boot[6] == 'F'))
524
                    {
525
                        for (uint16_t s=0; s<(0x7fff) ; s+=SPM_PAGESIZE)
526
                        {
527
                            uartPuts("\r\n");
528
                            uartPuts(uint16ToAh(buffer, s));
529
                            uartPuts(">");
530
531
                            while (txpos)
532
                                doTransmitUart();
533
534
535
                            for (uint8_t b=0; b<SPM_PAGESIZE ; b++)
536
                            {
537
                                uartPuts(uint8ToAh(buffer, pgm_read_byte((uint8_t*)(b+s)) ));
538
539
                                while (txpos)
540
                                    doTransmitUart();
541
                            }
542
                        }
543
                    }
544
                    else if ((rxbuffer_boot[5] == 'B') && (rxbuffer_boot[6] == 'T'))
545
                    {
546
                        sendPageSize(fromAddr);
547
                        //startMainApp();
548
                        //bootloader();  // jump back from bootloader to bootloader, does make sense ? :-)
549
                    }
550
                    else if ((rxbuffer_boot[5] == 'B') && (rxbuffer_boot[6] == 'P'))
551
                    {
552
                        //sendPageSize(fromAddr);
553
                        startMainApp();
554
                        //bootloader();  // jump back from bootloader to bootloader, does make sense ? :-)
555
                    }
556
                    else if ((rxbuffer_boot[5] == 'B') && (rxbuffer_boot[6] == 'L') && (rxpos_boot >= 9))
557
                    {
558
                        uint8_t bytecnt = ahtoUint8((uint8_t*)&rxbuffer_boot[7]);
559
                        uint8_t offset = 9;
560
                        if (rxpos_boot >= ((bytecnt*2)+offset+4))
561
                        {
562
                            uint16_t pos;
563
                            // reformat data to binary
564
                            // we reuse the rx array, as we are sure no additional data comes in (we are stuck here, right !)
565
                            // this is save
566
                            crc = 0;
567
                            for (uint16_t i = 0 ; i < bytecnt ; i++)
568
                            {
569
                                pos = (i*2)+offset;
570
                                rxbuffer_boot[i] = ahtoUint8((uint8_t*)&rxbuffer_boot[pos]);
571
                                crc = _crc_xmodem_update(crc, rxbuffer_boot[i]);
572
                            }
573
574
                            rxfwsize+=bytecnt;
575
576
                            // get should crc
577
                            pos = (bytecnt*2)+offset;
578
                            shouldCrc = ahtoUint16((uint8_t*)&rxbuffer_boot[pos]);
579
580
                            // Fill remainder of page
581
                            if (bytecnt < SPM_PAGESIZE)
582
                            {
583
                                for (uint16_t i = bytecnt ; i < SPM_PAGESIZE ; i++)
584
                                    rxbuffer_boot[i] = 0xFF;  // Will be ignored by CPU !
585
                            }
586
587
                            if (crc == shouldCrc)
588
                            {
589
                                // now write flash
590
                                boot_program_page(page, (uint8_t*)rxbuffer_boot);
591
592
                                page+=SPM_PAGESIZE;
593
594
                                if (rxfwsize >= fwsize)
595
                                {
596
                                    uint16_t iscrc = calcProgCRC();
597
                                    eeprom_write_word((uint16_t*)PROGMEM_CRC_ADDR, iscrc);
598
                                    sendProgCRC(ADDR_BROADCAST_DEF, iscrc);
599
                                }
600
601
                                sendBootloaderACK(fromAddr);
602
603
                            }
604
                            else
605
                            {
606
                                sendBootloaderNACKcrc(fromAddr);
607
                            }
608
                        }
609
                        else
610
                        {
611
                            sendBootloaderNACK(fromAddr);
612
                        }
613
                    }
614
                    else if ((rxbuffer_boot[5] == 'B') && (rxbuffer_boot[6] == 'S') && (rxpos_boot >= 15))
615
                    {
616
                        uint16_t id = ahtoUint16((uint8_t*)&rxbuffer_boot[7]);
617
                        if (id == MODULE_ID)
618
                        {
619
                            fwsize = ahtoUint16((uint8_t*)&rxbuffer_boot[11]);
620
                            if (fwsize > 0)
621
                            {
622
                                sendBootloaderStartACK(fromAddr, 1);
623
                                page = 0;
624
                            }
625
                            else
626
                            {
627
                                error = 2;
628
                            }
629
                        }
630
                        else
631
                            error = 1;
632
                        if (error)
633
                        {
634
                            sendBootloaderStartACK(fromAddr, 0);
635
                        }
636
                    }
637
                    else
638
                    {
639
                        //sendUnknownCmd(fromAddr);
640
                    }
641
                }
642
            }
643
            rxpos_boot = 0;
644
            cmd_rx = 0;
645
        }
646
    }
647
}

von Peter II (Gast)


Lesenswert?

Jürgen Sachs schrieb:
> Daher mal eine Bitte, ob ihr drüber sehen könntet und
> mir sagen, wo man optimieren könnte.

du hast zu viel doppelte code

ahtoUint8
ahtoUint16

die 2.Version braucht niemand.

uint16_t x = ahtoUint8(data) << 8 | ahtoUint8(data+2);

auch die ganze Send Funktionen lassen sich bestimmt sparsamer bauen.
Überall ist
1
    sendHeader(toAddr);
2
    uartPuts("BLA");
3
    uartPuts("\r");
drin.

von Jürgen S. (jsachs)


Lesenswert?

Peter II schrieb:
> Jürgen Sachs schrieb:
>> Daher mal eine Bitte, ob ihr drüber sehen könntet und
>> mir sagen, wo man optimieren könnte.
>
> du hast zu viel doppelte code
>
> ahtoUint8
> ahtoUint16
>
> die 2.Version braucht niemand.
>
> uint16_t x = ahtoUint8(data) << 8 | ahtoUint8(data+2);
>
> auch die ganze Send Funktionen lassen sich bestimmt sparsamer bauen.
> Überall ist
>
1
>     sendHeader(toAddr);
2
>     uartPuts("BLA");
3
>     uartPuts("\r");
4
>
> drin.
Das ging ja schnell :-)

Danke, auf die Idee Funktionen zu kombinieren we
1
uint16_t x = ahtoUint8(data) << 8 | ahtoUint8(data+2);
bin ich gar nicht gekommen.
aber wie ich den zweiten Punkt, den du erwähnt hast, vereinfachen soll, 
fehlt mir die Idee.
Die Funktionen sind ja schon eine Zusammenfassung, damit ich sich 
wiederholenden Code zusammen fasse.
Das einzige was mir auffällt, das die ganzen Zeichen nicht aus dem Flash 
kommen, sondern aus dem RAM. Aber davon habe ich ja im Bootloader die 
ganzen 2k für mich alleine.
Ich denke nicht, dass ich da Code größe sparen kann, zudem ich dann eine 
sendUart_p() Funktion machen müsste.

Aber die ersten 282 Byte Einsparung waren ja schon mal recht einfach.
Danke
Juergen

von Karl H. (kbuchegg)


Lesenswert?

Jürgen Sachs schrieb:

> Danke, auf die Idee Funktionen zu kombinieren we
>
1
uint16_t x = ahtoUint8(data) << 8 | ahtoUint8(data+2);
> bin ich gar nicht gekommen.

Solltest du aber.
Immer dann, wenn du COpy&Paste Programmierung machst und nur ein paar 
Zahlenwerte in der neuen Funktion austauscht, solltest du dir Gedanken 
machen, wie man die Dinge zusammenführen kann.

Das hier
1
void uartPuts(const char *sb)
2
{
3
    int len = strlen(sb);
4
    if ((txpos+len) < sizeof(txbuffer))
5
    {
6
        strcat(txbuffer, sb);
7
        txpos+=strlen(sb);
8
    }
9
}
ist auch viel zu kompliziert
1
void uartPuts(const char *sb)
2
{
3
  while( *sb && txpos < sizeof(txbuffer) )
4
    txbuffer[txpos++] = *sb++;
5
}

Nichts gegen die str... Funktionen. Aber oftmals ist es viel einfacher, 
wenn man selber die Dinge in die Hand nimmt.

> Die Funktionen sind ja schon eine Zusammenfassung, damit ich sich
> wiederholenden Code zusammen fasse.

zb ist allen Funktionen gemeinsam, dass zuerst der Header geschickt 
wird, gefolgt von einem String.
Warum muss jetzt in jeder Bearbeitungsfunktion wieder einzeln sendHeader 
und uartPuts aufgerufen werden? Warum gibt es nicht eine Funktion
1
void uartSendResponse( const char* str )
2
{
3
  sendHeader( .. );
4
  uartPuts( str );
5
}

die von jeder Funktion als erstes aufgerufen wird?
1
...
2
void sendPageSize(uint8_t toAddr)
3
{
4
    uartSendRespone( "BT" );
5
    uartPuts(uint16ToAh(buffer, SPM_PAGESIZE));
6
    uartPuts("\r");
7
}
8
...

und schon ist in jeder deiner Bearbeitungsfunktionen ein Funktionsaufruf 
weniger, der speichertechnisch zu Buche schlägt.

Auch kommt das zb
1
    uartPuts("\r");
oft vor. So oft, dass es sich ev. schon lohnen würde, dafür eine eigene 
Funktion zu machen, die genau einen \n in den txBuffer stellt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Müssen die ganzen Fuunktionen wirklich global sein oder tut es auch 
static?

von Karl H. (kbuchegg)


Lesenswert?

Deine ganzen ....ToAh Funktionen.
Alles viel zu kompliziert geschrieben.

Du hast genau 1 Funktion, die einen Wert im Bereich 0 bis 0xF übernimmt 
und das entsprechende Zeichen dafür generiert.
Die Funktionen für uint8_t etc. benutzen dann wieder diese kleinere 
Hilfsfunktion um die 2 Zeichen zu erzeugen, die es für 1 Byte braucht. 
Die Funktion für uint16_t greift wieder auf die Funktion zurück, die 1 
Byte konvertieren kann (1 uint16_t besteht ja aus 2 uint8_t)
1
char hexDigit( uint8_t val )
2
{
3
  if( val > 9 )
4
    return val + 'a' - 10;
5
6
  return val + '0';
7
}
8
9
char* uint8ToAh ( char *buffer, uint8_t val )
10
{
11
  buffer[0] = hexDigit( val >> 4 );
12
  buffer[1] = hexDigit( val & 0x0F );
13
14
  return buffer;
15
}
16
17
char* uint16ToAh ( char *buffer, uint16_t val )
18
{
19
  uint8ToAh( buffer, val >> 8 );
20
  uint8ToAh( buffer + 2, val );
21
22
  return buffer;
23
}


Du musst noch viel mehr in aufeinander aufbauenden Funktionen denken!

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Solche Konstrukte
1
    uartPuts(uint16ToAh(buffer, crc));

... also das Senden eines Zahlenwerts ....

scheinen mir auch hinreichend häufig vorzukommen, so dass sich eigene 
Funktionen
1
  uartPutUint8
2
bzw.
3
  uartPutUint16
lohnen würden.
Das könnte man jetzt natürlich auch so machen, dass man um 1 uint16_t zu 
senden, diesen Fall auf das versenden von 2 uint8_t zurückführt. Wodurch 
wiederrum die Konvertierfunktion für einen uin16_t überflüssig werden 
würde, was wiederrum Speicher spart.

Das befreit dich dann auch vom immer gleichen buffer, den du quer durch 
die Bank nur zur Konvertierung der Zahlen in vielen Funktionen brauchst.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Das könnte man jetzt natürlich auch so machen, dass man um 1 uint16_t zu
> senden, diesen Fall auf das versenden von 2 uint8_t zurückführt. Wodurch
> wiederrum die Konvertierfunktion für einen uin16_t überflüssig werden
> würde, was wiederrum Speicher spart.

Wohingegen das Versenden eines uint8_t wiederrum so einfach ist, dass 
man auch die Konvertierfunktion für einen uint8_t gar nicht mehr 
braucht.
Jetzt wäre es natürlich gut, wenn es eine Funktion gäbe, die ein 
einzelnes Zeichen in den Ausgabepuffer stellt. Ich postuliere mal, dass 
es so eine gibt.
1
void uartPutUint8( uint8_t val )
2
{
3
  uartPutC( hexDigit( val >> 4) );
4
  uartPutC( hexDigit( val & 0x0F );
5
}
6
7
void uartPutUint16( uint16_t val )
8
{
9
  uartPutUint8( val >> 8 );
10
  uartPutUint8( val );
11
}

und die ganzen Konvertierfunktionen für uint8_t bzw. uint16_t sind damit 
obsolet geworden.
Wohingegen sich die Bearbeitungsfunktionen zb so schreiben
1
void sendProgCRC(uint8_t toAddr, uint16_t crc)
2
{
3
    uartSendResponse( "CRC" );
4
    uartPutUint16( crc );
5
    uartPutUint16( eeprom_read_word(PROGMEM_CRC_ADDR) );
6
    uartPutC('\r');
7
}

NB: In deinem Bootloader gibt es ein kunterbuntes Mischmasch, ob du eine 
Ausgabe mit \r, mit \n, oder sogar mit \r\n abschliesst. Einige dich mit 
dir selbst auf eine Variante und zieh sie durch!

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Warum heisst es an manchen Stellen in deinem Programm
1
                            while (txpos)
2
                                doTransmitUart();
und an manch anderen Stellen
1
    while (doTransmitUart());  // Empty TX buffer

beides macht offenbar dasselbe: Es gibt alles aus, was zu diesem 
Zeitpunkt im TxBuffer gesammelt wurde.

Aber dann solltest du das auch immer auf die gleiche Art und Weise 
schreiben!

Und am einfachsten geht das, wenn es eine Funktion
1
void emptyTxBuffer()
2
{
3
  while (doTransmitUart())
4
    ;
5
}
gibt, und du die überall aufrufst, wo du jetzt die while Schleifen 
explizit drinnen hast.

von Karl H. (kbuchegg)


Lesenswert?

1
uint32_t page = 0;

ich weiss zwar nicht, was es mit 'page' genau auf sich hat.
Aber bist du sicher, dass das ein uint32_t sein muss?

Ist dir klar, welche Auswirkungen dieser Datentyp da hat?

Wenn page das aussagt, was ich vermute das es aussagt, dann kann ich mir 
nicht vorstellen, dass es jemals einen AVR geben wird, der den möglichen 
Wertebereich eines uin32_t auch nur annähernd ausnützt.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
>
1
> uint32_t page = 0;
2
>
>
> ich weiss zwar nicht, was es mit 'page' genau auf sich hat.
> Aber bist du sicher, dass das ein uint32_t sein muss?
>
> Ist dir klar, welche Auswirkungen dieser Datentyp da hat?
>
> Wenn page das aussagt, was ich vermute das es aussagt, dann kann ich mir
> nicht vorstellen, dass es jemals einen AVR geben wird, der den möglichen
> Wertebereich eines uin32_t auch nur annähernd ausnützt.

Ah, ja
1
void sendBootloaderACK(uint8_t toAddr)
2
{
3
...
4
    uartPuts(uint16ToAh(buffer, page));
5
...
6
}

soviel dazu :-)

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Wenn page das aussagt, was ich vermute das es aussagt


Muss ich zurücknehmen.
Die Variable ist einfach nur schlecht benannt.
Das ist die Byte-Startadresse im Flash, ab der geschrieben werden soll. 
Ich dachte das wäre eine Page-Nummer.

In dem Fall ist ein uint32_t ok, da es ja AVR mit mehr als 64K Flash 
gibt.

Aber dann sollte die Rückmeldefunktion auch mit einem uint32_t 
operieren!

von Jürgen S. (jsachs)


Lesenswert?

So, ich denke ich habe mal das meiste umgesetzt.
Alles ist doch mit ganz schön arbeit verbunden

das mit der page variable hatte ich damals so übernommen von der 
Funktion aus der avrlibc.

Aktuell ist er schon etwas kleiner geworden
laut avr size
text von 0xf9c auf 0xe60
data von 0x44 auf 0x40
bss von 0x14a auf 0x148
hex von 112a auf fe8

es wird....

Die Optimierung der umwandel Routinen muss ich mir noch mal ansehen

Ich danke euch allen mal für die Hilfe...

Falls ihr noch was findet, immer gerne

Gruss
Juergen

von Peter II (Gast)


Lesenswert?

Jürgen Sachs schrieb:
> Falls ihr noch was findet, immer gerne

kannst du mal die aktuelle Version, mit ordentlicher Formatierung 
einfügen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jürgen Sachs schrieb:
> Falls ihr noch was findet, immer gerne

Falls eine Funktion (statisch) nur 1x verwendet wird und nicht global 
sein muss, dann kann es günstiger sein, sie als static zu deklarieren. 
Dann kann sie inline eingefügt werden was u.U. deutlich Codegröße spart.


> Falls ihr noch was findet, immer gerne

Viele Funktionen verwenden einen Puffer der Speicherklasse auto wie z.B.
1
char buffer[6];
Falls die Funktion nicht reentrant sein muss — was bei deinem Bootloader 
vermutlich der Fall ist — kann es günstifer sein, den Puffer statisch zu 
vergeben, d.h. entweder local static oder modul-statisch.  Das spart 
evtl. das Anlegen eines Frames, was sehr teuer ist.


> Falls ihr noch was findet, immer gerne

Mitunter ist es teuer, Variablen wie rxpos_boot global zu haben.  Wenn 
sie wirklich global sein muss und sich die Verwenung an einer Stelle 
häuft, dann kann es günstiger sein, sie in eine lokale Variable zu 
laden.  Grund sind die Aliasing-Rules von C.


> Falls ihr noch was findet, immer gerne

Ist wirklich notwendig, strlen und strcat zu verwenden?  Im Bootloader 
brauch man doch keine Nebenläufigkeit, und die UART-Routinen bzw. puts 
et al. können blockierend arbeiten, d.h. Zeichenketten werden Zeichen 
für Zeichen ausgegeben bis der auszugebende String aufgebraucht ist.


...und noch ein paar Anmerkungen zum Stil:

> Falls ihr noch was findet, immer gerne

An mehreren Stellen lese ich ein CLI bzw. Operationen mit SREG, aber 
nirgends ist ein SEI.  Wozu das alles?


> Falls ihr noch was findet, immer gerne

Um die Anwendung zu starten springst du nach 0x0 (startMainApp) was 
mehrere Nachteile hat:

1) Die Handware wird nicht initialisiert, d.h. es bleiben Einstellungen 
des Bootloaders bestehen.  Geht die Anwendung davon aus, dass die 
Hardware frisch der Reset initialisiert ist (was fast alle Anwendungen 
tun), dann geht die Anwendung also von falschen Voraussetzungen aus.

Falls möglich, sollte ein Reset immer ein Hard-Reset sein!


> Falls ihr noch was findet, immer gerne

Ad '\r'

Ein Zeilenumbruch in C ist immer ein '\n', d.h. im Code wird man 
immer ein \n ausgeben.  Das '\r' hat im "normalen" Code nix zu suchen, 
denn der Code weiß ja nicht, welches OS die Gegenstelle fährt.  Ergo: 
Falls notwendig, gibt es eine einzige Stelle im Code, welche das \r 
ausgibt, und zwar die zentrale put-Routine selbst, etwa so:
1
    ...
2
#ifdef HOST_WINDOWS
3
    // For MS-Windows terminal: output newline as "\r\n".
4
    if (c == '\n')
5
        uart_putc ('\r');
6
#endif /* HOST_WINDOWS */
7
    uart_putc (c);
8
    ...
In diesem Beispiel wird mit -DHOST_WINDOWS compiliert falls die 
Gegenstelle ein Windows ist.  Falls es zur Compilezeit nicht bekannt 
ist, wird es etwas komplizierter.

Dies muss natürlich nur beachtet werden, wenn die Kommunikation mit 
einem terminal o.ä. geschieht; falls es sich um ein eigenes Protokoll 
mit einer definierten Gegenstelle handelt, ist die Unterscheidung i.d.R. 
nicht notwendig, aber dennoch ist es nicht ratsam, den Code mit \r zu 
übersäen.


> Falls ihr noch was findet, immer gerne

An vielen Stellen stehen Casts wie
1
ahtoUint16((uint8_t*)&rxbuff...
Besser den Cast in ahtoUint16 machen und als Parameter const void* 
übergeben.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> In diesem Beispiel wird mit -DHOST_WINDOWS compiliert falls die
> Gegenstelle ein Windows ist.  Falls es zur Compilezeit nicht bekannt
> ist, wird es etwas komplizierter.

Wieso? Ich beschreibe mal das Szenario für Unix/Linux:

Ich kann es mir nur so vorstellen: Ich starte Kermit oder ein ähnliches 
(Terminal-Emulations-)Programm, welches mit TTYs umgehen kann. Dieses 
Programm wird das TTY, an dem der µC via UART angeschlossen ist, im 
Normalfall im Raw-Mode öffnen, d.h. es findet keine 
CarriageReturn/Newline-Wandlung statt. Oder auch anders gesagt: Das Flag 
ONLCR ist normalerweise am TTY des µCs nicht gesetzt.

Ein Terminal - egal ob es ein echtes Terminal oder eine Emulation ist, 
erwartet standardmäßig immer ein Carriage Return ('\r' und ein Newline 
('\n'), um den Cursor:

  a) an den Anfang der(selben) Zeile zu setzen ('\r')
  b) den Cursor um eine Zeile nach unten zu setzen ('\n')

Das heisst: Auch wenn Dein µC mit einem Unix-/Linux-Host verbunden ist, 
muss der µC ein \r\n schicken, um einen korrekten Zeilenvorschub zu 
erzeugen.

Das ist komplett unabhängig vom Host.

Warum Unix-/Linux-Programme selber lediglich ein bloßes '\n' schicken 
(müssen), um einen korrekten Zeilenvorschub zu erzeugen, liegt an den 
Einstellungen des TTYs, an dem der Benutzer arbeitet: Dieses läuft im 
"Cooked-Mode", d.h. es sind (unter anderem) die Flags OPOST und ONLCR 
eingeschaltet. Damit wird ein Newline ('\n') vom TTY-Treiber in ein 
CarriageReturn + Newline ('\r\n') gewandelt. Aber wohlgemerkt: Das ist 
das TTY, an dem der User arbeitet und nicht das TTY, an dem der µC 
hängt!

Fazit: Es muss keine Unterscheidung bzgl. des Hosts gemacht werden. Wenn 
doch, ist das TTY für den µC am Unix-/Linux falsch eingestellt. 
Kommunikationsprogramme wie Kermit und andere 
Terminal-Emulationsprogramme arbeiten in dieser Hinsicht aber korrekt.

Auch wenn man ein eigenes Flash-Programm (und kein Terminalprogramm) 
verwendet, welches über das TTY mit dem µC kommuniziert und keine 
TTY-Flags ändert: Standardmäßig sind die Flags OPOST und ONLCR 
abgeschaltet auf TTYs, auf denen kein getty (o.ä.) läuft. D.h. die 
Zeichen werden 1:1 im Hostprogramm ankommen. Selbstverständlich muss es 
dann natürlich ein '\r\n' in der Antwort erwarten. Warum sollte es nur 
ein '\n' sein? Nur, weil es unter Linux läuft? Nein, das ist kein 
Kriterium, wenn es um TTY-Kommunikation geht.

: Bearbeitet durch Moderator
von JSachs (Gast)


Lesenswert?

Das Protokoll läuft zwischen dem atmega und einem speziellen Programm 
ab.
Das Protokoll ist nicht für die direkte Ausgabe in einem terminal 
gedacht.

Statt dem CR hätte ich auch ein etx (0x03) nehmen können.

Da das ganze über Bluetooth geht, will ich unnötige Zeichen vermeiden.

Zum Flaschen gibt es eine spezielle Software, die mit dem bootloader 
über Bluetooth spricht.

Im Normalbetrieb sind es 2 atmega, die sich per Bluetooth unterhalten.

Also spielt das CR nur eine untergeordnete Rolle.

Stelen und strcat sind, dank eurer Vorschläge, Vergangenheit.

Warum die Funktionen für den UART nicht blockend sind, hat einfach den 
Hintergrund, das die aus einer frühen Version des hauptcodes kommen und 
da muss das alles per interrupt laufen.

Das cli genutzt wird, aber kein sei hat einfach den Grund sicher zu 
sein, dass der interrupt aus ist, wenn ich z.b. vom Programm in den 
bootloader springe.

Und ich springe an Position 0x0000 da ein reset mich wieder in den 
bootloader bringt. Das ist per Fuse so eingestellt und beabsichtigt. 
Mein Programm initialisiert alles (hoffe ich)

Gruss
Juergen

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Hallo,
ich habe mir das jetzt nicht im Detail angeguckt: hast Du die 
Optimierung (-Os) des Compilers eingeschaltet? Ansonsten kannst Du mit 
--gc-sections noch dafür sorgen, dass nur referenzierter Code auch im 
binary landet. Und mit -ffunction-sections -fdata-sections dafür sorgen, 
dass jede Funktion und jedes Datum in einer eigenen section landet und 
damit nur dann im binary landet, wenn es referenziert wird. Danach würde 
ich dann mal im Mapfile gucken.

mfg Torsten

von Foka M. (foka)


Lesenswert?

Johann L. schrieb:
> An vielen Stellen stehen Casts
> wie
1
ahtoUint16((uint8_t*)&rxbuff...
> Besser den Cast in ahtoUint16
> machen und als Parameter const void* übergeben.

Koenntest Du Beschreiben welchen Vorteil man davon hat?
Eine Signatur mit
1
 void foo(const uint8_t* x);
oder
1
 void foo(const void* x);
fuehrt doch immer dazu, dass ein 16-Bit Wert (auf dem 8-Bit AVR) kopiert 
wird. Was uebersehe ich?

-Foka

von Jürgen S. (jsachs)


Angehängte Dateien:

Lesenswert?

So,

sorry für die späte Rückmeldung, aber es gab leider andere Dinge die 
Vorrang hatten.

Aktuell ist es so, dass ich die Maximale Größe des Bootloaders 
beanspruche, also 4k oder 2000 Worte. Da bin ich irgendwie durcheinander 
gekommen.
Beim compilieren ergibt sich daraus 3750 Bytes
1
avr-size bootloader.elf 
2
   text     data      bss      dec      hex  filename
3
   3678       74      330     4082      ff2  bootloader.elf
4
avr-size --mcu=atmega32 -C bootloader.elf
5
AVR Memory Usage
6
----------------
7
Device: atmega32
8
9
Program:    3750 bytes (11.4% Full)
10
(.text + .data + .bootloader)
11
12
Data:        402 bytes (19.6% Full)
13
(.data + .bss + .noinit)

Gibt es überhaupt eine Chance das auf unter 2000Byte zu bekommen?
Weil das wäre die nächst kleinere Größe des Bootloaders.

In Manchen Setups nehme ich schon eine größere CPU, aber beim manchen 
benutze ich das BCA8-BTM mit einem mega328p, da kann ich nicht größer 
werden.
Der Compile unten ist für einen mega32.

Zur Frage der Compiler Optionen
1
avr-gcc bootloader.cpp -mmcu=atmega32 -I. -MD -MP -MF .dep/bootloader.o.d -D_TYPE_1007_=1007 -DMODULE_TYPE=1007 -DMODULE_ID=0x1007 -DF_CPU=16000000UL -DBOOTLOADER_ADR=0x7000 -DMODULE_VERSION="0.2.1" -DADDR_PC_DEF=127 -DADDR_TX_LCD_DEF=102 -DADDR_TX_MAIN_DEF=101 -DADDR_RX_MAIN_DEF=1 -DADDR_BROADCAST_DEF=0 -DMY_DEF_ADDR=1 -DADDR_DEBUG_RS232_DEF=99 -DMAX_FUNC_CNT=32 -DTARGET_AVR -std=gnu99 -ffunction-sections

Linker
1
avr-gcc -mmcu=atmega32 -I.  -MD -MP -MF .dep/bootloader.elf.d -D_TYPE_1007_="1007" -DMODULE_TYPE="1007" -DMODULE_ID=0x1007 -DF_CPU=16000000UL -DBOOTLOADER_ADR=0x7000 -DMODULE_VERSION=\"0.2.1\" -DADDR_PC_DEF=127 -DADDR_TX_LCD_DEF=102 -DADDR_TX_MAIN_DEF=101  -DADDR_RX_MAIN_DEF=1 -DADDR_BROADCAST_DEF=0 -DMY_DEF_ADDR=1 -DADDR_DEBUG_RS232_DEF=99 -DMAX_FUNC_CNT=32 -DTARGET_AVR -std=gnu99 -ffunction-sections obj/bootloader.o obj/addr_list.o --output bootloader.elf -Wl,-Map=bootloader.map,--cref -Ttext=0x7000

Wenn ich etwas übersehen habe, bitte nochmal Fragen.

Danke
Jürgen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jürgen S. schrieb:
> Gibt es überhaupt eine Chance das auf unter 2000Byte zu bekommen?

Natürlich.

Aber:

../bootloader.cpp:16:40: fatal error: ../global_files/avr_helper.h: No 
such file or directory

von Jürgen S. (jsachs)


Angehängte Dateien:

Lesenswert?

Gut, da fehlen noch 3 Files, die aber nur "Konstanten" und 
Funktionsdefinitionen beinhalten.

Die liegen in einem anderen Ordner, da die aus allen Projekten 
eingebunden werden.

Wenn doch noch was fehlt, bitte kurz Info.

Gruss
Juergen

von Jürgen S. (jsachs)


Lesenswert?

Wichtig, nur mit den Compile Zeilen von mir wird das gehen, da ich hier 
"Defines" mit rein gebe.

Siehe "addr_list.cpp".

Gruss
Juergen

von Steffen R. (steffen_rose)


Lesenswert?

-ffunction-sections

vergrößert den Code (weil der Optimierer manche funktionsübergreifende 
Dinge Optimierungen nicht machen kann) und benötigt man nur, wenn man 
überflüssige Funktionen durch den Linker löschen lassen möchte.

Ich sehe keine Optimierer-Optionen -O?

von Jürgen S. (jsachs)


Lesenswert?

Steffen R. schrieb:
> -ffunction-sections
>
> vergrößert den Code (weil der Optimierer manche funktionsübergreifende
> Dinge Optimierungen nicht machen kann) und benötigt man nur, wenn man
> überflüssige Funktionen durch den Linker löschen lassen möchte.
>
> Ich sehe keine Optimierer-Optionen -O?

Ok, das mit dem fehlenden Optimier Optionen ist ein grobes Faul.
Im Makefile wird zwar eine Konstante gesetzt, aber die nirgends 
umgesetzt :-(
Eingebaut und wir sind ein großes Stück weiter. von 3754 Bytes auf 2254. 
Das ist doch mal eine Ansage :-)

Das -ffunction-section ist auch (wieder) entfernt. macht hier in der Tat 
nicht viel Sinn. und hat fast 80 Byte gebracht.
1
avr-nm -n bootloader.elf > bootloader.sym
2
avr-size bootloader.elf 
3
   text     data      bss      dec      hex  filename
4
   2254       72      330     2656      a60  bootloader.elf
5
avr-size --mcu=atmega32 -C bootloader.elf
6
AVR Memory Usage
7
----------------
8
Device: atmega32
9
10
Program:    2324 bytes (7.1% Full)
11
(.text + .data + .bootloader)
12
13
Data:        400 bytes (19.5% Full)
14
(.data + .bss + .noinit)

Danke für die Hinweise.
Jürgen

von Jürgen S. (jsachs)


Lesenswert?

Damit hat sich fast shcon mein Platz Problem erledigt.
Bisher war mein Code 28700 Byte groß, nach einfügen der fehlenden 
Optimierung 15002. Das hätte ich nicht erwartet.

Die Frage ist, ob noch alles geht :-)

Irgendwann muss ich mein UrMakefile mal die Optimierung entfernt haben.
Jetzt wieder drin.

Aber eventuell bekommt man den Bootloader ja noch unter 2000Bytes.

Aber wie bekannt ist, kosten die ersten 80% nur 20% der Zeit..

Gruss
Juergen

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jürgen S. schrieb:
> Ok, das mit dem fehlenden Optimier Optionen ist ein grobes Faul.

Ich hoffe mal, Du benutzt -Os. -O wäre nämlich zuwenig.

Warum gibst Du Deinen Sourcen eigentlich die Endung .cpp, wenn Du darin 
nur reines C schreibst?

von Steffen R. (steffen_rose)


Lesenswert?

Bei -Os bin ich mir etwas unsicher. Hier sollte man mal verschiedene 
durchtesten. -Os versucht ein Optimum zwischen Laufzeit und Codegröße zu 
schaffen, mit Präferenz auf Codegröße. Hier scheint aber die Codegröße 
viel wichtiger zu sein und die Laufzeit eher keine Rolle zu spielen.

An dieser Stelle fehlt mir aber die Erfahrung bei den kleinen Atmels.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Steffen R. schrieb:
> Hier scheint aber die Codegröße
> viel wichtiger zu sein und die Laufzeit eher keine Rolle zu spielen.

Eben. Und daher ist -Os genau dafür geschaffen.

Dass oft (aber natürlich nicht immer) durch geringe Codegröße auch 
effektiver Code entsteht, ist hier eher als Nebenprodukt zu betrachten.

von Jsachs (Gast)


Lesenswert?

Ich benutze -Os.

Alle anderen codes haben zum teil CPP classes.

Damit ich mein makefile nicht doppelt pflegen muss ist alles CPP.

Jürgen

von Peter D. (peda)


Lesenswert?

Ich benutze auch:
-fno-inline-small-functions

Der AVR-GCC ist etwas zu übereifrig, kleine häufig aufgerufene 
Funktionen zu inlinen.

von Jürgen S. (jsachs)


Lesenswert?

Peter D. schrieb:
> Ich benutze auch:
> -fno-inline-small-functions
>
> Der AVR-GCC ist etwas zu übereifrig, kleine häufig aufgerufene
> Funktionen zu inlinen.

Hab ich eingefügt, hatte keine Auswirkung.

War ein Versuch Wert ...

Danke
Juergen

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.